Compare commits

...

9 Commits

17 changed files with 330 additions and 44 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "sub-store",
"version": "2.14.59",
"version": "2.14.69",
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
"main": "src/main.js",
"scripts": {

View File

@@ -63,7 +63,7 @@ function parse(raw) {
return proxies;
}
async function process(proxies, operators = [], targetPlatform) {
async function process(proxies, operators = [], targetPlatform, source) {
for (const item of operators) {
// process script
let script;
@@ -122,6 +122,7 @@ async function process(proxies, operators = [], targetPlatform) {
script,
targetPlatform,
$arguments,
source,
);
} else {
processor = PROXY_PROCESSORS[item.type](item.args || {});
@@ -208,7 +209,7 @@ function lastParse(proxy) {
delete proxy.network;
}
}
if (['trojan', 'tuic', 'hysteria'].includes(proxy.type)) {
if (['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(proxy.type)) {
proxy.tls = true;
}
if (proxy.tls && !proxy.sni) {

View File

@@ -240,7 +240,7 @@ function URI_VMess() {
params.add = server;
}
const proxy = {
name: params.ps ?? params.remark,
name: params.ps ?? params.remarks,
type: 'vmess',
server: params.add,
port: parseInt(getIfPresent(params.port), 10),
@@ -267,9 +267,19 @@ function URI_VMess() {
params.obfs === 'http'
) {
proxy.network = 'http';
} else if (['grpc'].includes(params.net)) {
proxy.network = 'grpc';
}
if (proxy.network) {
let transportHost = params.host ?? params.obfsParam;
try {
const parsedObfs = JSON.parse(transportHost);
const parsedHost = parsedObfs?.Host;
if (parsedHost) {
transportHost = parsedHost;
}
// eslint-disable-next-line no-empty
} catch (e) {}
let transportPath = params.path;
if (proxy.network === 'http') {
@@ -285,10 +295,17 @@ function URI_VMess() {
}
}
if (transportPath || transportHost) {
proxy[`${proxy.network}-opts`] = {
path: getIfNotBlank(transportPath),
headers: { Host: getIfNotBlank(transportHost) },
};
if (['grpc'].includes(proxy.network)) {
proxy[`${proxy.network}-opts`] = {
'grpc-service-name': getIfNotBlank(transportPath),
'_grpc-type': getIfNotBlank(params.type),
};
} else {
proxy[`${proxy.network}-opts`] = {
path: getIfNotBlank(transportPath),
headers: { Host: getIfNotBlank(transportHost) },
};
}
} else {
delete proxy.network;
}
@@ -365,6 +382,10 @@ function URI_VLESS() {
if (params.serviceName) {
opts[`${proxy.network}-service-name`] = params.serviceName;
}
// https://github.com/XTLS/Xray-core/issues/91
if (['grpc'].includes(proxy.network)) {
opts['_grpc-type'] = params.mode || 'gun';
}
if (Object.keys(opts).length > 0) {
proxy[`${proxy.network}-opts`] = opts;
}
@@ -383,6 +404,53 @@ function URI_VLESS() {
};
return { name, test, parse };
}
function URI_Hysteria2() {
const name = 'URI Hysteria2 Parser';
const test = (line) => {
return /^hysteria2:\/\//.test(line);
};
const parse = (line) => {
line = line.split('hysteria2://')[1];
// eslint-disable-next-line no-unused-vars
let [__, password, server, ___, port, addons, name] =
/^(.*?)@(.*?)(:(\d+))?\/?\?(.*?)(?:#(.*?))$/.exec(line);
port = parseInt(`${port}`, 10);
if (isNaN(port)) {
port = 443;
}
password = decodeURIComponent(password);
name = decodeURIComponent(name) ?? `Hysteria2 ${server}:${port}`;
const proxy = {
type: 'hysteria2',
name,
server,
port,
password,
};
const params = {};
for (const addon of addons.split('&')) {
const [key, valueRaw] = addon.split('=');
let value = valueRaw;
value = decodeURIComponent(valueRaw);
params[key] = value;
}
proxy.sni = params.sni;
if (!proxy.sni && params.peer) {
proxy.sni = params.peer;
}
proxy.obfs = params.obfs;
proxy['obfs-password'] = params['obfs-password'];
proxy['skip-cert-verify'] = /(TRUE)|1/i.test(params.insecure);
proxy.tfo = /(TRUE)|1/i.test(params.fastopen);
proxy.fingerprint = params.pinSHA256;
return proxy;
};
return { name, test, parse };
}
// Trojan URI format
function URI_Trojan() {
@@ -423,6 +491,7 @@ function Clash_All() {
'tuic',
'vless',
'hysteria',
'hysteria2',
'wireguard',
].includes(proxy.type)
) {
@@ -762,11 +831,21 @@ function Surge_WireGuard() {
return { name, test, parse };
}
function Surge_Hysteria2() {
const name = 'Surge Hysteria2 Parser';
const test = (line) => {
return /^.*=\s*hysteria2/.test(line.split(',')[0]);
};
const parse = (line) => getSurgeParser().parse(line);
return { name, test, parse };
}
export default [
URI_SS(),
URI_SSR(),
URI_VMess(),
URI_VLESS(),
URI_Hysteria2(),
URI_Trojan(),
Clash_All(),
Surge_SS(),
@@ -776,6 +855,7 @@ export default [
Surge_Snell(),
Surge_Tuic(),
Surge_WireGuard(),
Surge_Hysteria2(),
Surge_Socks5(),
Loon_SS(),
Loon_SSR(),

View File

@@ -32,7 +32,7 @@ const grammars = String.raw`
}
}
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard) {
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2) {
return proxy;
}
@@ -67,7 +67,7 @@ https = tag equals "https" address (username password)? (sni/tls_fingerprint/tls
http = tag equals "http" address (username password)? (fast_open/others)* {
proxy.type = "http";
}
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/others)* {
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/reuse/others)* {
proxy.type = "snell";
// handle obfs
if (obfs.type == "http" || obfs.type === "tls") {
@@ -86,6 +86,9 @@ tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/tls_veri
wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/test_url/others)* {
proxy.type = "wireguard-surge";
}
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/download_bandwidth/others)* {
proxy.type = "hysteria2";
}
socks5 = tag equals "socks5" address (username password)? (fast_open/others)* {
proxy.type = "socks5";
}
@@ -188,11 +191,13 @@ uri = $[^,]+
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
reuse = comma "reuse" equals flag:bool { proxy.reuse = flag; }
tfo = comma "tfo" equals flag:bool { proxy.tfo = flag; }
ip_version = comma "ip-version" equals match:[^,]+ { proxy["ip-version"] = match.join(""); }
section_name = comma "section-name" equals match:[^,]+ { proxy["section-name"] = match.join(""); }
no_error_alert = comma "no-error-alert" equals match:[^,]+ { proxy["no-error-alert"] = match.join(""); }
underlying_proxy = comma "underlying-proxy" equals match:[^,]+ { proxy["underlying-proxy"] = match.join(""); }
download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); }
test_url = comma "test-url" equals match:[^,]+ { proxy["test-url"] = match.join(""); }
token = comma "token" equals match:[^,]+ { proxy.token = match.join(""); }
alpn = comma "alpn" equals match:[^,]+ { proxy.alpn = match.join(""); }

View File

@@ -30,7 +30,7 @@
}
}
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard) {
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2) {
return proxy;
}
@@ -65,7 +65,7 @@ https = tag equals "https" address (username password)? (sni/tls_fingerprint/tls
http = tag equals "http" address (username password)? (fast_open/others)* {
proxy.type = "http";
}
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/others)* {
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/reuse/others)* {
proxy.type = "snell";
// handle obfs
if (obfs.type == "http" || obfs.type === "tls") {
@@ -84,6 +84,9 @@ tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/tls_veri
wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/test_url/others)* {
proxy.type = "wireguard-surge";
}
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/download_bandwidth/others)* {
proxy.type = "hysteria2";
}
socks5 = tag equals "socks5" address (username password)? (fast_open/others)* {
proxy.type = "socks5";
}
@@ -186,11 +189,13 @@ uri = $[^,]+
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
reuse = comma "reuse" equals flag:bool { proxy.reuse = flag; }
tfo = comma "tfo" equals flag:bool { proxy.tfo = flag; }
ip_version = comma "ip-version" equals match:[^,]+ { proxy["ip-version"] = match.join(""); }
section_name = comma "section-name" equals match:[^,]+ { proxy["section-name"] = match.join(""); }
no_error_alert = comma "no-error-alert" equals match:[^,]+ { proxy["no-error-alert"] = match.join(""); }
underlying_proxy = comma "underlying-proxy" equals match:[^,]+ { proxy["underlying-proxy"] = match.join(""); }
download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); }
test_url = comma "test-url" equals match:[^,]+ { proxy["test-url"] = match.join(""); }
token = comma "token" equals match:[^,]+ { proxy.token = match.join(""); }
alpn = comma "alpn" equals match:[^,]+ { proxy.alpn = match.join(""); }

View File

@@ -7,6 +7,7 @@ import lodash from 'lodash';
import $ from '@/core/app';
import { hex_md5 } from '@/vendor/md5';
import { ProxyUtils } from '@/core/proxy-utils';
import env from '@/utils/env';
/**
The rule "(name CONTAINS "🇨🇳") AND (port IN [80, 443])" can be expressed as follows:
@@ -294,7 +295,7 @@ function RegexDeleteOperator(regex) {
1. This function name should be `operator`!
2. Always declare variables before using them!
*/
function ScriptOperator(script, targetPlatform, $arguments) {
function ScriptOperator(script, targetPlatform, $arguments, source) {
return {
name: 'Script Operator',
func: async (proxies) => {
@@ -305,7 +306,7 @@ function ScriptOperator(script, targetPlatform, $arguments) {
script,
$arguments,
);
output = operator(proxies, targetPlatform);
output = operator(proxies, targetPlatform, { source, ...env });
})();
return output;
},
@@ -562,7 +563,7 @@ function TypeFilter(types) {
1. This function name should be `filter`!
2. Always declare variables before using them!
*/
function ScriptFilter(script, targetPlatform, $arguments) {
function ScriptFilter(script, targetPlatform, $arguments, source) {
return {
name: 'Script Filter',
func: async (proxies) => {
@@ -573,7 +574,7 @@ function ScriptFilter(script, targetPlatform, $arguments) {
script,
$arguments,
);
output = filter(proxies, targetPlatform);
output = filter(proxies, targetPlatform, { source, ...env });
})();
return output;
},

View File

@@ -90,10 +90,22 @@ export default function Clash_Producer() {
proxy['http-opts'].headers.Host = [httpHost];
}
}
if (['trojan', 'tuic', 'hysteria'].includes(proxy.type)) {
if (
['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(
proxy.type,
)
) {
delete proxy.tls;
}
delete proxy['tls-fingerprint'];
delete proxy.subName;
delete proxy.collectionName;
if (
['grpc'].includes(proxy.network) &&
proxy[`${proxy.network}-opts`]
) {
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
}
return ' - ' + JSON.stringify(proxy) + '\n';
})
.join('')

View File

@@ -108,11 +108,23 @@ export default function ClashMeta_Producer() {
}
}
if (['trojan', 'tuic', 'hysteria'].includes(proxy.type)) {
if (
['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(
proxy.type,
)
) {
delete proxy.tls;
}
delete proxy['tls-fingerprint'];
delete proxy.subName;
delete proxy.collectionName;
if (
['grpc'].includes(proxy.network) &&
proxy[`${proxy.network}-opts`]
) {
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
}
return ' - ' + JSON.stringify(proxy) + '\n';
})
.join('')

View File

@@ -108,11 +108,23 @@ export default function ShadowRocket_Producer() {
}
}
if (['trojan', 'tuic', 'hysteria'].includes(proxy.type)) {
if (
['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(
proxy.type,
)
) {
delete proxy.tls;
}
delete proxy['tls-fingerprint'];
delete proxy.subName;
delete proxy.collectionName;
if (
['grpc'].includes(proxy.network) &&
proxy[`${proxy.network}-opts`]
) {
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
}
return ' - ' + JSON.stringify(proxy) + '\n';
})
.join('')

View File

@@ -120,10 +120,22 @@ export default function Stash_Producer() {
proxy['http-opts'].headers.Host = [httpHost];
}
}
if (['trojan', 'tuic', 'hysteria'].includes(proxy.type)) {
if (
['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(
proxy.type,
)
) {
delete proxy.tls;
}
delete proxy['tls-fingerprint'];
delete proxy.subName;
delete proxy.collectionName;
if (
['grpc'].includes(proxy.network) &&
proxy[`${proxy.network}-opts`]
) {
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
}
return ' - ' + JSON.stringify(proxy) + '\n';
})
.join('')

View File

@@ -31,6 +31,8 @@ export default function Surge_Producer() {
return tuic(proxy);
case 'wireguard-surge':
return wireguard(proxy);
case 'hysteria2':
return hysteria2(proxy);
}
throw new Error(
`Platform ${targetPlatform} does not support proxy type: ${proxy.type}`,
@@ -71,6 +73,12 @@ function shadowsocks(proxy) {
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
return result.toString();
}
@@ -107,6 +115,12 @@ function trojan(proxy) {
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
return result.toString();
}
@@ -150,6 +164,12 @@ function vmess(proxy) {
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
return result.toString();
}
@@ -182,6 +202,12 @@ function http(proxy) {
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
return result.toString();
}
@@ -216,6 +242,12 @@ function socks5(proxy) {
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
return result.toString();
}
@@ -245,6 +277,12 @@ function snell(proxy) {
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
// reuse
result.appendIfPresent(`,reuse=${proxy['reuse']}`, 'reuse');
@@ -288,6 +326,12 @@ function tuic(proxy) {
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
return result.toString();
}
@@ -304,10 +348,7 @@ function wireguard(proxy) {
`,no-error-alert=${proxy['no-error-alert']}`,
'no-error-alert',
);
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
result.appendIfPresent(
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
'ip-version',
@@ -316,6 +357,64 @@ function wireguard(proxy) {
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
return result.toString();
}
function hysteria2(proxy) {
if (proxy.obfs || proxy['obfs-password']) {
throw new Error(`obfs is unsupported`);
}
const result = new Result(proxy);
result.append(`${proxy.name}=hysteria2,${proxy.server},${proxy.port}`);
result.appendIfPresent(`,password=${proxy.password}`, 'password');
result.appendIfPresent(
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
'ip-version',
);
result.appendIfPresent(
`,no-error-alert=${proxy['no-error-alert']}`,
'no-error-alert',
);
// tls verification
result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
result.appendIfPresent(
`,skip-cert-verify=${proxy['skip-cert-verify']}`,
'skip-cert-verify',
);
result.appendIfPresent(
`,server-cert-fingerprint-sha256=${proxy.fingerprint}`,
'fingerprint',
);
// tfo
result.appendIfPresent(`,tfo=${proxy['fast-open']}`, 'fast-open');
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
// underlying-proxy
result.appendIfPresent(
`,underlying-proxy=${proxy['underlying-proxy']}`,
'underlying-proxy',
);
// download-bandwidth
result.appendIfPresent(
`,download-bandwidth=${`${proxy['down']}`.match(/\d+/)?.[0] || 0}`,
'down',
);
return result.toString();
}

View File

@@ -91,6 +91,16 @@ export default function URI_Producer() {
? vmessTransportHost[0]
: vmessTransportHost;
}
if (['grpc'].includes(proxy.network)) {
result.path =
proxy[`${proxy.network}-opts`]?.[
'grpc-service-name'
];
// https://github.com/XTLS/Xray-core/issues/91
result.type =
proxy[`${proxy.network}-opts`]?.['_grpc-type'] ||
'gun';
}
}
result = 'vmess://' + Base64.encode(JSON.stringify(result));
break;
@@ -141,6 +151,12 @@ export default function URI_Producer() {
let vlessTransport = `&type=${encodeURIComponent(
proxy.network,
)}`;
if (['grpc'].includes(proxy.network)) {
// https://github.com/XTLS/Xray-core/issues/91
vlessTransport += `&mode=${encodeURIComponent(
proxy[`${proxy.network}-opts`]?.['_grpc-type'] || 'gun',
)}`;
}
let vlessTransportServiceName =
proxy[`${proxy.network}-opts`]?.[

View File

@@ -1,7 +1,6 @@
import $ from '@/core/app';
import { ENV } from '@/vendor/open-api';
import { failed, success } from '@/restful/response';
import { version as substoreVersion } from '../../package.json';
import { updateArtifactStore, updateGitHubAvatar } from '@/restful/settings';
import resourceCache from '@/utils/resource-cache';
import {
@@ -12,6 +11,7 @@ import {
import { InternalServerError, RequestInvalidError } from '@/restful/errors';
import Gist from '@/utils/gist';
import migrate from '@/utils/migration';
import env from '@/utils/env';
export default function register($app) {
// utils
@@ -49,19 +49,7 @@ export default function register($app) {
}
function getEnv(req, res) {
const { isNode, isQX, isLoon, isSurge, isStash, isShadowRocket } = ENV();
let backend = 'Node';
if (isNode) backend = 'Node';
if (isQX) backend = 'QX';
if (isLoon) backend = 'Loon';
if (isSurge) backend = 'Surge';
if (isStash) backend = 'Stash';
if (isShadowRocket) backend = 'ShadowRocket';
success(res, {
backend,
version: substoreVersion,
});
success(res, env);
}
async function refresh(_, res) {

View File

@@ -39,6 +39,7 @@ async function compareSub(req, res) {
// add id
original.forEach((proxy, i) => {
proxy.id = i;
proxy.subName = sub.name;
});
// apply processors
@@ -46,6 +47,7 @@ async function compareSub(req, res) {
original,
sub.process || [],
target,
{ [sub.name]: sub },
);
// produce
@@ -82,11 +84,18 @@ async function compareCollection(req, res) {
}
// parse proxies
let currentProxies = ProxyUtils.parse(raw);
currentProxies.forEach((proxy) => {
proxy.subName = sub.name;
proxy.collectionName = collection.name;
});
// apply processors
currentProxies = await ProxyUtils.process(
currentProxies,
sub.process || [],
'JSON',
{ [sub.name]: sub, _collection: collection },
);
results[name] = currentProxies;
} catch (err) {
@@ -110,12 +119,14 @@ async function compareCollection(req, res) {
original.forEach((proxy, i) => {
proxy.id = i;
proxy.collectionName = collection.name;
});
const processed = await ProxyUtils.process(
original,
collection.process || [],
'JSON',
{ _collection: collection },
);
success(res, { original, processed });

View File

@@ -36,11 +36,15 @@ async function produceArtifact({ type, name, platform }) {
}
// parse proxies
let proxies = ProxyUtils.parse(raw);
proxies.forEach((proxy) => {
proxy.subName = sub.name;
});
// apply processors
proxies = await ProxyUtils.process(
proxies,
sub.process || [],
platform,
{ [sub.name]: sub },
);
if (proxies.length === 0) {
throw new Error(`订阅 ${name} 中不含有效节点`);
@@ -51,7 +55,7 @@ async function produceArtifact({ type, name, platform }) {
if (exist[proxy.name]) {
$.notify(
'🌍 Sub-Store',
'⚠️ 订阅包含重复节点!',
`⚠️ 订阅 ${name} 包含重复节点 ${proxy.name}`,
'请仔细检测配置!',
{
'media-url':
@@ -86,11 +90,18 @@ async function produceArtifact({ type, name, platform }) {
}
// parse proxies
let currentProxies = ProxyUtils.parse(raw);
currentProxies.forEach((proxy) => {
proxy.subName = sub.name;
proxy.collectionName = collection.name;
});
// apply processors
currentProxies = await ProxyUtils.process(
currentProxies,
sub.process || [],
platform,
{ [sub.name]: sub, _collection: collection },
);
results[name] = currentProxies;
processed++;
@@ -127,11 +138,16 @@ async function produceArtifact({ type, name, platform }) {
subnames.map((name) => results[name] || []),
);
proxies.forEach((proxy) => {
proxy.collectionName = collection.name;
});
// apply own processors
proxies = await ProxyUtils.process(
proxies,
collection.process || [],
platform,
{ _collection: collection },
);
if (proxies.length === 0) {
throw new Error(`组合订阅 ${name} 中不含有效节点`);
@@ -142,7 +158,7 @@ async function produceArtifact({ type, name, platform }) {
if (exist[proxy.name]) {
$.notify(
'🌍 Sub-Store',
'⚠️ 订阅包含重复节点!',
`⚠️ 组合订阅 ${name} 包含重复节点 ${proxy.name}`,
'请仔细检测配置!',
{
'media-url':

16
backend/src/utils/env.js Normal file
View File

@@ -0,0 +1,16 @@
import { version as substoreVersion } from '../../package.json';
import { ENV } from '@/vendor/open-api';
const { isNode, isQX, isLoon, isSurge, isStash, isShadowRocket } = ENV();
let backend = 'Node';
if (isNode) backend = 'Node';
if (isQX) backend = 'QX';
if (isLoon) backend = 'Loon';
if (isSurge) backend = 'Surge';
if (isStash) backend = 'Stash';
if (isShadowRocket) backend = 'ShadowRocket';
export default {
backend,
version: substoreVersion,
};

View File

@@ -60,7 +60,9 @@ export class OpenAPI {
});
this.root = {};
} else {
this.root = JSON.parse(this.node.fs.readFileSync(`${rootPath}`));
this.root = JSON.parse(
this.node.fs.readFileSync(`${rootPath}`),
);
}
// create a json file with the given name if not exists
@@ -72,9 +74,7 @@ export class OpenAPI {
});
this.cache = {};
} else {
this.cache = JSON.parse(
this.node.fs.readFileSync(`${fpath}`),
);
this.cache = JSON.parse(this.node.fs.readFileSync(`${fpath}`));
}
}
}