Compare commits

...

9 Commits

19 changed files with 289 additions and 51 deletions

View File

@@ -32,7 +32,7 @@ Core functionalities:
- [x] V2RayN URI
- [x] Hysteria2 URI
- [x] QX (SS, SSR, VMess, Trojan, HTTP)
- [x] Loon (SS, SSR, VMess, Trojan, HTTP, WireGuard, VLESS)
- [x] Loon (SS, SSR, VMess, Trojan, HTTP, WireGuard, VLESS, Hysteria2)
- [x] Surge (SS, VMess, Trojan, HTTP, TUIC, Snell, Hysteria2, SSR(external, only for macOS), WireGuard(Surge to Surge))
- [x] ShadowRocket (SS, SSR, VMess, Trojan, HTTP, Snell, VLESS, Hysteria2)
- [x] Clash.Meta (SS, SSR, VMess, Trojan, HTTP, Snell, VLESS, WireGuard, Hysteria, Hysteria2)
@@ -76,24 +76,20 @@ Core functionalities:
### Development
Go to `backend` and `web` directories, install node dependencies:
Install `pnpm`
Go to `backend` directories, install node dependencies:
```
npm install
pnpm install
```
1. In `backend`, run the backend server on http://localhost:3000
```
npm run serve
pnpm start
```
2. In`web`, start the vue-cli server
```
npm start
```
## LICENSE
This project is under the GPL V3 LICENSE.

View File

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

View File

@@ -212,6 +212,14 @@ function lastParse(proxy) {
if (['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(proxy.type)) {
proxy.tls = true;
}
if (proxy.network) {
let transportHost = proxy[`${proxy.network}-opts`]?.headers?.Host;
let transporthost = proxy[`${proxy.network}-opts`]?.headers?.host;
if (transporthost && !transportHost) {
proxy[`${proxy.network}-opts`].headers.Host = transporthost;
delete proxy[`${proxy.network}-opts`].headers.host;
}
}
if (proxy.tls && !proxy.sni) {
if (proxy.network) {
let transportHost = proxy[`${proxy.network}-opts`]?.headers?.Host;

View File

@@ -463,8 +463,16 @@ function URI_Trojan() {
};
const parse = (line) => {
let [newLine, name] = line.split(/#(.+)/, 2);
const parser = getTrojanURIParser();
const proxy = parser.parse(line);
const proxy = parser.parse(newLine);
if (isNotBlank(name)) {
try {
proxy.name = decodeURIComponent(name);
} catch (e) {
console.log(e);
}
}
return proxy;
};
return { name, test, parse };
@@ -487,7 +495,7 @@ function Clash_All() {
'ss',
'ssr',
'vmess',
'socks',
'socks5',
'http',
'snell',
'trojan',
@@ -519,6 +527,10 @@ function Clash_All() {
}
}
if (proxy['benchmark-url']) {
proxy['test-url'] = proxy['benchmark-url'];
}
return proxy;
};
return { name, test, parse };
@@ -642,6 +654,15 @@ function Loon_Trojan() {
const parse = (line) => getLoonParser().parse(line);
return { name, test, parse };
}
function Loon_Hysteria2() {
const name = 'Loon Hysteria2 Parser';
const test = (line) => {
return /^.*=\s*Hysteria2/i.test(line.split(',')[0]);
};
const parse = (line) => getLoonParser().parse(line);
return { name, test, parse };
}
function Loon_Http() {
const name = 'Loon HTTP Parser';
@@ -864,6 +885,7 @@ export default [
Loon_SSR(),
Loon_VMess(),
Loon_Vless(),
Loon_Hysteria2(),
Loon_Trojan(),
Loon_Http(),
Loon_WireGuard(),

View File

@@ -35,7 +35,7 @@ const grammars = String.raw`
}
}
start = (shadowsocksr/shadowsocks/vmess/vless/trojan/https/http) {
start = (shadowsocksr/shadowsocks/vmess/vless/trojan/https/http/hysteria2) {
return proxy;
}
@@ -68,6 +68,9 @@ trojan = tag equals "trojan"i address password (transport/transport_host/transpo
proxy.type = "trojan";
handleTransport();
}
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/udp_relay/download_bandwidth/ecn/others)* {
proxy.type = "hysteria2";
}
https = tag equals "https"i address (username password)? (tls_host/tls_verification/fast_open/udp_relay/others)* {
proxy.type = "http";
proxy.tls = true;
@@ -167,6 +170,9 @@ tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
ecn = comma "ecn" equals flag:bool { proxy.ecn = flag; }
download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); }
tag = match:[^=,]* { proxy.name = match.join("").trim(); }
comma = _ "," _
equals = _ "=" _

View File

@@ -33,7 +33,7 @@
}
}
start = (shadowsocksr/shadowsocks/vmess/vless/trojan/https/http) {
start = (shadowsocksr/shadowsocks/vmess/vless/trojan/https/http/hysteria2) {
return proxy;
}
@@ -66,6 +66,9 @@ trojan = tag equals "trojan"i address password (transport/transport_host/transpo
proxy.type = "trojan";
handleTransport();
}
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/udp_relay/download_bandwidth/ecn/others)* {
proxy.type = "hysteria2";
}
https = tag equals "https"i address (username password)? (tls_host/tls_verification/fast_open/udp_relay/others)* {
proxy.type = "http";
proxy.tls = true;
@@ -165,6 +168,9 @@ tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
ecn = comma "ecn" equals flag:bool { proxy.ecn = flag; }
download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); }
tag = match:[^=,]* { proxy.name = match.join("").trim(); }
comma = _ "," _
equals = _ "=" _

View File

@@ -43,13 +43,13 @@ start = (trojan/shadowsocks/vmess/http/socks5) {
}
trojan = "trojan" equals address
(password/over_tls/tls_host/tls_fingerprint/tls_verification/obfs/obfs_host/obfs_uri/tag/udp_relay/udp_over_tcp/fast_open/others)* {
(password/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/obfs/obfs_host/obfs_uri/tag/udp_relay/udp_over_tcp/fast_open/server_check_url/others)* {
proxy.type = "trojan";
handleObfs();
}
shadowsocks = "shadowsocks" equals address
(password/method/obfs_ssr/obfs_ss/obfs_host/obfs_uri/ssr_protocol/ssr_protocol_param/tls_fingerprint/tls_verification/udp_relay/udp_over_tcp/fast_open/tag/others)* {
(password/method/obfs_ssr/obfs_ss/obfs_host/obfs_uri/ssr_protocol/ssr_protocol_param/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/udp_relay/udp_over_tcp/fast_open/tag/server_check_url/others)* {
if (proxy.protocol) {
proxy.type = "ssr";
// handle ssr obfs
@@ -80,7 +80,7 @@ shadowsocks = "shadowsocks" equals address
}
vmess = "vmess" equals address
(uuid/method/over_tls/tls_host/tls_fingerprint/tls_verification/tag/obfs/obfs_host/obfs_uri/udp_relay/udp_over_tcp/fast_open/aead/others)* {
(uuid/method/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/obfs/obfs_host/obfs_uri/udp_relay/udp_over_tcp/fast_open/aead/server_check_url/others)* {
proxy.type = "vmess";
proxy.cipher = proxy.cipher || "none";
if (proxy.aead) {
@@ -92,12 +92,12 @@ vmess = "vmess" equals address
}
http = "http" equals address
(username/password/over_tls/tls_host/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/others)*{
(username/password/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/server_check_url/others)*{
proxy.type = "http";
}
socks5 = "socks5" equals address
(username/password/password/over_tls/tls_host/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/others)* {
(username/password/password/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/server_check_url/others)* {
proxy.type = "socks5";
}
@@ -155,6 +155,14 @@ tls_verification = comma "tls-verification" equals flag:bool {
proxy["skip-cert-verify"] = !flag;
}
tls_fingerprint = comma "tls-cert-sha256" equals tls_fingerprint:$[^,]+ { proxy["tls-fingerprint"] = tls_fingerprint.trim(); }
tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals param:$[^=,]+ { proxy["tls-pubkey-sha256"] = param; }
tls_alpn = comma "tls-alpn" equals param:$[^=,]+ { proxy["tls-alpn"] = param; }
tls_no_session_ticket = comma "tls-no-session-ticket" equals flag:bool {
proxy["tls-no-session-ticket"] = flag;
}
tls_no_session_reuse = comma "tls-no-session-reuse" equals flag:bool {
proxy["tls-no-session-reuse"] = flag;
}
obfs_ss = comma "obfs" equals type:("http"/"tls"/"wss"/"ws"/"over-tls") { obfs.type = type; return type; }
obfs_ssr = comma "obfs" equals type:("plain"/"http_simple"/"http_post"/"random_head"/"tls1.2_ticket_auth"/"tls1.2_ticket_fastauth") { obfs.type = type; return type; }
@@ -166,6 +174,8 @@ obfs_uri = comma "obfs-uri" equals uri:uri { obfs.path = uri; }
ssr_protocol = comma "ssr-protocol" equals protocol:("origin"/"auth_sha1_v4"/"auth_aes128_md5"/"auth_aes128_sha1"/"auth_chain_a"/"auth_chain_b") { proxy.protocol = protocol; return protocol; }
ssr_protocol_param = comma "ssr-protocol-param" equals param:$[^=,]+ { proxy["protocol-param"] = param; }
server_check_url = comma "server_check_url" equals param:$[^=,]+ { proxy["test-url"] = param; }
uri = $[^,]+
tag = comma "tag" equals tag:[^=,]+ { proxy.name = tag.join(""); }

View File

@@ -41,13 +41,13 @@ start = (trojan/shadowsocks/vmess/http/socks5) {
}
trojan = "trojan" equals address
(password/over_tls/tls_host/tls_fingerprint/tls_verification/obfs/obfs_host/obfs_uri/tag/udp_relay/udp_over_tcp/fast_open/others)* {
(password/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/obfs/obfs_host/obfs_uri/tag/udp_relay/udp_over_tcp/fast_open/server_check_url/others)* {
proxy.type = "trojan";
handleObfs();
}
shadowsocks = "shadowsocks" equals address
(password/method/obfs_ssr/obfs_ss/obfs_host/obfs_uri/ssr_protocol/ssr_protocol_param/tls_fingerprint/tls_verification/udp_relay/udp_over_tcp/fast_open/tag/others)* {
(password/method/obfs_ssr/obfs_ss/obfs_host/obfs_uri/ssr_protocol/ssr_protocol_param/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/udp_relay/udp_over_tcp/fast_open/tag/server_check_url/others)* {
if (proxy.protocol) {
proxy.type = "ssr";
// handle ssr obfs
@@ -78,7 +78,7 @@ shadowsocks = "shadowsocks" equals address
}
vmess = "vmess" equals address
(uuid/method/over_tls/tls_host/tls_fingerprint/tls_verification/tag/obfs/obfs_host/obfs_uri/udp_relay/udp_over_tcp/fast_open/aead/others)* {
(uuid/method/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/obfs/obfs_host/obfs_uri/udp_relay/udp_over_tcp/fast_open/aead/server_check_url/others)* {
proxy.type = "vmess";
proxy.cipher = proxy.cipher || "none";
if (proxy.aead) {
@@ -90,12 +90,12 @@ vmess = "vmess" equals address
}
http = "http" equals address
(username/password/over_tls/tls_host/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/others)*{
(username/password/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/server_check_url/others)*{
proxy.type = "http";
}
socks5 = "socks5" equals address
(username/password/password/over_tls/tls_host/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/others)* {
(username/password/password/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/fast_open/udp_relay/udp_over_tcp/server_check_url/others)* {
proxy.type = "socks5";
}
@@ -153,6 +153,14 @@ tls_verification = comma "tls-verification" equals flag:bool {
proxy["skip-cert-verify"] = !flag;
}
tls_fingerprint = comma "tls-cert-sha256" equals tls_fingerprint:$[^,]+ { proxy["tls-fingerprint"] = tls_fingerprint.trim(); }
tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals param:$[^=,]+ { proxy["tls-pubkey-sha256"] = param; }
tls_alpn = comma "tls-alpn" equals param:$[^=,]+ { proxy["tls-alpn"] = param; }
tls_no_session_ticket = comma "tls-no-session-ticket" equals flag:bool {
proxy["tls-no-session-ticket"] = flag;
}
tls_no_session_reuse = comma "tls-no-session-reuse" equals flag:bool {
proxy["tls-no-session-reuse"] = flag;
}
obfs_ss = comma "obfs" equals type:("http"/"tls"/"wss"/"ws"/"over-tls") { obfs.type = type; return type; }
obfs_ssr = comma "obfs" equals type:("plain"/"http_simple"/"http_post"/"random_head"/"tls1.2_ticket_auth"/"tls1.2_ticket_fastauth") { obfs.type = type; return type; }
@@ -164,6 +172,8 @@ obfs_uri = comma "obfs-uri" equals uri:uri { obfs.path = uri; }
ssr_protocol = comma "ssr-protocol" equals protocol:("origin"/"auth_sha1_v4"/"auth_aes128_md5"/"auth_aes128_sha1"/"auth_chain_a"/"auth_chain_b") { proxy.protocol = protocol; return protocol; }
ssr_protocol_param = comma "ssr-protocol-param" equals param:$[^=,]+ { proxy["protocol-param"] = param; }
server_check_url = comma "server_check_url" equals param:$[^=,]+ { proxy["test-url"] = param; }
uri = $[^,]+
tag = comma "tag" equals tag:[^=,]+ { proxy.name = tag.join(""); }

View File

@@ -60,11 +60,11 @@ trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tl
proxy.type = "trojan";
handleWebsocket();
}
https = tag equals "https" address (username password)? (sni/tls_fingerprint/tls_verification/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/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "http";
proxy.tls = true;
}
http = tag equals "http" address (username password)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
http = tag equals "http" address (username password)? (usernamek passwordk)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "http";
}
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
@@ -89,10 +89,10 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "hysteria2";
}
socks5 = tag equals "socks5" address (username password)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "socks5";
}
socks5_tls = tag equals "socks5-tls" address (username password)? (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)? (sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "socks5";
proxy.tls = true;
}
@@ -163,6 +163,7 @@ tls_fingerprint = comma "server-cert-fingerprint-sha256" equals tls_fingerprint:
snell_psk = comma "psk" equals match:[^,]+ { proxy.psk = match.join(""); }
snell_version = comma "version" equals match:$[0-9]+ { proxy.version = parseInt(match.trim()); }
usernamek = comma "username" equals match:[^,]+ { proxy.username = match.join(""); }
passwordk = comma "password" equals match:[^,]+ { proxy.password = match.join(""); }
vmess_uuid = comma "username" equals match:[^,]+ { proxy.uuid = match.join(""); }
vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }

View File

@@ -58,11 +58,11 @@ trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tl
proxy.type = "trojan";
handleWebsocket();
}
https = tag equals "https" address (username password)? (sni/tls_fingerprint/tls_verification/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/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "http";
proxy.tls = true;
}
http = tag equals "http" address (username password)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
http = tag equals "http" address (username password)? (usernamek passwordk)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "http";
}
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
@@ -87,10 +87,10 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "hysteria2";
}
socks5 = tag equals "socks5" address (username password)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "socks5";
}
socks5_tls = tag equals "socks5-tls" address (username password)? (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)? (sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
proxy.type = "socks5";
proxy.tls = true;
}
@@ -161,6 +161,7 @@ tls_fingerprint = comma "server-cert-fingerprint-sha256" equals tls_fingerprint:
snell_psk = comma "psk" equals match:[^,]+ { proxy.psk = match.join(""); }
snell_version = comma "version" equals match:$[0-9]+ { proxy.version = parseInt(match.trim()); }
usernamek = comma "username" equals match:[^,]+ { proxy.username = match.join(""); }
passwordk = comma "password" equals match:[^,]+ { proxy.password = match.join(""); }
vmess_uuid = comma "username" equals match:[^,]+ { proxy.uuid = match.join(""); }
vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }

View File

@@ -8,6 +8,7 @@ import $ from '@/core/app';
import { hex_md5 } from '@/vendor/md5';
import { ProxyUtils } from '@/core/proxy-utils';
import env from '@/utils/env';
import { getFlowHeaders, parseFlowHeaders, flowTransfer } from '@/utils/flow';
/**
The rule "(name CONTAINS "🇨🇳") AND (port IN [80, 443])" can be expressed as follows:
@@ -668,6 +669,7 @@ function removeFlag(str) {
}
function createDynamicFunction(name, script, $arguments) {
const flowUtils = { getFlowHeaders, parseFlowHeaders, flowTransfer };
if ($.env.isLoon) {
return new Function(
'$arguments',
@@ -678,6 +680,7 @@ function createDynamicFunction(name, script, $arguments) {
'$notification',
'ProxyUtils',
'scriptResourceCache',
'flowUtils',
`${script}\n return ${name}`,
)(
$arguments,
@@ -691,6 +694,7 @@ function createDynamicFunction(name, script, $arguments) {
$notification,
ProxyUtils,
scriptResourceCache,
flowUtils,
);
} else {
return new Function(
@@ -699,7 +703,9 @@ function createDynamicFunction(name, script, $arguments) {
'lodash',
'ProxyUtils',
'scriptResourceCache',
'flowUtils',
`${script}\n return ${name}`,
)($arguments, $, lodash, ProxyUtils, scriptResourceCache);
)($arguments, $, lodash, ProxyUtils, scriptResourceCache, flowUtils);
}
}

View File

@@ -14,7 +14,7 @@ export default function Clash_Producer() {
'ssr',
'vmess',
'vless',
'socks',
'socks5',
'http',
'snell',
'trojan',

View File

@@ -63,6 +63,13 @@ export default function ClashMeta_Producer() {
proxy.version = 5;
}
} else if (proxy.type === 'hysteria') {
// auth_str 将会在未来某个时候删除 但是有的机场不规范
if (
isPresent(proxy, 'auth_str') &&
!isPresent(proxy, 'auth-str')
) {
proxy['auth-str'] = proxy['auth_str'];
}
if (isPresent(proxy, 'alpn')) {
proxy.alpn = Array.isArray(proxy.alpn)
? proxy.alpn

View File

@@ -20,6 +20,8 @@ export default function Loon_Producer() {
return http(proxy);
case 'wireguard':
return wireguard(proxy);
case 'hysteria2':
return hysteria2(proxy);
}
throw new Error(
`Platform ${targetPlatform} does not support proxy type: ${proxy.type}`,
@@ -334,3 +336,33 @@ function wireguard(proxy) {
return result.toString();
}
function hysteria2(proxy) {
if (proxy.obfs || proxy['obfs-password']) {
throw new Error(`obfs is unsupported`);
}
const result = new Result(proxy);
result.append(`${proxy.name}=Hysteria2,${proxy.server},${proxy.port}`);
result.appendIfPresent(`,"${proxy.password}"`, 'password');
// sni
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
result.appendIfPresent(
`,skip-cert-verify=${proxy['skip-cert-verify']}`,
'skip-cert-verify',
);
// udp
result.appendIfPresent(`,udp=${proxy.udp}`, 'udp');
// download-bandwidth
result.appendIfPresent(
`,download-bandwidth=${`${proxy['down']}`.match(/\d+/)?.[0] || 0}`,
'down',
);
result.appendIfPresent(`,ecn=${proxy.ecn}`, 'ecn');
return result.toString();
}

View File

@@ -63,6 +63,19 @@ function shadowsocks(proxy) {
}
if (needTls(proxy)) {
appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
'tls-pubkey-sha256',
);
appendIfPresent(`,tls-alpn=${proxy['tls-alpn']}`, 'tls-alpn');
appendIfPresent(
`,tls-no-session-ticket=${proxy['tls-no-session-ticket']}`,
'tls-no-session-ticket',
);
appendIfPresent(
`,tls-no-session-reuse=${proxy['tls-no-session-reuse']}`,
'tls-no-session-reuse',
);
// tls fingerprint
appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
@@ -83,6 +96,12 @@ function shadowsocks(proxy) {
// udp
appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// server_check_url
result.appendIfPresent(
`,server_check_url=${proxy['test-url']}`,
'test-url',
);
// tag
append(`,tag=${proxy.name}`);
@@ -115,6 +134,12 @@ function shadowsocksr(proxy) {
// udp
appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// server_check_url
result.appendIfPresent(
`,server_check_url=${proxy['test-url']}`,
'test-url',
);
// tag
append(`,tag=${proxy.name}`);
@@ -153,6 +178,19 @@ function trojan(proxy) {
}
if (needTls(proxy)) {
appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
'tls-pubkey-sha256',
);
appendIfPresent(`,tls-alpn=${proxy['tls-alpn']}`, 'tls-alpn');
appendIfPresent(
`,tls-no-session-ticket=${proxy['tls-no-session-ticket']}`,
'tls-no-session-ticket',
);
appendIfPresent(
`,tls-no-session-reuse=${proxy['tls-no-session-reuse']}`,
'tls-no-session-reuse',
);
// tls fingerprint
appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
@@ -173,6 +211,12 @@ function trojan(proxy) {
// udp
appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// server_check_url
result.appendIfPresent(
`,server_check_url=${proxy['test-url']}`,
'test-url',
);
// tag
append(`,tag=${proxy.name}`);
@@ -230,6 +274,19 @@ function vmess(proxy) {
}
if (needTls(proxy)) {
appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
'tls-pubkey-sha256',
);
appendIfPresent(`,tls-alpn=${proxy['tls-alpn']}`, 'tls-alpn');
appendIfPresent(
`,tls-no-session-ticket=${proxy['tls-no-session-ticket']}`,
'tls-no-session-ticket',
);
appendIfPresent(
`,tls-no-session-reuse=${proxy['tls-no-session-reuse']}`,
'tls-no-session-reuse',
);
// tls fingerprint
appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
@@ -257,6 +314,12 @@ function vmess(proxy) {
// udp
appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// server_check_url
result.appendIfPresent(
`,server_check_url=${proxy['test-url']}`,
'test-url',
);
// tag
append(`,tag=${proxy.name}`);
@@ -279,6 +342,19 @@ function http(proxy) {
appendIfPresent(`,over-tls=${proxy.tls}`, 'tls');
if (needTls(proxy)) {
appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
'tls-pubkey-sha256',
);
appendIfPresent(`,tls-alpn=${proxy['tls-alpn']}`, 'tls-alpn');
appendIfPresent(
`,tls-no-session-ticket=${proxy['tls-no-session-ticket']}`,
'tls-no-session-ticket',
);
appendIfPresent(
`,tls-no-session-reuse=${proxy['tls-no-session-reuse']}`,
'tls-no-session-reuse',
);
// tls fingerprint
appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
@@ -299,6 +375,12 @@ function http(proxy) {
// udp
appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// server_check_url
result.appendIfPresent(
`,server_check_url=${proxy['test-url']}`,
'test-url',
);
// tag
append(`,tag=${proxy.name}`);
@@ -321,6 +403,19 @@ function socks5(proxy) {
appendIfPresent(`,over-tls=${proxy.tls}`, 'tls');
if (needTls(proxy)) {
appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
'tls-pubkey-sha256',
);
appendIfPresent(`,tls-alpn=${proxy['tls-alpn']}`, 'tls-alpn');
appendIfPresent(
`,tls-no-session-ticket=${proxy['tls-no-session-ticket']}`,
'tls-no-session-ticket',
);
appendIfPresent(
`,tls-no-session-reuse=${proxy['tls-no-session-reuse']}`,
'tls-no-session-reuse',
);
// tls fingerprint
appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
@@ -341,6 +436,12 @@ function socks5(proxy) {
// udp
appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// server_check_url
result.appendIfPresent(
`,server_check_url=${proxy['test-url']}`,
'test-url',
);
// tag
append(`,tag=${proxy.name}`);

View File

@@ -63,6 +63,13 @@ export default function ShadowRocket_Producer() {
proxy.version = 5;
}
} else if (proxy.type === 'hysteria') {
// auth_str 将会在未来某个时候删除 但是有的机场不规范
if (
isPresent(proxy, 'auth_str') &&
!isPresent(proxy, 'auth-str')
) {
proxy['auth-str'] = proxy['auth_str'];
}
if (isPresent(proxy, 'alpn')) {
proxy.alpn = Array.isArray(proxy.alpn)
? proxy.alpn

View File

@@ -12,7 +12,7 @@ export default function Stash_Producer() {
'ss',
'ssr',
'vmess',
'socks',
'socks5',
'http',
'snell',
'trojan',
@@ -79,6 +79,13 @@ export default function Stash_Producer() {
proxy.version = 5;
}
} else if (proxy.type === 'hysteria') {
// auth_str 将会在未来某个时候删除 但是有的机场不规范
if (
isPresent(proxy, 'auth_str') &&
!isPresent(proxy, 'auth-str')
) {
proxy['auth-str'] = proxy['auth_str'];
}
if (isPresent(proxy, 'alpn')) {
proxy.alpn = Array.isArray(proxy.alpn)
? proxy.alpn
@@ -178,6 +185,12 @@ export default function Stash_Producer() {
proxy.fingerprint = proxy['tls-fingerprint'];
}
delete proxy['tls-fingerprint'];
if (proxy['test-url']) {
proxy['benchmark-url'] = proxy['test-url'];
delete proxy['test-url'];
}
delete proxy.subName;
delete proxy.collectionName;
if (

View File

@@ -6,7 +6,7 @@ import {
} from './errors';
import { deleteByName, findByName, updateByName } from '@/utils/database';
import { SUBS_KEY, COLLECTIONS_KEY, ARTIFACTS_KEY } from '@/constants';
import { getFlowHeaders } from '@/utils/flow';
import { getFlowHeaders, parseFlowHeaders } from '@/utils/flow';
import { success, failed } from './response';
import $ from '@/core/app';
@@ -68,20 +68,7 @@ async function getFlowInfo(req, res) {
return;
}
// unit is KB
const uploadMatch = flowHeaders.match(/upload=(-?)(\d+)/);
const upload = Number(uploadMatch[1] + uploadMatch[2]);
const downloadMatch = flowHeaders.match(/download=(-?)(\d+)/);
const download = Number(downloadMatch[1] + downloadMatch[2]);
const total = Number(flowHeaders.match(/total=(\d+)/)[1]);
// optional expire timestamp
const match = flowHeaders.match(/expire=(\d+)/);
const expires = match ? Number(match[1]) : undefined;
success(res, { expires, total, usage: { upload, download } });
success(res, parseFlowHeaders(flowHeaders));
} catch (err) {
failed(
res,

View File

@@ -13,3 +13,28 @@ export async function getFlowHeaders(url) {
)[0];
return headers[subkey];
}
export function parseFlowHeaders(flowHeaders) {
if (!flowHeaders) return;
// unit is KB
const uploadMatch = flowHeaders.match(/upload=(-?)(\d+)/);
const upload = Number(uploadMatch[1] + uploadMatch[2]);
const downloadMatch = flowHeaders.match(/download=(-?)(\d+)/);
const download = Number(downloadMatch[1] + downloadMatch[2]);
const total = Number(flowHeaders.match(/total=(\d+)/)[1]);
// optional expire timestamp
const match = flowHeaders.match(/expire=(\d+)/);
const expires = match ? Number(match[1]) : undefined;
return { expires, total, usage: { upload, download } };
}
export function flowTransfer(flow, unit = 'B') {
const unitList = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
let unitIndex = unitList.indexOf(unit);
return flow < 1024
? { value: flow.toFixed(1), unit: unit }
: flowTransfer(flow / 1024, unitList[++unitIndex]);
}