mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f82294c49 | ||
|
|
7c398ba51c | ||
|
|
7002eee88d | ||
|
|
bd21d58fe7 | ||
|
|
2ea46dcbf1 | ||
|
|
4a2a2297f6 | ||
|
|
07d5a913f0 | ||
|
|
421df8f0d4 | ||
|
|
e14944dd19 | ||
|
|
bf18c51f6a | ||
|
|
23e8fbd1b7 | ||
|
|
b94b3c366b | ||
|
|
afb5f7b880 | ||
|
|
74ec133a79 | ||
|
|
2a76eb6462 | ||
|
|
9ac5e136a6 | ||
|
|
38f5a97a20 | ||
|
|
14a3488ce2 | ||
|
|
6afec4f668 | ||
|
|
b1874e510d | ||
|
|
48aaaf5c99 | ||
|
|
7385e17a4c | ||
|
|
c3daea55ab | ||
|
|
fc9ff48b1f | ||
|
|
fb21890b68 | ||
|
|
2155cc9639 | ||
|
|
03e320cbd0 | ||
|
|
e325b9a39a | ||
|
|
87597f6fc2 | ||
|
|
3462d36c35 | ||
|
|
02946ec81c | ||
|
|
c963c872ff | ||
|
|
c4a1bb4ea1 | ||
|
|
f96d9dea74 | ||
|
|
01eb69d8ae | ||
|
|
797ba6f601 | ||
|
|
128353a7f3 | ||
|
|
e6f6d51608 | ||
|
|
589a6bfadb | ||
|
|
75012503f8 |
19
.github/workflows/main.yml
vendored
19
.github/workflows/main.yml
vendored
@@ -1,5 +1,6 @@
|
||||
name: build
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -26,18 +27,18 @@ jobs:
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
cd backend && pnpm i --no-frozen-lockfile
|
||||
- name: Test
|
||||
run: |
|
||||
cd backend
|
||||
pnpm test
|
||||
- name: Build
|
||||
run: |
|
||||
cd backend
|
||||
pnpm run build
|
||||
# - name: Test
|
||||
# run: |
|
||||
# cd backend
|
||||
# pnpm test
|
||||
# - name: Build
|
||||
# run: |
|
||||
# cd backend
|
||||
# pnpm run build
|
||||
- name: Bundle
|
||||
run: |
|
||||
cd backend
|
||||
pnpm run bundle
|
||||
pnpm bundle:esbuild
|
||||
- id: tag
|
||||
name: Generate release tag
|
||||
run: |
|
||||
|
||||
@@ -26,9 +26,9 @@ Core functionalities:
|
||||
|
||||
### Supported Input Formats
|
||||
|
||||
> ⚠️ Do not use `Shadowrocket` to export URI and then import it as input. It is not a standard URI.
|
||||
> ⚠️ Do not use `Shadowrocket` or `NekoBox` to export URI and then import it as input. The URIs exported in this way may not be standard URIs.
|
||||
|
||||
- [x] Normal Proxy(`socks5`, `socks5+tls`, `http`, `https`(it's ok))
|
||||
- [x] Proxy URI Scheme(`socks5`, `socks5+tls`, `http`, `https`(it's ok))
|
||||
|
||||
example: `socks5+tls://user:pass@ip:port#name`
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* ╚════██║██║ ██║██╔══██╗╚════╝╚════██║ ██║ ██║ ██║██╔══██╗██╔══╝
|
||||
* ███████║╚██████╔╝██████╔╝ ███████║ ██║ ╚██████╔╝██║ ██║███████╗
|
||||
* ╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
|
||||
* Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket!
|
||||
* Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket!
|
||||
* @updated: <%= updated %>
|
||||
* @version: <%= pkg.version %>
|
||||
* @author: Peng-YM
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.16.3",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"version": "2.16.37",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
@@ -12,6 +12,7 @@
|
||||
"dev:run": "nodemon -w sub-store.min.js sub-store.min.js",
|
||||
"build": "gulp",
|
||||
"bundle": "node bundle.js",
|
||||
"bundle:esbuild": "node bundle-esbuild.js",
|
||||
"changelog": "conventional-changelog -p cli -i CHANGELOG.md -s"
|
||||
},
|
||||
"author": "Peng-YM",
|
||||
@@ -33,6 +34,7 @@
|
||||
"ms": "^2.1.3",
|
||||
"nanoid": "^3.3.3",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^7.6.3",
|
||||
"static-js-yaml": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
13502
backend/pnpm-lock.yaml
generated
13502
backend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ import {
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
isValidPortNumber,
|
||||
isValidUUID,
|
||||
isNotBlank,
|
||||
ipAddress,
|
||||
getRandomPort,
|
||||
@@ -21,6 +22,7 @@ import { findByName } from '@/utils/database';
|
||||
import { produceArtifact } from '@/restful/sync';
|
||||
import { getFlag, removeFlag, getISO, MMDB } from '@/utils/geo';
|
||||
import Gist from '@/utils/gist';
|
||||
import { isPresent } from './producers/utils';
|
||||
|
||||
function preprocess(raw) {
|
||||
for (const processor of PROXY_PREPROCESSORS) {
|
||||
@@ -75,7 +77,16 @@ function parse(raw) {
|
||||
$.error(`Failed to parse line: ${line}`);
|
||||
}
|
||||
}
|
||||
return proxies;
|
||||
return proxies.filter((proxy) => {
|
||||
if (['vless', 'vmess'].includes(proxy.type)) {
|
||||
const isProxyUUIDValid = isValidUUID(proxy.uuid);
|
||||
if (!isProxyUUIDValid) {
|
||||
$.error(`UUID is invalid: ${proxy.name} ${proxy.uuid}`);
|
||||
}
|
||||
return isProxyUUIDValid;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
async function processFn(
|
||||
@@ -214,10 +225,22 @@ function produce(proxies, targetPlatform, type, opts = {}) {
|
||||
);
|
||||
|
||||
// filter unsupported proxies
|
||||
proxies = proxies.filter(
|
||||
(proxy) =>
|
||||
!(proxy.supported && proxy.supported[targetPlatform] === false),
|
||||
);
|
||||
proxies = proxies.filter((proxy) => {
|
||||
// 检查代理是否支持目标平台
|
||||
if (proxy.supported && proxy.supported[targetPlatform] === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 对于 vless 和 vmess 代理,需要额外验证 UUID
|
||||
if (['vless', 'vmess'].includes(proxy.type)) {
|
||||
const isProxyUUIDValid = isValidUUID(proxy.uuid);
|
||||
if (!isProxyUUIDValid)
|
||||
$.error(`UUID is invalid: ${proxy.name} ${proxy.uuid}`);
|
||||
return isProxyUUIDValid;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
proxies = proxies.map((proxy) => {
|
||||
proxy._resolved = proxy.resolved;
|
||||
@@ -343,6 +366,14 @@ function lastParse(proxy) {
|
||||
if (typeof proxy.password === 'number') {
|
||||
proxy.password = numberToString(proxy.password);
|
||||
}
|
||||
if (
|
||||
['ss'].includes(proxy.type) &&
|
||||
proxy.cipher === 'none' &&
|
||||
!proxy.password
|
||||
) {
|
||||
// https://github.com/MetaCubeX/mihomo/issues/1677
|
||||
proxy.password = '';
|
||||
}
|
||||
if (proxy.interface) {
|
||||
proxy['interface-name'] = proxy.interface;
|
||||
delete proxy.interface;
|
||||
@@ -564,6 +595,20 @@ function lastParse(proxy) {
|
||||
if (!proxy['tls-fingerprint'] && caStr) {
|
||||
proxy['tls-fingerprint'] = rs.generateFingerprint(caStr);
|
||||
}
|
||||
if (
|
||||
['shadowsocks'].includes(proxy.type) &&
|
||||
isPresent(proxy, 'shadow-tls-password')
|
||||
) {
|
||||
proxy.plugin = 'shadow-tls';
|
||||
proxy['plugin-opts'] = {
|
||||
host: proxy['shadow-tls-sni'],
|
||||
password: proxy['shadow-tls-password'],
|
||||
version: proxy['shadow-tls-version'],
|
||||
};
|
||||
delete proxy['shadow-tls-sni'];
|
||||
delete proxy['shadow-tls-password'];
|
||||
delete proxy['shadow-tls-version'];
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import getSurgeParser from './peggy/surge';
|
||||
import getLoonParser from './peggy/loon';
|
||||
import getQXParser from './peggy/qx';
|
||||
import getTrojanURIParser from './peggy/trojan-uri';
|
||||
import $ from '@/core/app';
|
||||
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
@@ -40,8 +41,21 @@ function URI_PROXY() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, type, tls, username, password, server, port, query, name] =
|
||||
line.match(
|
||||
/^(socks5|http|http)(\+tls|s)?:\/\/(?:(.*?):(.*?)@)?(.*?):(\d+?)(\?.*?)?(?:#(.*?))?$/,
|
||||
/^(socks5|http|http)(\+tls|s)?:\/\/(?:(.*?):(.*?)@)?(.*?)(?::(\d+?))?(\?.*?)?(?:#(.*?))?$/,
|
||||
);
|
||||
if (port) {
|
||||
port = parseInt(port, 10);
|
||||
} else {
|
||||
if (tls) {
|
||||
port = 443;
|
||||
} else if (type === 'http') {
|
||||
port = 80;
|
||||
} else {
|
||||
$.error(`port is not present in line: ${line}`);
|
||||
throw new Error(`port is not present in line: ${line}`);
|
||||
}
|
||||
$.info(`port is not present in line: ${line}, set to ${port}`);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
name:
|
||||
@@ -114,6 +128,9 @@ function URI_SS() {
|
||||
}
|
||||
userInfoStr = content.split('@')[0];
|
||||
serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||
} else if (content.includes('?')) {
|
||||
const parsed = content.match(/(\?.*)$/);
|
||||
query = parsed[1];
|
||||
}
|
||||
|
||||
const serverAndPort = serverAndPortArray[1];
|
||||
@@ -132,11 +149,10 @@ function URI_SS() {
|
||||
// }
|
||||
|
||||
// handle obfs
|
||||
const idx = content.indexOf('?plugin=');
|
||||
if (idx !== -1) {
|
||||
const pluginMatch = content.match(/[?&]plugin=([^&]+)/);
|
||||
if (pluginMatch) {
|
||||
const pluginInfo = (
|
||||
'plugin=' +
|
||||
decodeURIComponent(content.split('?plugin=')[1].split('&')[0])
|
||||
'plugin=' + decodeURIComponent(pluginMatch[1])
|
||||
).split(';');
|
||||
const params = {};
|
||||
for (const item of pluginInfo) {
|
||||
@@ -161,6 +177,16 @@ function URI_SS() {
|
||||
tls: getIfPresent(params.tls),
|
||||
};
|
||||
break;
|
||||
case 'shadow-tls': {
|
||||
proxy.plugin = 'shadow-tls';
|
||||
const version = getIfNotBlank(params['version']);
|
||||
proxy['plugin-opts'] = {
|
||||
host: getIfNotBlank(params['host']),
|
||||
password: getIfNotBlank(params['password']),
|
||||
version: version ? parseInt(version, 10) : undefined,
|
||||
};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported plugin option: ${params.plugin}`,
|
||||
@@ -529,6 +555,9 @@ function URI_VLESS() {
|
||||
if (params.sid) {
|
||||
opts['short-id'] = params.sid;
|
||||
}
|
||||
if (params.spx) {
|
||||
opts['_spider-x'] = params.spx;
|
||||
}
|
||||
if (Object.keys(opts).length > 0) {
|
||||
// proxy[`${params.security}-opts`] = opts;
|
||||
proxy[`${params.security}-opts`] = opts;
|
||||
@@ -596,6 +625,13 @@ function URI_VLESS() {
|
||||
// mKCP 的伪装头部类型。当前可选值有 none / srtp / utp / wechat-video / dtls / wireguard。省略时默认值为 none,即不使用伪装头部,但不可以为空字符串。
|
||||
proxy.headerType = params.headerType || 'none';
|
||||
}
|
||||
|
||||
if (params.mode) {
|
||||
proxy._mode = params.mode;
|
||||
}
|
||||
if (params.extra) {
|
||||
proxy._extra = params.extra;
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
|
||||
@@ -39,12 +39,12 @@ start = (shadowsocksr/shadowsocks/vmess/vless/trojan/https/http/socks5/hysteria2
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/ssr_protocol_param/obfs_ssr/obfs_ssr_param/obfs_host/obfs_uri/fast_open/udp_relay/others)*{
|
||||
shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/ssr_protocol_param/obfs_ssr/obfs_ssr_param/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/others)*{
|
||||
proxy.type = "ssr";
|
||||
// handle ssr obfs
|
||||
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/others)* {
|
||||
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/ip_mode/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle ss obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -54,31 +54,31 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/others)* {
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
handleTransport();
|
||||
}
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "vless";
|
||||
handleTransport();
|
||||
}
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleTransport();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/others)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/others)* {
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
|
||||
@@ -169,6 +169,11 @@ ssr_protocol_param = comma "protocol-param" equals param:$[^=,]+ { proxy["protoc
|
||||
|
||||
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; }
|
||||
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; }
|
||||
@@ -177,6 +182,7 @@ tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pu
|
||||
|
||||
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
|
||||
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
|
||||
ip_mode = comma "ip-mode" equals match:[^,]+ { proxy["ip-version"] = match.join(""); }
|
||||
|
||||
ecn = comma "ecn" equals flag:bool { proxy.ecn = flag; }
|
||||
download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); }
|
||||
|
||||
@@ -37,12 +37,12 @@ start = (shadowsocksr/shadowsocks/vmess/vless/trojan/https/http/socks5/hysteria2
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/ssr_protocol_param/obfs_ssr/obfs_ssr_param/obfs_host/obfs_uri/fast_open/udp_relay/others)*{
|
||||
shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/ssr_protocol_param/obfs_ssr/obfs_ssr_param/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/others)*{
|
||||
proxy.type = "ssr";
|
||||
// handle ssr obfs
|
||||
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/others)* {
|
||||
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/ip_mode/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle ss obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -52,31 +52,31 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/others)* {
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
handleTransport();
|
||||
}
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "vless";
|
||||
handleTransport();
|
||||
}
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleTransport();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/others)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/others)* {
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/others)* {
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
|
||||
@@ -167,6 +167,11 @@ ssr_protocol_param = comma "protocol-param" equals param:$[^=,]+ { proxy["protoc
|
||||
|
||||
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; }
|
||||
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; }
|
||||
@@ -175,6 +180,7 @@ tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pu
|
||||
|
||||
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
|
||||
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
|
||||
ip_mode = comma "ip-mode" equals match:[^,]+ { proxy["ip-version"] = match.join(""); }
|
||||
|
||||
ecn = comma "ecn" equals flag:bool { proxy.ecn = flag; }
|
||||
download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); }
|
||||
|
||||
@@ -41,7 +41,7 @@ start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/udp_port/others)* {
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/udp_port/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -52,7 +52,7 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/
|
||||
}
|
||||
handleShadowTLS();
|
||||
}
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/tfo/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
if (proxy.aead) {
|
||||
@@ -63,25 +63,25 @@ vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/
|
||||
handleWebsocket();
|
||||
handleShadowTLS();
|
||||
}
|
||||
trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleWebsocket();
|
||||
handleShadowTLS();
|
||||
}
|
||||
https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
handleShadowTLS();
|
||||
}
|
||||
http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
handleShadowTLS();
|
||||
}
|
||||
ssh = tag equals "ssh" address (username password)? (usernamek passwordk)? (server_fingerprint/idle_timeout/private_key/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
ssh = tag equals "ssh" address (username password)? (usernamek passwordk)? (server_fingerprint/idle_timeout/private_key/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "ssh";
|
||||
handleShadowTLS();
|
||||
}
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "snell";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -104,20 +104,20 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
||||
proxy.type = "wireguard-surge";
|
||||
handleShadowTLS();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/fast_open/tfo/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
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)? (udp_relay/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
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)? (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/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
proxy.tls = true;
|
||||
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)* {
|
||||
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/tfo/block_quic/others)* {
|
||||
proxy.type = "direct";
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/udp_port/others)* {
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/udp_port/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -50,7 +50,7 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/
|
||||
}
|
||||
handleShadowTLS();
|
||||
}
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/tfo/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
if (proxy.aead) {
|
||||
@@ -61,25 +61,25 @@ vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/
|
||||
handleWebsocket();
|
||||
handleShadowTLS();
|
||||
}
|
||||
trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleWebsocket();
|
||||
handleShadowTLS();
|
||||
}
|
||||
https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
handleShadowTLS();
|
||||
}
|
||||
http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
handleShadowTLS();
|
||||
}
|
||||
ssh = tag equals "ssh" address (username password)? (usernamek passwordk)? (server_fingerprint/idle_timeout/private_key/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
ssh = tag equals "ssh" address (username password)? (usernamek passwordk)? (server_fingerprint/idle_timeout/private_key/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "ssh";
|
||||
handleShadowTLS();
|
||||
}
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "snell";
|
||||
// handle obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -106,16 +106,16 @@ hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying
|
||||
proxy.type = "hysteria2";
|
||||
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)? (udp_relay/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/fast_open/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
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)? (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/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
proxy.tls = true;
|
||||
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)* {
|
||||
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/tfo/block_quic/others)* {
|
||||
proxy.type = "direct";
|
||||
}
|
||||
address = comma server:server comma port:port {
|
||||
|
||||
@@ -80,6 +80,9 @@ port = digits:[0-9]+ {
|
||||
}
|
||||
|
||||
params = "?" head:param tail:("&"@param)* {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
params[key] = decodeURIComponent(value);
|
||||
}
|
||||
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
||||
proxy.sni = params["sni"] || params["peer"];
|
||||
proxy['client-fingerprint'] = params.fp;
|
||||
@@ -115,6 +118,27 @@ params = "?" head:param tail:("&"@param)* {
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade-fast-open", true);
|
||||
}
|
||||
}
|
||||
if (['reality'].includes(params.security)) {
|
||||
const opts = {};
|
||||
if (params.pbk) {
|
||||
opts['public-key'] = params.pbk;
|
||||
}
|
||||
if (params.sid) {
|
||||
opts['short-id'] = params.sid;
|
||||
}
|
||||
if (params.spx) {
|
||||
opts['_spider-x'] = params.spx;
|
||||
}
|
||||
if (params.mode) {
|
||||
proxy._mode = params.mode;
|
||||
}
|
||||
if (params.extra) {
|
||||
proxy._extra = params.extra;
|
||||
}
|
||||
if (Object.keys(opts).length > 0) {
|
||||
$set(proxy, params.security+"-opts", opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proxy.udp = toBool(params["udp"]);
|
||||
|
||||
@@ -78,6 +78,9 @@ port = digits:[0-9]+ {
|
||||
}
|
||||
|
||||
params = "?" head:param tail:("&"@param)* {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
params[key] = decodeURIComponent(value);
|
||||
}
|
||||
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
||||
proxy.sni = params["sni"] || params["peer"];
|
||||
proxy['client-fingerprint'] = params.fp;
|
||||
@@ -113,6 +116,27 @@ params = "?" head:param tail:("&"@param)* {
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade-fast-open", true);
|
||||
}
|
||||
}
|
||||
if (['reality'].includes(params.security)) {
|
||||
const opts = {};
|
||||
if (params.pbk) {
|
||||
opts['public-key'] = params.pbk;
|
||||
}
|
||||
if (params.sid) {
|
||||
opts['short-id'] = params.sid;
|
||||
}
|
||||
if (params.spx) {
|
||||
opts['_spider-x'] = params.spx;
|
||||
}
|
||||
if (params.mode) {
|
||||
proxy._mode = params.mode;
|
||||
}
|
||||
if (params.extra) {
|
||||
proxy._extra = params.extra;
|
||||
}
|
||||
if (Object.keys(opts).length > 0) {
|
||||
$set(proxy, params.security+"-opts", opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proxy.udp = toBool(params["udp"]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { safeLoad } from '@/utils/yaml';
|
||||
import { Base64 } from 'js-base64';
|
||||
import $ from '@/core/app';
|
||||
|
||||
function HTML() {
|
||||
const name = 'HTML';
|
||||
@@ -35,8 +36,15 @@ function Base64Encoded() {
|
||||
);
|
||||
};
|
||||
const parse = function (raw) {
|
||||
raw = Base64.decode(raw);
|
||||
return raw;
|
||||
const decoded = Base64.decode(raw);
|
||||
if (!/^\w+(:\/\/|\s*?=\s*?)\w+/m.test(decoded)) {
|
||||
$.error(
|
||||
`Base64 Pre-processor error: decoded line does not start with protocol`,
|
||||
);
|
||||
return raw;
|
||||
}
|
||||
|
||||
return decoded;
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
@@ -48,7 +56,7 @@ function Clash() {
|
||||
const content = safeLoad(raw);
|
||||
return content.proxies && Array.isArray(content.proxies);
|
||||
};
|
||||
const parse = function (raw) {
|
||||
const parse = function (raw, includeProxies) {
|
||||
// Clash YAML format
|
||||
|
||||
// 防止 VLESS节点 reality-opts 选项中的 short-id 被解析成 Infinity
|
||||
@@ -56,35 +64,40 @@ function Clash() {
|
||||
const afterReplace = raw.replace(
|
||||
/short-id:([ ]*[^,\n}]*)/g,
|
||||
(matched, value) => {
|
||||
const afterTrim = value.trim();
|
||||
|
||||
// 为空
|
||||
if (!afterTrim || afterTrim === '') {
|
||||
return 'short-id: ""'
|
||||
}
|
||||
|
||||
// 是否被引号包裹
|
||||
if (/^(['"]).*\1$/.test(afterTrim)) {
|
||||
return `short-id: ${afterTrim}`;
|
||||
} else {
|
||||
return `short-id: "${afterTrim}"`
|
||||
}
|
||||
}
|
||||
const afterTrim = value.trim();
|
||||
|
||||
// 为空
|
||||
if (!afterTrim || afterTrim === '') {
|
||||
return 'short-id: ""';
|
||||
}
|
||||
|
||||
// 是否被引号包裹
|
||||
if (/^(['"]).*\1$/.test(afterTrim)) {
|
||||
return `short-id: ${afterTrim}`;
|
||||
} else {
|
||||
return `short-id: "${afterTrim}"`;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const {
|
||||
proxies,
|
||||
'global-client-fingerprint': globalClientFingerprint,
|
||||
} = safeLoad(afterReplace);
|
||||
return proxies
|
||||
.map((p) => {
|
||||
// https://github.com/MetaCubeX/mihomo/blob/Alpha/docs/config.yaml#L73C1-L73C26
|
||||
if (globalClientFingerprint && !p['client-fingerprint']) {
|
||||
p['client-fingerprint'] = globalClientFingerprint;
|
||||
}
|
||||
return JSON.stringify(p);
|
||||
})
|
||||
.join('\n');
|
||||
return (
|
||||
(includeProxies ? 'proxies:\n' : '') +
|
||||
proxies
|
||||
.map((p) => {
|
||||
// https://github.com/MetaCubeX/mihomo/blob/Alpha/docs/config.yaml#L73C1-L73C26
|
||||
if (globalClientFingerprint && !p['client-fingerprint']) {
|
||||
p['client-fingerprint'] = globalClientFingerprint;
|
||||
}
|
||||
return `${includeProxies ? ' - ' : ''}${JSON.stringify(
|
||||
p,
|
||||
)}\n`;
|
||||
})
|
||||
.join('')
|
||||
);
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
@@ -365,7 +365,9 @@ function ScriptOperator(script, targetPlatform, $arguments, source, $options) {
|
||||
if (output?.$file?.type === 'mihomoProfile') {
|
||||
try {
|
||||
let patch = YAML.safeLoad(script);
|
||||
if (typeof patch !== 'object') patch = {};
|
||||
// if (typeof patch !== 'object') patch = {};
|
||||
if (typeof patch !== 'object')
|
||||
throw new Error('patch is not an object');
|
||||
output.$content = ProxyUtils.yaml.safeDump(
|
||||
deepMerge(
|
||||
{
|
||||
|
||||
@@ -81,6 +81,8 @@ export default function Clash_Producer() {
|
||||
proxy['preshared-key'] =
|
||||
proxy['preshared-key'] ?? proxy['pre-shared-key'];
|
||||
proxy['pre-shared-key'] = proxy['preshared-key'];
|
||||
} else if (proxy.type === 'snell' && proxy.version < 3) {
|
||||
delete proxy.udp;
|
||||
} else if (proxy.type === 'vless') {
|
||||
if (isPresent(proxy, 'sni')) {
|
||||
proxy.servername = proxy.sni;
|
||||
|
||||
@@ -32,9 +32,10 @@ export default function ClashMeta_Producer() {
|
||||
isPresent(proxy, 'cipher') &&
|
||||
![
|
||||
'auto',
|
||||
'none',
|
||||
'zero',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
'none',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
proxy.cipher = 'auto';
|
||||
@@ -86,6 +87,8 @@ export default function ClashMeta_Producer() {
|
||||
proxy['preshared-key'] =
|
||||
proxy['preshared-key'] ?? proxy['pre-shared-key'];
|
||||
proxy['pre-shared-key'] = proxy['preshared-key'];
|
||||
} else if (proxy.type === 'snell' && proxy.version < 3) {
|
||||
delete proxy.udp;
|
||||
} else if (proxy.type === 'vless') {
|
||||
if (isPresent(proxy, 'sni')) {
|
||||
proxy.servername = proxy.sni;
|
||||
|
||||
@@ -4,7 +4,7 @@ export default function Egern_Producer() {
|
||||
// https://egernapp.com/zh-CN/docs/configuration/proxies
|
||||
const list = proxies
|
||||
.filter((proxy) => {
|
||||
if (opts['include-unsupported-proxy']) return true;
|
||||
// if (opts['include-unsupported-proxy']) return true;
|
||||
if (
|
||||
![
|
||||
'http',
|
||||
@@ -14,6 +14,7 @@ export default function Egern_Producer() {
|
||||
'hysteria2',
|
||||
'vless',
|
||||
'vmess',
|
||||
'tuic',
|
||||
].includes(proxy.type) ||
|
||||
(proxy.type === 'ss' &&
|
||||
((proxy.plugin === 'obfs' &&
|
||||
@@ -47,17 +48,12 @@ export default function Egern_Producer() {
|
||||
'salsa20',
|
||||
'chacha20',
|
||||
'chacha20-ietf',
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
].includes(proxy.cipher))) ||
|
||||
(proxy.type === 'vmess' &&
|
||||
(![
|
||||
'auto',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
'none',
|
||||
'zero',
|
||||
].includes(proxy.cipher) ||
|
||||
(!['http', 'ws', 'tcp'].includes(proxy.network) &&
|
||||
proxy.network))) ||
|
||||
!['http', 'ws', 'tcp'].includes(proxy.network) &&
|
||||
proxy.network) ||
|
||||
(proxy.type === 'trojan' &&
|
||||
!['http', 'ws', 'tcp'].includes(proxy.network) &&
|
||||
proxy.network) ||
|
||||
@@ -65,7 +61,10 @@ export default function Egern_Producer() {
|
||||
(typeof proxy.flow !== 'undefined' ||
|
||||
proxy['reality-opts'] ||
|
||||
(!['http', 'ws', 'tcp'].includes(proxy.network) &&
|
||||
proxy.network)))
|
||||
proxy.network))) ||
|
||||
(proxy.type === 'tuic' &&
|
||||
proxy.token &&
|
||||
proxy.token.length !== 0)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -146,6 +145,23 @@ export default function Egern_Producer() {
|
||||
proxy.obfs = 'salamander';
|
||||
proxy.obfs_password = proxy['obfs-password'];
|
||||
}
|
||||
} else if (proxy.type === 'tuic') {
|
||||
proxy = {
|
||||
type: 'tuic',
|
||||
name: proxy.name,
|
||||
server: proxy.server,
|
||||
port: proxy.port,
|
||||
uuid: proxy.uuid,
|
||||
password: proxy.password,
|
||||
next_hop: proxy.next_hop,
|
||||
sni: proxy.sni,
|
||||
alpn: Array.isArray(proxy.alpn)
|
||||
? proxy.alpn
|
||||
: [proxy.alpn || 'h3'],
|
||||
skip_tls_verify: proxy['skip-cert-verify'],
|
||||
port_hopping: proxy.ports,
|
||||
port_hopping_interval: proxy['hop-interval'],
|
||||
};
|
||||
} else if (proxy.type === 'trojan') {
|
||||
if (proxy.network === 'ws') {
|
||||
proxy.websocket = {
|
||||
@@ -168,6 +184,20 @@ export default function Egern_Producer() {
|
||||
websocket: proxy.websocket,
|
||||
};
|
||||
} else if (proxy.type === 'vmess') {
|
||||
// Egern:传输层,支持 ws/wss/http1/http2/tls,不配置则为 tcp
|
||||
let security = proxy.cipher;
|
||||
if (
|
||||
security &&
|
||||
![
|
||||
'auto',
|
||||
'none',
|
||||
'zero',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
].includes(security)
|
||||
) {
|
||||
security = 'auto';
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
proxy.transport = {
|
||||
[proxy.tls ? 'wss' : 'ws']: {
|
||||
@@ -183,7 +213,7 @@ export default function Egern_Producer() {
|
||||
};
|
||||
} else if (proxy.network === 'http') {
|
||||
proxy.transport = {
|
||||
http: {
|
||||
http1: {
|
||||
method: proxy['http-opts']?.method,
|
||||
path: proxy['http-opts']?.path,
|
||||
headers: {
|
||||
@@ -196,9 +226,27 @@ export default function Egern_Producer() {
|
||||
skip_tls_verify: proxy['skip-cert-verify'],
|
||||
},
|
||||
};
|
||||
} else if (proxy.network === 'tcp' || !proxy.network) {
|
||||
} else if (proxy.network === 'h2') {
|
||||
proxy.transport = {
|
||||
[proxy.tls ? 'tls' : 'tcp']: {
|
||||
http2: {
|
||||
method: proxy['h2-opts']?.method,
|
||||
path: proxy['h2-opts']?.path,
|
||||
headers: {
|
||||
Host: Array.isArray(
|
||||
proxy['h2-opts']?.headers?.Host,
|
||||
)
|
||||
? proxy['h2-opts']?.headers?.Host[0]
|
||||
: proxy['h2-opts']?.headers?.Host,
|
||||
},
|
||||
skip_tls_verify: proxy['skip-cert-verify'],
|
||||
},
|
||||
};
|
||||
} else if (
|
||||
(proxy.network === 'tcp' || !proxy.network) &&
|
||||
proxy.tls
|
||||
) {
|
||||
proxy.transport = {
|
||||
tls: {
|
||||
sni: proxy.tls ? proxy.sni : undefined,
|
||||
skip_tls_verify: proxy.tls
|
||||
? proxy['skip-cert-verify']
|
||||
@@ -212,7 +260,7 @@ export default function Egern_Producer() {
|
||||
server: proxy.server,
|
||||
port: proxy.port,
|
||||
user_id: proxy.uuid,
|
||||
security: proxy.cipher,
|
||||
security,
|
||||
tfo: proxy.tfo || proxy['fast-open'],
|
||||
legacy: proxy.legacy,
|
||||
udp_relay:
|
||||
|
||||
@@ -3,11 +3,19 @@ const targetPlatform = 'Loon';
|
||||
import { isPresent, Result } from './utils';
|
||||
import { isIPv4, isIPv6 } from '@/utils';
|
||||
|
||||
const ipVersions = {
|
||||
dual: 'dual',
|
||||
ipv4: 'v4-only',
|
||||
ipv6: 'v6-only',
|
||||
'ipv4-prefer': 'prefer-v4',
|
||||
'ipv6-prefer': 'prefer-v6',
|
||||
};
|
||||
|
||||
export default function Loon_Producer() {
|
||||
const produce = (proxy, type, opts = {}) => {
|
||||
switch (proxy.type) {
|
||||
case 'ss':
|
||||
return shadowsocks(proxy, opts['include-unsupported-proxy']);
|
||||
return shadowsocks(proxy);
|
||||
case 'ssr':
|
||||
return shadowsocksr(proxy);
|
||||
case 'trojan':
|
||||
@@ -78,11 +86,50 @@ function shadowsocks(proxy) {
|
||||
`,obfs-uri=${proxy['plugin-opts'].path}`,
|
||||
'plugin-opts.path',
|
||||
);
|
||||
} else {
|
||||
} else if (!['shadow-tls'].includes(proxy.plugin)) {
|
||||
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');
|
||||
} 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',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
@@ -91,10 +138,13 @@ function shadowsocks(proxy) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
function shadowsocksr(proxy) {
|
||||
function shadowsocksr(proxy, includeUnsupportedProxy) {
|
||||
const result = new Result(proxy);
|
||||
result.append(
|
||||
`${proxy.name}=shadowsocksr,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.password}"`,
|
||||
@@ -111,6 +161,45 @@ function shadowsocksr(proxy) {
|
||||
result.appendIfPresent(`,obfs=${proxy.obfs}`, 'obfs');
|
||||
result.appendIfPresent(`,obfs-param=${proxy['obfs-param']}`, 'obfs-param');
|
||||
|
||||
// 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');
|
||||
} 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',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
@@ -119,6 +208,9 @@ function shadowsocksr(proxy) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@@ -171,6 +263,8 @@ function trojan(proxy) {
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
@@ -247,13 +341,16 @@ function vmess(proxy) {
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
const ip_version =
|
||||
ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
function vless(proxy) {
|
||||
if (proxy['reality-opts']) {
|
||||
throw new Error(`VLESS REALITY is unsupported`);
|
||||
if (typeof proxy.flow !== 'undefined' || proxy['reality-opts']) {
|
||||
throw new Error(`VLESS XTLS/REALITY is not supported`);
|
||||
}
|
||||
const result = new Result(proxy);
|
||||
result.append(
|
||||
@@ -319,6 +416,9 @@ function vless(proxy) {
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
const ip_version =
|
||||
ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
@@ -341,6 +441,8 @@ function http(proxy) {
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
@@ -369,6 +471,8 @@ function socks5(proxy) {
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
@@ -434,6 +538,8 @@ function wireguard(proxy) {
|
||||
presharedKey ?? ''
|
||||
}}]`,
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
@@ -481,6 +587,8 @@ function hysteria2(proxy) {
|
||||
);
|
||||
|
||||
result.appendIfPresent(`,ecn=${proxy.ecn}`, 'ecn');
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
|
||||
export default function ShadowRocket_Producer() {
|
||||
export default function Shadowrocket_Producer() {
|
||||
const type = 'ALL';
|
||||
const produce = (proxies, type, opts = {}) => {
|
||||
const list = proxies
|
||||
@@ -32,9 +32,10 @@ export default function ShadowRocket_Producer() {
|
||||
isPresent(proxy, 'cipher') &&
|
||||
![
|
||||
'auto',
|
||||
'none',
|
||||
'zero',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
'none',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
proxy.cipher = 'auto';
|
||||
@@ -102,6 +103,8 @@ export default function ShadowRocket_Producer() {
|
||||
proxy['preshared-key'] =
|
||||
proxy['preshared-key'] ?? proxy['pre-shared-key'];
|
||||
proxy['pre-shared-key'] = proxy['preshared-key'];
|
||||
} else if (proxy.type === 'snell' && proxy.version < 3) {
|
||||
delete proxy.udp;
|
||||
} else if (proxy.type === 'vless') {
|
||||
if (isPresent(proxy, 'sni')) {
|
||||
proxy.servername = proxy.sni;
|
||||
|
||||
@@ -187,6 +187,8 @@ export default function Stash_Producer() {
|
||||
proxy['preshared-key'] =
|
||||
proxy['preshared-key'] ?? proxy['pre-shared-key'];
|
||||
proxy['pre-shared-key'] = proxy['preshared-key'];
|
||||
} else if (proxy.type === 'snell' && proxy.version < 3) {
|
||||
delete proxy.udp;
|
||||
} else if (proxy.type === 'vless') {
|
||||
if (isPresent(proxy, 'sni')) {
|
||||
proxy.servername = proxy.sni;
|
||||
|
||||
@@ -53,7 +53,7 @@ export default function Surge_Producer() {
|
||||
return { produce };
|
||||
}
|
||||
|
||||
function shadowsocks(proxy, includeUnsupportedProxy) {
|
||||
function shadowsocks(proxy) {
|
||||
const result = new Result(proxy);
|
||||
result.append(`${proxy.name}=${proxy.type},${proxy.server},${proxy.port}`);
|
||||
if (!proxy.cipher) {
|
||||
@@ -87,9 +87,8 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
||||
'chacha20',
|
||||
'chacha20-ietf',
|
||||
'none',
|
||||
...(includeUnsupportedProxy
|
||||
? ['2022-blake3-aes-128-gcm', '2022-blake3-aes-256-gcm']
|
||||
: []),
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||
@@ -127,8 +126,6 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
|
||||
// udp-port
|
||||
result.appendIfPresent(`,udp-port=${proxy['udp-port']}`, 'udp-port');
|
||||
|
||||
// test-url
|
||||
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
|
||||
@@ -160,6 +157,8 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
||||
`,shadow-tls-sni=${proxy['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']) {
|
||||
const password = proxy['plugin-opts'].password;
|
||||
const host = proxy['plugin-opts'].host;
|
||||
@@ -177,6 +176,11 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
||||
}
|
||||
result.append(`,shadow-tls-version=${version}`);
|
||||
}
|
||||
// udp-port
|
||||
result.appendIfPresent(
|
||||
`,udp-port=${proxy['udp-port']}`,
|
||||
'udp-port',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ export default function URI_Producer() {
|
||||
}${opts.tls ? ';tls' : ''}`,
|
||||
);
|
||||
break;
|
||||
case 'shadow-tls':
|
||||
result += encodeURIComponent(
|
||||
`shadow-tls;host=${opts.host};password=${opts.password};version=${opts.version}`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported plugin option: ${proxy.plugin}`,
|
||||
@@ -149,6 +154,7 @@ export default function URI_Producer() {
|
||||
const isReality = proxy['reality-opts'];
|
||||
let sid = '';
|
||||
let pbk = '';
|
||||
let spx = '';
|
||||
if (isReality) {
|
||||
security = 'reality';
|
||||
const publicKey = proxy['reality-opts']?.['public-key'];
|
||||
@@ -159,6 +165,10 @@ export default function URI_Producer() {
|
||||
if (shortId) {
|
||||
sid = `&sid=${encodeURIComponent(shortId)}`;
|
||||
}
|
||||
const spiderX = proxy['reality-opts']?.['_spider-x'];
|
||||
if (spiderX) {
|
||||
spx = `&spx=${encodeURIComponent(spiderX)}`;
|
||||
}
|
||||
} else if (proxy.tls) {
|
||||
security = 'tls';
|
||||
}
|
||||
@@ -188,6 +198,14 @@ export default function URI_Producer() {
|
||||
if (proxy.flow) {
|
||||
flow = `&flow=${encodeURIComponent(proxy.flow)}`;
|
||||
}
|
||||
let extra = '';
|
||||
if (proxy._extra) {
|
||||
extra = `&extra=${encodeURIComponent(proxy._extra)}`;
|
||||
}
|
||||
let mode = '';
|
||||
if (proxy._mode) {
|
||||
mode = `&mode=${encodeURIComponent(proxy._mode)}`;
|
||||
}
|
||||
let vlessType = proxy.network;
|
||||
if (
|
||||
proxy.network === 'ws' &&
|
||||
@@ -254,7 +272,7 @@ export default function URI_Producer() {
|
||||
proxy.port
|
||||
}?security=${encodeURIComponent(
|
||||
security,
|
||||
)}${vlessTransport}${alpn}${allowInsecure}${sni}${fp}${flow}${sid}${pbk}#${encodeURIComponent(
|
||||
)}${vlessTransport}${alpn}${allowInsecure}${sni}${fp}${flow}${sid}${spx}${pbk}${mode}${extra}#${encodeURIComponent(
|
||||
proxy.name,
|
||||
)}`;
|
||||
break;
|
||||
@@ -324,11 +342,41 @@ export default function URI_Producer() {
|
||||
: proxy.alpn.join(','),
|
||||
)}`;
|
||||
}
|
||||
const trojanIsReality = proxy['reality-opts'];
|
||||
let trojanSid = '';
|
||||
let trojanPbk = '';
|
||||
let trojanSpx = '';
|
||||
let trojanSecurity = '';
|
||||
let trojanMode = '';
|
||||
let trojanExtra = '';
|
||||
if (trojanIsReality) {
|
||||
trojanSecurity = `&security=reality`;
|
||||
const publicKey = proxy['reality-opts']?.['public-key'];
|
||||
if (publicKey) {
|
||||
trojanPbk = `&pbk=${encodeURIComponent(publicKey)}`;
|
||||
}
|
||||
const shortId = proxy['reality-opts']?.['short-id'];
|
||||
if (shortId) {
|
||||
trojanSid = `&sid=${encodeURIComponent(shortId)}`;
|
||||
}
|
||||
const spiderX = proxy['reality-opts']?.['_spider-x'];
|
||||
if (spiderX) {
|
||||
trojanSpx = `&spx=${encodeURIComponent(spiderX)}`;
|
||||
}
|
||||
if (proxy._extra) {
|
||||
trojanExtra = `&extra=${encodeURIComponent(
|
||||
proxy._extra,
|
||||
)}`;
|
||||
}
|
||||
if (proxy._mode) {
|
||||
trojanMode = `&mode=${encodeURIComponent(proxy._mode)}`;
|
||||
}
|
||||
}
|
||||
result = `trojan://${proxy.password}@${proxy.server}:${
|
||||
proxy.port
|
||||
}?sni=${encodeURIComponent(proxy.sni || proxy.server)}${
|
||||
proxy['skip-cert-verify'] ? '&allowInsecure=1' : ''
|
||||
}${trojanTransport}${trojanAlpn}${trojanFp}#${encodeURIComponent(
|
||||
}${trojanTransport}${trojanAlpn}${trojanFp}${trojanSecurity}${trojanSid}${trojanPbk}${trojanSpx}${trojanMode}${trojanExtra}#${encodeURIComponent(
|
||||
proxy.name,
|
||||
)}`;
|
||||
break;
|
||||
|
||||
@@ -84,6 +84,7 @@ async function doSync() {
|
||||
const files = {};
|
||||
|
||||
try {
|
||||
const valid = [];
|
||||
const invalid = [];
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
@@ -156,27 +157,46 @@ async function doSync() {
|
||||
files[encodeURIComponent(artifact.name)] = {
|
||||
content: output,
|
||||
};
|
||||
|
||||
valid.push(artifact.name);
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`同步配置 ${artifact.name} 发生错误: ${e.message ?? e}`,
|
||||
`生成同步配置 ${artifact.name} 发生错误: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
invalid.push(artifact.name);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if (invalid.length > 0) {
|
||||
$.info(`${valid.length} 个同步配置生成成功: ${valid.join(', ')}`);
|
||||
$.info(`${invalid.length} 个同步配置生成失败: ${invalid.join(', ')}`);
|
||||
|
||||
if (valid.length === 0) {
|
||||
throw new Error(
|
||||
`同步配置 ${invalid.join(', ')} 发生错误 详情请查看日志`,
|
||||
`同步配置 ${invalid.join(', ')} 生成失败 详情请查看日志`,
|
||||
);
|
||||
}
|
||||
|
||||
const resp = await syncToGist(files);
|
||||
const body = JSON.parse(resp.body);
|
||||
delete body.history;
|
||||
delete body.forks;
|
||||
delete body.owner;
|
||||
Object.values(body.files).forEach((file) => {
|
||||
delete file.content;
|
||||
});
|
||||
$.info('上传配置响应:');
|
||||
$.info(JSON.stringify(body, null, 2));
|
||||
|
||||
for (const artifact of allArtifacts) {
|
||||
if (artifact.sync) {
|
||||
if (
|
||||
artifact.sync &&
|
||||
artifact.source &&
|
||||
valid.includes(artifact.name)
|
||||
) {
|
||||
artifact.updated = new Date().getTime();
|
||||
// extract real url from gist
|
||||
let files = body.files;
|
||||
@@ -204,9 +224,18 @@ async function doSync() {
|
||||
}
|
||||
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
$.notify('🌍 Sub-Store', '全部订阅同步成功!');
|
||||
$.info('上传配置成功');
|
||||
|
||||
if (invalid.length > 0) {
|
||||
$.notify(
|
||||
'🌍 Sub-Store',
|
||||
`同步配置成功 ${valid.length} 个, 失败 ${invalid.length} 个, 详情请查看日志`,
|
||||
);
|
||||
} else {
|
||||
$.notify('🌍 Sub-Store', '同步配置完成');
|
||||
}
|
||||
} catch (e) {
|
||||
$.notify('🌍 Sub-Store', '同步订阅失败', `原因:${e.message ?? e}`);
|
||||
$.error(`无法同步订阅配置到 Gist,原因:${e}`);
|
||||
$.notify('🌍 Sub-Store', '同步配置失败', `原因:${e.message ?? e}`);
|
||||
$.error(`无法同步配置到 Gist,原因:${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { getPlatformFromHeaders } from '@/utils/user-agent';
|
||||
import {
|
||||
getPlatformFromHeaders,
|
||||
shouldIncludeUnsupportedProxy,
|
||||
} from '@/utils/user-agent';
|
||||
import { ProxyUtils } from '@/core/proxy-utils';
|
||||
import { COLLECTIONS_KEY, SUBS_KEY } from '@/constants';
|
||||
import { findByName } from '@/utils/database';
|
||||
@@ -161,7 +164,19 @@ async function downloadSubscription(req, res) {
|
||||
}
|
||||
if (includeUnsupportedProxy) {
|
||||
includeUnsupportedProxy = decodeURIComponent(includeUnsupportedProxy);
|
||||
$.info(`包含不支持的节点: ${includeUnsupportedProxy}`);
|
||||
$.info(
|
||||
`包含官方/商店版/未续费订阅不支持的协议: ${includeUnsupportedProxy}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!includeUnsupportedProxy &&
|
||||
shouldIncludeUnsupportedProxy(platform, reqUA)
|
||||
) {
|
||||
includeUnsupportedProxy = true;
|
||||
$.info(
|
||||
`当前客户端可包含官方/商店版/未续费订阅不支持的协议: ${includeUnsupportedProxy}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (useMihomoExternal) {
|
||||
@@ -278,7 +293,10 @@ async function downloadSubscription(req, res) {
|
||||
}
|
||||
res.set(
|
||||
'subscription-userinfo',
|
||||
[subUserInfo, flowInfo].filter((i) => i).join('; '),
|
||||
[subUserInfo, flowInfo]
|
||||
.filter((i) => i)
|
||||
.join('; ')
|
||||
.replace(/\s*;\s*;\s*/g, ';'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -342,11 +360,9 @@ async function downloadCollection(req, res) {
|
||||
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const collection = findByName(allCols, name);
|
||||
|
||||
const reqUA = req.headers['user-agent'] || req.headers['User-Agent'];
|
||||
$.info(
|
||||
`正在下载组合订阅:${name}\n请求 User-Agent: ${
|
||||
req.headers['user-agent'] || req.headers['User-Agent']
|
||||
}\n请求 target: ${req.query.target}\n实际输出: ${platform}`,
|
||||
`正在下载组合订阅:${name}\n请求 User-Agent: ${reqUA}\n请求 target: ${req.query.target}\n实际输出: ${platform}`,
|
||||
);
|
||||
|
||||
let {
|
||||
@@ -393,7 +409,18 @@ async function downloadCollection(req, res) {
|
||||
|
||||
if (includeUnsupportedProxy) {
|
||||
includeUnsupportedProxy = decodeURIComponent(includeUnsupportedProxy);
|
||||
$.info(`包含不支持的节点: ${includeUnsupportedProxy}`);
|
||||
$.info(
|
||||
`包含官方/商店版/未续费订阅不支持的协议: ${includeUnsupportedProxy}`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
!includeUnsupportedProxy &&
|
||||
shouldIncludeUnsupportedProxy(platform, reqUA)
|
||||
) {
|
||||
includeUnsupportedProxy = true;
|
||||
$.info(
|
||||
`当前客户端可包含官方/商店版/未续费订阅不支持的协议: ${includeUnsupportedProxy}`,
|
||||
);
|
||||
}
|
||||
if (useMihomoExternal) {
|
||||
$.info(`手动指定了 target 为 SurgeMac, 将使用 Mihomo External`);
|
||||
@@ -417,6 +444,7 @@ async function downloadCollection(req, res) {
|
||||
$options,
|
||||
proxy,
|
||||
noCache,
|
||||
ua: reqUA,
|
||||
});
|
||||
let subUserInfoOfSub;
|
||||
// forward flow header from the first subscription in this collection
|
||||
@@ -522,13 +550,15 @@ async function downloadCollection(req, res) {
|
||||
} else {
|
||||
subUserInfoOfCol = collection.subUserinfo;
|
||||
}
|
||||
res.set(
|
||||
'subscription-userinfo',
|
||||
[subUserInfoOfCol, subUserInfoOfSub]
|
||||
.filter((i) => i)
|
||||
.join('; '),
|
||||
);
|
||||
|
||||
const subUserInfo = [subUserInfoOfCol, subUserInfoOfSub]
|
||||
.filter((i) => i)
|
||||
.join('; ');
|
||||
if (subUserInfo) {
|
||||
res.set(
|
||||
'subscription-userinfo',
|
||||
subUserInfo.replace(/\s*;\s*;\s*/g, ';'),
|
||||
);
|
||||
}
|
||||
if (platform === 'JSON') {
|
||||
if (resultFormat === 'nezha') {
|
||||
output = nezhaTransform(output);
|
||||
|
||||
@@ -146,7 +146,10 @@ async function getFile(req, res) {
|
||||
proxy || file.proxy,
|
||||
);
|
||||
if (flowInfo) {
|
||||
res.set('subscription-userinfo', flowInfo);
|
||||
res.set(
|
||||
'subscription-userinfo',
|
||||
flowInfo.replace(/\s*;\s*;\s*/g, ';'),
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -216,6 +216,14 @@ async function produceArtifact({
|
||||
await Promise.all(
|
||||
subnames.map(async (name) => {
|
||||
const sub = findByName(allSubs, name);
|
||||
const passThroughUA = sub.passThroughUA;
|
||||
let reqUA = sub.ua;
|
||||
if (passThroughUA) {
|
||||
$.info(
|
||||
`订阅开启了透传 User-Agent, 使用请求的 User-Agent: ${ua}`,
|
||||
);
|
||||
reqUA = ua;
|
||||
}
|
||||
try {
|
||||
$.info(`正在处理子订阅:${sub.name}...`);
|
||||
let raw;
|
||||
@@ -237,7 +245,7 @@ async function produceArtifact({
|
||||
try {
|
||||
return await download(
|
||||
url,
|
||||
sub.ua,
|
||||
reqUA,
|
||||
undefined,
|
||||
proxy ||
|
||||
sub.proxy ||
|
||||
@@ -532,6 +540,7 @@ async function syncArtifacts() {
|
||||
const files = {};
|
||||
|
||||
try {
|
||||
const valid = [];
|
||||
const invalid = [];
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
@@ -606,27 +615,47 @@ async function syncArtifacts() {
|
||||
files[encodeURIComponent(artifact.name)] = {
|
||||
content: output,
|
||||
};
|
||||
|
||||
valid.push(artifact.name);
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`同步配置 ${artifact.name} 发生错误: ${e.message ?? e}`,
|
||||
`生成同步配置 ${artifact.name} 发生错误: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
invalid.push(artifact.name);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if (invalid.length > 0) {
|
||||
$.info(`${valid.length} 个同步配置生成成功: ${valid.join(', ')}`);
|
||||
$.info(`${invalid.length} 个同步配置生成失败: ${invalid.join(', ')}`);
|
||||
|
||||
if (valid.length === 0) {
|
||||
throw new Error(
|
||||
`同步配置 ${invalid.join(', ')} 发生错误 详情请查看日志`,
|
||||
`同步配置 ${invalid.join(', ')} 生成失败 详情请查看日志`,
|
||||
);
|
||||
}
|
||||
|
||||
const resp = await syncToGist(files);
|
||||
const body = JSON.parse(resp.body);
|
||||
|
||||
delete body.history;
|
||||
delete body.forks;
|
||||
delete body.owner;
|
||||
Object.values(body.files).forEach((file) => {
|
||||
delete file.content;
|
||||
});
|
||||
$.info('上传配置响应:');
|
||||
$.info(JSON.stringify(body, null, 2));
|
||||
|
||||
for (const artifact of allArtifacts) {
|
||||
if (artifact.sync) {
|
||||
if (
|
||||
artifact.sync &&
|
||||
artifact.source &&
|
||||
valid.includes(artifact.name)
|
||||
) {
|
||||
artifact.updated = new Date().getTime();
|
||||
// extract real url from gist
|
||||
let files = body.files;
|
||||
@@ -654,9 +683,17 @@ async function syncArtifacts() {
|
||||
}
|
||||
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
$.info('全部订阅同步成功!');
|
||||
$.info('上传配置成功');
|
||||
|
||||
if (invalid.length > 0) {
|
||||
throw new Error(
|
||||
`同步配置成功 ${valid.length} 个, 失败 ${invalid.length} 个, 详情请查看日志`,
|
||||
);
|
||||
} else {
|
||||
$.info(`同步配置成功 ${valid.length} 个`);
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(`同步订阅失败,原因:${e.message ?? e}`);
|
||||
$.error(`同步配置失败,原因:${e.message ?? e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -666,7 +703,7 @@ async function syncAllArtifacts(_, res) {
|
||||
await syncArtifacts();
|
||||
success(res);
|
||||
} catch (e) {
|
||||
$.error(`同步订阅失败,原因:${e.message ?? e}`);
|
||||
$.error(`同步配置失败,原因:${e.message ?? e}`);
|
||||
failed(
|
||||
res,
|
||||
new InternalServerError(
|
||||
@@ -743,6 +780,16 @@ async function syncArtifact(req, res) {
|
||||
});
|
||||
artifact.updated = new Date().getTime();
|
||||
const body = JSON.parse(resp.body);
|
||||
|
||||
delete body.history;
|
||||
delete body.forks;
|
||||
delete body.owner;
|
||||
Object.values(body.files).forEach((file) => {
|
||||
delete file.content;
|
||||
});
|
||||
$.info('上传配置响应:');
|
||||
$.info(JSON.stringify(body, null, 2));
|
||||
|
||||
let files = body.files;
|
||||
let isGitLab;
|
||||
if (Array.isArray(files)) {
|
||||
|
||||
@@ -207,7 +207,7 @@ export default async function download(
|
||||
if (preprocess) {
|
||||
try {
|
||||
if (clashPreprocessor.test(body)) {
|
||||
body = clashPreprocessor.parse(body);
|
||||
body = clashPreprocessor.parse(body, true);
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(`Clash Pre-processor error: ${e}`);
|
||||
|
||||
@@ -114,15 +114,17 @@ export default class Gist {
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
return this.http.get('/gists').then((response) => {
|
||||
const gists = JSON.parse(response.body);
|
||||
for (let g of gists) {
|
||||
if (g.description === this.key) {
|
||||
return g;
|
||||
return this.http
|
||||
.get('/gists?per_page=100&page=1')
|
||||
.then((response) => {
|
||||
const gists = JSON.parse(response.body);
|
||||
for (let g of gists) {
|
||||
if (g.description === this.key) {
|
||||
return g;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
});
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,17 @@ function numberToString(value) {
|
||||
: BigInt(value).toString();
|
||||
}
|
||||
|
||||
function isValidUUID(uuid) {
|
||||
return (
|
||||
typeof uuid === 'string' &&
|
||||
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(
|
||||
uuid,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
isValidUUID,
|
||||
ipAddress,
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import gte from 'semver/functions/gte';
|
||||
import coerce from 'semver/functions/coerce';
|
||||
import $ from '@/core/app';
|
||||
|
||||
export function getUserAgentFromHeaders(headers) {
|
||||
const keys = Object.keys(headers);
|
||||
let UA = '';
|
||||
@@ -56,3 +60,32 @@ export function getPlatformFromHeaders(headers) {
|
||||
const { UA, ua, accept } = getUserAgentFromHeaders(headers);
|
||||
return getPlatformFromUserAgent({ ua, UA, accept });
|
||||
}
|
||||
export function shouldIncludeUnsupportedProxy(platform, ua) {
|
||||
try {
|
||||
const target = getPlatformFromUserAgent({
|
||||
UA: ua,
|
||||
ua: ua.toLowerCase(),
|
||||
});
|
||||
if (!['Stash', 'Egern'].includes(target)) {
|
||||
return false;
|
||||
}
|
||||
const version = coerce(ua).version;
|
||||
if (
|
||||
platform === 'Stash' &&
|
||||
target === 'Stash' &&
|
||||
gte(version, '2.8.0')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
platform === 'Egern' &&
|
||||
target === 'Egern' &&
|
||||
gte(version, '1.29.0')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(`获取版本号失败: ${e}`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user