mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f505752ae | ||
|
|
497bc264e3 | ||
|
|
feb207b333 | ||
|
|
9ac1112b37 | ||
|
|
96769598ef | ||
|
|
f8ed6a3342 | ||
|
|
99b19c410d | ||
|
|
9e54507bbb | ||
|
|
20afa0ad22 | ||
|
|
c5b6960b35 | ||
|
|
4dd86cb368 | ||
|
|
4a0319e95f | ||
|
|
090d8a978f | ||
|
|
bc9fae6062 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sub-store",
|
"name": "sub-store",
|
||||||
"version": "2.14.10",
|
"version": "2.14.22",
|
||||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import download from '@/utils/download';
|
import download from '@/utils/download';
|
||||||
|
import { isIPv4, isIPv6 } from '@/utils';
|
||||||
import PROXY_PROCESSORS, { ApplyProcessor } from './processors';
|
import PROXY_PROCESSORS, { ApplyProcessor } from './processors';
|
||||||
import PROXY_PREPROCESSORS from './preprocessors';
|
import PROXY_PREPROCESSORS from './preprocessors';
|
||||||
import PROXY_PRODUCERS from './producers';
|
import PROXY_PRODUCERS from './producers';
|
||||||
@@ -36,7 +36,7 @@ function parse(raw) {
|
|||||||
if (lastParser) {
|
if (lastParser) {
|
||||||
const [proxy, error] = tryParse(lastParser, line);
|
const [proxy, error] = tryParse(lastParser, line);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
proxies.push(proxy);
|
proxies.push(lastParse(proxy));
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ function parse(raw) {
|
|||||||
for (const parser of PROXY_PARSERS) {
|
for (const parser of PROXY_PARSERS) {
|
||||||
const [proxy, error] = tryParse(parser, line);
|
const [proxy, error] = tryParse(parser, line);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
proxies.push(proxy);
|
proxies.push(lastParse(proxy));
|
||||||
lastParser = parser;
|
lastParser = parser;
|
||||||
success = true;
|
success = true;
|
||||||
$.info(`${parser.name} is activated`);
|
$.info(`${parser.name} is activated`);
|
||||||
@@ -182,3 +182,28 @@ function safeMatch(parser, line) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lastParse(proxy) {
|
||||||
|
if (proxy.type === 'trojan') {
|
||||||
|
proxy.tls = true;
|
||||||
|
}
|
||||||
|
if (proxy.tls && !proxy.sni) {
|
||||||
|
if (proxy.network) {
|
||||||
|
let transportHost = proxy[`${proxy.network}-opts`]?.headers?.Host;
|
||||||
|
transportHost = Array.isArray(transportHost)
|
||||||
|
? transportHost[0]
|
||||||
|
: transportHost;
|
||||||
|
if (transportHost) {
|
||||||
|
proxy.sni = transportHost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!proxy.sni && !isIP(proxy.server)) {
|
||||||
|
proxy.sni = proxy.server;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIP(ip) {
|
||||||
|
return isIPv4(ip) || isIPv6(ip);
|
||||||
|
}
|
||||||
|
|||||||
@@ -216,14 +216,18 @@ function URI_VMess() {
|
|||||||
type: 'vmess',
|
type: 'vmess',
|
||||||
server: params.add,
|
server: params.add,
|
||||||
port: params.port,
|
port: params.port,
|
||||||
cipher: 'auto', // V2rayN has no default cipher! use aes-128-gcm as default.
|
cipher: getIfPresent(params.scy, 'auto'),
|
||||||
uuid: params.id,
|
uuid: params.id,
|
||||||
alterId: getIfPresent(params.aid, 0),
|
alterId: parseInt(getIfPresent(params.aid, 0)),
|
||||||
tls: params.tls === 'tls' || params.tls === true,
|
tls: params.tls === 'tls' || params.tls === true,
|
||||||
'skip-cert-verify': isPresent(params.verify_cert)
|
'skip-cert-verify': isPresent(params.verify_cert)
|
||||||
? !params.verify_cert
|
? !params.verify_cert
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
// https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
|
||||||
|
if (proxy.tls && proxy.sni) {
|
||||||
|
proxy.sni = params.sni;
|
||||||
|
}
|
||||||
// handle obfs
|
// handle obfs
|
||||||
if (params.net === 'ws') {
|
if (params.net === 'ws') {
|
||||||
proxy.network = 'ws';
|
proxy.network = 'ws';
|
||||||
@@ -231,7 +235,9 @@ function URI_VMess() {
|
|||||||
path: getIfNotBlank(params.path),
|
path: getIfNotBlank(params.path),
|
||||||
headers: { Host: getIfNotBlank(params.host) },
|
headers: { Host: getIfNotBlank(params.host) },
|
||||||
};
|
};
|
||||||
if (proxy.tls && params.host) {
|
// https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml#L413
|
||||||
|
// sni 优先级应高于 host
|
||||||
|
if (proxy.tls && !proxy.sni && params.host) {
|
||||||
proxy.sni = params.host;
|
proxy.sni = params.host;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,6 +295,16 @@ function Clash_All() {
|
|||||||
if (proxy.type === 'vmess') {
|
if (proxy.type === 'vmess') {
|
||||||
proxy.sni = proxy.servername;
|
proxy.sni = proxy.servername;
|
||||||
delete proxy.servername;
|
delete proxy.servername;
|
||||||
|
if (proxy.tls && !proxy.sni) {
|
||||||
|
if (proxy.network === 'ws') {
|
||||||
|
proxy.sni = proxy['ws-opts']?.headers?.Host;
|
||||||
|
} else if (proxy.network === 'http') {
|
||||||
|
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||||
|
proxy.sni = Array.isArray(httpHost)
|
||||||
|
? httpHost[0]
|
||||||
|
: httpHost;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return proxy;
|
return proxy;
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ const grammars = String.raw`
|
|||||||
proxy.network = "ws";
|
proxy.network = "ws";
|
||||||
$set(proxy, "ws-opts.path", obfs.path);
|
$set(proxy, "ws-opts.path", obfs.path);
|
||||||
$set(proxy, "ws-opts.headers", obfs['ws-headers']);
|
$set(proxy, "ws-opts.headers", obfs['ws-headers']);
|
||||||
|
if (proxy['ws-opts'] && proxy['ws-opts']['headers'] && proxy['ws-opts']['headers'].Host) {
|
||||||
|
proxy['ws-opts']['headers'].Host = proxy['ws-opts']['headers'].Host.replace(/^"(.*)"$/, '$1')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
proxy.network = "ws";
|
proxy.network = "ws";
|
||||||
$set(proxy, "ws-opts.path", obfs.path);
|
$set(proxy, "ws-opts.path", obfs.path);
|
||||||
$set(proxy, "ws-opts.headers", obfs['ws-headers']);
|
$set(proxy, "ws-opts.headers", obfs['ws-headers']);
|
||||||
|
if (proxy['ws-opts'] && proxy['ws-opts']['headers'] && proxy['ws-opts']['headers'].Host) {
|
||||||
|
proxy['ws-opts']['headers'].Host = proxy['ws-opts']['headers'].Host.replace(/^"(.*)"$/, '$1')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ port = digits:[0-9]+ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params = "?" head:param tail:("&"@param)* {
|
params = "/"? "?" head:param tail:("&"@param)* {
|
||||||
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
||||||
proxy.sni = params["sni"] || params["peer"];
|
proxy.sni = params["sni"] || params["peer"];
|
||||||
|
|
||||||
@@ -87,6 +87,15 @@ params = "?" head:param tail:("&"@param)* {
|
|||||||
proxy.network = "ws";
|
proxy.network = "ws";
|
||||||
$set(proxy, "ws-opts.path", params["wspath"]);
|
$set(proxy, "ws-opts.path", params["wspath"]);
|
||||||
}
|
}
|
||||||
|
if (params["type"]) {
|
||||||
|
proxy.network = params["type"]
|
||||||
|
if (params["path"]) {
|
||||||
|
$set(proxy, proxy.network+"-opts.path", decodeURIComponent(params["path"]));
|
||||||
|
}
|
||||||
|
if (params["host"]) {
|
||||||
|
$set(proxy, proxy.network+"-opts.headers.Host", decodeURIComponent(params["host"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proxy.udp = toBool(params["udp"]);
|
proxy.udp = toBool(params["udp"]);
|
||||||
proxy.tfo = toBool(params["tfo"]);
|
proxy.tfo = toBool(params["tfo"]);
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ port = digits:[0-9]+ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params = "?" head:param tail:("&"@param)* {
|
params = "/"? "?" head:param tail:("&"@param)* {
|
||||||
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
||||||
proxy.sni = params["sni"] || params["peer"];
|
proxy.sni = params["sni"] || params["peer"];
|
||||||
|
|
||||||
@@ -85,6 +85,16 @@ params = "?" head:param tail:("&"@param)* {
|
|||||||
proxy.network = "ws";
|
proxy.network = "ws";
|
||||||
$set(proxy, "ws-opts.path", params["wspath"]);
|
$set(proxy, "ws-opts.path", params["wspath"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params["type"]) {
|
||||||
|
proxy.network = params["type"]
|
||||||
|
if (params["path"]) {
|
||||||
|
$set(proxy, proxy.network+"-opts.path", decodeURIComponent(params["path"]));
|
||||||
|
}
|
||||||
|
if (params["host"]) {
|
||||||
|
$set(proxy, proxy.network+"-opts.headers.Host", decodeURIComponent(params["host"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proxy.udp = toBool(params["udp"]);
|
proxy.udp = toBool(params["udp"]);
|
||||||
proxy.tfo = toBool(params["tfo"]);
|
proxy.tfo = toBool(params["tfo"]);
|
||||||
|
|||||||
@@ -55,6 +55,26 @@ export default function Clash_Producer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
['vmess', 'vless'].includes(proxy.type) &&
|
||||||
|
proxy.network === 'http'
|
||||||
|
) {
|
||||||
|
let httpPath = proxy['http-opts']?.path;
|
||||||
|
if (
|
||||||
|
isPresent(proxy, 'http-opts.path') &&
|
||||||
|
!Array.isArray(httpPath)
|
||||||
|
) {
|
||||||
|
proxy['http-opts'].path = [httpPath];
|
||||||
|
}
|
||||||
|
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||||
|
if (
|
||||||
|
isPresent(proxy, 'http-opts.headers.Host') &&
|
||||||
|
!Array.isArray(httpHost)
|
||||||
|
) {
|
||||||
|
proxy['http-opts'].headers.Host = [httpHost];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete proxy['tls-fingerprint'];
|
delete proxy['tls-fingerprint'];
|
||||||
return ' - ' + JSON.stringify(proxy) + '\n';
|
return ' - ' + JSON.stringify(proxy) + '\n';
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -127,9 +127,7 @@ function trojan(proxy) {
|
|||||||
function vmess(proxy) {
|
function vmess(proxy) {
|
||||||
const result = new Result(proxy);
|
const result = new Result(proxy);
|
||||||
result.append(
|
result.append(
|
||||||
`${proxy.name}=vmess,${proxy.server},${proxy.port},${
|
`${proxy.name}=vmess,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.uuid}"`,
|
||||||
proxy.cipher === 'auto' ? 'none' : proxy.cipher
|
|
||||||
},"${proxy.uuid}"`,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// transport
|
// transport
|
||||||
@@ -146,12 +144,14 @@ function vmess(proxy) {
|
|||||||
);
|
);
|
||||||
} else if (proxy.network === 'http') {
|
} else if (proxy.network === 'http') {
|
||||||
result.append(`,transport=http`);
|
result.append(`,transport=http`);
|
||||||
|
let httpPath = proxy['http-opts']?.path;
|
||||||
|
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||||
result.appendIfPresent(
|
result.appendIfPresent(
|
||||||
`,path=${proxy['http-opts'].path}`,
|
`,path=${Array.isArray(httpPath) ? httpPath[0] : httpPath}`,
|
||||||
'http-opts.path',
|
'http-opts.path',
|
||||||
);
|
);
|
||||||
result.appendIfPresent(
|
result.appendIfPresent(
|
||||||
`,host=${proxy['http-opts'].headers.Host}`,
|
`,host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`,
|
||||||
'http-opts.headers.Host',
|
'http-opts.headers.Host',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -208,12 +208,14 @@ function vless(proxy) {
|
|||||||
);
|
);
|
||||||
} else if (proxy.network === 'http') {
|
} else if (proxy.network === 'http') {
|
||||||
result.append(`,transport=http`);
|
result.append(`,transport=http`);
|
||||||
|
let httpPath = proxy['http-opts']?.path;
|
||||||
|
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||||
result.appendIfPresent(
|
result.appendIfPresent(
|
||||||
`,path=${proxy['http-opts'].path}`,
|
`,path=${Array.isArray(httpPath) ? httpPath[0] : httpPath}`,
|
||||||
'http-opts.path',
|
'http-opts.path',
|
||||||
);
|
);
|
||||||
result.appendIfPresent(
|
result.appendIfPresent(
|
||||||
`,host=${proxy['http-opts'].headers.Host}`,
|
`,host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`,
|
||||||
'http-opts.headers.Host',
|
'http-opts.headers.Host',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -62,18 +62,20 @@ function shadowsocks(proxy) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tls fingerprint
|
if (needTls(proxy)) {
|
||||||
appendIfPresent(
|
// tls fingerprint
|
||||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
appendIfPresent(
|
||||||
'tls-fingerprint',
|
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||||
);
|
'tls-fingerprint',
|
||||||
|
);
|
||||||
|
|
||||||
// tls verification
|
// tls verification
|
||||||
appendIfPresent(
|
appendIfPresent(
|
||||||
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
||||||
'skip-cert-verify',
|
'skip-cert-verify',
|
||||||
);
|
);
|
||||||
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
||||||
|
}
|
||||||
|
|
||||||
// tfo
|
// tfo
|
||||||
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||||
@@ -150,18 +152,20 @@ function trojan(proxy) {
|
|||||||
append(`,over-tls=true`);
|
append(`,over-tls=true`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tls fingerprint
|
if (needTls(proxy)) {
|
||||||
appendIfPresent(
|
// tls fingerprint
|
||||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
appendIfPresent(
|
||||||
'tls-fingerprint',
|
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||||
);
|
'tls-fingerprint',
|
||||||
|
);
|
||||||
|
|
||||||
// tls verification
|
// tls verification
|
||||||
appendIfPresent(
|
appendIfPresent(
|
||||||
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
||||||
'skip-cert-verify',
|
'skip-cert-verify',
|
||||||
);
|
);
|
||||||
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
||||||
|
}
|
||||||
|
|
||||||
// tfo
|
// tfo
|
||||||
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||||
@@ -206,12 +210,18 @@ function vmess(proxy) {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error(`network ${proxy.network} is unsupported`);
|
throw new Error(`network ${proxy.network} is unsupported`);
|
||||||
}
|
}
|
||||||
|
let transportPath = proxy[`${proxy.network}-opts`]?.path;
|
||||||
|
let transportHost = proxy[`${proxy.network}-opts`]?.headers?.Host;
|
||||||
appendIfPresent(
|
appendIfPresent(
|
||||||
`,obfs-uri=${proxy[`${proxy.network}-opts`].path}`,
|
`,obfs-uri=${
|
||||||
|
Array.isArray(transportPath) ? transportPath[0] : transportPath
|
||||||
|
}`,
|
||||||
`${proxy.network}-opts.path`,
|
`${proxy.network}-opts.path`,
|
||||||
);
|
);
|
||||||
appendIfPresent(
|
appendIfPresent(
|
||||||
`,obfs-host=${proxy[`${proxy.network}-opts`].headers.Host}`,
|
`,obfs-host=${
|
||||||
|
Array.isArray(transportHost) ? transportHost[0] : transportHost
|
||||||
|
}`,
|
||||||
`${proxy.network}-opts.headers.Host`,
|
`${proxy.network}-opts.headers.Host`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -219,18 +229,20 @@ function vmess(proxy) {
|
|||||||
if (proxy.tls) append(`,obfs=over-tls`);
|
if (proxy.tls) append(`,obfs=over-tls`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tls fingerprint
|
if (needTls(proxy)) {
|
||||||
appendIfPresent(
|
// tls fingerprint
|
||||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
appendIfPresent(
|
||||||
'tls-fingerprint',
|
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||||
);
|
'tls-fingerprint',
|
||||||
|
);
|
||||||
|
|
||||||
// tls verification
|
// tls verification
|
||||||
appendIfPresent(
|
appendIfPresent(
|
||||||
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
||||||
'skip-cert-verify',
|
'skip-cert-verify',
|
||||||
);
|
);
|
||||||
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
||||||
|
}
|
||||||
|
|
||||||
// AEAD
|
// AEAD
|
||||||
if (isPresent(proxy, 'aead')) {
|
if (isPresent(proxy, 'aead')) {
|
||||||
@@ -266,18 +278,20 @@ function http(proxy) {
|
|||||||
}
|
}
|
||||||
appendIfPresent(`,over-tls=${proxy.tls}`, 'tls');
|
appendIfPresent(`,over-tls=${proxy.tls}`, 'tls');
|
||||||
|
|
||||||
// tls fingerprint
|
if (needTls(proxy)) {
|
||||||
appendIfPresent(
|
// tls fingerprint
|
||||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
appendIfPresent(
|
||||||
'tls-fingerprint',
|
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||||
);
|
'tls-fingerprint',
|
||||||
|
);
|
||||||
|
|
||||||
// tls verification
|
// tls verification
|
||||||
appendIfPresent(
|
appendIfPresent(
|
||||||
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
||||||
'skip-cert-verify',
|
'skip-cert-verify',
|
||||||
);
|
);
|
||||||
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
||||||
|
}
|
||||||
|
|
||||||
// tfo
|
// tfo
|
||||||
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||||
@@ -306,18 +320,20 @@ function socks5(proxy) {
|
|||||||
}
|
}
|
||||||
appendIfPresent(`,over-tls=${proxy.tls}`, 'tls');
|
appendIfPresent(`,over-tls=${proxy.tls}`, 'tls');
|
||||||
|
|
||||||
// tls fingerprint
|
if (needTls(proxy)) {
|
||||||
appendIfPresent(
|
// tls fingerprint
|
||||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
appendIfPresent(
|
||||||
'tls-fingerprint',
|
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||||
);
|
'tls-fingerprint',
|
||||||
|
);
|
||||||
|
|
||||||
// tls verification
|
// tls verification
|
||||||
appendIfPresent(
|
appendIfPresent(
|
||||||
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
`,tls-verification=${!proxy['skip-cert-verify']}`,
|
||||||
'skip-cert-verify',
|
'skip-cert-verify',
|
||||||
);
|
);
|
||||||
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
appendIfPresent(`,tls-host=${proxy.sni}`, 'sni');
|
||||||
|
}
|
||||||
|
|
||||||
// tfo
|
// tfo
|
||||||
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||||
@@ -332,11 +348,5 @@ function socks5(proxy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function needTls(proxy) {
|
function needTls(proxy) {
|
||||||
return (
|
return proxy.tls;
|
||||||
proxy.tls ||
|
|
||||||
proxy.sni ||
|
|
||||||
typeof proxy['skip-cert-verify'] !== 'undefined' ||
|
|
||||||
typeof proxy['tls-fingerprint'] !== 'undefined' ||
|
|
||||||
typeof proxy['tls-host'] !== 'undefined'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,26 @@ export default function Stash_Producer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
['vmess', 'vless'].includes(proxy.type) &&
|
||||||
|
proxy.network === 'http'
|
||||||
|
) {
|
||||||
|
let httpPath = proxy['http-opts']?.path;
|
||||||
|
if (
|
||||||
|
isPresent(proxy, 'http-opts.path') &&
|
||||||
|
!Array.isArray(httpPath)
|
||||||
|
) {
|
||||||
|
proxy['http-opts'].path = [httpPath];
|
||||||
|
}
|
||||||
|
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||||
|
if (
|
||||||
|
isPresent(proxy, 'http-opts.headers.Host') &&
|
||||||
|
!Array.isArray(httpHost)
|
||||||
|
) {
|
||||||
|
proxy['http-opts'].headers.Host = [httpHost];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete proxy['tls-fingerprint'];
|
delete proxy['tls-fingerprint'];
|
||||||
return ' - ' + JSON.stringify(proxy) + '\n';
|
return ' - ' + JSON.stringify(proxy) + '\n';
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -301,7 +301,13 @@ function handleTransport(result, proxy) {
|
|||||||
if (isPresent(proxy, 'ws-opts.headers')) {
|
if (isPresent(proxy, 'ws-opts.headers')) {
|
||||||
const headers = proxy['ws-opts'].headers;
|
const headers = proxy['ws-opts'].headers;
|
||||||
const value = Object.keys(headers)
|
const value = Object.keys(headers)
|
||||||
.map((k) => `${k}:${headers[k]}`)
|
.map((k) => {
|
||||||
|
let v = headers[k];
|
||||||
|
if (['Host'].includes(k)) {
|
||||||
|
v = `"${v}"`;
|
||||||
|
}
|
||||||
|
return `${k}:${v}`;
|
||||||
|
})
|
||||||
.join('|');
|
.join('|');
|
||||||
if (isNotBlank(value)) {
|
if (isNotBlank(value)) {
|
||||||
result.append(`,ws-headers=${value}`);
|
result.append(`,ws-headers=${value}`);
|
||||||
|
|||||||
@@ -65,17 +65,45 @@ export default function URI_Producer() {
|
|||||||
net: proxy.network || 'tcp',
|
net: proxy.network || 'tcp',
|
||||||
tls: proxy.tls ? 'tls' : '',
|
tls: proxy.tls ? 'tls' : '',
|
||||||
};
|
};
|
||||||
|
if (proxy.tls && proxy.sni) {
|
||||||
|
result.sni = proxy.sni;
|
||||||
|
}
|
||||||
// obfs
|
// obfs
|
||||||
if (proxy.network === 'ws') {
|
if (proxy.network === 'ws') {
|
||||||
result.path = proxy['ws-opts'].path || '/';
|
result.path = proxy['ws-opts'].path || '/';
|
||||||
result.host = proxy['ws-opts'].headers.Host || proxy.server;
|
if (proxy['ws-opts'].headers.Host) {
|
||||||
|
result.host = proxy['ws-opts'].headers.Host;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result = 'vmess://' + Base64.encode(JSON.stringify(result));
|
result = 'vmess://' + Base64.encode(JSON.stringify(result));
|
||||||
break;
|
break;
|
||||||
case 'trojan':
|
case 'trojan':
|
||||||
|
let transport = '';
|
||||||
|
if (proxy.network) {
|
||||||
|
transport = `&type=${proxy.network}`;
|
||||||
|
let transportPath = proxy[`${proxy.network}-opts`]?.path;
|
||||||
|
let transportHost =
|
||||||
|
proxy[`${proxy.network}-opts`]?.headers?.Host;
|
||||||
|
if (transportPath) {
|
||||||
|
transport += `&path=${encodeURIComponent(
|
||||||
|
Array.isArray(transportPath)
|
||||||
|
? transportPath[0]
|
||||||
|
: transportPath,
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
if (transportHost) {
|
||||||
|
transport += `&host=${encodeURIComponent(
|
||||||
|
Array.isArray(transportHost)
|
||||||
|
? transportHost[0]
|
||||||
|
: transportHost,
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
result = `trojan://${proxy.password}@${proxy.server}:${
|
result = `trojan://${proxy.password}@${proxy.server}:${
|
||||||
proxy.port
|
proxy.port
|
||||||
}#${encodeURIComponent(proxy.name)}`;
|
}?sni=${encodeURIComponent(proxy.sni || proxy.server)}${
|
||||||
|
proxy['skip-cert-verify'] ? '&allowInsecure=1' : ''
|
||||||
|
}${transport}#${encodeURIComponent(proxy.name)}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ export default function register($app) {
|
|||||||
if (!$.read(ARTIFACTS_KEY)) $.write({}, ARTIFACTS_KEY);
|
if (!$.read(ARTIFACTS_KEY)) $.write({}, ARTIFACTS_KEY);
|
||||||
|
|
||||||
// RESTful APIs
|
// RESTful APIs
|
||||||
$app.route('/api/artifacts').get(getAllArtifacts).post(createArtifact);
|
$app.route('/api/artifacts')
|
||||||
|
.get(getAllArtifacts)
|
||||||
|
.post(createArtifact)
|
||||||
|
.put(replaceArtifact);
|
||||||
|
|
||||||
$app.route('/api/artifact/:name')
|
$app.route('/api/artifact/:name')
|
||||||
.get(getArtifact)
|
.get(getArtifact)
|
||||||
@@ -32,6 +35,12 @@ function getAllArtifacts(req, res) {
|
|||||||
success(res, allArtifacts);
|
success(res, allArtifacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function replaceArtifact(req, res) {
|
||||||
|
const allArtifacts = req.body;
|
||||||
|
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||||
|
success(res);
|
||||||
|
}
|
||||||
|
|
||||||
async function getArtifact(req, res) {
|
async function getArtifact(req, res) {
|
||||||
let { name } = req.params;
|
let { name } = req.params;
|
||||||
name = decodeURIComponent(name);
|
name = decodeURIComponent(name);
|
||||||
@@ -131,7 +140,7 @@ async function deleteArtifact(req, res) {
|
|||||||
files[encodeURIComponent(artifact.name)] = {
|
files[encodeURIComponent(artifact.name)] = {
|
||||||
content: '',
|
content: '',
|
||||||
};
|
};
|
||||||
// 当别的Sub 删了同步订阅 或 gist里面删了 当前设备没有删除 时 无法删除的bug
|
// 当别的Sub 删了同步订阅 或 gist里面删了 当前设备没有删除 时 无法删除的bug
|
||||||
try {
|
try {
|
||||||
await syncToGist(files);
|
await syncToGist(files);
|
||||||
} catch (i) {
|
} catch (i) {
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ export function getFlag(name) {
|
|||||||
'🇲🇴': ['Macao', '澳门', '澳門', 'CTM'],
|
'🇲🇴': ['Macao', '澳门', '澳門', 'CTM'],
|
||||||
'🇲🇹': ['Malta', '马耳他'],
|
'🇲🇹': ['Malta', '马耳他'],
|
||||||
'🇲🇽': ['Mexico', '墨西哥'],
|
'🇲🇽': ['Mexico', '墨西哥'],
|
||||||
'🇲🇾': ['Malaysia', '马来西亚', '馬來西亞', '吉隆坡', '大馬'],
|
'🇲🇾': ['Malaysia', '马来', '馬來', '吉隆坡', '大馬'],
|
||||||
'🇳🇱': ['Netherlands', '荷兰', '荷蘭', '尼德蘭', '阿姆斯特丹'],
|
'🇳🇱': ['Netherlands', '荷兰', '荷蘭', '尼德蘭', '阿姆斯特丹'],
|
||||||
'🇳🇴': ['Norway', '挪威'],
|
'🇳🇴': ['Norway', '挪威'],
|
||||||
'🇳🇵': ['Nepal', '尼泊尔'],
|
'🇳🇵': ['Nepal', '尼泊尔'],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!name=Sub-Store
|
#!name=Sub-Store
|
||||||
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用原版
|
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用带 ability 参数
|
||||||
|
|
||||||
[MITM]
|
[MITM]
|
||||||
hostname = %APPEND% sub.store
|
hostname = %APPEND% sub.store
|
||||||
|
|||||||
11
config/Surge-ability.sgmodule
Normal file
11
config/Surge-ability.sgmodule
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!name=Sub-Store
|
||||||
|
#!desc=高级订阅管理工具 @Peng-YM 带 ability 参数版本, 可能会爆内存, 如果不需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用不带 ability 参数版本
|
||||||
|
|
||||||
|
[MITM]
|
||||||
|
hostname = %APPEND% sub.store
|
||||||
|
|
||||||
|
[Script]
|
||||||
|
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120,ability=http-client-policy
|
||||||
|
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true
|
||||||
|
|
||||||
|
Sub-Store Sync=type=cron,cronexp=0 0 * * *,wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
#!name=Sub-Store
|
#!name=Sub-Store
|
||||||
#!desc=高级订阅管理工具 @Peng-YM
|
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用带 ability 参数
|
||||||
|
|
||||||
[MITM]
|
[MITM]
|
||||||
hostname=%APPEND% sub.store
|
hostname = %APPEND% sub.store
|
||||||
|
|
||||||
[Script]
|
[Script]
|
||||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120,ability=http-client-policy
|
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120
|
||||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true
|
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true
|
||||||
|
|
||||||
Sub-Store Sync=type=cron,cronexp=0 0 * * *,wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
Sub-Store Sync=type=cron,cronexp=0 0 * * *,wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||||
|
|||||||
Reference in New Issue
Block a user