mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe804fcf2c | ||
|
|
c7c9b21f79 | ||
|
|
d84f761b5d | ||
|
|
b7d7346ef7 | ||
|
|
cbfe528c5e | ||
|
|
59ea2bd174 | ||
|
|
1c9ae2a079 | ||
|
|
22e39dc18f | ||
|
|
4276869033 | ||
|
|
c902ad8c87 | ||
|
|
10fe493162 | ||
|
|
3f9867512b | ||
|
|
8ab694efcc | ||
|
|
4977c4ac43 |
18
README.md
18
README.md
@@ -99,22 +99,16 @@ Go to `backend` directories, install node dependencies:
|
||||
pnpm i
|
||||
```
|
||||
|
||||
1. In `backend`, run the backend server on http://localhost:3000
|
||||
|
||||
babel(old school)
|
||||
|
||||
```
|
||||
pnpm start
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
esbuild(experimental)
|
||||
|
||||
```
|
||||
SUB_STORE_BACKEND_API_PORT=3000 pnpm run --parallel "/^dev:.*/"
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
pnpm bundle:esbuild
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
|
||||
This project is under the GPL V3 LICENSE.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.19.90",
|
||||
"version": "2.20.2",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -13,6 +13,7 @@ import getQXParser from './peggy/qx';
|
||||
import getTrojanURIParser from './peggy/trojan-uri';
|
||||
import $ from '@/core/app';
|
||||
import JSON5 from 'json5';
|
||||
import YAML from '@/utils/yaml';
|
||||
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
@@ -1130,15 +1131,21 @@ function URI_Trojan() {
|
||||
function Clash_All() {
|
||||
const name = 'Clash Parser';
|
||||
const test = (line) => {
|
||||
let proxy;
|
||||
try {
|
||||
JSON5.parse(line);
|
||||
proxy = JSON5.parse(line);
|
||||
} catch (e) {
|
||||
return false;
|
||||
proxy = YAML.parse(line);
|
||||
}
|
||||
return true;
|
||||
return !!proxy?.type;
|
||||
};
|
||||
const parse = (line) => {
|
||||
const proxy = JSON5.parse(line);
|
||||
let proxy;
|
||||
try {
|
||||
proxy = JSON5.parse(line);
|
||||
} catch (e) {
|
||||
proxy = YAML.parse(line);
|
||||
}
|
||||
if (
|
||||
![
|
||||
'anytls',
|
||||
|
||||
@@ -54,31 +54,31 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/public_key/short_id/block_quic/others)* {
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/public_key/short_id/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
handleTransport();
|
||||
}
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/flow/public_key/short_id/block_quic/others)* {
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/flow/public_key/short_id/block_quic/others)* {
|
||||
proxy.type = "vless";
|
||||
handleTransport();
|
||||
}
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleTransport();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/block_quic/others)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/block_quic/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
https = tag equals "https"i address (username password)? (tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
|
||||
@@ -175,7 +175,8 @@ shadow_tls_sni = comma "shadow-tls-sni" equals match:[^,]+ { proxy["shadow-tls-s
|
||||
shadow_tls_password = comma "shadow-tls-password" equals match:[^,]+ { proxy["shadow-tls-password"] = match.join(""); }
|
||||
|
||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
||||
tls_host = comma sni:("tls-name"/"sni") equals host:domain { proxy.sni = host; }
|
||||
tls_name = comma sni:("tls-name") equals host:domain { proxy.sni = host; }
|
||||
sni = comma sni:("sni") equals host:domain { proxy.sni = host; }
|
||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||
tls_cert_sha256 = comma "tls-cert-sha256" equals match:[^,]+ { proxy["tls-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pubkey-sha256"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
|
||||
@@ -52,31 +52,31 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/public_key/short_id/block_quic/others)* {
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/public_key/short_id/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
handleTransport();
|
||||
}
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/flow/public_key/short_id/block_quic/others)* {
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/flow/public_key/short_id/block_quic/others)* {
|
||||
proxy.type = "vless";
|
||||
handleTransport();
|
||||
}
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleTransport();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/block_quic/others)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/block_quic/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
https = tag equals "https"i address (username password)? (tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
|
||||
@@ -173,7 +173,8 @@ shadow_tls_sni = comma "shadow-tls-sni" equals match:[^,]+ { proxy["shadow-tls-s
|
||||
shadow_tls_password = comma "shadow-tls-password" equals match:[^,]+ { proxy["shadow-tls-password"] = match.join(""); }
|
||||
|
||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
||||
tls_host = comma sni:("tls-name"/"sni") equals host:domain { proxy.sni = host; }
|
||||
tls_name = comma sni:("tls-name") equals host:domain { proxy.sni = host; }
|
||||
sni = comma sni:("sni") equals host:domain { proxy.sni = host; }
|
||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||
tls_cert_sha256 = comma "tls-cert-sha256" equals match:[^,]+ { proxy["tls-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pubkey-sha256"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
|
||||
@@ -371,6 +371,14 @@ export default function Egern_Producer() {
|
||||
};
|
||||
}
|
||||
}
|
||||
if (
|
||||
['ss'].includes(original.type) &&
|
||||
proxy.shadow_tls &&
|
||||
original['udp-port'] > 0 &&
|
||||
original['udp-port'] <= 65535
|
||||
) {
|
||||
proxy['udp_port'] = original['udp-port'];
|
||||
}
|
||||
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
|
||||
@@ -181,7 +181,7 @@ const h1Parser = (proxy, parsedProxy) => {
|
||||
host = `${host}`.split(',').map((i) => i.trim());
|
||||
if (host.length > 0) transport.host = host;
|
||||
}
|
||||
if (!transport.host) return;
|
||||
// if (!transport.host) return;
|
||||
if (proxy['http-path'] && proxy['http-path'] !== '') {
|
||||
const path = proxy['http-path'];
|
||||
if (Array.isArray(path)) {
|
||||
@@ -190,7 +190,7 @@ const h1Parser = (proxy, parsedProxy) => {
|
||||
}
|
||||
if (parsedProxy.tls.insecure)
|
||||
parsedProxy.tls.server_name = transport.host[0];
|
||||
if (transport.host.length === 1) transport.host = transport.host[0];
|
||||
if (transport.host?.length === 1) transport.host = transport.host[0];
|
||||
for (const key of Object.keys(transport.headers)) {
|
||||
const value = transport.headers[key];
|
||||
if (value.length === 1) transport.headers[key] = value[0];
|
||||
@@ -569,6 +569,8 @@ const vlessParser = (proxy = {}) => {
|
||||
// if (['xtls-rprx-vision', ''].includes(proxy.flow)) parsedProxy.flow = proxy.flow;
|
||||
if (proxy.flow != null) parsedProxy.flow = proxy.flow;
|
||||
if (proxy.network === 'ws') wsParser(proxy, parsedProxy);
|
||||
if (proxy.network === 'h2') h2Parser(proxy, parsedProxy);
|
||||
if (proxy.network === 'http') h1Parser(proxy, parsedProxy);
|
||||
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
||||
networkParser(proxy, parsedProxy);
|
||||
tfoParser(proxy, parsedProxy);
|
||||
|
||||
@@ -112,7 +112,6 @@ function replaceArtifact(req, res) {
|
||||
|
||||
async function getArtifact(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY);
|
||||
const artifact = findByName(allArtifacts, name);
|
||||
|
||||
@@ -163,7 +162,6 @@ function createArtifact(req, res) {
|
||||
function updateArtifact(req, res) {
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY);
|
||||
let oldName = req.params.name;
|
||||
oldName = decodeURIComponent(oldName);
|
||||
const artifact = findByName(allArtifacts, oldName);
|
||||
if (artifact) {
|
||||
$.info(`正在更新远程配置:${artifact.name}`);
|
||||
@@ -197,7 +195,6 @@ function updateArtifact(req, res) {
|
||||
|
||||
async function deleteArtifact(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`正在删除远程配置:${name}`);
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY);
|
||||
try {
|
||||
|
||||
@@ -52,7 +52,6 @@ function createCollection(req, res) {
|
||||
function getCollection(req, res) {
|
||||
let { name } = req.params;
|
||||
let { raw } = req.query;
|
||||
name = decodeURIComponent(name);
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const collection = findByName(allCols, name);
|
||||
if (collection) {
|
||||
@@ -84,7 +83,6 @@ function getCollection(req, res) {
|
||||
|
||||
function updateCollection(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
let collection = req.body;
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const oldCol = findByName(allCols, name);
|
||||
@@ -137,7 +135,6 @@ function updateCollection(req, res) {
|
||||
|
||||
function deleteCollection(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`正在删除组合订阅:${name}`);
|
||||
let allCols = $.read(COLLECTIONS_KEY);
|
||||
deleteByName(allCols, name);
|
||||
|
||||
@@ -88,8 +88,6 @@ export default function register($app) {
|
||||
|
||||
async function downloadSubscription(req, res) {
|
||||
let { name, nezhaIndex } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
nezhaIndex = decodeURIComponent(nezhaIndex);
|
||||
|
||||
const useMihomoExternal = req.query.target === 'SurgeMac';
|
||||
|
||||
@@ -140,7 +138,6 @@ async function downloadSubscription(req, res) {
|
||||
$.info(`传入 $options: ${JSON.stringify($options)}`);
|
||||
}
|
||||
if (url) {
|
||||
url = decodeURIComponent(url);
|
||||
$.info(`指定远程订阅 URL: ${url}`);
|
||||
if (!/^https?:\/\//.test(url)) {
|
||||
content = url;
|
||||
@@ -148,32 +145,25 @@ async function downloadSubscription(req, res) {
|
||||
}
|
||||
}
|
||||
if (content) {
|
||||
content = decodeURIComponent(content);
|
||||
$.info(`指定本地订阅: ${content}`);
|
||||
}
|
||||
if (proxy) {
|
||||
proxy = decodeURIComponent(proxy);
|
||||
$.info(`指定远程订阅使用代理/策略 proxy: ${proxy}`);
|
||||
}
|
||||
if (ua) {
|
||||
ua = decodeURIComponent(ua);
|
||||
$.info(`指定远程订阅 User-Agent: ${ua}`);
|
||||
}
|
||||
|
||||
if (mergeSources) {
|
||||
mergeSources = decodeURIComponent(mergeSources);
|
||||
$.info(`指定合并来源: ${mergeSources}`);
|
||||
}
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
ignoreFailedRemoteSub = decodeURIComponent(ignoreFailedRemoteSub);
|
||||
$.info(`指定忽略失败的远程订阅: ${ignoreFailedRemoteSub}`);
|
||||
}
|
||||
if (produceType) {
|
||||
produceType = decodeURIComponent(produceType);
|
||||
$.info(`指定生产类型: ${produceType}`);
|
||||
}
|
||||
if (includeUnsupportedProxy) {
|
||||
includeUnsupportedProxy = decodeURIComponent(includeUnsupportedProxy);
|
||||
$.info(
|
||||
`包含官方/商店版/未续费订阅不支持的协议: ${includeUnsupportedProxy}`,
|
||||
);
|
||||
@@ -362,8 +352,6 @@ async function downloadSubscription(req, res) {
|
||||
|
||||
async function downloadCollection(req, res) {
|
||||
let { name, nezhaIndex } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
nezhaIndex = decodeURIComponent(nezhaIndex);
|
||||
|
||||
const useMihomoExternal = req.query.target === 'SurgeMac';
|
||||
|
||||
@@ -416,21 +404,17 @@ async function downloadCollection(req, res) {
|
||||
}
|
||||
|
||||
if (proxy) {
|
||||
proxy = decodeURIComponent(proxy);
|
||||
$.info(`指定远程订阅使用代理/策略 proxy: ${proxy}`);
|
||||
}
|
||||
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
ignoreFailedRemoteSub = decodeURIComponent(ignoreFailedRemoteSub);
|
||||
$.info(`指定忽略失败的远程订阅: ${ignoreFailedRemoteSub}`);
|
||||
}
|
||||
if (produceType) {
|
||||
produceType = decodeURIComponent(produceType);
|
||||
$.info(`指定生产类型: ${produceType}`);
|
||||
}
|
||||
|
||||
if (includeUnsupportedProxy) {
|
||||
includeUnsupportedProxy = decodeURIComponent(includeUnsupportedProxy);
|
||||
$.info(
|
||||
`包含官方/商店版/未续费订阅不支持的协议: ${includeUnsupportedProxy}`,
|
||||
);
|
||||
|
||||
@@ -51,7 +51,6 @@ function createFile(req, res) {
|
||||
|
||||
async function getFile(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
const reqUA = req.headers['user-agent'] || req.headers['User-Agent'];
|
||||
$.info(`正在下载文件:${name}\n请求 User-Agent: ${reqUA}`);
|
||||
let {
|
||||
@@ -95,42 +94,33 @@ async function getFile(req, res) {
|
||||
$.info(`传入 $options: ${JSON.stringify($options)}`);
|
||||
}
|
||||
if (url) {
|
||||
url = decodeURIComponent(url);
|
||||
$.info(`指定远程文件 URL: ${url}`);
|
||||
}
|
||||
if (proxy) {
|
||||
proxy = decodeURIComponent(proxy);
|
||||
$.info(`指定远程订阅使用代理/策略 proxy: ${proxy}`);
|
||||
}
|
||||
if (ua) {
|
||||
ua = decodeURIComponent(ua);
|
||||
$.info(`指定远程文件 User-Agent: ${ua}`);
|
||||
}
|
||||
if (subInfoUrl) {
|
||||
subInfoUrl = decodeURIComponent(subInfoUrl);
|
||||
$.info(`指定获取流量的 subInfoUrl: ${subInfoUrl}`);
|
||||
}
|
||||
if (subInfoUserAgent) {
|
||||
subInfoUserAgent = decodeURIComponent(subInfoUserAgent);
|
||||
$.info(`指定获取流量的 subInfoUserAgent: ${subInfoUserAgent}`);
|
||||
}
|
||||
if (content) {
|
||||
content = decodeURIComponent(content);
|
||||
$.info(`指定本地文件: ${content}`);
|
||||
}
|
||||
if (mergeSources) {
|
||||
mergeSources = decodeURIComponent(mergeSources);
|
||||
$.info(`指定合并来源: ${mergeSources}`);
|
||||
}
|
||||
if (ignoreFailedRemoteFile != null && ignoreFailedRemoteFile !== '') {
|
||||
ignoreFailedRemoteFile = decodeURIComponent(ignoreFailedRemoteFile);
|
||||
$.info(`指定忽略失败的远程文件: ${ignoreFailedRemoteFile}`);
|
||||
}
|
||||
if (noCache) {
|
||||
$.info(`指定不使用缓存: ${noCache}`);
|
||||
}
|
||||
if (produceType) {
|
||||
produceType = decodeURIComponent(produceType);
|
||||
$.info(`指定生产类型: ${produceType}`);
|
||||
}
|
||||
|
||||
@@ -225,7 +215,6 @@ async function getFile(req, res) {
|
||||
function getWholeFile(req, res) {
|
||||
let { name } = req.params;
|
||||
let { raw } = req.query;
|
||||
name = decodeURIComponent(name);
|
||||
const allFiles = $.read(FILES_KEY);
|
||||
const file = findByName(allFiles, name);
|
||||
if (file) {
|
||||
@@ -257,7 +246,6 @@ function getWholeFile(req, res) {
|
||||
|
||||
function updateFile(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
let file = req.body;
|
||||
const allFiles = $.read(FILES_KEY);
|
||||
const oldFile = findByName(allFiles, name);
|
||||
@@ -299,7 +287,6 @@ function updateFile(req, res) {
|
||||
|
||||
function deleteFile(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`正在删除文件:${name}`);
|
||||
let allFiles = $.read(FILES_KEY);
|
||||
deleteByName(allFiles, name);
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function serve() {
|
||||
staticFileMiddleware(req, res, next);
|
||||
return;
|
||||
}
|
||||
res.status(403).end('Forbbiden');
|
||||
res.status(404).end();
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ function createModule(req, res) {
|
||||
|
||||
function getModule(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
const allModules = $.read(MODULES_KEY);
|
||||
const module = findByName(allModules, name);
|
||||
if (module) {
|
||||
@@ -64,7 +63,6 @@ function getModule(req, res) {
|
||||
|
||||
function updateModule(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
let module = req.body;
|
||||
const allModules = $.read(MODULES_KEY);
|
||||
const oldModule = findByName(allModules, name);
|
||||
@@ -92,7 +90,6 @@ function updateModule(req, res) {
|
||||
|
||||
function deleteModule(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`正在删除模块:${name}`);
|
||||
let allModules = $.read(MODULES_KEY);
|
||||
deleteByName(allModules, name);
|
||||
|
||||
@@ -12,7 +12,9 @@ export function failed(resp, error, statusCode) {
|
||||
code: error.code,
|
||||
type: error.type,
|
||||
message: error.message,
|
||||
details: error.details,
|
||||
details: resp.req?.route?.path?.startsWith('/share/')
|
||||
? '详情请查看日志'
|
||||
: error.details,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ async function updateSettings(req, res) {
|
||||
|
||||
export async function updateAvatar() {
|
||||
const settings = $.read(SETTINGS_KEY);
|
||||
const { githubUser: username, syncPlatform } = settings;
|
||||
const { githubUser: username, syncPlatform, githubProxy } = settings;
|
||||
if (username) {
|
||||
if (syncPlatform === 'gitlab') {
|
||||
try {
|
||||
@@ -92,7 +92,9 @@ export async function updateAvatar() {
|
||||
try {
|
||||
const data = await $.http
|
||||
.get({
|
||||
url: `https://api.github.com/users/${encodeURIComponent(
|
||||
url: `${
|
||||
githubProxy ? `${githubProxy}/` : ''
|
||||
}https://api.github.com/users/${encodeURIComponent(
|
||||
username,
|
||||
)}`,
|
||||
headers: {
|
||||
|
||||
@@ -39,10 +39,8 @@ export default function register($app) {
|
||||
// subscriptions API
|
||||
async function getFlowInfo(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
let { url } = req.query;
|
||||
if (url) {
|
||||
url = decodeURIComponent(url);
|
||||
$.info(`指定远程订阅 URL: ${url}`);
|
||||
}
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
@@ -232,6 +230,7 @@ async function getFlowInfo(req, res) {
|
||||
|
||||
function createSubscription(req, res) {
|
||||
const sub = req.body;
|
||||
delete sub.subscriptions;
|
||||
$.info(`正在创建订阅: ${sub.name}`);
|
||||
if (/\//.test(sub.name)) {
|
||||
failed(
|
||||
@@ -262,9 +261,9 @@ function createSubscription(req, res) {
|
||||
function getSubscription(req, res) {
|
||||
let { name } = req.params;
|
||||
let { raw } = req.query;
|
||||
name = decodeURIComponent(name);
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const sub = findByName(allSubs, name);
|
||||
delete sub.subscriptions;
|
||||
if (sub) {
|
||||
if (raw) {
|
||||
res.set('content-type', 'application/json')
|
||||
@@ -294,8 +293,8 @@ function getSubscription(req, res) {
|
||||
|
||||
function updateSubscription(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name); // the original name
|
||||
let sub = req.body;
|
||||
delete sub.subscriptions;
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const oldSub = findByName(allSubs, name);
|
||||
if (oldSub) {
|
||||
@@ -357,7 +356,6 @@ function updateSubscription(req, res) {
|
||||
|
||||
function deleteSubscription(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`删除订阅:${name}...`);
|
||||
// delete from subscriptions
|
||||
let allSubs = $.read(SUBS_KEY);
|
||||
|
||||
@@ -792,7 +792,6 @@ async function syncAllArtifacts(_, res) {
|
||||
|
||||
async function syncArtifact(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`开始同步远程配置 ${name}...`);
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY);
|
||||
const artifact = findByName(allArtifacts, name);
|
||||
|
||||
@@ -17,7 +17,6 @@ export default function register($app) {
|
||||
|
||||
function deleteToken(req, res) {
|
||||
let { token } = req.params;
|
||||
token = decodeURIComponent(token);
|
||||
$.info(`正在删除:${token}`);
|
||||
let allTokens = $.read(TOKENS_KEY);
|
||||
deleteByName(allTokens, token, 'token');
|
||||
|
||||
@@ -260,37 +260,27 @@ export default async function download(
|
||||
shouldCache = false;
|
||||
}
|
||||
}
|
||||
if (preprocess) {
|
||||
try {
|
||||
const proxies = ProxyUtils.parse(body);
|
||||
if (!Array.isArray(proxies) || proxies.length === 0) {
|
||||
$.error(`URL ${url} 不包含有效节点, 不缓存`);
|
||||
shouldCache = false;
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`URL ${url} 尝试解析节点失败 ${e.message ?? e}, 不缓存`,
|
||||
);
|
||||
shouldCache = false;
|
||||
}
|
||||
}
|
||||
if (shouldCache) {
|
||||
resourceCache.set(id, body);
|
||||
if (customCacheKey) {
|
||||
let shouldWriteCustomCacheKey = true;
|
||||
if (preprocess) {
|
||||
try {
|
||||
const proxies = ProxyUtils.parse(body);
|
||||
if (
|
||||
!Array.isArray(proxies) ||
|
||||
proxies.length === 0
|
||||
) {
|
||||
$.error(
|
||||
`URL ${url} 不包含有效节点\n不写入自定义缓存 ${$arguments?.cacheKey}`,
|
||||
);
|
||||
shouldWriteCustomCacheKey = false;
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`URL ${url} 尝试解析节点失败 ${
|
||||
e.message ?? e
|
||||
}\n不写入自定义缓存 ${$arguments?.cacheKey}`,
|
||||
);
|
||||
shouldWriteCustomCacheKey = false;
|
||||
}
|
||||
}
|
||||
if (shouldWriteCustomCacheKey) {
|
||||
$.info(
|
||||
`URL ${url}\n写入自定义缓存 ${$arguments?.cacheKey}`,
|
||||
);
|
||||
$.write(body, customCacheKey);
|
||||
}
|
||||
$.info(
|
||||
`URL ${url}\n写入自定义缓存 ${$arguments?.cacheKey}`,
|
||||
);
|
||||
$.write(body, customCacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ export function flowTransfer(flow, unit = 'B') {
|
||||
let unitIndex = unitList.indexOf(unit);
|
||||
|
||||
return flow < 1024 || unitIndex === unitList.length - 1
|
||||
? { value: flow.toFixed(1), unit: unit }
|
||||
? { value: (Math.round(flow * 100) / 100).toString(), unit: unit }
|
||||
: flowTransfer(flow / 1024, unitList[++unitIndex]);
|
||||
}
|
||||
|
||||
@@ -342,6 +342,12 @@ export function normalizeFlowHeader(flowHeaders) {
|
||||
) {
|
||||
try {
|
||||
decodedValue = Number(decodedValue).toFixed(0);
|
||||
if (
|
||||
['expire'].includes(key) &&
|
||||
decodedValue <= 0
|
||||
) {
|
||||
decodedValue = '';
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`Failed to convert value for key "${key}=${encodedValue}": ${
|
||||
|
||||
@@ -46,6 +46,7 @@ const ISOFlags = {
|
||||
'🇭🇰': ['HK', 'HKG', 'HKT', 'HKBN', 'HGC', 'WTT', 'CMI'],
|
||||
'🇭🇷': ['HR', 'HRV'],
|
||||
'🇭🇺': ['HU', 'HUN'],
|
||||
'🇮🇶': ['IQ', 'IRQ'], // 伊拉克
|
||||
'🇯🇴': ['JO', 'JOR'],
|
||||
'🇯🇵': ['JP', 'JPN', 'TYO'],
|
||||
'🇰🇪': ['KE', 'KEN'],
|
||||
@@ -99,6 +100,7 @@ const ISOFlags = {
|
||||
'🇸🇬': ['SG', 'SGP'],
|
||||
'🇸🇮': ['SI', 'SVN'],
|
||||
'🇸🇰': ['SK', 'SVK'],
|
||||
'🇹🇬': ['TG', 'TGO'], // 多哥
|
||||
'🇹🇭': ['TH', 'THA'],
|
||||
'🇹🇳': ['TN', 'TUN'],
|
||||
'🇹🇷': ['TR', 'TUR'],
|
||||
@@ -220,6 +222,7 @@ export function getFlag(name) {
|
||||
],
|
||||
'🇭🇷': ['Croatia', '克罗地亚', '克羅地亞'],
|
||||
'🇭🇺': ['Hungary', '匈牙利'],
|
||||
'🇮🇶': ['Iraq', '伊拉克', '巴格达', 'Baghdad'], // 伊拉克
|
||||
'🇯🇴': ['Jordan', '约旦'],
|
||||
'🇯🇵': [
|
||||
'Japan',
|
||||
@@ -338,6 +341,7 @@ export function getFlag(name) {
|
||||
],
|
||||
'🇸🇮': ['Slovenia', '斯洛文尼亚'],
|
||||
'🇸🇰': ['Slovakia', '斯洛伐克'],
|
||||
'🇹🇬': ['Togo', '多哥', '洛美', 'Lomé', 'Lome'], // 多哥
|
||||
'🇹🇭': ['Thailand', '泰国', '泰國', '曼谷'],
|
||||
'🇹🇳': ['Tunisia', '突尼斯'],
|
||||
'🇹🇷': ['Turkey', '土耳其', '伊斯坦布尔', 'Istanbul'],
|
||||
|
||||
@@ -9,7 +9,11 @@ import { SETTINGS_KEY } from '@/constants';
|
||||
export default class Gist {
|
||||
constructor({ token, key, syncPlatform }) {
|
||||
const { isStash, isLoon, isShadowRocket, isQX } = ENV();
|
||||
const { defaultProxy, defaultTimeout: timeout } = $.read(SETTINGS_KEY);
|
||||
const {
|
||||
defaultProxy,
|
||||
defaultTimeout: timeout,
|
||||
githubProxy,
|
||||
} = $.read(SETTINGS_KEY);
|
||||
let proxy = defaultProxy;
|
||||
if ($.env.isNode) {
|
||||
proxy =
|
||||
@@ -63,7 +67,9 @@ export default class Gist {
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36',
|
||||
};
|
||||
this.http = HTTP({
|
||||
baseURL: 'https://api.github.com',
|
||||
baseURL: `${
|
||||
githubProxy ? `${githubProxy}/` : ''
|
||||
}https://api.github.com`,
|
||||
headers: {
|
||||
...this.headers,
|
||||
...(isStash && proxy
|
||||
|
||||
7
backend/src/vendor/express.js
vendored
7
backend/src/vendor/express.js
vendored
@@ -36,6 +36,9 @@ export default function express({ substore: $, port, host }) {
|
||||
|
||||
// adapter
|
||||
app.start = () => {
|
||||
app.get('*', function (req, res) {
|
||||
res.status(404).end();
|
||||
});
|
||||
const listener = app.listen(port, host, () => {
|
||||
const { address, port } = listener.address();
|
||||
$.info(`[BACKEND] listening on ${address}:${port}`);
|
||||
@@ -267,7 +270,7 @@ function extractURL(url) {
|
||||
let hashes = url.slice(url.indexOf('?') + 1).split('&');
|
||||
for (let i = 0; i < hashes.length; i++) {
|
||||
const hash = hashes[i].split('=');
|
||||
query[hash[0]] = hash[1];
|
||||
query[hash[0]] = decodeURIComponent(hash[1]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
@@ -291,7 +294,7 @@ function extractPathParams(pattern, path) {
|
||||
while (path[j] !== '/' && j < path.length) {
|
||||
val.push(path[j++]);
|
||||
}
|
||||
params[key.join('')] = val.join('');
|
||||
params[key.join('')] = decodeURIComponent(val.join(''));
|
||||
} else {
|
||||
if (pattern[i] !== path[j]) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user