mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be25919d81 |
1
.github/workflows/main.yml
vendored
1
.github/workflows/main.yml
vendored
@@ -1,6 +1,5 @@
|
|||||||
name: build
|
name: build
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|||||||
@@ -28,18 +28,14 @@ Core functionalities:
|
|||||||
|
|
||||||
> ⚠️ Do not use `Shadowrocket` to export URI and then import it as input. It is not a standard URI.
|
> ⚠️ Do not use `Shadowrocket` to export URI and then import it as input. It is not a standard URI.
|
||||||
|
|
||||||
- [x] Normal Proxy(`socks5`, `socks5+tls`, `http`, `https`(it's ok))
|
|
||||||
|
|
||||||
example: `socks5+tls://user:pass@ip:port#name`
|
|
||||||
|
|
||||||
- [x] URI(SS, SSR, VMess, VLESS, Trojan, Hysteria, Hysteria 2, TUIC v5, WireGuard)
|
- [x] URI(SS, SSR, VMess, VLESS, Trojan, Hysteria, Hysteria 2, TUIC v5, WireGuard)
|
||||||
- [x] Clash Proxies YAML
|
- [x] Clash Proxies YAML
|
||||||
- [x] Clash Proxy JSON(single line)
|
- [x] Clash Proxy JSON(single line)
|
||||||
- [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5, VLESS)
|
- [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5, VLESS)
|
||||||
- [x] Loon (SS, SSR, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, WireGuard, VLESS, Hysteria 2)
|
- [x] Loon (SS, SSR, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, WireGuard, VLESS, Hysteria 2)
|
||||||
- [x] Surge (Direct, 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] 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] Surfboard (SS, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, WireGuard(Surfboard to Surfboard))
|
||||||
- [x] Clash.Meta (Direct, SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria 2, TUIC, SSH, mieru)
|
- [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] 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)
|
- [x] Clash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sub-store",
|
"name": "sub-store",
|
||||||
"version": "2.16.7",
|
"version": "2.14.433",
|
||||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -27,42 +27,6 @@ function surge_port_hopping(raw) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function URI_PROXY() {
|
|
||||||
// socks5+tls
|
|
||||||
// socks5
|
|
||||||
// http, https(可以这么写)
|
|
||||||
const name = 'URI PROXY Parser';
|
|
||||||
const test = (line) => {
|
|
||||||
return /^(socks5\+tls|socks5|http|https):\/\//.test(line);
|
|
||||||
};
|
|
||||||
const parse = (line) => {
|
|
||||||
// parse url
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
let [__, type, tls, username, password, server, port, query, name] =
|
|
||||||
line.match(
|
|
||||||
/^(socks5|http|http)(\+tls|s)?:\/\/(?:(.*?):(.*?)@)?(.*?):(\d+?)(\?.*?)?(?:#(.*?))?$/,
|
|
||||||
);
|
|
||||||
|
|
||||||
const proxy = {
|
|
||||||
name:
|
|
||||||
name != null
|
|
||||||
? decodeURIComponent(name)
|
|
||||||
: `${type} ${server}:${port}`,
|
|
||||||
type,
|
|
||||||
tls: tls ? true : false,
|
|
||||||
server,
|
|
||||||
port,
|
|
||||||
username:
|
|
||||||
username != null ? decodeURIComponent(username) : undefined,
|
|
||||||
password:
|
|
||||||
password != null ? decodeURIComponent(password) : undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
return proxy;
|
|
||||||
};
|
|
||||||
return { name, test, parse };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
|
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
|
||||||
// reference: https://github.com/shadowsocks/shadowsocks-org/wiki/SIP002-URI-Scheme
|
// reference: https://github.com/shadowsocks/shadowsocks-org/wiki/SIP002-URI-Scheme
|
||||||
function URI_SS() {
|
function URI_SS() {
|
||||||
@@ -82,15 +46,7 @@ function URI_SS() {
|
|||||||
content = content.split('#')[0]; // strip proxy name
|
content = content.split('#')[0]; // strip proxy name
|
||||||
// handle IPV4 and IPV6
|
// handle IPV4 and IPV6
|
||||||
let serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
let serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||||
|
let userInfoStr = Base64.decode(content.split('@')[0]);
|
||||||
let rawUserInfoStr = decodeURIComponent(content.split('@')[0]); // 其实应该分隔之后, 用户名和密码再 decodeURIComponent. 但是问题不大
|
|
||||||
let userInfoStr;
|
|
||||||
if (rawUserInfoStr?.startsWith('2022-blake3-')) {
|
|
||||||
userInfoStr = rawUserInfoStr;
|
|
||||||
} else {
|
|
||||||
userInfoStr = Base64.decode(rawUserInfoStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
let query = '';
|
let query = '';
|
||||||
if (!serverAndPortArray) {
|
if (!serverAndPortArray) {
|
||||||
if (content.includes('?')) {
|
if (content.includes('?')) {
|
||||||
@@ -115,21 +71,16 @@ function URI_SS() {
|
|||||||
userInfoStr = content.split('@')[0];
|
userInfoStr = content.split('@')[0];
|
||||||
serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverAndPort = serverAndPortArray[1];
|
const serverAndPort = serverAndPortArray[1];
|
||||||
const portIdx = serverAndPort.lastIndexOf(':');
|
const portIdx = serverAndPort.lastIndexOf(':');
|
||||||
proxy.server = serverAndPort.substring(0, portIdx);
|
proxy.server = serverAndPort.substring(0, portIdx);
|
||||||
proxy.port = `${serverAndPort.substring(portIdx + 1)}`.match(
|
proxy.port = `${serverAndPort.substring(portIdx + 1)}`.match(
|
||||||
/\d+/,
|
/\d+/,
|
||||||
)?.[0];
|
)?.[0];
|
||||||
let userInfo = userInfoStr.match(/(^.*?):(.*$)/);
|
|
||||||
proxy.cipher = userInfo?.[1];
|
const userInfo = userInfoStr.match(/(^.*?):(.*$)/);
|
||||||
proxy.password = userInfo?.[2];
|
proxy.cipher = userInfo[1];
|
||||||
// if (!proxy.cipher || !proxy.password) {
|
proxy.password = userInfo[2];
|
||||||
// userInfo = rawUserInfoStr.match(/(^.*?):(.*$)/);
|
|
||||||
// proxy.cipher = userInfo?.[1];
|
|
||||||
// proxy.password = userInfo?.[2];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// handle obfs
|
// handle obfs
|
||||||
const idx = content.indexOf('?plugin=');
|
const idx = content.indexOf('?plugin=');
|
||||||
@@ -429,7 +380,6 @@ function URI_VMess() {
|
|||||||
proxy[`${proxy.network}-opts`] = {
|
proxy[`${proxy.network}-opts`] = {
|
||||||
'grpc-service-name': getIfNotBlank(transportPath),
|
'grpc-service-name': getIfNotBlank(transportPath),
|
||||||
'_grpc-type': getIfNotBlank(params.type),
|
'_grpc-type': getIfNotBlank(params.type),
|
||||||
'_grpc-authority': getIfNotBlank(params.authority),
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const opts = {
|
const opts = {
|
||||||
@@ -565,9 +515,6 @@ function URI_VLESS() {
|
|||||||
}
|
}
|
||||||
if (params.serviceName) {
|
if (params.serviceName) {
|
||||||
opts[`${proxy.network}-service-name`] = params.serviceName;
|
opts[`${proxy.network}-service-name`] = params.serviceName;
|
||||||
if (['grpc'].includes(proxy.network) && params.authority) {
|
|
||||||
opts['_grpc-authority'] = params.authority;
|
|
||||||
}
|
|
||||||
} else if (isShadowrocket && params.path) {
|
} else if (isShadowrocket && params.path) {
|
||||||
if (!['ws', 'http', 'h2'].includes(proxy.network)) {
|
if (!['ws', 'http', 'h2'].includes(proxy.network)) {
|
||||||
opts[`${proxy.network}-service-name`] = params.path;
|
opts[`${proxy.network}-service-name`] = params.path;
|
||||||
@@ -929,8 +876,6 @@ function Clash_All() {
|
|||||||
const proxy = JSON.parse(line);
|
const proxy = JSON.parse(line);
|
||||||
if (
|
if (
|
||||||
![
|
![
|
||||||
'mieru',
|
|
||||||
'juicity',
|
|
||||||
'ss',
|
'ss',
|
||||||
'ssr',
|
'ssr',
|
||||||
'vmess',
|
'vmess',
|
||||||
@@ -944,7 +889,6 @@ function Clash_All() {
|
|||||||
'hysteria2',
|
'hysteria2',
|
||||||
'wireguard',
|
'wireguard',
|
||||||
'ssh',
|
'ssh',
|
||||||
'direct',
|
|
||||||
].includes(proxy.type)
|
].includes(proxy.type)
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -1243,14 +1187,6 @@ function Loon_WireGuard() {
|
|||||||
return { name, test, parse };
|
return { name, test, parse };
|
||||||
}
|
}
|
||||||
|
|
||||||
function Surge_Direct() {
|
|
||||||
const name = 'Surge Direct Parser';
|
|
||||||
const test = (line) => {
|
|
||||||
return /^.*=\s*direct/.test(line.split(',')[0]);
|
|
||||||
};
|
|
||||||
const parse = (line) => getSurgeParser().parse(line);
|
|
||||||
return { name, test, parse };
|
|
||||||
}
|
|
||||||
function Surge_SSH() {
|
function Surge_SSH() {
|
||||||
const name = 'Surge SSH Parser';
|
const name = 'Surge SSH Parser';
|
||||||
const test = (line) => {
|
const test = (line) => {
|
||||||
@@ -1430,7 +1366,6 @@ function isIP(ip) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
URI_PROXY(),
|
|
||||||
URI_SS(),
|
URI_SS(),
|
||||||
URI_SSR(),
|
URI_SSR(),
|
||||||
URI_VMess(),
|
URI_VMess(),
|
||||||
@@ -1441,7 +1376,6 @@ export default [
|
|||||||
URI_Hysteria2(),
|
URI_Hysteria2(),
|
||||||
URI_Trojan(),
|
URI_Trojan(),
|
||||||
Clash_All(),
|
Clash_All(),
|
||||||
Surge_Direct(),
|
|
||||||
Surge_SSH(),
|
Surge_SSH(),
|
||||||
Surge_SS(),
|
Surge_SS(),
|
||||||
Surge_VMess(),
|
Surge_VMess(),
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/
|
|||||||
// handle ssr obfs
|
// handle ssr obfs
|
||||||
proxy.obfs = obfs.type;
|
proxy.obfs = obfs.type;
|
||||||
}
|
}
|
||||||
shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs_hostv)? (obfs_ss/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* {
|
shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs_hostv)? (obfs_ss/obfs_host/obfs_uri/fast_open/udp_relay/others)* {
|
||||||
proxy.type = "ss";
|
proxy.type = "ss";
|
||||||
// handle ss obfs
|
// handle ss obfs
|
||||||
if (obfs.type == "http" || obfs.type === "tls") {
|
if (obfs.type == "http" || obfs.type === "tls") {
|
||||||
@@ -120,7 +120,7 @@ port = digits:[0-9]+ {
|
|||||||
method = comma cipher:cipher {
|
method = comma cipher:cipher {
|
||||||
proxy.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"/"auto"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none"/"rc4-md5"/"rc4"/"salsa20"/"xchacha20-ietf-poly1305"/"2022-blake3-aes-128-gcm"/"2022-blake3-aes-256-gcm");
|
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"/"auto"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none"/"rc4-md5"/"rc4"/"salsa20"/"xchacha20-ietf-poly1305");
|
||||||
|
|
||||||
username = & {
|
username = & {
|
||||||
let j = peg$currPos;
|
let j = peg$currPos;
|
||||||
@@ -169,11 +169,6 @@ ssr_protocol_param = comma "protocol-param" equals param:$[^=,]+ { proxy["protoc
|
|||||||
|
|
||||||
vmess_alterId = comma "alterId" equals alterId:$[0-9]+ { proxy.alterId = parseInt(alterId); }
|
vmess_alterId = comma "alterId" equals alterId:$[0-9]+ { proxy.alterId = parseInt(alterId); }
|
||||||
|
|
||||||
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(""); }
|
|
||||||
|
|
||||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
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_host = comma sni:("tls-name"/"sni") equals host:domain { proxy.sni = host; }
|
||||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/
|
|||||||
// handle ssr obfs
|
// handle ssr obfs
|
||||||
proxy.obfs = obfs.type;
|
proxy.obfs = obfs.type;
|
||||||
}
|
}
|
||||||
shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs_hostv)? (obfs_ss/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* {
|
shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs_hostv)? (obfs_ss/obfs_host/obfs_uri/fast_open/udp_relay/others)* {
|
||||||
proxy.type = "ss";
|
proxy.type = "ss";
|
||||||
// handle ss obfs
|
// handle ss obfs
|
||||||
if (obfs.type == "http" || obfs.type === "tls") {
|
if (obfs.type == "http" || obfs.type === "tls") {
|
||||||
@@ -118,7 +118,7 @@ port = digits:[0-9]+ {
|
|||||||
method = comma cipher:cipher {
|
method = comma cipher:cipher {
|
||||||
proxy.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"/"auto"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none"/"rc4-md5"/"rc4"/"salsa20"/"xchacha20-ietf-poly1305"/"2022-blake3-aes-128-gcm"/"2022-blake3-aes-256-gcm");
|
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"/"auto"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none"/"rc4-md5"/"rc4"/"salsa20"/"xchacha20-ietf-poly1305");
|
||||||
|
|
||||||
username = & {
|
username = & {
|
||||||
let j = peg$currPos;
|
let j = peg$currPos;
|
||||||
@@ -167,11 +167,6 @@ ssr_protocol_param = comma "protocol-param" equals param:$[^=,]+ { proxy["protoc
|
|||||||
|
|
||||||
vmess_alterId = comma "alterId" equals alterId:$[0-9]+ { proxy.alterId = parseInt(alterId); }
|
vmess_alterId = comma "alterId" equals alterId:$[0-9]+ { proxy.alterId = parseInt(alterId); }
|
||||||
|
|
||||||
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(""); }
|
|
||||||
|
|
||||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
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_host = comma sni:("tls-name"/"sni") equals host:domain { proxy.sni = host; }
|
||||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const grammars = String.raw`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2/ssh/direct) {
|
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2/ssh) {
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,18 +108,15 @@ hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying
|
|||||||
proxy.type = "hysteria2";
|
proxy.type = "hysteria2";
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (udp_relay/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||||
proxy.type = "socks5";
|
proxy.type = "socks5";
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (udp_relay/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||||
proxy.type = "socks5";
|
proxy.type = "socks5";
|
||||||
proxy.tls = true;
|
proxy.tls = true;
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
direct = tag equals "direct" (udp_relay/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/block_quic/others)* {
|
|
||||||
proxy.type = "direct";
|
|
||||||
}
|
|
||||||
|
|
||||||
address = comma server:server comma port:port {
|
address = comma server:server comma port:port {
|
||||||
proxy.server = server;
|
proxy.server = server;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2/ssh/direct) {
|
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2/ssh) {
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,18 +106,16 @@ hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying
|
|||||||
proxy.type = "hysteria2";
|
proxy.type = "hysteria2";
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (udp_relay/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||||
proxy.type = "socks5";
|
proxy.type = "socks5";
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (udp_relay/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||||
proxy.type = "socks5";
|
proxy.type = "socks5";
|
||||||
proxy.tls = true;
|
proxy.tls = true;
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
direct = tag equals "direct" (udp_relay/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/block_quic/others)* {
|
|
||||||
proxy.type = "direct";
|
|
||||||
}
|
|
||||||
address = comma server:server comma port:port {
|
address = comma server:server comma port:port {
|
||||||
proxy.server = server;
|
proxy.server = server;
|
||||||
proxy.port = port;
|
proxy.port = port;
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ params = "?" head:param tail:("&"@param)* {
|
|||||||
proxy[proxy.network + '-opts'] = {
|
proxy[proxy.network + '-opts'] = {
|
||||||
'grpc-service-name': params["serviceName"],
|
'grpc-service-name': params["serviceName"],
|
||||||
'_grpc-type': params["mode"],
|
'_grpc-type': params["mode"],
|
||||||
'_grpc-authority': params["authority"],
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (params["path"]) {
|
if (params["path"]) {
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ params = "?" head:param tail:("&"@param)* {
|
|||||||
proxy[proxy.network + '-opts'] = {
|
proxy[proxy.network + '-opts'] = {
|
||||||
'grpc-service-name': params["serviceName"],
|
'grpc-service-name': params["serviceName"],
|
||||||
'_grpc-type': params["mode"],
|
'_grpc-type': params["mode"],
|
||||||
'_grpc-authority': params["authority"],
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (params["path"]) {
|
if (params["path"]) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { safeLoad } from '@/utils/yaml';
|
import { safeLoad } from '@/utils/yaml';
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
import $ from '@/core/app';
|
|
||||||
|
|
||||||
function HTML() {
|
function HTML() {
|
||||||
const name = 'HTML';
|
const name = 'HTML';
|
||||||
@@ -36,15 +35,8 @@ function Base64Encoded() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
const parse = function (raw) {
|
const parse = function (raw) {
|
||||||
const decoded = Base64.decode(raw);
|
raw = Base64.decode(raw);
|
||||||
if (!/^\w+:\/\/\w+/m.test(decoded)) {
|
return raw;
|
||||||
$.error(
|
|
||||||
`Base64 Pre-processor error: decoded line does not start with protocol`,
|
|
||||||
);
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoded;
|
|
||||||
};
|
};
|
||||||
return { name, test, parse };
|
return { name, test, parse };
|
||||||
}
|
}
|
||||||
@@ -56,7 +48,7 @@ function Clash() {
|
|||||||
const content = safeLoad(raw);
|
const content = safeLoad(raw);
|
||||||
return content.proxies && Array.isArray(content.proxies);
|
return content.proxies && Array.isArray(content.proxies);
|
||||||
};
|
};
|
||||||
const parse = function (raw, includeProxies) {
|
const parse = function (raw) {
|
||||||
// Clash YAML format
|
// Clash YAML format
|
||||||
|
|
||||||
// 防止 VLESS节点 reality-opts 选项中的 short-id 被解析成 Infinity
|
// 防止 VLESS节点 reality-opts 选项中的 short-id 被解析成 Infinity
|
||||||
@@ -64,40 +56,35 @@ function Clash() {
|
|||||||
const afterReplace = raw.replace(
|
const afterReplace = raw.replace(
|
||||||
/short-id:([ ]*[^,\n}]*)/g,
|
/short-id:([ ]*[^,\n}]*)/g,
|
||||||
(matched, value) => {
|
(matched, value) => {
|
||||||
const afterTrim = value.trim();
|
const afterTrim = value.trim();
|
||||||
|
|
||||||
// 为空
|
// 为空
|
||||||
if (!afterTrim || afterTrim === '') {
|
if (!afterTrim || afterTrim === '') {
|
||||||
return 'short-id: ""';
|
return 'short-id: ""'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 是否被引号包裹
|
// 是否被引号包裹
|
||||||
if (/^(['"]).*\1$/.test(afterTrim)) {
|
if (/^(['"]).*\1$/.test(afterTrim)) {
|
||||||
return `short-id: ${afterTrim}`;
|
return `short-id: ${afterTrim}`;
|
||||||
} else {
|
} else {
|
||||||
return `short-id: "${afterTrim}"`;
|
return `short-id: "${afterTrim}"`
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
proxies,
|
proxies,
|
||||||
'global-client-fingerprint': globalClientFingerprint,
|
'global-client-fingerprint': globalClientFingerprint,
|
||||||
} = safeLoad(afterReplace);
|
} = safeLoad(afterReplace);
|
||||||
return (
|
return proxies
|
||||||
(includeProxies ? 'proxies:\n' : '') +
|
.map((p) => {
|
||||||
proxies
|
// https://github.com/MetaCubeX/mihomo/blob/Alpha/docs/config.yaml#L73C1-L73C26
|
||||||
.map((p) => {
|
if (globalClientFingerprint && !p['client-fingerprint']) {
|
||||||
// https://github.com/MetaCubeX/mihomo/blob/Alpha/docs/config.yaml#L73C1-L73C26
|
p['client-fingerprint'] = globalClientFingerprint;
|
||||||
if (globalClientFingerprint && !p['client-fingerprint']) {
|
}
|
||||||
p['client-fingerprint'] = globalClientFingerprint;
|
return JSON.stringify(p);
|
||||||
}
|
})
|
||||||
return `${includeProxies ? ' - ' : ''}${JSON.stringify(
|
.join('\n');
|
||||||
p,
|
|
||||||
)}\n`;
|
|
||||||
})
|
|
||||||
.join('')
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
return { name, test, parse };
|
return { name, test, parse };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { hex_md5 } from '@/vendor/md5';
|
|||||||
import { ProxyUtils } from '@/core/proxy-utils';
|
import { ProxyUtils } from '@/core/proxy-utils';
|
||||||
import { produceArtifact } from '@/restful/sync';
|
import { produceArtifact } from '@/restful/sync';
|
||||||
import { SETTINGS_KEY } from '@/constants';
|
import { SETTINGS_KEY } from '@/constants';
|
||||||
import YAML from '@/utils/yaml';
|
|
||||||
|
|
||||||
import env from '@/utils/env';
|
import env from '@/utils/env';
|
||||||
import {
|
import {
|
||||||
@@ -22,46 +21,6 @@ import {
|
|||||||
getRmainingDays,
|
getRmainingDays,
|
||||||
} from '@/utils/flow';
|
} from '@/utils/flow';
|
||||||
|
|
||||||
function isObject(item) {
|
|
||||||
return item && typeof item === 'object' && !Array.isArray(item);
|
|
||||||
}
|
|
||||||
function trimWrap(str) {
|
|
||||||
if (str.startsWith('<') && str.endsWith('>')) {
|
|
||||||
return str.slice(1, -1);
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
function deepMerge(target, _other) {
|
|
||||||
const other = typeof _other === 'string' ? JSON.parse(_other) : _other;
|
|
||||||
for (const key in other) {
|
|
||||||
if (isObject(other[key])) {
|
|
||||||
if (key.endsWith('!')) {
|
|
||||||
const k = trimWrap(key.slice(0, -1));
|
|
||||||
target[k] = other[key];
|
|
||||||
} else {
|
|
||||||
const k = trimWrap(key);
|
|
||||||
if (!target[k]) Object.assign(target, { [k]: {} });
|
|
||||||
deepMerge(target[k], other[k]);
|
|
||||||
}
|
|
||||||
} else if (Array.isArray(other[key])) {
|
|
||||||
if (key.startsWith('+')) {
|
|
||||||
const k = trimWrap(key.slice(1));
|
|
||||||
if (!target[k]) Object.assign(target, { [k]: [] });
|
|
||||||
target[k] = [...other[key], ...target[k]];
|
|
||||||
} else if (key.endsWith('+')) {
|
|
||||||
const k = trimWrap(key.slice(0, -1));
|
|
||||||
if (!target[k]) Object.assign(target, { [k]: [] });
|
|
||||||
target[k] = [...target[k], ...other[key]];
|
|
||||||
} else {
|
|
||||||
const k = trimWrap(key);
|
|
||||||
Object.assign(target, { [k]: other[key] });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Object.assign(target, { [key]: other[key] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
The rule "(name CONTAINS "🇨🇳") AND (port IN [80, 443])" can be expressed as follows:
|
The rule "(name CONTAINS "🇨🇳") AND (port IN [80, 443])" can be expressed as follows:
|
||||||
{
|
{
|
||||||
@@ -362,33 +321,6 @@ function ScriptOperator(script, targetPlatform, $arguments, source, $options) {
|
|||||||
name: 'Script Operator',
|
name: 'Script Operator',
|
||||||
func: async (proxies) => {
|
func: async (proxies) => {
|
||||||
let output = proxies;
|
let output = proxies;
|
||||||
if (output?.$file?.type === 'mihomoProfile') {
|
|
||||||
try {
|
|
||||||
let patch = YAML.safeLoad(script);
|
|
||||||
if (typeof patch !== 'object') patch = {};
|
|
||||||
output.$content = ProxyUtils.yaml.safeDump(
|
|
||||||
deepMerge(
|
|
||||||
{
|
|
||||||
proxies: await produceArtifact({
|
|
||||||
type:
|
|
||||||
output?.$file?.sourceType ||
|
|
||||||
'collection',
|
|
||||||
name: output?.$file?.sourceName,
|
|
||||||
platform: 'mihomo',
|
|
||||||
produceType: 'internal',
|
|
||||||
produceOpts: {
|
|
||||||
'delete-underscore-fields': true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
patch,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return output;
|
|
||||||
} catch (e) {
|
|
||||||
// console.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await (async function () {
|
await (async function () {
|
||||||
const operator = createDynamicFunction(
|
const operator = createDynamicFunction(
|
||||||
'operator',
|
'operator',
|
||||||
@@ -407,27 +339,9 @@ function ScriptOperator(script, targetPlatform, $arguments, source, $options) {
|
|||||||
'operator',
|
'operator',
|
||||||
`async function operator(input = []) {
|
`async function operator(input = []) {
|
||||||
if (input && (input.$files || input.$content)) {
|
if (input && (input.$files || input.$content)) {
|
||||||
let { $content, $files, $options, $file } = input
|
let { $content, $files, $options } = input
|
||||||
if($file.type === 'mihomoProfile') {
|
${script}
|
||||||
${script}
|
return { $content, $files, $options }
|
||||||
if(typeof main === 'function') {
|
|
||||||
const config = {
|
|
||||||
proxies: await produceArtifact({
|
|
||||||
type: $file.sourceType || 'collection',
|
|
||||||
name: $file.sourceName,
|
|
||||||
platform: 'mihomo',
|
|
||||||
produceType: 'internal',
|
|
||||||
produceOpts: {
|
|
||||||
'delete-underscore-fields': true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
$content = ProxyUtils.yaml.safeDump(await main(config))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
${script}
|
|
||||||
}
|
|
||||||
return { $content, $files, $options, $file }
|
|
||||||
} else {
|
} else {
|
||||||
let proxies = input
|
let proxies = input
|
||||||
let list = []
|
let list = []
|
||||||
@@ -785,45 +699,24 @@ function isIP(ip) {
|
|||||||
|
|
||||||
ResolveDomainOperator.resolver = DOMAIN_RESOLVERS;
|
ResolveDomainOperator.resolver = DOMAIN_RESOLVERS;
|
||||||
|
|
||||||
function isAscii(str) {
|
|
||||||
// eslint-disable-next-line no-control-regex
|
|
||||||
var pattern = /^[\x00-\x7F]+$/; // ASCII 范围的 Unicode 编码
|
|
||||||
return pattern.test(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************** Filters ***************************************/
|
/**************************** Filters ***************************************/
|
||||||
// filter useless proxies
|
// filter useless proxies
|
||||||
function UselessFilter() {
|
function UselessFilter() {
|
||||||
|
const KEYWORDS = [
|
||||||
|
'网址',
|
||||||
|
'流量',
|
||||||
|
'时间',
|
||||||
|
'应急',
|
||||||
|
'过期',
|
||||||
|
'Bandwidth',
|
||||||
|
'expire',
|
||||||
|
];
|
||||||
return {
|
return {
|
||||||
name: 'Useless Filter',
|
name: 'Useless Filter',
|
||||||
func: (proxies) => {
|
func: RegexFilter({
|
||||||
return proxies.map((proxy) => {
|
regex: KEYWORDS,
|
||||||
if (proxy.cipher && !isAscii(proxy.cipher)) {
|
keep: false,
|
||||||
return false;
|
}).func,
|
||||||
} else if (proxy.password && !isAscii(proxy.password)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if (proxy.network) {
|
|
||||||
let transportHosts =
|
|
||||||
proxy[`${proxy.network}-opts`]?.headers?.Host ||
|
|
||||||
proxy[`${proxy.network}-opts`]?.headers?.host;
|
|
||||||
transportHosts = Array.isArray(transportHosts)
|
|
||||||
? transportHosts
|
|
||||||
: [transportHosts];
|
|
||||||
if (
|
|
||||||
transportHosts.some(
|
|
||||||
(host) => host && !isAscii(host),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !/网址|流量|时间|应急|过期|Bandwidth|expire/.test(
|
|
||||||
proxy.name,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,6 @@ export default function Clash_Producer() {
|
|||||||
proxy[`${proxy.network}-opts`]
|
proxy[`${proxy.network}-opts`]
|
||||||
) {
|
) {
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-authority'];
|
|
||||||
}
|
}
|
||||||
return proxy;
|
return proxy;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ export default function ClashMeta_Producer() {
|
|||||||
if (opts['include-unsupported-proxy']) return true;
|
if (opts['include-unsupported-proxy']) return true;
|
||||||
if (proxy.type === 'snell' && String(proxy.version) === '4') {
|
if (proxy.type === 'snell' && String(proxy.version) === '4') {
|
||||||
return false;
|
return false;
|
||||||
} else if (['juicity'].includes(proxy.type)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
@@ -180,7 +178,7 @@ export default function ClashMeta_Producer() {
|
|||||||
delete proxy.id;
|
delete proxy.id;
|
||||||
delete proxy.resolved;
|
delete proxy.resolved;
|
||||||
delete proxy['no-resolve'];
|
delete proxy['no-resolve'];
|
||||||
if (type !== 'internal' || opts['delete-underscore-fields']) {
|
if (type !== 'internal') {
|
||||||
for (const key in proxy) {
|
for (const key in proxy) {
|
||||||
if (proxy[key] == null || /^_/i.test(key)) {
|
if (proxy[key] == null || /^_/i.test(key)) {
|
||||||
delete proxy[key];
|
delete proxy[key];
|
||||||
@@ -192,7 +190,6 @@ export default function ClashMeta_Producer() {
|
|||||||
proxy[`${proxy.network}-opts`]
|
proxy[`${proxy.network}-opts`]
|
||||||
) {
|
) {
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-authority'];
|
|
||||||
}
|
}
|
||||||
return proxy;
|
return proxy;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -72,15 +72,6 @@ export default function Egern_Producer() {
|
|||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((proxy) => {
|
.map((proxy) => {
|
||||||
if (proxy.tls && !proxy.sni) {
|
|
||||||
proxy.sni = proxy.server;
|
|
||||||
}
|
|
||||||
const prev_hop =
|
|
||||||
proxy.prev_hop ||
|
|
||||||
proxy['underlying-proxy'] ||
|
|
||||||
proxy['dialer-proxy'] ||
|
|
||||||
proxy.detour;
|
|
||||||
|
|
||||||
if (proxy.type === 'http') {
|
if (proxy.type === 'http') {
|
||||||
proxy = {
|
proxy = {
|
||||||
type: 'http',
|
type: 'http',
|
||||||
@@ -139,8 +130,6 @@ export default function Egern_Producer() {
|
|||||||
next_hop: proxy.next_hop,
|
next_hop: proxy.next_hop,
|
||||||
sni: proxy.sni,
|
sni: proxy.sni,
|
||||||
skip_tls_verify: proxy['skip-cert-verify'],
|
skip_tls_verify: proxy['skip-cert-verify'],
|
||||||
port_hopping: proxy.ports,
|
|
||||||
port_hopping_interval: proxy['hop-interval'],
|
|
||||||
};
|
};
|
||||||
if (proxy['obfs-password'] && proxy.obfs == 'salamander') {
|
if (proxy['obfs-password'] && proxy.obfs == 'salamander') {
|
||||||
proxy.obfs = 'salamander';
|
proxy.obfs = 'salamander';
|
||||||
@@ -295,7 +284,6 @@ export default function Egern_Producer() {
|
|||||||
[proxy.type]: {
|
[proxy.type]: {
|
||||||
...proxy,
|
...proxy,
|
||||||
type: undefined,
|
type: undefined,
|
||||||
prev_hop,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,37 +20,20 @@ function JSON_Producer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
qx: QX_Producer(),
|
|
||||||
QX: QX_Producer(),
|
QX: QX_Producer(),
|
||||||
QuantumultX: QX_Producer(),
|
QuantumultX: QX_Producer(),
|
||||||
surge: Surge_Producer(),
|
|
||||||
Surge: Surge_Producer(),
|
Surge: Surge_Producer(),
|
||||||
SurgeMac: SurgeMac_Producer(),
|
SurgeMac: SurgeMac_Producer(),
|
||||||
Loon: Loon_Producer(),
|
Loon: Loon_Producer(),
|
||||||
Clash: Clash_Producer(),
|
Clash: Clash_Producer(),
|
||||||
meta: ClashMeta_Producer(),
|
|
||||||
clashmeta: ClashMeta_Producer(),
|
|
||||||
'clash.meta': ClashMeta_Producer(),
|
|
||||||
'Clash.Meta': ClashMeta_Producer(),
|
|
||||||
ClashMeta: ClashMeta_Producer(),
|
ClashMeta: ClashMeta_Producer(),
|
||||||
mihomo: ClashMeta_Producer(),
|
|
||||||
Mihomo: ClashMeta_Producer(),
|
|
||||||
uri: URI_Producer(),
|
|
||||||
URI: URI_Producer(),
|
URI: URI_Producer(),
|
||||||
v2: V2Ray_Producer(),
|
|
||||||
v2ray: V2Ray_Producer(),
|
|
||||||
V2Ray: V2Ray_Producer(),
|
V2Ray: V2Ray_Producer(),
|
||||||
json: JSON_Producer(),
|
|
||||||
JSON: JSON_Producer(),
|
JSON: JSON_Producer(),
|
||||||
stash: Stash_Producer(),
|
|
||||||
Stash: Stash_Producer(),
|
Stash: Stash_Producer(),
|
||||||
shadowrocket: Shadowrocket_Producer(),
|
|
||||||
Shadowrocket: Shadowrocket_Producer(),
|
Shadowrocket: Shadowrocket_Producer(),
|
||||||
ShadowRocket: Shadowrocket_Producer(),
|
ShadowRocket: Shadowrocket_Producer(),
|
||||||
surfboard: Surfboard_Producer(),
|
|
||||||
Surfboard: Surfboard_Producer(),
|
Surfboard: Surfboard_Producer(),
|
||||||
singbox: singbox_Producer(),
|
|
||||||
'sing-box': singbox_Producer(),
|
'sing-box': singbox_Producer(),
|
||||||
egern: Egern_Producer(),
|
|
||||||
Egern: Egern_Producer(),
|
Egern: Egern_Producer(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { isPresent, Result } from './utils';
|
|||||||
import { isIPv4, isIPv6 } from '@/utils';
|
import { isIPv4, isIPv6 } from '@/utils';
|
||||||
|
|
||||||
export default function Loon_Producer() {
|
export default function Loon_Producer() {
|
||||||
const produce = (proxy, type, opts = {}) => {
|
const produce = (proxy) => {
|
||||||
switch (proxy.type) {
|
switch (proxy.type) {
|
||||||
case 'ss':
|
case 'ss':
|
||||||
return shadowsocks(proxy, opts['include-unsupported-proxy']);
|
return shadowsocks(proxy);
|
||||||
case 'ssr':
|
case 'ssr':
|
||||||
return shadowsocksr(proxy);
|
return shadowsocksr(proxy);
|
||||||
case 'trojan':
|
case 'trojan':
|
||||||
@@ -32,7 +32,7 @@ export default function Loon_Producer() {
|
|||||||
return { produce };
|
return { produce };
|
||||||
}
|
}
|
||||||
|
|
||||||
function shadowsocks(proxy, includeUnsupportedProxy) {
|
function shadowsocks(proxy) {
|
||||||
const result = new Result(proxy);
|
const result = new Result(proxy);
|
||||||
if (
|
if (
|
||||||
![
|
![
|
||||||
@@ -56,8 +56,6 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
|||||||
'aes-256-gcm',
|
'aes-256-gcm',
|
||||||
'chacha20-ietf-poly1305',
|
'chacha20-ietf-poly1305',
|
||||||
'xchacha20-ietf-poly1305',
|
'xchacha20-ietf-poly1305',
|
||||||
'2022-blake3-aes-128-gcm',
|
|
||||||
'2022-blake3-aes-256-gcm',
|
|
||||||
].includes(proxy.cipher)
|
].includes(proxy.cipher)
|
||||||
) {
|
) {
|
||||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||||
@@ -66,8 +64,6 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
|||||||
`${proxy.name}=shadowsocks,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.password}"`,
|
`${proxy.name}=shadowsocks,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.password}"`,
|
||||||
);
|
);
|
||||||
|
|
||||||
let isShadowTLS;
|
|
||||||
|
|
||||||
// obfs
|
// obfs
|
||||||
if (isPresent(proxy, 'plugin')) {
|
if (isPresent(proxy, 'plugin')) {
|
||||||
if (proxy.plugin === 'obfs') {
|
if (proxy.plugin === 'obfs') {
|
||||||
@@ -80,52 +76,11 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
|||||||
`,obfs-uri=${proxy['plugin-opts'].path}`,
|
`,obfs-uri=${proxy['plugin-opts'].path}`,
|
||||||
'plugin-opts.path',
|
'plugin-opts.path',
|
||||||
);
|
);
|
||||||
} else if (!['shadow-tls'].includes(proxy.plugin)) {
|
} else {
|
||||||
throw new Error(`plugin ${proxy.plugin} is not supported`);
|
throw new Error(`plugin ${proxy.plugin} is not supported`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shadow-tls
|
|
||||||
if (isPresent(proxy, 'shadow-tls-password')) {
|
|
||||||
result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`);
|
|
||||||
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,shadow-tls-version=${proxy['shadow-tls-version']}`,
|
|
||||||
'shadow-tls-version',
|
|
||||||
);
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,shadow-tls-sni=${proxy['shadow-tls-sni']}`,
|
|
||||||
'shadow-tls-sni',
|
|
||||||
);
|
|
||||||
// udp-port
|
|
||||||
result.appendIfPresent(`,udp-port=${proxy['udp-port']}`, 'udp-port');
|
|
||||||
isShadowTLS = true;
|
|
||||||
} else if (['shadow-tls'].includes(proxy.plugin) && proxy['plugin-opts']) {
|
|
||||||
const password = proxy['plugin-opts'].password;
|
|
||||||
const host = proxy['plugin-opts'].host;
|
|
||||||
const version = proxy['plugin-opts'].version;
|
|
||||||
if (password) {
|
|
||||||
result.append(`,shadow-tls-password=${password}`);
|
|
||||||
if (host) {
|
|
||||||
result.append(`,shadow-tls-sni=${host}`);
|
|
||||||
}
|
|
||||||
if (version) {
|
|
||||||
if (version < 2) {
|
|
||||||
throw new Error(
|
|
||||||
`shadow-tls version ${version} is not supported`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
result.append(`,shadow-tls-version=${version}`);
|
|
||||||
}
|
|
||||||
// udp-port
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,udp-port=${proxy['udp-port']}`,
|
|
||||||
'udp-port',
|
|
||||||
);
|
|
||||||
isShadowTLS = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// tfo
|
// tfo
|
||||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||||
|
|
||||||
@@ -134,12 +89,6 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
|||||||
result.append(`,udp=true`);
|
result.append(`,udp=true`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!includeUnsupportedProxy && isShadowTLS) {
|
|
||||||
throw new Error(
|
|
||||||
`shadow-tls is not supported(请使用 includeUnsupportedProxy 参数)`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ export default function ShadowRocket_Producer() {
|
|||||||
if (opts['include-unsupported-proxy']) return true;
|
if (opts['include-unsupported-proxy']) return true;
|
||||||
if (proxy.type === 'snell' && String(proxy.version) === '4') {
|
if (proxy.type === 'snell' && String(proxy.version) === '4') {
|
||||||
return false;
|
return false;
|
||||||
} else if (['mieru'].includes(proxy.type)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
@@ -195,7 +193,6 @@ export default function ShadowRocket_Producer() {
|
|||||||
proxy[`${proxy.network}-opts`]
|
proxy[`${proxy.network}-opts`]
|
||||||
) {
|
) {
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-authority'];
|
|
||||||
}
|
}
|
||||||
return proxy;
|
return proxy;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ import $ from '@/core/app';
|
|||||||
import { isIPv4, isIPv6 } from '@/utils';
|
import { isIPv4, isIPv6 } from '@/utils';
|
||||||
|
|
||||||
const detourParser = (proxy, parsedProxy) => {
|
const detourParser = (proxy, parsedProxy) => {
|
||||||
parsedProxy.detour = proxy['dialer-proxy'] || proxy.detour;
|
if (proxy['dialer-proxy']) parsedProxy.detour = proxy['dialer-proxy'];
|
||||||
};
|
|
||||||
const networkParser = (proxy, parsedProxy) => {
|
|
||||||
if (['tcp', 'udp'].includes(proxy._network))
|
|
||||||
parsedProxy.network = proxy._network;
|
|
||||||
};
|
};
|
||||||
const tfoParser = (proxy, parsedProxy) => {
|
const tfoParser = (proxy, parsedProxy) => {
|
||||||
parsedProxy.tcp_fast_open = false;
|
parsedProxy.tcp_fast_open = false;
|
||||||
@@ -218,11 +214,7 @@ const tlsParser = (proxy, parsedProxy) => {
|
|||||||
proxy['reality-opts']['short-id'];
|
proxy['reality-opts']['short-id'];
|
||||||
parsedProxy.tls.utls = { enabled: true };
|
parsedProxy.tls.utls = { enabled: true };
|
||||||
}
|
}
|
||||||
if (
|
if (proxy['client-fingerprint'] && proxy['client-fingerprint'] !== '')
|
||||||
!['hysteria', 'hysteria2', 'tuic'].includes(proxy.type) &&
|
|
||||||
proxy['client-fingerprint'] &&
|
|
||||||
proxy['client-fingerprint'] !== ''
|
|
||||||
)
|
|
||||||
parsedProxy.tls.utls = {
|
parsedProxy.tls.utls = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
fingerprint: proxy['client-fingerprint'],
|
fingerprint: proxy['client-fingerprint'],
|
||||||
@@ -309,7 +301,6 @@ const socks5Parser = (proxy = {}) => {
|
|||||||
if (proxy.uot) parsedProxy.udp_over_tcp = true;
|
if (proxy.uot) parsedProxy.udp_over_tcp = true;
|
||||||
if (proxy['udp-over-tcp']) parsedProxy.udp_over_tcp = true;
|
if (proxy['udp-over-tcp']) parsedProxy.udp_over_tcp = true;
|
||||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
@@ -361,7 +352,6 @@ const ssParser = (proxy = {}) => {
|
|||||||
if (proxy.uot) parsedProxy.udp_over_tcp = true;
|
if (proxy.uot) parsedProxy.udp_over_tcp = true;
|
||||||
if (proxy['udp-over-tcp']) parsedProxy.udp_over_tcp = true;
|
if (proxy['udp-over-tcp']) parsedProxy.udp_over_tcp = true;
|
||||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
@@ -476,7 +466,7 @@ const vmessParser = (proxy = {}) => {
|
|||||||
if (proxy.network === 'h2') h2Parser(proxy, parsedProxy);
|
if (proxy.network === 'h2') h2Parser(proxy, parsedProxy);
|
||||||
if (proxy.network === 'http') h1Parser(proxy, parsedProxy);
|
if (proxy.network === 'http') h1Parser(proxy, parsedProxy);
|
||||||
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
@@ -499,7 +489,7 @@ const vlessParser = (proxy = {}) => {
|
|||||||
if (proxy.flow === 'xtls-rprx-vision') parsedProxy.flow = proxy.flow;
|
if (proxy.flow === 'xtls-rprx-vision') parsedProxy.flow = proxy.flow;
|
||||||
if (proxy.network === 'ws') wsParser(proxy, parsedProxy);
|
if (proxy.network === 'ws') wsParser(proxy, parsedProxy);
|
||||||
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
@@ -520,7 +510,7 @@ const trojanParser = (proxy = {}) => {
|
|||||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||||
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
||||||
if (proxy.network === 'ws') wsParser(proxy, parsedProxy);
|
if (proxy.network === 'ws') wsParser(proxy, parsedProxy);
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
@@ -568,14 +558,13 @@ const hysteriaParser = (proxy = {}) => {
|
|||||||
parsedProxy.disable_mtu_discovery = true;
|
parsedProxy.disable_mtu_discovery = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
const hysteria2Parser = (proxy = {}, includeUnsupportedProxy) => {
|
const hysteria2Parser = (proxy = {}) => {
|
||||||
const parsedProxy = {
|
const parsedProxy = {
|
||||||
tag: proxy.name,
|
tag: proxy.name,
|
||||||
type: 'hysteria2',
|
type: 'hysteria2',
|
||||||
@@ -587,23 +576,12 @@ const hysteria2Parser = (proxy = {}, includeUnsupportedProxy) => {
|
|||||||
};
|
};
|
||||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||||
throw 'invalid port';
|
throw 'invalid port';
|
||||||
if (includeUnsupportedProxy) {
|
|
||||||
if (proxy['hop-interval'])
|
|
||||||
parsedProxy.hop_interval = /^\d+$/.test(proxy['hop-interval'])
|
|
||||||
? `${proxy['hop-interval']}s`
|
|
||||||
: proxy['hop-interval'];
|
|
||||||
if (proxy['ports'])
|
|
||||||
parsedProxy.server_ports = proxy['ports']
|
|
||||||
.split(/\s*,\s*/)
|
|
||||||
.map((p) => p.replace(/\s*-\s*/g, ':'));
|
|
||||||
}
|
|
||||||
if (proxy.up) parsedProxy.up_mbps = parseInt(`${proxy.up}`, 10);
|
if (proxy.up) parsedProxy.up_mbps = parseInt(`${proxy.up}`, 10);
|
||||||
if (proxy.down) parsedProxy.down_mbps = parseInt(`${proxy.down}`, 10);
|
if (proxy.down) parsedProxy.down_mbps = parseInt(`${proxy.down}`, 10);
|
||||||
if (proxy.obfs === 'salamander') parsedProxy.obfs.type = 'salamander';
|
if (proxy.obfs === 'salamander') parsedProxy.obfs.type = 'salamander';
|
||||||
if (proxy['obfs-password'])
|
if (proxy['obfs-password'])
|
||||||
parsedProxy.obfs.password = proxy['obfs-password'];
|
parsedProxy.obfs.password = proxy['obfs-password'];
|
||||||
if (!parsedProxy.obfs.type) delete parsedProxy.obfs;
|
if (!parsedProxy.obfs.type) delete parsedProxy.obfs;
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
@@ -634,7 +612,6 @@ const tuic5Parser = (proxy = {}) => {
|
|||||||
if (proxy['udp-over-stream']) parsedProxy.udp_over_stream = true;
|
if (proxy['udp-over-stream']) parsedProxy.udp_over_stream = true;
|
||||||
if (proxy['heartbeat-interval'])
|
if (proxy['heartbeat-interval'])
|
||||||
parsedProxy.heartbeat = `${proxy['heartbeat-interval']}ms`;
|
parsedProxy.heartbeat = `${proxy['heartbeat-interval']}ms`;
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
@@ -692,7 +669,6 @@ const wireguardParser = (proxy = {}) => {
|
|||||||
parsedProxy.peers.push(peer);
|
parsedProxy.peers.push(peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
networkParser(proxy, parsedProxy);
|
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
@@ -810,12 +786,7 @@ export default function singbox_Producer() {
|
|||||||
list.push(hysteriaParser(proxy));
|
list.push(hysteriaParser(proxy));
|
||||||
break;
|
break;
|
||||||
case 'hysteria2':
|
case 'hysteria2':
|
||||||
list.push(
|
list.push(hysteria2Parser(proxy));
|
||||||
hysteria2Parser(
|
|
||||||
proxy,
|
|
||||||
opts['include-unsupported-proxy'],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case 'tuic':
|
case 'tuic':
|
||||||
if (!proxy.token || proxy.token.length === 0) {
|
if (!proxy.token || proxy.token.length === 0) {
|
||||||
|
|||||||
@@ -289,7 +289,6 @@ export default function Stash_Producer() {
|
|||||||
proxy[`${proxy.network}-opts`]
|
proxy[`${proxy.network}-opts`]
|
||||||
) {
|
) {
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-authority'];
|
|
||||||
}
|
}
|
||||||
return proxy;
|
return proxy;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ export default function Surge_Producer() {
|
|||||||
return vmess(proxy, opts['include-unsupported-proxy']);
|
return vmess(proxy, opts['include-unsupported-proxy']);
|
||||||
case 'http':
|
case 'http':
|
||||||
return http(proxy);
|
return http(proxy);
|
||||||
case 'direct':
|
|
||||||
return direct(proxy);
|
|
||||||
case 'socks5':
|
case 'socks5':
|
||||||
return socks5(proxy);
|
return socks5(proxy);
|
||||||
case 'snell':
|
case 'snell':
|
||||||
@@ -127,6 +125,8 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
|||||||
|
|
||||||
// udp
|
// udp
|
||||||
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
|
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
|
||||||
|
// udp-port
|
||||||
|
result.appendIfPresent(`,udp-port=${proxy['udp-port']}`, 'udp-port');
|
||||||
|
|
||||||
// test-url
|
// test-url
|
||||||
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
|
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
|
||||||
@@ -158,8 +158,6 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
|||||||
`,shadow-tls-sni=${proxy['shadow-tls-sni']}`,
|
`,shadow-tls-sni=${proxy['shadow-tls-sni']}`,
|
||||||
'shadow-tls-sni',
|
'shadow-tls-sni',
|
||||||
);
|
);
|
||||||
// udp-port
|
|
||||||
result.appendIfPresent(`,udp-port=${proxy['udp-port']}`, 'udp-port');
|
|
||||||
} else if (['shadow-tls'].includes(proxy.plugin) && proxy['plugin-opts']) {
|
} else if (['shadow-tls'].includes(proxy.plugin) && proxy['plugin-opts']) {
|
||||||
const password = proxy['plugin-opts'].password;
|
const password = proxy['plugin-opts'].password;
|
||||||
const host = proxy['plugin-opts'].host;
|
const host = proxy['plugin-opts'].host;
|
||||||
@@ -177,11 +175,6 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
|||||||
}
|
}
|
||||||
result.append(`,shadow-tls-version=${version}`);
|
result.append(`,shadow-tls-version=${version}`);
|
||||||
}
|
}
|
||||||
// udp-port
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,udp-port=${proxy['udp-port']}`,
|
|
||||||
'udp-port',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,54 +503,6 @@ function http(proxy) {
|
|||||||
|
|
||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
function direct(proxy) {
|
|
||||||
const result = new Result(proxy);
|
|
||||||
const type = 'direct';
|
|
||||||
result.append(`${proxy.name}=${type}`);
|
|
||||||
|
|
||||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
|
||||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
|
||||||
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
|
||||||
'no-error-alert',
|
|
||||||
);
|
|
||||||
|
|
||||||
// tfo
|
|
||||||
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
|
|
||||||
|
|
||||||
// udp
|
|
||||||
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
|
|
||||||
|
|
||||||
// test-url
|
|
||||||
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,test-timeout=${proxy['test-timeout']}`,
|
|
||||||
'test-timeout',
|
|
||||||
);
|
|
||||||
result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp');
|
|
||||||
result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid');
|
|
||||||
result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos');
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,allow-other-interface=${proxy['allow-other-interface']}`,
|
|
||||||
'allow-other-interface',
|
|
||||||
);
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,interface=${proxy['interface-name']}`,
|
|
||||||
'interface-name',
|
|
||||||
);
|
|
||||||
|
|
||||||
// block-quic
|
|
||||||
result.appendIfPresent(`,block-quic=${proxy['block-quic']}`, 'block-quic');
|
|
||||||
|
|
||||||
// underlying-proxy
|
|
||||||
result.appendIfPresent(
|
|
||||||
`,underlying-proxy=${proxy['underlying-proxy']}`,
|
|
||||||
'underlying-proxy',
|
|
||||||
);
|
|
||||||
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
function socks5(proxy) {
|
function socks5(proxy) {
|
||||||
const result = new Result(proxy);
|
const result = new Result(proxy);
|
||||||
|
|||||||
@@ -29,13 +29,9 @@ export default function URI_Producer() {
|
|||||||
switch (proxy.type) {
|
switch (proxy.type) {
|
||||||
case 'ss':
|
case 'ss':
|
||||||
const userinfo = `${proxy.cipher}:${proxy.password}`;
|
const userinfo = `${proxy.cipher}:${proxy.password}`;
|
||||||
result = `ss://${
|
result = `ss://${Base64.encode(userinfo)}@${proxy.server}:${
|
||||||
proxy.cipher?.startsWith('2022-blake3-')
|
proxy.port
|
||||||
? `${encodeURIComponent(
|
}${proxy.plugin ? '/' : ''}`;
|
||||||
proxy.cipher,
|
|
||||||
)}:${encodeURIComponent(proxy.password)}`
|
|
||||||
: Base64.encode(userinfo)
|
|
||||||
}@${proxy.server}:${proxy.port}${proxy.plugin ? '/' : ''}`;
|
|
||||||
if (proxy.plugin) {
|
if (proxy.plugin) {
|
||||||
result += '?plugin=';
|
result += '?plugin=';
|
||||||
const opts = proxy['plugin-opts'];
|
const opts = proxy['plugin-opts'];
|
||||||
@@ -138,8 +134,6 @@ export default function URI_Producer() {
|
|||||||
result.type =
|
result.type =
|
||||||
proxy[`${proxy.network}-opts`]?.['_grpc-type'] ||
|
proxy[`${proxy.network}-opts`]?.['_grpc-type'] ||
|
||||||
'gun';
|
'gun';
|
||||||
result.host =
|
|
||||||
proxy[`${proxy.network}-opts`]?.['_grpc-authority'];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = 'vmess://' + Base64.encode(JSON.stringify(result));
|
result = 'vmess://' + Base64.encode(JSON.stringify(result));
|
||||||
@@ -202,13 +196,6 @@ export default function URI_Producer() {
|
|||||||
vlessTransport += `&mode=${encodeURIComponent(
|
vlessTransport += `&mode=${encodeURIComponent(
|
||||||
proxy[`${proxy.network}-opts`]?.['_grpc-type'] || 'gun',
|
proxy[`${proxy.network}-opts`]?.['_grpc-type'] || 'gun',
|
||||||
)}`;
|
)}`;
|
||||||
const authority =
|
|
||||||
proxy[`${proxy.network}-opts`]?.['_grpc-authority'];
|
|
||||||
if (authority) {
|
|
||||||
vlessTransport += `&authority=${encodeURIComponent(
|
|
||||||
authority,
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let vlessTransportServiceName =
|
let vlessTransportServiceName =
|
||||||
@@ -274,18 +261,11 @@ export default function URI_Producer() {
|
|||||||
proxy[`${proxy.network}-opts`]?.[
|
proxy[`${proxy.network}-opts`]?.[
|
||||||
`${proxy.network}-service-name`
|
`${proxy.network}-service-name`
|
||||||
];
|
];
|
||||||
let trojanTransportAuthority =
|
|
||||||
proxy[`${proxy.network}-opts`]?.['_grpc-authority'];
|
|
||||||
if (trojanTransportServiceName) {
|
if (trojanTransportServiceName) {
|
||||||
trojanTransport += `&serviceName=${encodeURIComponent(
|
trojanTransport += `&serviceName=${encodeURIComponent(
|
||||||
trojanTransportServiceName,
|
trojanTransportServiceName,
|
||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
if (trojanTransportAuthority) {
|
|
||||||
trojanTransport += `&authority=${encodeURIComponent(
|
|
||||||
trojanTransportAuthority,
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
trojanTransport += `&mode=${encodeURIComponent(
|
trojanTransport += `&mode=${encodeURIComponent(
|
||||||
proxy[`${proxy.network}-opts`]?.['_grpc-type'] ||
|
proxy[`${proxy.network}-opts`]?.['_grpc-type'] ||
|
||||||
'gun',
|
'gun',
|
||||||
|
|||||||
@@ -37,9 +37,7 @@ let resourceUrl = typeof $resourceUrl !== 'undefined' ? $resourceUrl : '';
|
|||||||
if (resourceType === RESOURCE_TYPE.PROXY) {
|
if (resourceType === RESOURCE_TYPE.PROXY) {
|
||||||
try {
|
try {
|
||||||
let proxies = ProxyUtils.parse(resource);
|
let proxies = ProxyUtils.parse(resource);
|
||||||
result = ProxyUtils.produce(proxies, 'Loon', undefined, {
|
result = ProxyUtils.produce(proxies, 'Loon');
|
||||||
'include-unsupported-proxy': arg?.includeUnsupportedProxy,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('解析器: 使用 resource 出现错误');
|
console.log('解析器: 使用 resource 出现错误');
|
||||||
console.log(e.message ?? e);
|
console.log(e.message ?? e);
|
||||||
@@ -47,20 +45,9 @@ let resourceUrl = typeof $resourceUrl !== 'undefined' ? $resourceUrl : '';
|
|||||||
if ((!result || /^\s*$/.test(result)) && resourceUrl) {
|
if ((!result || /^\s*$/.test(result)) && resourceUrl) {
|
||||||
console.log(`解析器: 尝试从 ${resourceUrl} 获取订阅`);
|
console.log(`解析器: 尝试从 ${resourceUrl} 获取订阅`);
|
||||||
try {
|
try {
|
||||||
let raw = await download(
|
let raw = await download(resourceUrl, arg?.ua, arg?.timeout);
|
||||||
resourceUrl,
|
|
||||||
arg?.ua,
|
|
||||||
arg?.timeout,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
let proxies = ProxyUtils.parse(raw);
|
let proxies = ProxyUtils.parse(raw);
|
||||||
result = ProxyUtils.produce(proxies, 'Loon', undefined, {
|
result = ProxyUtils.produce(proxies, 'Loon');
|
||||||
'include-unsupported-proxy': arg?.includeUnsupportedProxy,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e.message ?? e);
|
console.log(e.message ?? e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,21 +50,11 @@ function createCollection(req, res) {
|
|||||||
|
|
||||||
function getCollection(req, res) {
|
function getCollection(req, res) {
|
||||||
let { name } = req.params;
|
let { name } = req.params;
|
||||||
let { raw } = req.query;
|
|
||||||
name = decodeURIComponent(name);
|
name = decodeURIComponent(name);
|
||||||
const allCols = $.read(COLLECTIONS_KEY);
|
const allCols = $.read(COLLECTIONS_KEY);
|
||||||
const collection = findByName(allCols, name);
|
const collection = findByName(allCols, name);
|
||||||
if (collection) {
|
if (collection) {
|
||||||
if (raw) {
|
success(res, collection);
|
||||||
res.set('content-type', 'application/json')
|
|
||||||
.set(
|
|
||||||
'content-disposition',
|
|
||||||
`attachment; filename="${encodeURIComponent(name)}.json"`,
|
|
||||||
)
|
|
||||||
.send(JSON.stringify(collection));
|
|
||||||
} else {
|
|
||||||
success(res, collection);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
failed(
|
failed(
|
||||||
res,
|
res,
|
||||||
|
|||||||
@@ -13,44 +13,11 @@ import { getISO } from '@/utils/geo';
|
|||||||
import env from '@/utils/env';
|
import env from '@/utils/env';
|
||||||
|
|
||||||
export default function register($app) {
|
export default function register($app) {
|
||||||
$app.get('/share/col/:name/:target', async (req, res) => {
|
|
||||||
const { target } = req.params;
|
|
||||||
if (target) {
|
|
||||||
req.query.target = target;
|
|
||||||
$.info(`使用路由指定目标: ${target}`);
|
|
||||||
}
|
|
||||||
await downloadCollection(req, res);
|
|
||||||
});
|
|
||||||
$app.get('/share/col/:name', downloadCollection);
|
$app.get('/share/col/:name', downloadCollection);
|
||||||
$app.get('/share/sub/:name/:target', async (req, res) => {
|
|
||||||
const { target } = req.params;
|
|
||||||
if (target) {
|
|
||||||
req.query.target = target;
|
|
||||||
$.info(`使用路由指定目标: ${target}`);
|
|
||||||
}
|
|
||||||
await downloadSubscription(req, res);
|
|
||||||
});
|
|
||||||
$app.get('/share/sub/:name', downloadSubscription);
|
$app.get('/share/sub/:name', downloadSubscription);
|
||||||
|
|
||||||
$app.get('/download/collection/:name/:target', async (req, res) => {
|
|
||||||
const { target } = req.params;
|
|
||||||
if (target) {
|
|
||||||
req.query.target = target;
|
|
||||||
$.info(`使用路由指定目标: ${target}`);
|
|
||||||
}
|
|
||||||
await downloadCollection(req, res);
|
|
||||||
});
|
|
||||||
$app.get('/download/collection/:name', downloadCollection);
|
$app.get('/download/collection/:name', downloadCollection);
|
||||||
$app.get('/download/:name/:target', async (req, res) => {
|
|
||||||
const { target } = req.params;
|
|
||||||
if (target) {
|
|
||||||
req.query.target = target;
|
|
||||||
$.info(`使用路由指定目标: ${target}`);
|
|
||||||
}
|
|
||||||
await downloadSubscription(req, res);
|
|
||||||
});
|
|
||||||
$app.get('/download/:name', downloadSubscription);
|
$app.get('/download/:name', downloadSubscription);
|
||||||
|
|
||||||
$app.get(
|
$app.get(
|
||||||
'/download/collection/:name/api/v1/server/details',
|
'/download/collection/:name/api/v1/server/details',
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
@@ -92,9 +59,11 @@ async function downloadSubscription(req, res) {
|
|||||||
|
|
||||||
const platform =
|
const platform =
|
||||||
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
|
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
|
||||||
const reqUA = req.headers['user-agent'] || req.headers['User-Agent'];
|
|
||||||
$.info(
|
$.info(
|
||||||
`正在下载订阅:${name}\n请求 User-Agent: ${reqUA}\n请求 target: ${req.query.target}\n实际输出: ${platform}`,
|
`正在下载订阅:${name}\n请求 User-Agent: ${
|
||||||
|
req.headers['user-agent'] || req.headers['User-Agent']
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
let {
|
let {
|
||||||
url,
|
url,
|
||||||
@@ -129,14 +98,6 @@ async function downloadSubscription(req, res) {
|
|||||||
if (url) {
|
if (url) {
|
||||||
url = decodeURIComponent(url);
|
url = decodeURIComponent(url);
|
||||||
$.info(`指定远程订阅 URL: ${url}`);
|
$.info(`指定远程订阅 URL: ${url}`);
|
||||||
if (!/^https?:\/\//.test(url)) {
|
|
||||||
content = url;
|
|
||||||
$.info(`URL 不是链接,视为本地订阅`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (content) {
|
|
||||||
content = decodeURIComponent(content);
|
|
||||||
$.info(`指定本地订阅: ${content}`);
|
|
||||||
}
|
}
|
||||||
if (proxy) {
|
if (proxy) {
|
||||||
proxy = decodeURIComponent(proxy);
|
proxy = decodeURIComponent(proxy);
|
||||||
@@ -146,7 +107,10 @@ async function downloadSubscription(req, res) {
|
|||||||
ua = decodeURIComponent(ua);
|
ua = decodeURIComponent(ua);
|
||||||
$.info(`指定远程订阅 User-Agent: ${ua}`);
|
$.info(`指定远程订阅 User-Agent: ${ua}`);
|
||||||
}
|
}
|
||||||
|
if (content) {
|
||||||
|
content = decodeURIComponent(content);
|
||||||
|
$.info(`指定本地订阅: ${content}`);
|
||||||
|
}
|
||||||
if (mergeSources) {
|
if (mergeSources) {
|
||||||
mergeSources = decodeURIComponent(mergeSources);
|
mergeSources = decodeURIComponent(mergeSources);
|
||||||
$.info(`指定合并来源: ${mergeSources}`);
|
$.info(`指定合并来源: ${mergeSources}`);
|
||||||
@@ -176,13 +140,6 @@ async function downloadSubscription(req, res) {
|
|||||||
const sub = findByName(allSubs, name);
|
const sub = findByName(allSubs, name);
|
||||||
if (sub) {
|
if (sub) {
|
||||||
try {
|
try {
|
||||||
const passThroughUA = sub.passThroughUA;
|
|
||||||
if (passThroughUA) {
|
|
||||||
$.info(
|
|
||||||
`订阅开启了透传 User-Agent, 使用请求的 User-Agent: ${reqUA}`,
|
|
||||||
);
|
|
||||||
ua = reqUA;
|
|
||||||
}
|
|
||||||
let output = await produceArtifact({
|
let output = await produceArtifact({
|
||||||
type: 'subscription',
|
type: 'subscription',
|
||||||
name,
|
name,
|
||||||
@@ -256,29 +213,9 @@ async function downloadSubscription(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sub.subUserinfo) {
|
if (sub.subUserinfo) {
|
||||||
let subUserInfo;
|
|
||||||
if (/^https?:\/\//.test(sub.subUserinfo)) {
|
|
||||||
try {
|
|
||||||
subUserInfo = await getFlowHeaders(
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
proxy || sub.proxy,
|
|
||||||
sub.subUserinfo,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
$.error(
|
|
||||||
`订阅 ${name} 使用自定义流量链接 ${
|
|
||||||
sub.subUserinfo
|
|
||||||
} 获取流量信息时发生错误: ${JSON.stringify(e)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
subUserInfo = sub.subUserinfo;
|
|
||||||
}
|
|
||||||
res.set(
|
res.set(
|
||||||
'subscription-userinfo',
|
'subscription-userinfo',
|
||||||
[subUserInfo, flowInfo].filter((i) => i).join('; '),
|
[sub.subUserinfo, flowInfo].filter((i) => i).join('; '),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +255,7 @@ async function downloadSubscription(req, res) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$.error(`🌍 Sub-Store 下载订阅失败\n❌ 未找到订阅:${name}!`);
|
$.error(`🌍 Sub-Store 下载订阅失败`, `❌ 未找到订阅:${name}!`);
|
||||||
failed(
|
failed(
|
||||||
res,
|
res,
|
||||||
new ResourceNotFoundError(
|
new ResourceNotFoundError(
|
||||||
@@ -346,7 +283,7 @@ async function downloadCollection(req, res) {
|
|||||||
$.info(
|
$.info(
|
||||||
`正在下载组合订阅:${name}\n请求 User-Agent: ${
|
`正在下载组合订阅:${name}\n请求 User-Agent: ${
|
||||||
req.headers['user-agent'] || req.headers['User-Agent']
|
req.headers['user-agent'] || req.headers['User-Agent']
|
||||||
}\n请求 target: ${req.query.target}\n实际输出: ${platform}`,
|
}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@@ -418,12 +355,13 @@ async function downloadCollection(req, res) {
|
|||||||
proxy,
|
proxy,
|
||||||
noCache,
|
noCache,
|
||||||
});
|
});
|
||||||
let subUserInfoOfSub;
|
|
||||||
// forward flow header from the first subscription in this collection
|
// forward flow header from the first subscription in this collection
|
||||||
const allSubs = $.read(SUBS_KEY);
|
const allSubs = $.read(SUBS_KEY);
|
||||||
const subnames = collection.subscriptions;
|
const subnames = collection.subscriptions;
|
||||||
if (subnames.length > 0) {
|
if (subnames.length > 0) {
|
||||||
const sub = findByName(allSubs, subnames[0]);
|
const sub = findByName(allSubs, subnames[0]);
|
||||||
|
let flowInfo;
|
||||||
if (
|
if (
|
||||||
sub.source !== 'local' ||
|
sub.source !== 'local' ||
|
||||||
['localFirst', 'remoteFirst'].includes(sub.mergeSources)
|
['localFirst', 'remoteFirst'].includes(sub.mergeSources)
|
||||||
@@ -457,13 +395,16 @@ async function downloadCollection(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$arguments.noFlow) {
|
if (!$arguments.noFlow) {
|
||||||
subUserInfoOfSub = await getFlowHeaders(
|
flowInfo = await getFlowHeaders(
|
||||||
$arguments?.insecure ? `${url}#insecure` : url,
|
$arguments?.insecure ? `${url}#insecure` : url,
|
||||||
$arguments.flowUserAgent,
|
$arguments.flowUserAgent,
|
||||||
undefined,
|
undefined,
|
||||||
proxy || sub.proxy || collection.proxy,
|
proxy || sub.proxy || collection.proxy,
|
||||||
$arguments.flowUrl,
|
$arguments.flowUrl,
|
||||||
);
|
);
|
||||||
|
if (flowInfo) {
|
||||||
|
res.set('subscription-userinfo', flowInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
$.error(
|
$.error(
|
||||||
@@ -474,60 +415,12 @@ async function downloadCollection(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sub.subUserinfo) {
|
if (sub.subUserinfo) {
|
||||||
let subUserInfo;
|
res.set(
|
||||||
if (/^https?:\/\//.test(sub.subUserinfo)) {
|
'subscription-userinfo',
|
||||||
try {
|
[sub.subUserinfo, flowInfo].filter((i) => i).join('; '),
|
||||||
subUserInfo = await getFlowHeaders(
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
proxy || sub.proxy,
|
|
||||||
sub.subUserinfo,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
$.error(
|
|
||||||
`组合订阅 ${name} 使用自定义流量链接 ${
|
|
||||||
sub.subUserinfo
|
|
||||||
} 获取流量信息时发生错误: ${JSON.stringify(e)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
subUserInfo = sub.subUserinfo;
|
|
||||||
}
|
|
||||||
subUserInfoOfSub = [subUserInfo, subUserInfoOfSub]
|
|
||||||
.filter((i) => i)
|
|
||||||
.join('; ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$.info(`组合订阅 ${name} 透传的的流量信息: ${subUserInfoOfSub}`);
|
|
||||||
|
|
||||||
let subUserInfoOfCol;
|
|
||||||
if (/^https?:\/\//.test(collection.subUserinfo)) {
|
|
||||||
try {
|
|
||||||
subUserInfoOfCol = await getFlowHeaders(
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
proxy || collection.proxy,
|
|
||||||
collection.subUserinfo,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
$.error(
|
|
||||||
`组合订阅 ${name} 使用自定义流量链接 ${
|
|
||||||
collection.subUserinfo
|
|
||||||
} 获取流量信息时发生错误: ${JSON.stringify(e)}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
subUserInfoOfCol = collection.subUserinfo;
|
|
||||||
}
|
}
|
||||||
res.set(
|
|
||||||
'subscription-userinfo',
|
|
||||||
[subUserInfoOfCol, subUserInfoOfSub]
|
|
||||||
.filter((i) => i)
|
|
||||||
.join('; '),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (platform === 'JSON') {
|
if (platform === 'JSON') {
|
||||||
if (resultFormat === 'nezha') {
|
if (resultFormat === 'nezha') {
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ async function getFile(req, res) {
|
|||||||
mergeSources,
|
mergeSources,
|
||||||
ignoreFailedRemoteFile,
|
ignoreFailedRemoteFile,
|
||||||
proxy,
|
proxy,
|
||||||
noCache,
|
|
||||||
} = req.query;
|
} = req.query;
|
||||||
let $options = {};
|
let $options = {};
|
||||||
if (req.query.$options) {
|
if (req.query.$options) {
|
||||||
@@ -114,9 +113,6 @@ async function getFile(req, res) {
|
|||||||
ignoreFailedRemoteFile = decodeURIComponent(ignoreFailedRemoteFile);
|
ignoreFailedRemoteFile = decodeURIComponent(ignoreFailedRemoteFile);
|
||||||
$.info(`指定忽略失败的远程文件: ${ignoreFailedRemoteFile}`);
|
$.info(`指定忽略失败的远程文件: ${ignoreFailedRemoteFile}`);
|
||||||
}
|
}
|
||||||
if (noCache) {
|
|
||||||
$.info(`指定不使用缓存: ${noCache}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const allFiles = $.read(FILES_KEY);
|
const allFiles = $.read(FILES_KEY);
|
||||||
const file = findByName(allFiles, name);
|
const file = findByName(allFiles, name);
|
||||||
@@ -132,7 +128,6 @@ async function getFile(req, res) {
|
|||||||
ignoreFailedRemoteFile,
|
ignoreFailedRemoteFile,
|
||||||
$options,
|
$options,
|
||||||
proxy,
|
proxy,
|
||||||
noCache,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -184,7 +179,7 @@ async function getFile(req, res) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$.error(`🌍 Sub-Store 下载文件失败\n❌ 未找到文件:${name}!`);
|
$.error(`🌍 Sub-Store 下载文件失败`, `❌ 未找到文件:${name}!`);
|
||||||
failed(
|
failed(
|
||||||
res,
|
res,
|
||||||
new ResourceNotFoundError(
|
new ResourceNotFoundError(
|
||||||
@@ -197,21 +192,11 @@ async function getFile(req, res) {
|
|||||||
}
|
}
|
||||||
function getWholeFile(req, res) {
|
function getWholeFile(req, res) {
|
||||||
let { name } = req.params;
|
let { name } = req.params;
|
||||||
let { raw } = req.query;
|
|
||||||
name = decodeURIComponent(name);
|
name = decodeURIComponent(name);
|
||||||
const allFiles = $.read(FILES_KEY);
|
const allFiles = $.read(FILES_KEY);
|
||||||
const file = findByName(allFiles, name);
|
const file = findByName(allFiles, name);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (raw) {
|
success(res, file);
|
||||||
res.set('content-type', 'application/json')
|
|
||||||
.set(
|
|
||||||
'content-disposition',
|
|
||||||
`attachment; filename="${encodeURIComponent(name)}.json"`,
|
|
||||||
)
|
|
||||||
.send(JSON.stringify(file));
|
|
||||||
} else {
|
|
||||||
success(res, file);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
failed(
|
failed(
|
||||||
res,
|
res,
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ async function previewFile(req, res) {
|
|||||||
const processed =
|
const processed =
|
||||||
Array.isArray(file.process) && file.process.length > 0
|
Array.isArray(file.process) && file.process.length > 0
|
||||||
? await ProxyUtils.process(
|
? await ProxyUtils.process(
|
||||||
{ $files: files, $content: filesContent, $file: file },
|
{ $files: files, $content: filesContent },
|
||||||
file.process,
|
file.process,
|
||||||
)
|
)
|
||||||
: { $content: filesContent, $files: files };
|
: { $content: filesContent, $files: files };
|
||||||
@@ -114,10 +114,6 @@ async function compareSub(req, res) {
|
|||||||
sub.ua,
|
sub.ua,
|
||||||
undefined,
|
undefined,
|
||||||
sub.proxy,
|
sub.proxy,
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errors[url] = err;
|
errors[url] = err;
|
||||||
@@ -223,10 +219,6 @@ async function compareCollection(req, res) {
|
|||||||
sub.ua,
|
sub.ua,
|
||||||
undefined,
|
undefined,
|
||||||
sub.proxy,
|
sub.proxy,
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errors[url] = err;
|
errors[url] = err;
|
||||||
|
|||||||
@@ -57,29 +57,9 @@ async function getFlowInfo(req, res) {
|
|||||||
!['localFirst', 'remoteFirst'].includes(sub.mergeSources)
|
!['localFirst', 'remoteFirst'].includes(sub.mergeSources)
|
||||||
) {
|
) {
|
||||||
if (sub.subUserinfo) {
|
if (sub.subUserinfo) {
|
||||||
let subUserInfo;
|
|
||||||
if (/^https?:\/\//.test(sub.subUserinfo)) {
|
|
||||||
try {
|
|
||||||
subUserInfo = await getFlowHeaders(
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
sub.proxy,
|
|
||||||
sub.subUserinfo,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
$.error(
|
|
||||||
`订阅 ${name} 使用自定义流量链接 ${
|
|
||||||
sub.subUserinfo
|
|
||||||
} 获取流量信息时发生错误: ${JSON.stringify(e)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
subUserInfo = sub.subUserinfo;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
success(res, {
|
success(res, {
|
||||||
...parseFlowHeaders(subUserInfo),
|
...parseFlowHeaders(sub.subUserinfo),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$.error(
|
$.error(
|
||||||
@@ -169,29 +149,9 @@ async function getFlowInfo(req, res) {
|
|||||||
startDate: $arguments.startDate,
|
startDate: $arguments.startDate,
|
||||||
cycleDays: $arguments.cycleDays,
|
cycleDays: $arguments.cycleDays,
|
||||||
});
|
});
|
||||||
let subUserInfo;
|
|
||||||
if (/^https?:\/\//.test(sub.subUserinfo)) {
|
|
||||||
try {
|
|
||||||
subUserInfo = await getFlowHeaders(
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
sub.proxy,
|
|
||||||
sub.subUserinfo,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
$.error(
|
|
||||||
`订阅 ${name} 使用自定义流量链接 ${
|
|
||||||
sub.subUserinfo
|
|
||||||
} 获取流量信息时发生错误: ${JSON.stringify(e)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
subUserInfo = sub.subUserinfo;
|
|
||||||
}
|
|
||||||
const result = {
|
const result = {
|
||||||
...parseFlowHeaders(
|
...parseFlowHeaders(
|
||||||
[subUserInfo, flowHeaders].filter((i) => i).join('; '),
|
[sub.subUserinfo, flowHeaders].filter((i) => i).join('; '),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
if (remainingDays != null) {
|
if (remainingDays != null) {
|
||||||
|
|||||||
@@ -73,8 +73,7 @@ async function produceArtifact({
|
|||||||
proxy || sub.proxy,
|
proxy || sub.proxy,
|
||||||
undefined,
|
undefined,
|
||||||
awaitCustomCache,
|
awaitCustomCache,
|
||||||
noCache || sub.noCache,
|
noCache,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errors[url] = err;
|
errors[url] = err;
|
||||||
@@ -122,8 +121,7 @@ async function produceArtifact({
|
|||||||
proxy || sub.proxy,
|
proxy || sub.proxy,
|
||||||
undefined,
|
undefined,
|
||||||
awaitCustomCache,
|
awaitCustomCache,
|
||||||
noCache || sub.noCache,
|
noCache,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errors[url] = err;
|
errors[url] = err;
|
||||||
@@ -244,8 +242,7 @@ async function produceArtifact({
|
|||||||
collection.proxy,
|
collection.proxy,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
noCache || sub.noCache,
|
noCache,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errors[url] = err;
|
errors[url] = err;
|
||||||
@@ -512,12 +509,7 @@ async function produceArtifact({
|
|||||||
const processed =
|
const processed =
|
||||||
Array.isArray(file.process) && file.process.length > 0
|
Array.isArray(file.process) && file.process.length > 0
|
||||||
? await ProxyUtils.process(
|
? await ProxyUtils.process(
|
||||||
{
|
{ $files: files, $content: filesContent, $options },
|
||||||
$files: files,
|
|
||||||
$content: filesContent,
|
|
||||||
$options,
|
|
||||||
$file: file,
|
|
||||||
},
|
|
||||||
file.process,
|
file.process,
|
||||||
)
|
)
|
||||||
: { $content: filesContent, $files: files, $options };
|
: { $content: filesContent, $files: files, $options };
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import $ from '@/core/app';
|
import $ from '@/core/app';
|
||||||
import dnsPacket from 'dns-packet';
|
import dnsPacket from 'dns-packet';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import { isIPv4 } from '@/utils';
|
|
||||||
|
|
||||||
export async function doh({ url, domain, type = 'A', timeout, edns }) {
|
export async function doh({ url, domain, type = 'A', timeout, edns }) {
|
||||||
const buf = dnsPacket.encode({
|
const buf = dnsPacket.encode({
|
||||||
@@ -24,7 +23,7 @@ export async function doh({ url, domain, type = 'A', timeout, edns }) {
|
|||||||
{
|
{
|
||||||
code: 'CLIENT_SUBNET',
|
code: 'CLIENT_SUBNET',
|
||||||
ip: edns,
|
ip: edns,
|
||||||
sourcePrefixLength: isIPv4(edns) ? 24 : 56,
|
sourcePrefixLength: 24,
|
||||||
scopePrefixLength: 0,
|
scopePrefixLength: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ import {
|
|||||||
validCheck,
|
validCheck,
|
||||||
} from '@/utils/flow';
|
} from '@/utils/flow';
|
||||||
import $ from '@/core/app';
|
import $ from '@/core/app';
|
||||||
import PROXY_PREPROCESSORS from '@/core/proxy-utils/preprocessors';
|
|
||||||
const clashPreprocessor = PROXY_PREPROCESSORS.find(
|
|
||||||
(processor) => processor.name === 'Clash Pre-processor',
|
|
||||||
);
|
|
||||||
|
|
||||||
const tasks = new Map();
|
const tasks = new Map();
|
||||||
|
|
||||||
@@ -26,7 +22,6 @@ export default async function download(
|
|||||||
skipCustomCache,
|
skipCustomCache,
|
||||||
awaitCustomCache,
|
awaitCustomCache,
|
||||||
noCache,
|
noCache,
|
||||||
preprocess,
|
|
||||||
) {
|
) {
|
||||||
let $arguments = {};
|
let $arguments = {};
|
||||||
let url = rawUrl.replace(/#noFlow$/, '');
|
let url = rawUrl.replace(/#noFlow$/, '');
|
||||||
@@ -92,9 +87,6 @@ export default async function download(
|
|||||||
timeout,
|
timeout,
|
||||||
proxy,
|
proxy,
|
||||||
true,
|
true,
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
preprocess,
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$.error(
|
$.error(
|
||||||
@@ -115,9 +107,6 @@ export default async function download(
|
|||||||
timeout,
|
timeout,
|
||||||
proxy,
|
proxy,
|
||||||
true,
|
true,
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
preprocess,
|
|
||||||
).catch((e) => {
|
).catch((e) => {
|
||||||
$.error(
|
$.error(
|
||||||
`乐观缓存: URL ${url} 异步更新缓存发生错误 ${
|
`乐观缓存: URL ${url} 异步更新缓存发生错误 ${
|
||||||
@@ -180,10 +169,10 @@ export default async function download(
|
|||||||
: { insecure: true }
|
: { insecure: true }
|
||||||
: undefined;
|
: undefined;
|
||||||
$.info(
|
$.info(
|
||||||
`Downloading...\nUser-Agent: ${userAgent}\nTimeout: ${requestTimeout}\nProxy: ${proxy}\nInsecure: ${!!insecure}\nPreprocess: ${preprocess}\nURL: ${url}`,
|
`Downloading...\nUser-Agent: ${userAgent}\nTimeout: ${requestTimeout}\nProxy: ${proxy}\nInsecure: ${!!insecure}\nURL: ${url}`,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
let { body, headers, statusCode } = await http.get({
|
const { body, headers } = await http.get({
|
||||||
url,
|
url,
|
||||||
...(proxy ? { proxy } : {}),
|
...(proxy ? { proxy } : {}),
|
||||||
...(isLoon && proxy ? { node: proxy } : {}),
|
...(isLoon && proxy ? { node: proxy } : {}),
|
||||||
@@ -191,10 +180,6 @@ export default async function download(
|
|||||||
...(proxy ? getPolicyDescriptor(proxy) : {}),
|
...(proxy ? getPolicyDescriptor(proxy) : {}),
|
||||||
...(insecure ? insecure : {}),
|
...(insecure ? insecure : {}),
|
||||||
});
|
});
|
||||||
$.info(`statusCode: ${statusCode}`);
|
|
||||||
if (statusCode < 200 || statusCode >= 400) {
|
|
||||||
throw new Error(`statusCode: ${statusCode}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers) {
|
if (headers) {
|
||||||
const flowInfo = getFlowField(headers);
|
const flowInfo = getFlowField(headers);
|
||||||
@@ -204,15 +189,6 @@ export default async function download(
|
|||||||
}
|
}
|
||||||
if (body.replace(/\s/g, '').length === 0)
|
if (body.replace(/\s/g, '').length === 0)
|
||||||
throw new Error(new Error('远程资源内容为空'));
|
throw new Error(new Error('远程资源内容为空'));
|
||||||
if (preprocess) {
|
|
||||||
try {
|
|
||||||
if (clashPreprocessor.test(body)) {
|
|
||||||
body = clashPreprocessor.parse(body, true);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
$.error(`Clash Pre-processor error: ${e}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let shouldCache = true;
|
let shouldCache = true;
|
||||||
if (cacheThreshold) {
|
if (cacheThreshold) {
|
||||||
const size = body.length / 1024;
|
const size = body.length / 1024;
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ export function getFlowField(headers) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${sub || ''}${
|
return `${sub || ''}${webPage ? `;app_url=${webPage}` : ''}`;
|
||||||
webPage ? `; app_url=${encodeURIComponent(webPage)}` : ''
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
export async function getFlowHeaders(
|
export async function getFlowHeaders(
|
||||||
rawUrl,
|
rawUrl,
|
||||||
@@ -167,9 +165,6 @@ export async function getFlowHeaders(
|
|||||||
flowInfo = getFlowField(headers);
|
flowInfo = getFlowField(headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flowInfo) {
|
|
||||||
flowInfo = flowInfo.trim();
|
|
||||||
}
|
|
||||||
if (flowInfo) {
|
if (flowInfo) {
|
||||||
headersResourceCache.set(id, flowInfo);
|
headersResourceCache.set(id, flowInfo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ const ISOFlags = {
|
|||||||
'🇧🇪': ['BE', 'BEL'],
|
'🇧🇪': ['BE', 'BEL'],
|
||||||
'🇧🇬': ['BG', 'BGR'],
|
'🇧🇬': ['BG', 'BGR'],
|
||||||
'🇧🇭': ['BH', 'BHR'],
|
'🇧🇭': ['BH', 'BHR'],
|
||||||
'🇧🇴': ['BO', 'BOL'],
|
|
||||||
'🇧🇷': ['BR', 'BRA'],
|
'🇧🇷': ['BR', 'BRA'],
|
||||||
'🇧🇾': ['BY', 'BLR'],
|
'🇧🇾': ['BY', 'BLR'],
|
||||||
'🇨🇦': ['CA', 'CAN'],
|
'🇨🇦': ['CA', 'CAN'],
|
||||||
@@ -39,7 +38,6 @@ const ISOFlags = {
|
|||||||
'🇬🇧': ['GB', 'GBR', 'UK'],
|
'🇬🇧': ['GB', 'GBR', 'UK'],
|
||||||
'🇬🇪': ['GE', 'GEO'],
|
'🇬🇪': ['GE', 'GEO'],
|
||||||
'🇬🇷': ['GR', 'GRC'],
|
'🇬🇷': ['GR', 'GRC'],
|
||||||
'🇬🇹': ['GT', 'GTM'],
|
|
||||||
'🇭🇰': ['HK', 'HKG', 'HKT', 'HKBN', 'HGC', 'WTT', 'CMI'],
|
'🇭🇰': ['HK', 'HKG', 'HKT', 'HKBN', 'HGC', 'WTT', 'CMI'],
|
||||||
'🇭🇷': ['HR', 'HRV'],
|
'🇭🇷': ['HR', 'HRV'],
|
||||||
'🇭🇺': ['HU', 'HUN'],
|
'🇭🇺': ['HU', 'HUN'],
|
||||||
@@ -143,7 +141,6 @@ export function getFlag(name) {
|
|||||||
'🇧🇭': ['Bahrain', '巴林'],
|
'🇧🇭': ['Bahrain', '巴林'],
|
||||||
'🇧🇷': ['Brazil', '巴西', '圣保罗'],
|
'🇧🇷': ['Brazil', '巴西', '圣保罗'],
|
||||||
'🇧🇾': ['Belarus', '白俄罗斯', '白俄'],
|
'🇧🇾': ['Belarus', '白俄罗斯', '白俄'],
|
||||||
'🇧🇴': ['Bolivia', '玻利维亚'],
|
|
||||||
'🇨🇦': [
|
'🇨🇦': [
|
||||||
'Canada',
|
'Canada',
|
||||||
'加拿大',
|
'加拿大',
|
||||||
@@ -194,7 +191,6 @@ export function getFlag(name) {
|
|||||||
],
|
],
|
||||||
'🇬🇪': ['Georgia', '格鲁吉亚', '格魯吉亞'],
|
'🇬🇪': ['Georgia', '格鲁吉亚', '格魯吉亞'],
|
||||||
'🇬🇷': ['Greece', '希腊', '希臘'],
|
'🇬🇷': ['Greece', '希腊', '希臘'],
|
||||||
'🇬🇹': ['Guatemala', '危地马拉'],
|
|
||||||
'🇭🇰': [
|
'🇭🇰': [
|
||||||
'Hongkong',
|
'Hongkong',
|
||||||
'香港',
|
'香港',
|
||||||
@@ -281,7 +277,7 @@ export function getFlag(name) {
|
|||||||
'🇵🇪': ['Peru', '秘鲁', '祕魯'],
|
'🇵🇪': ['Peru', '秘鲁', '祕魯'],
|
||||||
'🇵🇭': ['Philippines', '菲律宾', '菲律賓'],
|
'🇵🇭': ['Philippines', '菲律宾', '菲律賓'],
|
||||||
'🇵🇰': ['Pakistan', '巴基斯坦'],
|
'🇵🇰': ['Pakistan', '巴基斯坦'],
|
||||||
'🇵🇱': ['Poland', '波兰', '波蘭', '华沙', 'Warsaw'],
|
'🇵🇱': ['Poland', '波兰', '波蘭'],
|
||||||
'🇵🇷': ['Puerto Rico', '波多黎各'],
|
'🇵🇷': ['Puerto Rico', '波多黎各'],
|
||||||
'🇵🇹': ['Portugal', '葡萄牙'],
|
'🇵🇹': ['Portugal', '葡萄牙'],
|
||||||
'🇵🇾': ['Paraguay', '巴拉圭'],
|
'🇵🇾': ['Paraguay', '巴拉圭'],
|
||||||
@@ -326,7 +322,7 @@ export function getFlag(name) {
|
|||||||
'🇸🇰': ['Slovakia', '斯洛伐克'],
|
'🇸🇰': ['Slovakia', '斯洛伐克'],
|
||||||
'🇹🇭': ['Thailand', '泰国', '泰國', '曼谷'],
|
'🇹🇭': ['Thailand', '泰国', '泰國', '曼谷'],
|
||||||
'🇹🇳': ['Tunisia', '突尼斯'],
|
'🇹🇳': ['Tunisia', '突尼斯'],
|
||||||
'🇹🇷': ['Turkey', '土耳其', '伊斯坦布尔', 'Istanbul'],
|
'🇹🇷': ['Turkey', '土耳其', '伊斯坦布尔'],
|
||||||
'🇹🇼': [
|
'🇹🇼': [
|
||||||
'Taiwan',
|
'Taiwan',
|
||||||
'台湾',
|
'台湾',
|
||||||
@@ -374,10 +370,6 @@ export function getFlag(name) {
|
|||||||
'Michigan',
|
'Michigan',
|
||||||
'俄亥俄',
|
'俄亥俄',
|
||||||
'Ohio',
|
'Ohio',
|
||||||
'马纳萨斯',
|
|
||||||
'Manassas',
|
|
||||||
'弗吉尼亚',
|
|
||||||
'Virginia',
|
|
||||||
],
|
],
|
||||||
'🇺🇾': ['Uruguay', '乌拉圭'],
|
'🇺🇾': ['Uruguay', '乌拉圭'],
|
||||||
'🇻🇪': ['Venezuela', '委内瑞拉'],
|
'🇻🇪': ['Venezuela', '委内瑞拉'],
|
||||||
@@ -438,12 +430,8 @@ export function getFlag(name) {
|
|||||||
RegExp(`(^|[^a-zA-Z])${keyword}([^a-zA-Z]|$)`).test(name),
|
RegExp(`(^|[^a-zA-Z])${keyword}([^a-zA-Z]|$)`).test(name),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
const isCN2 =
|
//console.log(`ISOFlag = ${flag}`)
|
||||||
flag == '🇨🇳' &&
|
return (Flag = flag);
|
||||||
RegExp(`(^|[^a-zA-Z])CN2([^a-zA-Z]|$)`).test(name);
|
|
||||||
if (!isCN2) {
|
|
||||||
return (Flag = flag);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,17 +114,15 @@ export default class Gist {
|
|||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return this.http
|
return this.http.get('/gists').then((response) => {
|
||||||
.get('/gists?per_page=100&page=1')
|
const gists = JSON.parse(response.body);
|
||||||
.then((response) => {
|
for (let g of gists) {
|
||||||
const gists = JSON.parse(response.body);
|
if (g.description === this.key) {
|
||||||
for (let g of gists) {
|
return g;
|
||||||
if (g.description === this.key) {
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
}
|
||||||
});
|
return;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ function operator(proxies = [], targetPlatform, context) {
|
|||||||
// 13. `test-url` 为测延迟链接, `test-timeout` 为测延迟超时
|
// 13. `test-url` 为测延迟链接, `test-timeout` 为测延迟超时
|
||||||
// 14. `ports` 为端口跳跃, `hop-interval` 变换端口号的时间间隔
|
// 14. `ports` 为端口跳跃, `hop-interval` 变换端口号的时间间隔
|
||||||
// 15. `ip-version` 设置节点使用 IP 版本,可选:dual,ipv4,ipv6,ipv4-prefer,ipv6-prefer. 会进行内部转换, 若无法匹配则使用原始值
|
// 15. `ip-version` 设置节点使用 IP 版本,可选:dual,ipv4,ipv6,ipv4-prefer,ipv6-prefer. 会进行内部转换, 若无法匹配则使用原始值
|
||||||
// 16. `sing-box` 支持使用 `_network` 来设置 `network`, 例如 `tcp`, `udp`
|
|
||||||
|
|
||||||
// require 为 Node.js 的 require, 在 Node.js 运行环境下 可以用来引入模块
|
// require 为 Node.js 的 require, 在 Node.js 运行环境下 可以用来引入模块
|
||||||
|
|
||||||
@@ -166,20 +165,6 @@ function operator(proxies = [], targetPlatform, context) {
|
|||||||
|
|
||||||
// 若不存在 `source._collection`, 说明输出结果为单条订阅, 脚本设置在此单条订阅上
|
// 若不存在 `source._collection`, 说明输出结果为单条订阅, 脚本设置在此单条订阅上
|
||||||
|
|
||||||
// 这个历史遗留原因, 是有点复杂. 提供一个例子, 用来取当前脚本所在的组合订阅或单条订阅名称
|
|
||||||
|
|
||||||
// let name = ''
|
|
||||||
// for (const [key, value] of Object.entries(env.source)) {
|
|
||||||
// if (!key.startsWith('_')) {
|
|
||||||
// name = value.displayName || value.name
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (!name) {
|
|
||||||
// const collection = env.source._collection
|
|
||||||
// name = collection.displayName || collection.name
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 1. 输出单条订阅 sub-1 时, 该单条订阅中的脚本上下文为:
|
// 1. 输出单条订阅 sub-1 时, 该单条订阅中的脚本上下文为:
|
||||||
// {
|
// {
|
||||||
// "source": {
|
// "source": {
|
||||||
|
|||||||
Reference in New Issue
Block a user