mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e09d66060d | ||
|
|
b048ecdfff | ||
|
|
aac72fb9a3 | ||
|
|
baec193e5c | ||
|
|
8fe818f826 | ||
|
|
72286984ec | ||
|
|
27e693c308 | ||
|
|
6cf8080cd3 | ||
|
|
839fcacf63 | ||
|
|
a2e45bcb10 | ||
|
|
ea0eb91691 | ||
|
|
1f0ddf2d28 | ||
|
|
a660c6ff90 |
@@ -26,6 +26,8 @@ Core functionalities:
|
||||
|
||||
### Supported Input Formats
|
||||
|
||||
> ⚠️ Do not use `Shadowrocket` to export URI and then import it as input. It is not a standard URI.
|
||||
|
||||
- [x] URI(SS, SSR, VMess, VLESS, Trojan, Hysteria, Hysteria 2, TUIC v5, WireGuard)
|
||||
- [x] Clash Proxies YAML
|
||||
- [x] Clash Proxy JSON(single line)
|
||||
@@ -33,7 +35,6 @@ Core functionalities:
|
||||
- [x] Loon (SS, SSR, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, WireGuard, VLESS, Hysteria 2)
|
||||
- [x] Surge (SS, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, TUIC, Snell, Hysteria 2, SSH(Password authentication only), External Proxy Program(only for macOS), WireGuard(Surge to Surge))
|
||||
- [x] Surfboard (SS, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, WireGuard(Surfboard to Surfboard))
|
||||
- [x] Shadowrocket (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria 2, TUIC)
|
||||
- [x] Clash.Meta (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria 2, TUIC)
|
||||
- [x] Stash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, TUIC, Juicity, SSH)
|
||||
- [x] Clash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.392",
|
||||
"version": "2.14.406",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -408,20 +408,7 @@ function lastParse(proxy) {
|
||||
proxy['h2-opts'].path = path[0];
|
||||
}
|
||||
}
|
||||
if (proxy.tls && !proxy.sni) {
|
||||
if (proxy.network) {
|
||||
let transportHost = proxy[`${proxy.network}-opts`]?.headers?.Host;
|
||||
transportHost = Array.isArray(transportHost)
|
||||
? transportHost[0]
|
||||
: transportHost;
|
||||
if (transportHost) {
|
||||
proxy.sni = transportHost;
|
||||
}
|
||||
}
|
||||
if (!proxy.sni && !isIP(proxy.server)) {
|
||||
proxy.sni = proxy.server;
|
||||
}
|
||||
}
|
||||
|
||||
// 非 tls, 有 ws/http 传输层, 使用域名的节点, 将设置传输层 Host 防止之后域名解析后丢失域名(不覆盖现有的 Host)
|
||||
if (
|
||||
!proxy.tls &&
|
||||
@@ -448,6 +435,20 @@ function lastParse(proxy) {
|
||||
proxy[`${proxy.network}-opts`].path = [transportPath];
|
||||
}
|
||||
}
|
||||
if (proxy.tls && !proxy.sni) {
|
||||
if (!isIP(proxy.server)) {
|
||||
proxy.sni = proxy.server;
|
||||
}
|
||||
if (!proxy.sni && proxy.network) {
|
||||
let transportHost = proxy[`${proxy.network}-opts`]?.headers?.Host;
|
||||
transportHost = Array.isArray(transportHost)
|
||||
? transportHost[0]
|
||||
: transportHost;
|
||||
if (transportHost) {
|
||||
proxy.sni = transportHost;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (['hysteria', 'hysteria2', 'tuic'].includes(proxy.type)) {
|
||||
if (proxy.ports) {
|
||||
proxy.ports = String(proxy.ports).replace(/\//g, ',');
|
||||
|
||||
@@ -391,12 +391,6 @@ function URI_VMess() {
|
||||
} else {
|
||||
delete proxy.network;
|
||||
}
|
||||
|
||||
// https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml#L413
|
||||
// sni 优先级应高于 host
|
||||
if (proxy.tls && !proxy.sni && transportHost) {
|
||||
proxy.sni = transportHost;
|
||||
}
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
@@ -537,14 +531,13 @@ function URI_VLESS() {
|
||||
if (Object.keys(opts).length > 0) {
|
||||
proxy[`${proxy.network}-opts`] = opts;
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy.tls && !proxy.sni) {
|
||||
if (proxy.network === 'ws') {
|
||||
proxy.sni = proxy['ws-opts']?.headers?.Host;
|
||||
} else if (proxy.network === 'http') {
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
proxy.sni = Array.isArray(httpHost) ? httpHost[0] : httpHost;
|
||||
if (proxy.network === 'kcp') {
|
||||
// mKCP 种子。省略时不使用种子,但不可以为空字符串。建议 mKCP 用户使用 seed。
|
||||
if (params.seed) {
|
||||
proxy.seed = params.seed;
|
||||
}
|
||||
// mKCP 的伪装头部类型。当前可选值有 none / srtp / utp / wechat-video / dtls / wireguard。省略时默认值为 none,即不使用伪装头部,但不可以为空字符串。
|
||||
proxy.headerType = params.headerType || 'none';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,18 +891,7 @@ function Clash_All() {
|
||||
if (['vmess', 'vless'].includes(proxy.type)) {
|
||||
proxy.sni = proxy.servername;
|
||||
delete proxy.servername;
|
||||
if (proxy.tls && !proxy.sni) {
|
||||
if (proxy.network === 'ws') {
|
||||
proxy.sni = proxy['ws-opts']?.headers?.Host;
|
||||
} else if (proxy.network === 'http') {
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
proxy.sni = Array.isArray(httpHost)
|
||||
? httpHost[0]
|
||||
: httpHost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy['server-cert-fingerprint']) {
|
||||
proxy['tls-fingerprint'] = proxy['server-cert-fingerprint'];
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/udp_port/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -200,7 +200,7 @@ vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
||||
method = comma "encrypt-method" equals cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
}
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"idea-cfb"/"none"/"rc2-cfb"/"rc4-md5"/"rc4"/"salsa20"/"seed-cfb"/"xchacha20-ietf-poly1305");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"idea-cfb"/"none"/"rc2-cfb"/"rc4-md5"/"rc4"/"salsa20"/"seed-cfb"/"xchacha20-ietf-poly1305"/"2022-blake3-aes-128-gcm"/"2022-blake3-aes-256-gcm");
|
||||
|
||||
ws = comma "ws" equals flag:bool { obfs.type = "ws"; }
|
||||
ws_headers = comma "ws-headers" equals headers:$[^,]+ {
|
||||
@@ -219,7 +219,7 @@ obfs_host = comma "obfs-host" equals host:domain { obfs.host = host; };
|
||||
obfs_uri = comma "obfs-uri" equals path:uri { obfs.path = path }
|
||||
uri = $[^,]+
|
||||
|
||||
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
|
||||
udp_relay = comma "udp-relay" 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; }
|
||||
ecn = comma "ecn" equals flag:bool { proxy.ecn = flag; }
|
||||
@@ -240,6 +240,7 @@ idle_timeout = comma "idle-timeout" equals match:$[0-9]+ { proxy["idle-timeout"]
|
||||
private_key = comma "private-key" equals match:[^,]+ { proxy["keystore-private-key"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
server_fingerprint = comma "server-fingerprint" equals match:[^,]+ { proxy["server-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
block_quic = comma "block-quic" equals match:[^,]+ { proxy["block-quic"] = match.join(""); }
|
||||
udp_port = comma "udp-port" equals match:$[0-9]+ { proxy["udp-port"] = parseInt(match.trim()); }
|
||||
shadow_tls_version = comma "shadow-tls-version" equals match:$[0-9]+ { proxy["shadow-tls-version"] = parseInt(match.trim()); }
|
||||
shadow_tls_sni = comma "shadow-tls-sni" equals match:[^,]+ { proxy["shadow-tls-sni"] = match.join(""); }
|
||||
shadow_tls_password = comma "shadow-tls-password" equals match:[^,]+ { proxy["shadow-tls-password"] = match.join(""); }
|
||||
|
||||
@@ -39,7 +39,7 @@ start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/udp_port/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -198,7 +198,7 @@ vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
||||
method = comma "encrypt-method" equals cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
}
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"idea-cfb"/"none"/"rc2-cfb"/"rc4-md5"/"rc4"/"salsa20"/"seed-cfb"/"xchacha20-ietf-poly1305");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"idea-cfb"/"none"/"rc2-cfb"/"rc4-md5"/"rc4"/"salsa20"/"seed-cfb"/"xchacha20-ietf-poly1305"/"2022-blake3-aes-128-gcm"/"2022-blake3-aes-256-gcm");
|
||||
|
||||
ws = comma "ws" equals flag:bool { obfs.type = "ws"; }
|
||||
ws_headers = comma "ws-headers" equals headers:$[^,]+ {
|
||||
@@ -217,7 +217,7 @@ obfs_host = comma "obfs-host" equals host:domain { obfs.host = host; };
|
||||
obfs_uri = comma "obfs-uri" equals path:uri { obfs.path = path }
|
||||
uri = $[^,]+
|
||||
|
||||
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
|
||||
udp_relay = comma "udp-relay" 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; }
|
||||
ecn = comma "ecn" equals flag:bool { proxy.ecn = flag; }
|
||||
@@ -238,6 +238,7 @@ idle_timeout = comma "idle-timeout" equals match:$[0-9]+ { proxy["idle-timeout"]
|
||||
private_key = comma "private-key" equals match:[^,]+ { proxy["keystore-private-key"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
server_fingerprint = comma "server-fingerprint" equals match:[^,]+ { proxy["server-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
block_quic = comma "block-quic" equals match:[^,]+ { proxy["block-quic"] = match.join(""); }
|
||||
udp_port = comma "udp-port" equals match:$[0-9]+ { proxy["udp-port"] = parseInt(match.trim()); }
|
||||
shadow_tls_version = comma "shadow-tls-version" equals match:$[0-9]+ { proxy["shadow-tls-version"] = parseInt(match.trim()); }
|
||||
shadow_tls_sni = comma "shadow-tls-sni" equals match:[^,]+ { proxy["shadow-tls-sni"] = match.join(""); }
|
||||
shadow_tls_password = comma "shadow-tls-password" equals match:[^,]+ { proxy["shadow-tls-password"] = match.join(""); }
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function Surge_Producer() {
|
||||
}
|
||||
switch (proxy.type) {
|
||||
case 'ss':
|
||||
return shadowsocks(proxy);
|
||||
return shadowsocks(proxy, opts['include-unsupported-proxy']);
|
||||
case 'trojan':
|
||||
return trojan(proxy);
|
||||
case 'vmess':
|
||||
@@ -51,7 +51,7 @@ export default function Surge_Producer() {
|
||||
return { produce };
|
||||
}
|
||||
|
||||
function shadowsocks(proxy) {
|
||||
function shadowsocks(proxy, includeUnsupportedProxy) {
|
||||
const result = new Result(proxy);
|
||||
result.append(`${proxy.name}=${proxy.type},${proxy.server},${proxy.port}`);
|
||||
if (!proxy.cipher) {
|
||||
@@ -85,6 +85,9 @@ function shadowsocks(proxy) {
|
||||
'chacha20',
|
||||
'chacha20-ietf',
|
||||
'none',
|
||||
...(includeUnsupportedProxy
|
||||
? ['2022-blake3-aes-128-gcm', '2022-blake3-aes-256-gcm']
|
||||
: []),
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||
@@ -122,6 +125,8 @@ function shadowsocks(proxy) {
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
|
||||
// udp-port
|
||||
result.appendIfPresent(`,udp-port=${proxy['udp-port']}`, 'udp-port');
|
||||
|
||||
// test-url
|
||||
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
|
||||
|
||||
@@ -20,10 +20,16 @@ export default function SurgeMac_Producer() {
|
||||
try {
|
||||
return surge_Producer.produce(proxy, type, opts);
|
||||
} catch (e) {
|
||||
$.log(
|
||||
`${proxy.name} is not supported on ${targetPlatform}, try to use Mihomo(SurgeMac - External Proxy Program) instead`,
|
||||
);
|
||||
return mihomo(proxy, type, opts);
|
||||
if (opts.useMihomoExternal) {
|
||||
$.log(
|
||||
`${proxy.name} is not supported on ${targetPlatform}, try to use Mihomo(SurgeMac - External Proxy Program) instead`,
|
||||
);
|
||||
return mihomo(proxy, type, opts);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Surge for macOS 可手动指定链接参数 target=SurgeMac 或在 同步配置 中指定 SurgeMac 来启用 mihomo 支援 Surge 本身不支持的协议`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,6 +224,18 @@ export default function URI_Producer() {
|
||||
vlessTransportServiceName,
|
||||
)}`;
|
||||
}
|
||||
if (proxy.network === 'kcp') {
|
||||
if (proxy.seed) {
|
||||
vlessTransport += `&seed=${encodeURIComponent(
|
||||
proxy.seed,
|
||||
)}`;
|
||||
}
|
||||
if (proxy.headerType) {
|
||||
vlessTransport += `&headerType=${encodeURIComponent(
|
||||
proxy.headerType,
|
||||
)}`;
|
||||
}
|
||||
}
|
||||
|
||||
result = `vless://${proxy.uuid}@${proxy.server}:${
|
||||
proxy.port
|
||||
|
||||
@@ -130,6 +130,15 @@ async function doSync() {
|
||||
try {
|
||||
if (artifact.sync && artifact.source) {
|
||||
$.info(`正在同步云配置:${artifact.name}...`);
|
||||
|
||||
const useMihomoExternal =
|
||||
artifact.platform === 'SurgeMac';
|
||||
|
||||
if (useMihomoExternal) {
|
||||
$.info(
|
||||
`手动指定了 target 为 SurgeMac, 将使用 Mihomo External`,
|
||||
);
|
||||
}
|
||||
const output = await produceArtifact({
|
||||
type: artifact.type,
|
||||
name: artifact.source,
|
||||
@@ -137,6 +146,7 @@ async function doSync() {
|
||||
produceOpts: {
|
||||
'include-unsupported-proxy':
|
||||
artifact.includeUnsupportedProxy,
|
||||
useMihomoExternal,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ async function downloadSubscription(req, res) {
|
||||
name = decodeURIComponent(name);
|
||||
nezhaIndex = decodeURIComponent(nezhaIndex);
|
||||
|
||||
const useMihomoExternal = req.query.target === 'SurgeMac';
|
||||
|
||||
const platform =
|
||||
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
|
||||
|
||||
@@ -122,6 +124,10 @@ async function downloadSubscription(req, res) {
|
||||
$.info(`包含不支持的节点: ${includeUnsupportedProxy}`);
|
||||
}
|
||||
|
||||
if (useMihomoExternal) {
|
||||
$.info(`手动指定了 target 为 SurgeMac, 将使用 Mihomo External`);
|
||||
}
|
||||
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const sub = findByName(allSubs, name);
|
||||
if (sub) {
|
||||
@@ -138,6 +144,7 @@ async function downloadSubscription(req, res) {
|
||||
produceType,
|
||||
produceOpts: {
|
||||
'include-unsupported-proxy': includeUnsupportedProxy,
|
||||
useMihomoExternal,
|
||||
},
|
||||
$options,
|
||||
proxy,
|
||||
@@ -178,7 +185,7 @@ async function downloadSubscription(req, res) {
|
||||
if (!$arguments.noFlow) {
|
||||
// forward flow headers
|
||||
const flowInfo = await getFlowHeaders(
|
||||
url,
|
||||
$arguments?.insecure ? `${url}#insecure` : url,
|
||||
$arguments.flowUserAgent,
|
||||
undefined,
|
||||
proxy || sub.proxy,
|
||||
@@ -253,6 +260,8 @@ async function downloadCollection(req, res) {
|
||||
name = decodeURIComponent(name);
|
||||
nezhaIndex = decodeURIComponent(nezhaIndex);
|
||||
|
||||
const useMihomoExternal = req.query.target === 'SurgeMac';
|
||||
|
||||
const platform =
|
||||
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
|
||||
|
||||
@@ -310,6 +319,9 @@ async function downloadCollection(req, res) {
|
||||
includeUnsupportedProxy = decodeURIComponent(includeUnsupportedProxy);
|
||||
$.info(`包含不支持的节点: ${includeUnsupportedProxy}`);
|
||||
}
|
||||
if (useMihomoExternal) {
|
||||
$.info(`手动指定了 target 为 SurgeMac, 将使用 Mihomo External`);
|
||||
}
|
||||
|
||||
if (collection) {
|
||||
try {
|
||||
@@ -321,6 +333,7 @@ async function downloadCollection(req, res) {
|
||||
produceType,
|
||||
produceOpts: {
|
||||
'include-unsupported-proxy': includeUnsupportedProxy,
|
||||
useMihomoExternal,
|
||||
},
|
||||
$options,
|
||||
proxy,
|
||||
@@ -365,7 +378,7 @@ async function downloadCollection(req, res) {
|
||||
}
|
||||
if (!$arguments.noFlow) {
|
||||
const flowInfo = await getFlowHeaders(
|
||||
url,
|
||||
$arguments?.insecure ? `${url}#insecure` : url,
|
||||
$arguments.flowUserAgent,
|
||||
undefined,
|
||||
proxy || sub.proxy || collection.proxy,
|
||||
|
||||
@@ -177,7 +177,20 @@ async function compareCollection(req, res) {
|
||||
try {
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const collection = req.body;
|
||||
const subnames = collection.subscriptions;
|
||||
const subnames = [...collection.subscriptions];
|
||||
let subscriptionTags = collection.subscriptionTags;
|
||||
if (Array.isArray(subscriptionTags) && subscriptionTags.length > 0) {
|
||||
allSubs.forEach((sub) => {
|
||||
if (
|
||||
Array.isArray(sub.tag) &&
|
||||
sub.tag.length > 0 &&
|
||||
!subnames.includes(sub.name) &&
|
||||
sub.tag.some((tag) => subscriptionTags.includes(tag))
|
||||
) {
|
||||
subnames.push(sub.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
const results = {};
|
||||
const errors = {};
|
||||
await Promise.all(
|
||||
|
||||
@@ -57,9 +57,25 @@ async function getFlowInfo(req, res) {
|
||||
!['localFirst', 'remoteFirst'].includes(sub.mergeSources)
|
||||
) {
|
||||
if (sub.subUserinfo) {
|
||||
success(res, {
|
||||
...parseFlowHeaders(sub.subUserinfo),
|
||||
});
|
||||
try {
|
||||
success(res, {
|
||||
...parseFlowHeaders(sub.subUserinfo),
|
||||
});
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`Failed to parse flow info for local subscription ${name}: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
failed(
|
||||
res,
|
||||
new RequestInvalidError(
|
||||
'NO_FLOW_INFO',
|
||||
'N/A',
|
||||
`Failed to parse flow info`,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
failed(
|
||||
res,
|
||||
@@ -110,17 +126,33 @@ async function getFlowInfo(req, res) {
|
||||
return;
|
||||
}
|
||||
if (sub.subUserinfo) {
|
||||
success(res, {
|
||||
...parseFlowHeaders(sub.subUserinfo),
|
||||
remainingDays: getRmainingDays({
|
||||
resetDay: $arguments.resetDay,
|
||||
startDate: $arguments.startDate,
|
||||
cycleDays: $arguments.cycleDays,
|
||||
}),
|
||||
});
|
||||
try {
|
||||
success(res, {
|
||||
...parseFlowHeaders(sub.subUserinfo),
|
||||
remainingDays: getRmainingDays({
|
||||
resetDay: $arguments.resetDay,
|
||||
startDate: $arguments.startDate,
|
||||
cycleDays: $arguments.cycleDays,
|
||||
}),
|
||||
});
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`Failed to parse flow info for local subscription ${name}: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
failed(
|
||||
res,
|
||||
new RequestInvalidError(
|
||||
'NO_FLOW_INFO',
|
||||
'N/A',
|
||||
`Failed to parse flow info`,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const flowHeaders = await getFlowHeaders(
|
||||
url,
|
||||
$arguments?.insecure ? `${url}#insecure` : url,
|
||||
$arguments.flowUserAgent,
|
||||
undefined,
|
||||
sub.proxy,
|
||||
|
||||
@@ -190,7 +190,20 @@ async function produceArtifact({
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const collection = findByName(allCols, name);
|
||||
if (!collection) throw new Error(`找不到组合订阅 ${name}`);
|
||||
const subnames = collection.subscriptions;
|
||||
const subnames = [...collection.subscriptions];
|
||||
let subscriptionTags = collection.subscriptionTags;
|
||||
if (Array.isArray(subscriptionTags) && subscriptionTags.length > 0) {
|
||||
allSubs.forEach((sub) => {
|
||||
if (
|
||||
Array.isArray(sub.tag) &&
|
||||
sub.tag.length > 0 &&
|
||||
!subnames.includes(sub.name) &&
|
||||
sub.tag.some((tag) => subscriptionTags.includes(tag))
|
||||
) {
|
||||
subnames.push(sub.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
const results = {};
|
||||
const errors = {};
|
||||
let processed = 0;
|
||||
@@ -546,6 +559,16 @@ async function syncArtifacts() {
|
||||
try {
|
||||
if (artifact.sync && artifact.source) {
|
||||
$.info(`正在同步云配置:${artifact.name}...`);
|
||||
|
||||
const useMihomoExternal =
|
||||
artifact.platform === 'SurgeMac';
|
||||
|
||||
if (useMihomoExternal) {
|
||||
$.info(
|
||||
`手动指定了 target 为 SurgeMac, 将使用 Mihomo External`,
|
||||
);
|
||||
}
|
||||
|
||||
const output = await produceArtifact({
|
||||
type: artifact.type,
|
||||
name: artifact.source,
|
||||
@@ -553,6 +576,7 @@ async function syncArtifacts() {
|
||||
produceOpts: {
|
||||
'include-unsupported-proxy':
|
||||
artifact.includeUnsupportedProxy,
|
||||
useMihomoExternal,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -668,12 +692,18 @@ async function syncArtifact(req, res) {
|
||||
}
|
||||
|
||||
try {
|
||||
const useMihomoExternal = artifact.platform === 'SurgeMac';
|
||||
|
||||
if (useMihomoExternal) {
|
||||
$.info(`手动指定了 target 为 SurgeMac, 将使用 Mihomo External`);
|
||||
}
|
||||
const output = await produceArtifact({
|
||||
type: artifact.type,
|
||||
name: artifact.source,
|
||||
platform: artifact.platform,
|
||||
produceOpts: {
|
||||
'include-unsupported-proxy': artifact.includeUnsupportedProxy,
|
||||
useMihomoExternal,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -157,8 +157,13 @@ export default async function download(
|
||||
$.write(cached, customCacheKey);
|
||||
}
|
||||
} else {
|
||||
const insecure = $arguments?.insecure
|
||||
? isNode
|
||||
? { strictSSL: false }
|
||||
: { insecure: true }
|
||||
: undefined;
|
||||
$.info(
|
||||
`Downloading...\nUser-Agent: ${userAgent}\nTimeout: ${requestTimeout}\nProxy: ${proxy}\nURL: ${url}`,
|
||||
`Downloading...\nUser-Agent: ${userAgent}\nTimeout: ${requestTimeout}\nProxy: ${proxy}\nInsecure: ${!!insecure}\nURL: ${url}`,
|
||||
);
|
||||
try {
|
||||
const { body, headers } = await http.get({
|
||||
@@ -167,6 +172,7 @@ export default async function download(
|
||||
...(isLoon && proxy ? { node: proxy } : {}),
|
||||
...(isQX && proxy ? { opts: { policy: proxy } } : {}),
|
||||
...(proxy ? getPolicyDescriptor(proxy) : {}),
|
||||
...(insecure ? insecure : {}),
|
||||
});
|
||||
|
||||
if (headers) {
|
||||
|
||||
@@ -47,6 +47,11 @@ export async function getFlowHeaders(
|
||||
// $.info(`使用缓存的流量信息: ${url}`);
|
||||
flowInfo = cached;
|
||||
} else {
|
||||
const insecure = $arguments?.insecure
|
||||
? $.env.isNode
|
||||
? { strictSSL: false }
|
||||
: { insecure: true }
|
||||
: undefined;
|
||||
const { defaultProxy, defaultFlowUserAgent, defaultTimeout } =
|
||||
$.read(SETTINGS_KEY);
|
||||
let proxy = customProxy || defaultProxy;
|
||||
@@ -64,7 +69,7 @@ export async function getFlowHeaders(
|
||||
$.info(
|
||||
`使用 GET 方法从响应体获取流量信息: ${flowUrl}, User-Agent: ${
|
||||
userAgent || ''
|
||||
}`,
|
||||
}, Insecure: ${!!insecure}, Proxy: ${proxy}`,
|
||||
);
|
||||
const { body } = await http.get({
|
||||
url: flowUrl,
|
||||
@@ -72,6 +77,11 @@ export async function getFlowHeaders(
|
||||
'User-Agent': userAgent,
|
||||
},
|
||||
timeout: requestTimeout,
|
||||
...(proxy ? { proxy } : {}),
|
||||
...(isLoon && proxy ? { node: proxy } : {}),
|
||||
...(isQX && proxy ? { opts: { policy: proxy } } : {}),
|
||||
...(proxy ? getPolicyDescriptor(proxy) : {}),
|
||||
...(insecure ? insecure : {}),
|
||||
});
|
||||
flowInfo = body;
|
||||
} else {
|
||||
@@ -79,7 +89,7 @@ export async function getFlowHeaders(
|
||||
$.info(
|
||||
`使用 HEAD 方法从响应头获取流量信息: ${url}, User-Agent: ${
|
||||
userAgent || ''
|
||||
}, Proxy: ${proxy}`,
|
||||
}, Insecure: ${!!insecure}, Proxy: ${proxy}`,
|
||||
);
|
||||
const { headers } = await http.head({
|
||||
url: url
|
||||
@@ -103,20 +113,23 @@ export async function getFlowHeaders(
|
||||
...(isLoon && proxy ? { node: proxy } : {}),
|
||||
...(isQX && proxy ? { opts: { policy: proxy } } : {}),
|
||||
...(proxy ? getPolicyDescriptor(proxy) : {}),
|
||||
...(insecure ? insecure : {}),
|
||||
});
|
||||
flowInfo = getFlowField(headers);
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`使用 HEAD 方法从响应头获取流量信息失败: ${url}, User-Agent: ${
|
||||
userAgent || ''
|
||||
}, Proxy: ${proxy}: ${e.message ?? e}`,
|
||||
}, Insecure: ${!!insecure}, Proxy: ${proxy}: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
}
|
||||
if (!flowInfo) {
|
||||
$.info(
|
||||
`使用 GET 方法获取流量信息: ${url}, User-Agent: ${
|
||||
userAgent || ''
|
||||
}, Proxy: ${proxy}`,
|
||||
}, Insecure: ${!!insecure}, Proxy: ${proxy}`,
|
||||
);
|
||||
const { headers } = await http.get({
|
||||
url: url
|
||||
@@ -140,6 +153,7 @@ export async function getFlowHeaders(
|
||||
...(isLoon && proxy ? { node: proxy } : {}),
|
||||
...(isQX && proxy ? { opts: { policy: proxy } } : {}),
|
||||
...(proxy ? getPolicyDescriptor(proxy) : {}),
|
||||
...(insecure ? insecure : {}),
|
||||
});
|
||||
flowInfo = getFlowField(headers);
|
||||
}
|
||||
|
||||
@@ -17,16 +17,16 @@ function retry(fn, content, ...args) {
|
||||
}
|
||||
|
||||
export function safeLoad(content, ...args) {
|
||||
return retry(YAML.safeLoad, content, ...args);
|
||||
return retry(YAML.safeLoad, JSON.parse(JSON.stringify(content)), ...args);
|
||||
}
|
||||
export function load(content, ...args) {
|
||||
return retry(YAML.load, content, ...args);
|
||||
return retry(YAML.load, JSON.parse(JSON.stringify(content)), ...args);
|
||||
}
|
||||
export function safeDump(...args) {
|
||||
return YAML.safeDump(...args);
|
||||
export function safeDump(content, ...args) {
|
||||
return YAML.safeDump(JSON.parse(JSON.stringify(content)), ...args);
|
||||
}
|
||||
export function dump(...args) {
|
||||
return YAML.dump(...args);
|
||||
export function dump(content, ...args) {
|
||||
return YAML.dump(JSON.parse(JSON.stringify(content)), ...args);
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
Reference in New Issue
Block a user