mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
556d5f393c | ||
|
|
a8c05207c0 | ||
|
|
e97fb1e6d9 | ||
|
|
e40b9a77c4 | ||
|
|
df0ac8a218 | ||
|
|
36377f3c20 | ||
|
|
47f26bdac8 | ||
|
|
a8a89ee2a2 | ||
|
|
199c5fb337 | ||
|
|
df505616ec | ||
|
|
c5b11f8b36 | ||
|
|
b19b49d2fa | ||
|
|
395c6e4e4a | ||
|
|
ae1c738f70 | ||
|
|
02d54208b0 | ||
|
|
5d3fc499ce | ||
|
|
d23bc7663e | ||
|
|
15704ea1c9 | ||
|
|
68cb393a7e | ||
|
|
2e99f28aa5 | ||
|
|
1a18e65e47 | ||
|
|
bbd5341d7a | ||
|
|
623802d73f | ||
|
|
19920dbfa3 | ||
|
|
8764e01d7e | ||
|
|
31b6dd0507 | ||
|
|
a84007d39e | ||
|
|
751e50bf99 | ||
|
|
a91f978042 | ||
|
|
1248e6b32a | ||
|
|
d0f255d9c6 |
12
README.md
12
README.md
@@ -84,15 +84,25 @@ Install `pnpm`
|
||||
Go to `backend` directories, install node dependencies:
|
||||
|
||||
```
|
||||
pnpm install
|
||||
pnpm i
|
||||
```
|
||||
|
||||
1. In `backend`, run the backend server on http://localhost:3000
|
||||
|
||||
babel(old school)
|
||||
|
||||
```
|
||||
pnpm start
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
esbuild(experimental)
|
||||
|
||||
```
|
||||
pnpm run --parallel "/^dev:.*/"
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
|
||||
This project is under the GPL V3 LICENSE.
|
||||
|
||||
77
backend/bundle-esbuild.js
Normal file
77
backend/bundle-esbuild.js
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { build } = require('esbuild');
|
||||
|
||||
!(async () => {
|
||||
const version = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'),
|
||||
).version.trim();
|
||||
|
||||
const artifacts = [
|
||||
{ src: 'src/main.js', dest: 'sub-store.min.js' },
|
||||
{
|
||||
src: 'src/products/resource-parser.loon.js',
|
||||
dest: 'dist/sub-store-parser.loon.min.js',
|
||||
},
|
||||
{
|
||||
src: 'src/products/cron-sync-artifacts.js',
|
||||
dest: 'dist/cron-sync-artifacts.min.js',
|
||||
},
|
||||
{ src: 'src/products/sub-store-0.js', dest: 'dist/sub-store-0.min.js' },
|
||||
{ src: 'src/products/sub-store-1.js', dest: 'dist/sub-store-1.min.js' },
|
||||
];
|
||||
|
||||
for await (const artifact of artifacts) {
|
||||
await build({
|
||||
entryPoints: [artifact.src],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: false,
|
||||
platform: 'browser',
|
||||
format: 'iife',
|
||||
outfile: artifact.dest,
|
||||
});
|
||||
}
|
||||
|
||||
let content = fs.readFileSync(path.join(__dirname, 'sub-store.min.js'), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
content = content.replace(
|
||||
/eval\(('|")(require\(('|").*?('|")\))('|")\)/g,
|
||||
'$2',
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, 'dist/sub-store.no-bundle.js'),
|
||||
content,
|
||||
{
|
||||
encoding: 'utf8',
|
||||
},
|
||||
);
|
||||
|
||||
await build({
|
||||
entryPoints: ['dist/sub-store.no-bundle.js'],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: false,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: 'dist/sub-store.bundle.js',
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, 'dist/sub-store.bundle.js'),
|
||||
`// SUB_STORE_BACKEND_VERSION: ${version}
|
||||
${fs.readFileSync(path.join(__dirname, 'dist/sub-store.bundle.js'), {
|
||||
encoding: 'utf8',
|
||||
})}`,
|
||||
{
|
||||
encoding: 'utf8',
|
||||
},
|
||||
);
|
||||
})()
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
})
|
||||
.finally(() => {
|
||||
console.log('done');
|
||||
});
|
||||
@@ -3,23 +3,49 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { build } = require('esbuild');
|
||||
|
||||
let content = fs.readFileSync(path.join(__dirname, 'sub-store.min.js'), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
content = content.replace(
|
||||
/eval\(('|")(require\(('|").*?('|")\))('|")\)/g,
|
||||
'$2',
|
||||
);
|
||||
fs.writeFileSync(path.join(__dirname, 'dist/sub-store.no-bundle.js'), content, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
!(async () => {
|
||||
const version = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'),
|
||||
).version.trim();
|
||||
|
||||
build({
|
||||
entryPoints: ['dist/sub-store.no-bundle.js'],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: 'dist/sub-store.bundle.js',
|
||||
});
|
||||
let content = fs.readFileSync(path.join(__dirname, 'sub-store.min.js'), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
content = content.replace(
|
||||
/eval\(('|")(require\(('|").*?('|")\))('|")\)/g,
|
||||
'$2',
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, 'dist/sub-store.no-bundle.js'),
|
||||
content,
|
||||
{
|
||||
encoding: 'utf8',
|
||||
},
|
||||
);
|
||||
|
||||
await build({
|
||||
entryPoints: ['dist/sub-store.no-bundle.js'],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: 'dist/sub-store.bundle.js',
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, 'dist/sub-store.bundle.js'),
|
||||
`// SUB_STORE_BACKEND_VERSION: ${version}
|
||||
${fs.readFileSync(path.join(__dirname, 'dist/sub-store.bundle.js'), {
|
||||
encoding: 'utf8',
|
||||
})}`,
|
||||
{
|
||||
encoding: 'utf8',
|
||||
},
|
||||
);
|
||||
})()
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
})
|
||||
.finally(() => {
|
||||
console.log('done');
|
||||
});
|
||||
|
||||
24
backend/dev-esbuild.js
Normal file
24
backend/dev-esbuild.js
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env node
|
||||
const { build } = require('esbuild');
|
||||
|
||||
!(async () => {
|
||||
const artifacts = [{ src: 'src/main.js', dest: 'sub-store.min.js' }];
|
||||
|
||||
for await (const artifact of artifacts) {
|
||||
await build({
|
||||
entryPoints: [artifact.src],
|
||||
bundle: true,
|
||||
minify: false,
|
||||
sourcemap: false,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: artifact.dest,
|
||||
});
|
||||
}
|
||||
})()
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
})
|
||||
.finally(() => {
|
||||
console.log('done');
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.192",
|
||||
"version": "2.14.217",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
@@ -8,6 +8,8 @@
|
||||
"test": "gulp peggy && npx cross-env BABEL_ENV=test mocha src/test/**/*.spec.js --require @babel/register --recursive",
|
||||
"serve": "node sub-store.min.js",
|
||||
"start": "nodemon -w src -w package.json --exec babel-node src/main.js",
|
||||
"dev:esbuild": "nodemon -w src -w package.json dev-esbuild.js",
|
||||
"dev:run": "nodemon -w sub-store.min.js sub-store.min.js",
|
||||
"build": "gulp",
|
||||
"bundle": "node bundle.js"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import YAML from 'static-js-yaml';
|
||||
import YAML from '@/utils/yaml';
|
||||
import download from '@/utils/download';
|
||||
import { isIPv4, isIPv6, isValidPortNumber } from '@/utils';
|
||||
import PROXY_PROCESSORS, { ApplyProcessor } from './processors';
|
||||
@@ -63,7 +63,7 @@ function parse(raw) {
|
||||
return proxies;
|
||||
}
|
||||
|
||||
async function process(proxies, operators = [], targetPlatform, source) {
|
||||
async function processFn(proxies, operators = [], targetPlatform, source) {
|
||||
for (const item of operators) {
|
||||
// process script
|
||||
let script;
|
||||
@@ -188,7 +188,7 @@ function produce(proxies, targetPlatform, type, opts = {}) {
|
||||
|
||||
export const ProxyUtils = {
|
||||
parse,
|
||||
process,
|
||||
process: processFn,
|
||||
produce,
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
@@ -266,9 +266,13 @@ function lastParse(proxy) {
|
||||
}
|
||||
if (proxy.network === 'h2') {
|
||||
const host = proxy['h2-opts']?.headers?.host;
|
||||
const path = proxy['h2-opts']?.path;
|
||||
if (host && !Array.isArray(host)) {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
if (Array.isArray(path)) {
|
||||
proxy['h2-opts'].path = path[0];
|
||||
}
|
||||
}
|
||||
if (proxy.tls && !proxy.sni) {
|
||||
if (proxy.network) {
|
||||
@@ -313,6 +317,17 @@ function lastParse(proxy) {
|
||||
if (['hysteria', 'hysteria2'].includes(proxy.type) && !proxy.ports) {
|
||||
delete proxy.ports;
|
||||
}
|
||||
if (['vless'].includes(proxy.type)) {
|
||||
if (['http'].includes(proxy.network)) {
|
||||
let transportPath = proxy[`${proxy.network}-opts`]?.path;
|
||||
if (!transportPath) {
|
||||
if (!proxy[`${proxy.network}-opts`]) {
|
||||
proxy[`${proxy.network}-opts`] = {};
|
||||
}
|
||||
proxy[`${proxy.network}-opts`].path = ['/'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ function URI_SS() {
|
||||
const parsed = query.match(/(&|\?)v2ray-plugin=(.*?)(&|$)/);
|
||||
let v2rayPlugin = parsed[2];
|
||||
if (v2rayPlugin) {
|
||||
proxy.obfs = 'v2ray-plugin';
|
||||
proxy.plugin = 'v2ray-plugin';
|
||||
proxy['plugin-opts'] = JSON.parse(
|
||||
Base64.decode(v2rayPlugin),
|
||||
);
|
||||
@@ -89,7 +89,7 @@ function URI_SS() {
|
||||
};
|
||||
break;
|
||||
case 'v2ray-plugin':
|
||||
proxy.obfs = 'v2ray-plugin';
|
||||
proxy.plugin = 'v2ray-plugin';
|
||||
proxy['plugin-opts'] = {
|
||||
mode: 'websocket',
|
||||
host: getIfNotBlank(params['obfs-host']),
|
||||
@@ -545,6 +545,123 @@ function URI_Hysteria2() {
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
function URI_Hysteria() {
|
||||
const name = 'URI Hysteria Parser';
|
||||
const test = (line) => {
|
||||
return /^(hysteria|hy):\/\//.test(line);
|
||||
};
|
||||
const parse = (line) => {
|
||||
line = line.split(/(hysteria|hy):\/\//)[2];
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, server, ___, port, ____, addons = '', name] =
|
||||
/^(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line);
|
||||
port = parseInt(`${port}`, 10);
|
||||
if (isNaN(port)) {
|
||||
port = 443;
|
||||
}
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
name = name ?? `Hysteria ${server}:${port}`;
|
||||
|
||||
const proxy = {
|
||||
type: 'hysteria',
|
||||
name,
|
||||
server,
|
||||
port,
|
||||
};
|
||||
const params = {};
|
||||
for (const addon of addons.split('&')) {
|
||||
let [key, value] = addon.split('=');
|
||||
key = key.replace(/_/, '-');
|
||||
value = decodeURIComponent(value);
|
||||
if (['alpn'].includes(key)) {
|
||||
proxy[key] = value ? value.split(',') : undefined;
|
||||
} else if (['insecure'].includes(key)) {
|
||||
proxy['skip-cert-verify'] = /(TRUE)|1/i.test(value);
|
||||
} else if (['auth'].includes(key)) {
|
||||
proxy['auth-str'] = value;
|
||||
} else if (['mport'].includes(key)) {
|
||||
proxy['ports'] = value;
|
||||
} else if (['obfsParam'].includes(key)) {
|
||||
proxy['obfs'] = value;
|
||||
} else if (['upmbps'].includes(key)) {
|
||||
proxy['up'] = value;
|
||||
} else if (['downmbps'].includes(key)) {
|
||||
proxy['down'] = value;
|
||||
} else if (['obfs'].includes(key)) {
|
||||
// obfs: Obfuscation mode (optional, empty or "xplus")
|
||||
proxy['_obfs'] = value || '';
|
||||
} else if (['fast-open', 'peer'].includes(key)) {
|
||||
params[key] = value;
|
||||
} else {
|
||||
proxy[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!proxy.sni && params.peer) {
|
||||
proxy.sni = params.peer;
|
||||
}
|
||||
if (!proxy['fast-open'] && params.fastopen) {
|
||||
proxy['fast-open'] = true;
|
||||
}
|
||||
if (!proxy.protocol) {
|
||||
// protocol: protocol to use ("udp", "wechat-video", "faketcp") (optional, default: "udp")
|
||||
proxy.protocol = 'udp';
|
||||
}
|
||||
|
||||
return proxy;
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
function URI_TUIC() {
|
||||
const name = 'URI TUIC Parser';
|
||||
const test = (line) => {
|
||||
return /^tuic:\/\//.test(line);
|
||||
};
|
||||
const parse = (line) => {
|
||||
line = line.split(/tuic:\/\//)[1];
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, uuid, password, server, ___, port, ____, addons = '', name] =
|
||||
/^(.*?):(.*?)@(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line);
|
||||
port = parseInt(`${port}`, 10);
|
||||
if (isNaN(port)) {
|
||||
port = 443;
|
||||
}
|
||||
password = decodeURIComponent(password);
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
name = name ?? `TUIC ${server}:${port}`;
|
||||
|
||||
const proxy = {
|
||||
type: 'tuic',
|
||||
name,
|
||||
server,
|
||||
port,
|
||||
password,
|
||||
uuid,
|
||||
};
|
||||
|
||||
for (const addon of addons.split('&')) {
|
||||
let [key, value] = addon.split('=');
|
||||
key = key.replace(/_/, '-');
|
||||
value = decodeURIComponent(value);
|
||||
if (['alpn'].includes(key)) {
|
||||
proxy[key] = value ? value.split(',') : undefined;
|
||||
} else if (['allow-insecure'].includes(key)) {
|
||||
proxy['skip-cert-verify'] = /(TRUE)|1/i.test(value);
|
||||
} else if (['disable-sni', 'reduce-rtt'].includes(key)) {
|
||||
proxy[key] = /(TRUE)|1/i.test(value);
|
||||
} else {
|
||||
proxy[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
// Trojan URI format
|
||||
function URI_Trojan() {
|
||||
@@ -1051,6 +1168,8 @@ export default [
|
||||
URI_SSR(),
|
||||
URI_VMess(),
|
||||
URI_VLESS(),
|
||||
URI_TUIC(),
|
||||
URI_Hysteria(),
|
||||
URI_Hysteria2(),
|
||||
URI_Trojan(),
|
||||
Clash_All(),
|
||||
|
||||
@@ -68,7 +68,7 @@ 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)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/udp_relay/fast_open/download_bandwidth/ecn/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
@@ -117,7 +117,7 @@ port = digits:[0-9]+ {
|
||||
method = comma cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
}
|
||||
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"xchacha20-ietf-poly1305"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none"/"auto");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"auto"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none"/"rc4-md5"/"rc4"/"salsa20"/"xchacha20-ietf-poly1305");
|
||||
|
||||
username = & {
|
||||
let j = peg$currPos;
|
||||
|
||||
@@ -66,7 +66,7 @@ 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)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/udp_relay/fast_open/download_bandwidth/ecn/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/fast_open/udp_relay/others)* {
|
||||
@@ -115,7 +115,7 @@ port = digits:[0-9]+ {
|
||||
method = comma cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
}
|
||||
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"xchacha20-ietf-poly1305"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none"/"auto");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"auto"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none"/"rc4-md5"/"rc4"/"salsa20"/"xchacha20-ietf-poly1305");
|
||||
|
||||
username = & {
|
||||
let j = peg$currPos;
|
||||
|
||||
@@ -149,7 +149,7 @@ uuid = comma "password" equals uuid:[^=,]+ { proxy.uuid = uuid.join("").trim();
|
||||
method = comma "method" equals cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
};
|
||||
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"xchacha20-ietf-poly1305"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"none"/"rc2-cfb"/"rc4-md5-6"/"rc4-md5"/"salsa20"/"xchacha20-ietf-poly1305");
|
||||
aead = comma "aead" equals flag:bool { proxy.aead = flag; }
|
||||
|
||||
udp_relay = comma "udp-relay" equals flag:bool { proxy.udp = flag; }
|
||||
|
||||
@@ -147,7 +147,7 @@ uuid = comma "password" equals uuid:[^=,]+ { proxy.uuid = uuid.join("").trim();
|
||||
method = comma "method" equals cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
};
|
||||
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"xchacha20-ietf-poly1305"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"none"/"rc2-cfb"/"rc4-md5-6"/"rc4-md5"/"salsa20"/"xchacha20-ietf-poly1305");
|
||||
aead = comma "aead" equals flag:bool { proxy.aead = flag; }
|
||||
|
||||
udp_relay = comma "udp-relay" equals flag:bool { proxy.udp = flag; }
|
||||
|
||||
@@ -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/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/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/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/test_url/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)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
if (proxy.aead) {
|
||||
@@ -63,21 +63,21 @@ 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/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/test_url/no_error_alert/fast_open/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/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/test_url/no_error_alert/fast_open/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/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/test_url/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
handleShadowTLS();
|
||||
}
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/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/test_url/no_error_alert/fast_open/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") {
|
||||
@@ -87,11 +87,11 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
|
||||
}
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/test_url/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/test_url/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
proxy.version = 5;
|
||||
handleShadowTLS();
|
||||
@@ -104,11 +104,11 @@ hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying
|
||||
proxy.type = "hysteria2";
|
||||
handleShadowTLS();
|
||||
}
|
||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/test_url/fast_open/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)? (no_error_alert/ip_version/underlying_proxy/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/test_url/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;
|
||||
handleShadowTLS();
|
||||
@@ -188,7 +188,7 @@ vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
||||
method = comma "encrypt-method" equals cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
}
|
||||
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"xchacha20-ietf-poly1305"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"idea-cfb"/"none"/"rc2-cfb"/"rc4-md5"/"rc4"/"salsa20"/"seed-cfb"/"xchacha20-ietf-poly1305");
|
||||
|
||||
ws = comma "ws" equals flag:bool { obfs.type = "ws"; }
|
||||
ws_headers = comma "ws-headers" equals headers:$[^,]+ {
|
||||
|
||||
@@ -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/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/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/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/test_url/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)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
if (proxy.aead) {
|
||||
@@ -61,21 +61,21 @@ 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/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/test_url/no_error_alert/fast_open/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/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/test_url/no_error_alert/fast_open/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/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/test_url/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
handleShadowTLS();
|
||||
}
|
||||
snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/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/test_url/no_error_alert/fast_open/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") {
|
||||
@@ -85,11 +85,11 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
|
||||
}
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/test_url/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/test_url/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "tuic";
|
||||
proxy.version = 5;
|
||||
handleShadowTLS();
|
||||
@@ -102,11 +102,11 @@ hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying
|
||||
proxy.type = "hysteria2";
|
||||
handleShadowTLS();
|
||||
}
|
||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/test_url/fast_open/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)? (no_error_alert/ip_version/underlying_proxy/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/test_url/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;
|
||||
handleShadowTLS();
|
||||
@@ -186,7 +186,7 @@ vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
||||
method = comma "encrypt-method" equals cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
}
|
||||
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"xchacha20-ietf-poly1305"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"camellia-128-cfb"/"camellia-192-cfb"/"camellia-256-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"idea-cfb"/"none"/"rc2-cfb"/"rc4-md5"/"rc4"/"salsa20"/"seed-cfb"/"xchacha20-ietf-poly1305");
|
||||
|
||||
ws = comma "ws" equals flag:bool { obfs.type = "ws"; }
|
||||
ws_headers = comma "ws-headers" equals headers:$[^,]+ {
|
||||
|
||||
@@ -30,7 +30,7 @@ start = (trojan) {
|
||||
return proxy
|
||||
}
|
||||
|
||||
trojan = "trojan://" password:password "@" server:server ":" port:port params? name:name?{
|
||||
trojan = "trojan://" password:password "@" server:server ":" port:port "/"? params? name:name?{
|
||||
proxy.type = "trojan";
|
||||
proxy.password = password;
|
||||
proxy.server = server;
|
||||
@@ -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.sni = params["sni"] || params["peer"];
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ start = (trojan) {
|
||||
return proxy
|
||||
}
|
||||
|
||||
trojan = "trojan://" password:password "@" server:server ":" port:port params? name:name?{
|
||||
trojan = "trojan://" password:password "@" server:server ":" port:port "/"? params? name:name?{
|
||||
proxy.type = "trojan";
|
||||
proxy.password = password;
|
||||
proxy.server = server;
|
||||
@@ -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.sni = params["sni"] || params["peer"];
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { safeLoad } from 'static-js-yaml';
|
||||
import { safeLoad } from '@/utils/yaml';
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
function HTML() {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
getFlowField,
|
||||
getFlowHeaders,
|
||||
parseFlowHeaders,
|
||||
validCheck,
|
||||
flowTransfer,
|
||||
} from '@/utils/flow';
|
||||
|
||||
@@ -86,12 +87,15 @@ function QuickSettingOperator(args) {
|
||||
if (get(args.useless)) {
|
||||
const filter = UselessFilter();
|
||||
const selected = filter.func(proxies);
|
||||
proxies.filter((_, i) => selected[i]);
|
||||
proxies = proxies.filter(
|
||||
(p, i) => selected[i] && p.port > 0 && p.port <= 65535,
|
||||
);
|
||||
}
|
||||
|
||||
return proxies.map((proxy) => {
|
||||
proxy.udp = get(args.udp, proxy.udp);
|
||||
proxy.tfo = get(args.tfo, proxy.tfo);
|
||||
proxy['fast-open'] = get(args.tfo, proxy['fast-open']);
|
||||
proxy['skip-cert-verify'] = get(
|
||||
args.scert,
|
||||
proxy['skip-cert-verify'],
|
||||
@@ -509,7 +513,7 @@ function ResolveDomainOperator({ provider, type, filter }) {
|
||||
|
||||
return proxies.filter((p) => {
|
||||
if (filter === 'removeFailed') {
|
||||
return p['no-resolve'] || p.resolved;
|
||||
return isIP(p.server) || p['no-resolve'] || p.resolved;
|
||||
} else if (filter === 'IPOnly') {
|
||||
return isIP(p.server);
|
||||
} else if (filter === 'IPv4Only') {
|
||||
@@ -806,6 +810,7 @@ function createDynamicFunction(name, script, $arguments) {
|
||||
getFlowHeaders,
|
||||
parseFlowHeaders,
|
||||
flowTransfer,
|
||||
validCheck,
|
||||
};
|
||||
if ($.env.isLoon) {
|
||||
return new Function(
|
||||
|
||||
@@ -144,6 +144,10 @@ export default function Clash_Producer() {
|
||||
proxy.fingerprint = proxy['tls-fingerprint'];
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
if (isPresent(proxy, 'tls') && typeof proxy.tls !== 'boolean') {
|
||||
delete proxy.tls;
|
||||
}
|
||||
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
if (
|
||||
|
||||
@@ -160,6 +160,9 @@ export default function ClashMeta_Producer() {
|
||||
proxy.fingerprint = proxy['tls-fingerprint'];
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
if (isPresent(proxy, 'tls') && typeof proxy.tls !== 'boolean') {
|
||||
delete proxy.tls;
|
||||
}
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
if (
|
||||
|
||||
@@ -32,6 +32,32 @@ export default function Loon_Producer() {
|
||||
|
||||
function shadowsocks(proxy) {
|
||||
const result = new Result(proxy);
|
||||
if (
|
||||
![
|
||||
'rc4',
|
||||
'rc4-md5',
|
||||
'aes-128-cfb',
|
||||
'aes-192-cfb',
|
||||
'aes-256-cfb',
|
||||
'aes-128-ctr',
|
||||
'aes-192-ctr',
|
||||
'aes-256-ctr',
|
||||
'bf-cfb',
|
||||
'camellia-128-cfb',
|
||||
'camellia-192-cfb',
|
||||
'camellia-256-cfb',
|
||||
'salsa20',
|
||||
'chacha20',
|
||||
'chacha20-ietf',
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||
}
|
||||
result.append(
|
||||
`${proxy.name}=shadowsocks,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.password}"`,
|
||||
);
|
||||
@@ -57,7 +83,9 @@ function shadowsocks(proxy) {
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp=${proxy.udp}`, 'udp');
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
@@ -83,7 +111,9 @@ function shadowsocksr(proxy) {
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp=${proxy.udp}`, 'udp');
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
@@ -126,7 +156,9 @@ function trojan(proxy) {
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp=${proxy.udp}`, 'udp');
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
@@ -193,7 +225,9 @@ function vmess(proxy) {
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp=${proxy.udp}`, 'udp');
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@@ -255,7 +289,9 @@ function vless(proxy) {
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp=${proxy.udp}`, 'udp');
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@@ -278,8 +314,6 @@ function http(proxy) {
|
||||
// tfo
|
||||
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@@ -310,7 +344,11 @@ function wireguard(proxy) {
|
||||
if (proxy.dns) {
|
||||
if (Array.isArray(proxy.dns)) {
|
||||
proxy.dnsv6 = proxy.dns.find((i) => isIPv6(i));
|
||||
proxy.dns = proxy.dns.find((i) => isIPv4(i));
|
||||
let dns = proxy.dns.find((i) => isIPv4(i));
|
||||
if (!dns) {
|
||||
dns = proxy.dns.find((i) => !isIPv4(i) && !isIPv6(i));
|
||||
}
|
||||
proxy.dns = dns;
|
||||
}
|
||||
}
|
||||
result.appendIfPresent(`,dns=${proxy.dns}`, 'dns');
|
||||
@@ -360,8 +398,13 @@ function hysteria2(proxy) {
|
||||
'skip-cert-verify',
|
||||
);
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// udp
|
||||
result.appendIfPresent(`,udp=${proxy.udp}`, 'udp');
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
}
|
||||
|
||||
// download-bandwidth
|
||||
result.appendIfPresent(
|
||||
|
||||
@@ -37,7 +37,36 @@ function shadowsocks(proxy) {
|
||||
const result = new Result(proxy);
|
||||
const append = result.append.bind(result);
|
||||
const appendIfPresent = result.appendIfPresent.bind(result);
|
||||
|
||||
if (!proxy.cipher) {
|
||||
proxy.cipher = 'none';
|
||||
}
|
||||
if (
|
||||
![
|
||||
'none',
|
||||
'rc4-md5',
|
||||
'rc4-md5-6',
|
||||
'aes-128-cfb',
|
||||
'aes-192-cfb',
|
||||
'aes-256-cfb',
|
||||
'aes-128-ctr',
|
||||
'aes-192-ctr',
|
||||
'aes-256-ctr',
|
||||
'bf-cfb',
|
||||
'cast5-cfb',
|
||||
'des-cfb',
|
||||
'rc2-cfb',
|
||||
'salsa20',
|
||||
'chacha20',
|
||||
'chacha20-ietf',
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||
}
|
||||
append(`shadowsocks=${proxy.server}:${proxy.port}`);
|
||||
append(`,method=${proxy.cipher}`);
|
||||
append(`,password=${proxy.password}`);
|
||||
|
||||
@@ -163,6 +163,9 @@ export default function ShadowRocket_Producer() {
|
||||
proxy.fingerprint = proxy['tls-fingerprint'];
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
if (isPresent(proxy, 'tls') && typeof proxy.tls !== 'boolean') {
|
||||
delete proxy.tls;
|
||||
}
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
if (
|
||||
|
||||
@@ -225,7 +225,7 @@ const httpParser = (proxy = {}) => {
|
||||
server_port: parseInt(`${proxy.port}`, 10),
|
||||
tls: { enabled: false, server_name: proxy.server, insecure: false },
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy.username) parsedProxy.username = proxy.username;
|
||||
if (proxy.password) parsedProxy.password = proxy.password;
|
||||
@@ -252,7 +252,7 @@ const socks5Parser = (proxy = {}) => {
|
||||
password: proxy.password,
|
||||
version: '5',
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy.username) parsedProxy.username = proxy.username;
|
||||
if (proxy.password) parsedProxy.password = proxy.password;
|
||||
@@ -287,7 +287,7 @@ const shadowTLSParser = (proxy = {}) => {
|
||||
},
|
||||
},
|
||||
};
|
||||
if (stPart.server_port < 1 || stPart.server_port > 65535)
|
||||
if (stPart.server_port < 0 || stPart.server_port > 65535)
|
||||
throw '端口值非法';
|
||||
if (proxy['fast-open'] === true) stPart.udp_fragment = true;
|
||||
tfoParser(proxy, stPart);
|
||||
@@ -303,7 +303,7 @@ const ssParser = (proxy = {}) => {
|
||||
method: proxy.cipher,
|
||||
password: proxy.password,
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy.uot) parsedProxy.udp_over_tcp = true;
|
||||
if (proxy['udp-over-tcp']) parsedProxy.udp_over_tcp = true;
|
||||
@@ -379,7 +379,7 @@ const ssrParser = (proxy = {}) => {
|
||||
obfs: proxy.obfs,
|
||||
protocol: proxy.protocol,
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy['obfs-param']) parsedProxy.obfs_param = proxy['obfs-param'];
|
||||
if (proxy['protocol-param'] && proxy['protocol-param'] !== '')
|
||||
@@ -412,7 +412,7 @@ const vmessParser = (proxy = {}) => {
|
||||
].indexOf(parsedProxy.security) === -1
|
||||
)
|
||||
parsedProxy.security = 'auto';
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy.xudp) parsedProxy.packet_encoding = 'xudp';
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
@@ -436,7 +436,7 @@ const vlessParser = (proxy = {}) => {
|
||||
uuid: proxy.uuid,
|
||||
tls: { enabled: false, server_name: proxy.server, insecure: false },
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
if (proxy.flow === 'xtls-rprx-vision') parsedProxy.flow = proxy.flow;
|
||||
@@ -457,7 +457,7 @@ const trojanParser = (proxy = {}) => {
|
||||
password: proxy.password,
|
||||
tls: { enabled: true, server_name: proxy.server, insecure: false },
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
||||
@@ -477,7 +477,7 @@ const hysteriaParser = (proxy = {}) => {
|
||||
disable_mtu_discovery: false,
|
||||
tls: { enabled: true, server_name: proxy.server, insecure: false },
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy.auth_str) parsedProxy.auth_str = `${proxy.auth_str}`;
|
||||
if (proxy['auth-str']) parsedProxy.auth_str = `${proxy['auth-str']}`;
|
||||
@@ -524,7 +524,7 @@ const hysteria2Parser = (proxy = {}) => {
|
||||
obfs: {},
|
||||
tls: { enabled: true, server_name: proxy.server, insecure: false },
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy.up) parsedProxy.up_mbps = parseInt(`${proxy.up}`, 10);
|
||||
if (proxy.down) parsedProxy.down_mbps = parseInt(`${proxy.down}`, 10);
|
||||
@@ -547,7 +547,7 @@ const tuic5Parser = (proxy = {}) => {
|
||||
password: proxy.password,
|
||||
tls: { enabled: true, server_name: proxy.server, insecure: false },
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
if (
|
||||
@@ -583,7 +583,7 @@ const wireguardParser = (proxy = {}) => {
|
||||
pre_shared_key: proxy['pre-shared-key'],
|
||||
reserved: [],
|
||||
};
|
||||
if (parsedProxy.server_port < 1 || parsedProxy.server_port > 65535)
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
if (typeof proxy.reserved === 'string') {
|
||||
@@ -637,6 +637,35 @@ export default function singbox_Producer() {
|
||||
}
|
||||
break;
|
||||
case 'ss':
|
||||
// if (!proxy.cipher) {
|
||||
// proxy.cipher = 'none';
|
||||
// }
|
||||
// if (
|
||||
// ![
|
||||
// '2022-blake3-aes-128-gcm',
|
||||
// '2022-blake3-aes-256-gcm',
|
||||
// '2022-blake3-chacha20-poly1305',
|
||||
// 'aes-128-cfb',
|
||||
// 'aes-128-ctr',
|
||||
// 'aes-128-gcm',
|
||||
// 'aes-192-cfb',
|
||||
// 'aes-192-ctr',
|
||||
// 'aes-192-gcm',
|
||||
// 'aes-256-cfb',
|
||||
// 'aes-256-ctr',
|
||||
// 'aes-256-gcm',
|
||||
// 'chacha20-ietf',
|
||||
// 'chacha20-ietf-poly1305',
|
||||
// 'none',
|
||||
// 'rc4-md5',
|
||||
// 'xchacha20',
|
||||
// 'xchacha20-ietf-poly1305',
|
||||
// ].includes(proxy.cipher)
|
||||
// ) {
|
||||
// throw new Error(
|
||||
// `cipher ${proxy.cipher} is not supported`,
|
||||
// );
|
||||
// }
|
||||
if (proxy.plugin === 'shadow-tls') {
|
||||
const { ssPart, stPart } =
|
||||
shadowTLSParser(proxy);
|
||||
|
||||
@@ -242,6 +242,9 @@ export default function Stash_Producer() {
|
||||
proxy.fingerprint = proxy['tls-fingerprint'];
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
if (isPresent(proxy, 'tls') && typeof proxy.tls !== 'boolean') {
|
||||
delete proxy.tls;
|
||||
}
|
||||
|
||||
if (proxy['test-url']) {
|
||||
proxy['benchmark-url'] = proxy['test-url'];
|
||||
|
||||
@@ -31,6 +31,32 @@ export default function Surfboard_Producer() {
|
||||
function shadowsocks(proxy) {
|
||||
const result = new Result(proxy);
|
||||
result.append(`${proxy.name}=${proxy.type},${proxy.server},${proxy.port}`);
|
||||
if (
|
||||
![
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
'rc4',
|
||||
'rc4-md5',
|
||||
'aes-128-cfb',
|
||||
'aes-192-cfb',
|
||||
'aes-256-cfb',
|
||||
'aes-128-ctr',
|
||||
'aes-192-ctr',
|
||||
'aes-256-ctr',
|
||||
'bf-cfb',
|
||||
'camellia-128-cfb',
|
||||
'camellia-192-cfb',
|
||||
'camellia-256-cfb',
|
||||
'salsa20',
|
||||
'chacha20',
|
||||
'chacha20-ietf',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||
}
|
||||
result.append(`,encrypt-method=${proxy.cipher}`);
|
||||
result.appendIfPresent(`,password=${proxy.password}`, 'password');
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Result, isPresent } from './utils';
|
||||
import { isNotBlank } from '@/utils';
|
||||
import { isNotBlank, getIfNotBlank } from '@/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
const targetPlatform = 'Surge';
|
||||
@@ -13,7 +13,7 @@ const ipVersions = {
|
||||
};
|
||||
|
||||
export default function Surge_Producer() {
|
||||
const produce = (proxy) => {
|
||||
const produce = (proxy, type, opts = {}) => {
|
||||
switch (proxy.type) {
|
||||
case 'ss':
|
||||
return shadowsocks(proxy);
|
||||
@@ -30,10 +30,14 @@ export default function Surge_Producer() {
|
||||
case 'tuic':
|
||||
return tuic(proxy);
|
||||
case 'wireguard-surge':
|
||||
return wireguard(proxy);
|
||||
return wireguard_surge(proxy);
|
||||
case 'hysteria2':
|
||||
return hysteria2(proxy);
|
||||
}
|
||||
|
||||
if (opts['include-unsupported-proxy'] && proxy.type === 'wireguard') {
|
||||
return wireguard(proxy);
|
||||
}
|
||||
throw new Error(
|
||||
`Platform ${targetPlatform} does not support proxy type: ${proxy.type}`,
|
||||
);
|
||||
@@ -44,13 +48,46 @@ export default function Surge_Producer() {
|
||||
function shadowsocks(proxy) {
|
||||
const result = new Result(proxy);
|
||||
result.append(`${proxy.name}=${proxy.type},${proxy.server},${proxy.port}`);
|
||||
if (!proxy.cipher) {
|
||||
proxy.cipher = 'none';
|
||||
}
|
||||
if (
|
||||
![
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
'rc4',
|
||||
'rc4-md5',
|
||||
'aes-128-cfb',
|
||||
'aes-192-cfb',
|
||||
'aes-256-cfb',
|
||||
'aes-128-ctr',
|
||||
'aes-192-ctr',
|
||||
'aes-256-ctr',
|
||||
'bf-cfb',
|
||||
'camellia-128-cfb',
|
||||
'camellia-192-cfb',
|
||||
'camellia-256-cfb',
|
||||
'cast5-cfb',
|
||||
'des-cfb',
|
||||
'idea-cfb',
|
||||
'rc2-cfb',
|
||||
'seed-cfb',
|
||||
'salsa20',
|
||||
'chacha20',
|
||||
'chacha20-ietf',
|
||||
'none',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||
}
|
||||
result.append(`,encrypt-method=${proxy.cipher}`);
|
||||
result.appendIfPresent(`,password=${proxy.password}`, 'password');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
@@ -132,10 +169,8 @@ function trojan(proxy) {
|
||||
result.append(`${proxy.name}=${proxy.type},${proxy.server},${proxy.port}`);
|
||||
result.appendIfPresent(`,password=${proxy.password}`, 'password');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
@@ -201,10 +236,8 @@ function vmess(proxy) {
|
||||
result.append(`${proxy.name}=${proxy.type},${proxy.server},${proxy.port}`);
|
||||
result.appendIfPresent(`,username=${proxy.uuid}`, 'uuid');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
@@ -279,10 +312,8 @@ function http(proxy) {
|
||||
result.appendIfPresent(`,${proxy.username}`, 'username');
|
||||
result.appendIfPresent(`,${proxy.password}`, 'password');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
@@ -344,10 +375,8 @@ function socks5(proxy) {
|
||||
result.appendIfPresent(`,${proxy.username}`, 'username');
|
||||
result.appendIfPresent(`,${proxy.password}`, 'password');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
@@ -410,10 +439,8 @@ function snell(proxy) {
|
||||
result.appendIfPresent(`,version=${proxy.version}`, 'version');
|
||||
result.appendIfPresent(`,psk=${proxy.psk}`, 'psk');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
@@ -490,10 +517,8 @@ function tuic(proxy) {
|
||||
'alpn',
|
||||
);
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
@@ -552,6 +577,107 @@ function tuic(proxy) {
|
||||
}
|
||||
|
||||
function wireguard(proxy) {
|
||||
if (Array.isArray(proxy.peers) && proxy.peers.length > 0) {
|
||||
proxy.server = proxy.peers[0].server;
|
||||
proxy.port = proxy.peers[0].port;
|
||||
proxy.ip = proxy.peers[0].ip;
|
||||
proxy.ipv6 = proxy.peers[0].ipv6;
|
||||
proxy['public-key'] = proxy.peers[0]['public-key'];
|
||||
proxy['preshared-key'] = proxy.peers[0]['pre-shared-key'];
|
||||
// https://github.com/MetaCubeX/mihomo/blob/0404e35be8736b695eae018a08debb175c1f96e6/docs/config.yaml#L717
|
||||
proxy['allowed-ips'] = proxy.peers[0]['allowed-ips'];
|
||||
proxy.reserved = proxy.peers[0].reserved;
|
||||
}
|
||||
const result = new Result(proxy);
|
||||
|
||||
result.append(`# WireGuard Proxy ${proxy.name}
|
||||
${proxy.name}=wireguard`);
|
||||
|
||||
proxy['section-name'] = getIfNotBlank(proxy['section-name'], proxy.name);
|
||||
|
||||
result.appendIfPresent(
|
||||
`,section-name=${proxy['section-name']}`,
|
||||
'section-name',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
'no-error-alert',
|
||||
);
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
// test-url
|
||||
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
|
||||
|
||||
// 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',
|
||||
);
|
||||
}
|
||||
|
||||
// block-quic
|
||||
result.appendIfPresent(`,block-quic=${proxy['block-quic']}`, 'block-quic');
|
||||
|
||||
// underlying-proxy
|
||||
result.appendIfPresent(
|
||||
`,underlying-proxy=${proxy['underlying-proxy']}`,
|
||||
'underlying-proxy',
|
||||
);
|
||||
|
||||
result.append(`
|
||||
# WireGuard Section ${proxy.name}
|
||||
[WireGuard ${proxy['section-name']}]
|
||||
private-key = ${proxy['private-key']}`);
|
||||
|
||||
result.appendIfPresent(`\nself-ip = ${proxy.ip}`, 'ip');
|
||||
result.appendIfPresent(`\nself-ip-v6 = ${proxy.ipv6}`, 'ipv6');
|
||||
if (proxy.dns) {
|
||||
if (Array.isArray(proxy.dns)) {
|
||||
proxy.dns = proxy.dns.join(', ');
|
||||
}
|
||||
result.append(`\ndns-server = ${proxy.dns}`);
|
||||
}
|
||||
result.appendIfPresent(`\nmtu = ${proxy.mtu}`, 'mtu');
|
||||
|
||||
if (ip_version === 'prefer-v6') {
|
||||
result.append(`\nprefer-ipv6 = true`);
|
||||
}
|
||||
const allowedIps = Array.isArray(proxy['allowed-ips'])
|
||||
? proxy['allowed-ips'].join(',')
|
||||
: proxy['allowed-ips'];
|
||||
let reserved = Array.isArray(proxy.reserved)
|
||||
? proxy.reserved.join('/')
|
||||
: proxy.reserved;
|
||||
let presharedKey = proxy['preshared-key'] ?? proxy['pre-shared-key'];
|
||||
if (presharedKey) {
|
||||
presharedKey = `,preshared-key="${presharedKey}"`;
|
||||
}
|
||||
const peer = {
|
||||
'public-key': proxy['public-key'],
|
||||
'allowed-ips': allowedIps,
|
||||
endpoint: `${proxy.server}:${proxy.port}`,
|
||||
keepalive: proxy['persistent-keepalive'] || proxy.keepalive,
|
||||
'client-id': reserved,
|
||||
'preshared-key': presharedKey,
|
||||
};
|
||||
result.append(
|
||||
`\npeer = (${Object.keys(peer)
|
||||
.filter((k) => peer[k] != null)
|
||||
.map((k) => `${k} = ${peer[k]}`)
|
||||
.join(', ')})`,
|
||||
);
|
||||
return result.toString();
|
||||
}
|
||||
function wireguard_surge(proxy) {
|
||||
const result = new Result(proxy);
|
||||
|
||||
result.append(`${proxy.name}=wireguard`);
|
||||
@@ -565,10 +691,8 @@ function wireguard(proxy) {
|
||||
'no-error-alert',
|
||||
);
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
// test-url
|
||||
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
|
||||
@@ -608,10 +732,8 @@ function hysteria2(proxy) {
|
||||
|
||||
result.appendIfPresent(`,password=${proxy.password}`, 'password');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
|
||||
'ip-version',
|
||||
);
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
result.appendIfPresent(
|
||||
`,no-error-alert=${proxy['no-error-alert']}`,
|
||||
|
||||
@@ -6,6 +6,11 @@ export default function URI_Producer() {
|
||||
const type = 'SINGLE';
|
||||
const produce = (proxy) => {
|
||||
let result = '';
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
if (['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(proxy.type)) {
|
||||
delete proxy.tls;
|
||||
}
|
||||
if (proxy.server && isIPv6(proxy.server)) {
|
||||
proxy.server = `[${proxy.server}]`;
|
||||
}
|
||||
@@ -197,9 +202,9 @@ export default function URI_Producer() {
|
||||
|
||||
result = `vless://${proxy.uuid}@${proxy.server}:${
|
||||
proxy.port
|
||||
}?${vlessTransport}&security=${encodeURIComponent(
|
||||
}?security=${encodeURIComponent(
|
||||
security,
|
||||
)}${alpn}${allowInsecure}${sni}${fp}${flow}${sid}${pbk}#${encodeURIComponent(
|
||||
)}${vlessTransport}${alpn}${allowInsecure}${sni}${fp}${flow}${sid}${pbk}#${encodeURIComponent(
|
||||
proxy.name,
|
||||
)}`;
|
||||
break;
|
||||
@@ -285,6 +290,119 @@ export default function URI_Producer() {
|
||||
'&',
|
||||
)}#${encodeURIComponent(proxy.name)}`;
|
||||
break;
|
||||
case 'hysteria':
|
||||
let hysteriaParams = [];
|
||||
Object.keys(proxy).forEach((key) => {
|
||||
if (!['name', 'type', 'server', 'port'].includes(key)) {
|
||||
const i = key.replace(/-/, '_');
|
||||
if (['alpn'].includes(key)) {
|
||||
if (proxy[key]) {
|
||||
hysteriaParams.push(
|
||||
`${i}=${encodeURIComponent(
|
||||
Array.isArray(proxy[key])
|
||||
? proxy[key][0]
|
||||
: proxy[key],
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
} else if (['skip-cert-verify'].includes(key)) {
|
||||
if (proxy[key]) {
|
||||
hysteriaParams.push(`insecure=1`);
|
||||
}
|
||||
} else if (['tfo', 'fast-open'].includes(key)) {
|
||||
if (
|
||||
proxy[key] &&
|
||||
!hysteriaParams.includes('fastopen=1')
|
||||
) {
|
||||
hysteriaParams.push(`fastopen=1`);
|
||||
}
|
||||
} else if (['ports'].includes(key)) {
|
||||
hysteriaParams.push(`mport=${proxy[key]}`);
|
||||
} else if (['auth-str'].includes(key)) {
|
||||
hysteriaParams.push(`auth=${proxy[key]}`);
|
||||
} else if (['up'].includes(key)) {
|
||||
hysteriaParams.push(`upmbps=${proxy[key]}`);
|
||||
} else if (['down'].includes(key)) {
|
||||
hysteriaParams.push(`downmbps=${proxy[key]}`);
|
||||
} else if (['_obfs'].includes(key)) {
|
||||
hysteriaParams.push(`obfs=${proxy[key]}`);
|
||||
} else if (['obfs'].includes(key)) {
|
||||
hysteriaParams.push(`obfsParam=${proxy[key]}`);
|
||||
} else if (['sni'].includes(key)) {
|
||||
hysteriaParams.push(`peer=${proxy[key]}`);
|
||||
} else if (proxy[key]) {
|
||||
hysteriaParams.push(
|
||||
`${i}=${encodeURIComponent(proxy[key])}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
result = `hysteria://${proxy.server}:${
|
||||
proxy.port
|
||||
}?${hysteriaParams.join('&')}#${encodeURIComponent(
|
||||
proxy.name,
|
||||
)}`;
|
||||
break;
|
||||
|
||||
case 'tuic':
|
||||
if (!proxy.token || proxy.token.length === 0) {
|
||||
let tuicParams = [];
|
||||
Object.keys(proxy).forEach((key) => {
|
||||
if (
|
||||
![
|
||||
'name',
|
||||
'type',
|
||||
'uuid',
|
||||
'password',
|
||||
'server',
|
||||
'port',
|
||||
].includes(key)
|
||||
) {
|
||||
const i = key.replace(/-/, '_');
|
||||
if (['alpn'].includes(key)) {
|
||||
if (proxy[key]) {
|
||||
tuicParams.push(
|
||||
`${i}=${encodeURIComponent(
|
||||
Array.isArray(proxy[key])
|
||||
? proxy[key][0]
|
||||
: proxy[key],
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
} else if (['skip-cert-verify'].includes(key)) {
|
||||
if (proxy[key]) {
|
||||
tuicParams.push(`allow_insecure=1`);
|
||||
}
|
||||
} else if (['tfo', 'fast-open'].includes(key)) {
|
||||
if (
|
||||
proxy[key] &&
|
||||
!tuicParams.includes('fast_open=1')
|
||||
) {
|
||||
tuicParams.push(`fast_open=1`);
|
||||
}
|
||||
} else if (
|
||||
['disable-sni', 'reduce-rtt'].includes(key) &&
|
||||
proxy[key]
|
||||
) {
|
||||
tuicParams.push(`${i}=1`);
|
||||
} else if (proxy[key]) {
|
||||
tuicParams.push(
|
||||
`${i}=${encodeURIComponent(proxy[key])}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
result = `tuic://${encodeURIComponent(
|
||||
proxy.uuid,
|
||||
)}:${encodeURIComponent(proxy.password)}@${proxy.server}:${
|
||||
proxy.port
|
||||
}?${tuicParams.join('&')}#${encodeURIComponent(
|
||||
proxy.name,
|
||||
)}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import YAML from 'static-js-yaml';
|
||||
import YAML from '@/utils/yaml';
|
||||
|
||||
function QXFilter() {
|
||||
const type = 'SINGLE';
|
||||
|
||||
@@ -2,28 +2,79 @@
|
||||
import { ProxyUtils } from '@/core/proxy-utils';
|
||||
import { RuleUtils } from '@/core/rule-utils';
|
||||
import { version } from '../../package.json';
|
||||
import download from '@/utils/download';
|
||||
|
||||
console.log(
|
||||
`
|
||||
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
|
||||
Sub-Store -- v${version}
|
||||
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
|
||||
`,
|
||||
);
|
||||
let result = '';
|
||||
let resource = typeof $resource !== 'undefined' ? $resource : '';
|
||||
let resourceType = typeof $resourceType !== 'undefined' ? $resourceType : '';
|
||||
let resourceUrl = typeof $resourceUrl !== 'undefined' ? $resourceUrl : '';
|
||||
|
||||
const RESOURCE_TYPE = {
|
||||
PROXY: 1,
|
||||
RULE: 2,
|
||||
};
|
||||
!(async () => {
|
||||
console.log(
|
||||
`
|
||||
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
|
||||
Sub-Store -- v${version}
|
||||
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
|
||||
`,
|
||||
);
|
||||
|
||||
let result = $resource;
|
||||
let arg;
|
||||
if (typeof $argument != 'undefined') {
|
||||
arg = Object.fromEntries(
|
||||
$argument.split('&').map((item) => item.split('=')),
|
||||
);
|
||||
} else {
|
||||
arg = {};
|
||||
}
|
||||
|
||||
if ($resourceType === RESOURCE_TYPE.PROXY) {
|
||||
const proxies = ProxyUtils.parse($resource);
|
||||
result = ProxyUtils.produce(proxies, 'Loon');
|
||||
} else if ($resourceType === RESOURCE_TYPE.RULE) {
|
||||
const rules = RuleUtils.parse($resource);
|
||||
result = RuleUtils.produce(rules, 'Loon');
|
||||
}
|
||||
const RESOURCE_TYPE = {
|
||||
PROXY: 1,
|
||||
RULE: 2,
|
||||
};
|
||||
|
||||
$done(result);
|
||||
result = resource;
|
||||
|
||||
if (resourceType === RESOURCE_TYPE.PROXY) {
|
||||
try {
|
||||
let proxies = ProxyUtils.parse(resource);
|
||||
result = ProxyUtils.produce(proxies, 'Loon');
|
||||
} catch (e) {
|
||||
console.log('解析器: 使用 resource 出现错误');
|
||||
console.log(e.message ?? e);
|
||||
}
|
||||
if ((!result || /^\s*$/.test(result)) && resourceUrl) {
|
||||
console.log(`解析器: 尝试从 ${resourceUrl} 获取订阅`);
|
||||
try {
|
||||
let raw = await download(resourceUrl, arg?.ua, arg?.timeout);
|
||||
let proxies = ProxyUtils.parse(raw);
|
||||
result = ProxyUtils.produce(proxies, 'Loon');
|
||||
} catch (e) {
|
||||
console.log(e.message ?? e);
|
||||
}
|
||||
}
|
||||
} else if (resourceType === RESOURCE_TYPE.RULE) {
|
||||
try {
|
||||
const rules = RuleUtils.parse(resource);
|
||||
result = RuleUtils.produce(rules, 'Loon');
|
||||
} catch (e) {
|
||||
console.log(e.message ?? e);
|
||||
}
|
||||
if ((!result || /^\s*$/.test(result)) && resourceUrl) {
|
||||
console.log(`解析器: 尝试从 ${resourceUrl} 获取规则`);
|
||||
try {
|
||||
let raw = await download(resourceUrl, arg?.ua, arg?.timeout);
|
||||
let rules = RuleUtils.parse(raw);
|
||||
result = RuleUtils.produce(rules, 'Loon');
|
||||
} catch (e) {
|
||||
console.log(e.message ?? e);
|
||||
}
|
||||
}
|
||||
}
|
||||
})()
|
||||
.catch(async (e) => {
|
||||
console.log('解析器: 出现错误');
|
||||
console.log(e.message ?? e);
|
||||
})
|
||||
.finally(() => {
|
||||
$done(result || '');
|
||||
});
|
||||
|
||||
@@ -4,7 +4,12 @@ import { HTTP, ENV } from '@/vendor/open-api';
|
||||
import { hex_md5 } from '@/vendor/md5';
|
||||
import resourceCache from '@/utils/resource-cache';
|
||||
import headersResourceCache from '@/utils/headers-resource-cache';
|
||||
import { getFlowField } from '@/utils/flow';
|
||||
import {
|
||||
getFlowField,
|
||||
getFlowHeaders,
|
||||
parseFlowHeaders,
|
||||
validCheck,
|
||||
} from '@/utils/flow';
|
||||
import $ from '@/core/app';
|
||||
|
||||
const tasks = new Map();
|
||||
@@ -64,36 +69,40 @@ export default async function download(rawUrl, ua, timeout) {
|
||||
timeout: requestTimeout,
|
||||
});
|
||||
|
||||
const result = new Promise((resolve, reject) => {
|
||||
// try to find in app cache
|
||||
const cached = resourceCache.get(id);
|
||||
if (!$arguments?.noCache && cached) {
|
||||
resolve(cached);
|
||||
} else {
|
||||
$.info(
|
||||
`Downloading...\nUser-Agent: ${userAgent}\nTimeout: ${requestTimeout}\nURL: ${url}`,
|
||||
);
|
||||
http.get(url)
|
||||
.then((resp) => {
|
||||
const { body, headers } = resp;
|
||||
if (headers) {
|
||||
const flowInfo = getFlowField(headers);
|
||||
if (flowInfo) {
|
||||
headersResourceCache.set(url, flowInfo);
|
||||
}
|
||||
}
|
||||
if (body.replace(/\s/g, '').length === 0)
|
||||
reject(new Error('远程资源内容为空!'));
|
||||
else {
|
||||
resourceCache.set(id, body);
|
||||
resolve(body);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reject(new Error(`无法下载 URL:${url}`));
|
||||
});
|
||||
let result;
|
||||
|
||||
// try to find in app cache
|
||||
const cached = resourceCache.get(id);
|
||||
if (!$arguments?.noCache && cached) {
|
||||
result = cached;
|
||||
} else {
|
||||
$.info(
|
||||
`Downloading...\nUser-Agent: ${userAgent}\nTimeout: ${requestTimeout}\nURL: ${url}`,
|
||||
);
|
||||
try {
|
||||
const { body, headers } = await http.get(url);
|
||||
|
||||
if (headers) {
|
||||
const flowInfo = getFlowField(headers);
|
||||
if (flowInfo) {
|
||||
headersResourceCache.set(url, flowInfo);
|
||||
}
|
||||
}
|
||||
if (body.replace(/\s/g, '').length === 0)
|
||||
throw new Error(new Error('远程资源内容为空'));
|
||||
|
||||
resourceCache.set(id, body);
|
||||
result = body;
|
||||
} catch (e) {
|
||||
throw new Error(`无法下载 URL ${url}: ${e.message ?? e}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 检查订阅有效性
|
||||
|
||||
if ($arguments?.validCheck) {
|
||||
await validCheck(parseFlowHeaders(await getFlowHeaders(url)));
|
||||
}
|
||||
|
||||
if (!isNode) {
|
||||
tasks.set(id, result);
|
||||
|
||||
@@ -120,3 +120,26 @@ export function flowTransfer(flow, unit = 'B') {
|
||||
? { value: flow.toFixed(1), unit: unit }
|
||||
: flowTransfer(flow / 1024, unitList[++unitIndex]);
|
||||
}
|
||||
|
||||
export function validCheck(flow) {
|
||||
if (!flow) {
|
||||
throw new Error('没有流量信息');
|
||||
}
|
||||
if (flow?.expires && flow.expires * 1000 < Date.now()) {
|
||||
const date = new Date(flow.expires * 1000).toLocaleDateString();
|
||||
throw new Error(`订阅已过期: ${date}`);
|
||||
}
|
||||
if (flow?.total) {
|
||||
const upload = flow.usage?.upload || 0;
|
||||
const download = flow.usage?.download || 0;
|
||||
if (flow.total - upload - download < 0) {
|
||||
const current = upload + download;
|
||||
const currT = flowTransfer(Math.abs(current));
|
||||
currT.value = current < 0 ? '-' + currT.value : currT.value;
|
||||
const totalT = flowTransfer(flow.total);
|
||||
throw new Error(
|
||||
`流量已用完: ${currT.value} ${currT.unit} / ${totalT.value} ${totalT.unit}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,17 @@ class ResourceCache {
|
||||
if (!$.read(HEADERS_RESOURCE_CACHE_KEY)) {
|
||||
$.write('{}', HEADERS_RESOURCE_CACHE_KEY);
|
||||
}
|
||||
this.resourceCache = JSON.parse($.read(HEADERS_RESOURCE_CACHE_KEY));
|
||||
try {
|
||||
this.resourceCache = JSON.parse($.read(HEADERS_RESOURCE_CACHE_KEY));
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`解析持久化缓存中的 ${HEADERS_RESOURCE_CACHE_KEY} 失败, 重置为 {}, 错误: ${
|
||||
e?.message ?? e
|
||||
}`,
|
||||
);
|
||||
this.resourceCache = {};
|
||||
$.write('{}', HEADERS_RESOURCE_CACHE_KEY);
|
||||
}
|
||||
this._cleanup();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,17 @@ class ResourceCache {
|
||||
if (!$.read(RESOURCE_CACHE_KEY)) {
|
||||
$.write('{}', RESOURCE_CACHE_KEY);
|
||||
}
|
||||
this.resourceCache = JSON.parse($.read(RESOURCE_CACHE_KEY));
|
||||
try {
|
||||
this.resourceCache = JSON.parse($.read(RESOURCE_CACHE_KEY));
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`解析持久化缓存中的 ${RESOURCE_CACHE_KEY} 失败, 重置为 {}, 错误: ${
|
||||
e?.message ?? e
|
||||
}`,
|
||||
);
|
||||
this.resourceCache = {};
|
||||
$.write('{}', RESOURCE_CACHE_KEY);
|
||||
}
|
||||
this._cleanup();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,17 @@ class ResourceCache {
|
||||
if (!$.read(SCRIPT_RESOURCE_CACHE_KEY)) {
|
||||
$.write('{}', SCRIPT_RESOURCE_CACHE_KEY);
|
||||
}
|
||||
this.resourceCache = JSON.parse($.read(SCRIPT_RESOURCE_CACHE_KEY));
|
||||
try {
|
||||
this.resourceCache = JSON.parse($.read(SCRIPT_RESOURCE_CACHE_KEY));
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`解析持久化缓存中的 ${SCRIPT_RESOURCE_CACHE_KEY} 失败, 重置为 {}, 错误: ${
|
||||
e?.message ?? e
|
||||
}`,
|
||||
);
|
||||
this.resourceCache = {};
|
||||
$.write('{}', SCRIPT_RESOURCE_CACHE_KEY);
|
||||
}
|
||||
this._cleanup();
|
||||
}
|
||||
|
||||
|
||||
37
backend/src/utils/yaml.js
Normal file
37
backend/src/utils/yaml.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import YAML from 'static-js-yaml';
|
||||
|
||||
function retry(fn, content, ...args) {
|
||||
try {
|
||||
return fn(content, ...args);
|
||||
} catch (e) {
|
||||
return fn(
|
||||
dump(
|
||||
fn(
|
||||
content.replace(/!<str>\s*/g, '__SubStoreJSYAMLString__'),
|
||||
...args,
|
||||
),
|
||||
).replace(/__SubStoreJSYAMLString__/g, ''),
|
||||
...args,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function safeLoad(content, ...args) {
|
||||
return retry(YAML.safeLoad, content, ...args);
|
||||
}
|
||||
export function load(content, ...args) {
|
||||
return retry(YAML.load, content, ...args);
|
||||
}
|
||||
export function safeDump(...args) {
|
||||
return YAML.safeDump(...args);
|
||||
}
|
||||
export function dump(...args) {
|
||||
return YAML.dump(...args);
|
||||
}
|
||||
|
||||
export default {
|
||||
safeLoad,
|
||||
load,
|
||||
safeDump,
|
||||
dump,
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
name: Sub-Store
|
||||
desc: 高级订阅管理工具 @Peng-YM. 定时任务默认为每天 0 点
|
||||
icon: https://raw.githubusercontent.com/cc63/ICON/main/Sub-Store.png
|
||||
|
||||
http:
|
||||
mitm:
|
||||
|
||||
131
scripts/demo.js
Normal file
131
scripts/demo.js
Normal file
@@ -0,0 +1,131 @@
|
||||
function operator(proxies = [], targetPlatform, context) {
|
||||
// 支持快捷操作 不一定要写一个 function
|
||||
// 可参考 https://t.me/zhetengsha/970
|
||||
// https://t.me/zhetengsha/1009
|
||||
|
||||
|
||||
// proxies 为传入的内部节点数组
|
||||
// 结构大致参考了 Clash.Meta(mihomo) 有私货
|
||||
// 可在预览界面点击节点查看 JSON 结构 或查看 `target=JSON` 的通用订阅
|
||||
// 1. `no-resolve` 为不解析域名
|
||||
// 2. 域名解析后 会多一个 `resolved` 字段
|
||||
// 3. 节点字段 `exec` 为 `ssr-local` 路径, 默认 `/usr/local/bin/ssr-local`; 端口从 10000 开始递增(暂不支持配置)
|
||||
|
||||
// $arguments 为传入的脚本参数
|
||||
|
||||
// targetPlatform 为输出的目标平台
|
||||
|
||||
// lodash
|
||||
|
||||
// $substore 为 OpenAPI
|
||||
// 参考 https://github.com/Peng-YM/QuanX/blob/master/Tools/OpenAPI/README.md
|
||||
|
||||
// scriptResourceCache 缓存
|
||||
// 可参考 https://t.me/zhetengsha/1003
|
||||
|
||||
// ProxyUtils 为节点处理工具
|
||||
// 可参考 https://t.me/zhetengsha/1066
|
||||
// const ProxyUtils = {
|
||||
// parse, // 订阅解析
|
||||
// process, // 节点操作/文件操作
|
||||
// produce, // 输出订阅
|
||||
// isIPv4,
|
||||
// isIPv6,
|
||||
// isIP,
|
||||
// yaml, // yaml 解析和生成
|
||||
// }
|
||||
|
||||
// flowUtils 为机场订阅流量信息处理工具
|
||||
// 可参考 https://t.me/zhetengsha/948
|
||||
// https://github.com/sub-store-org/Sub-Store/blob/31b6dd0507a9286d6ab834ec94ad3050f6bdc86b/backend/src/utils/download.js#L104
|
||||
|
||||
// context 为传入的上下文
|
||||
// 有三种情况, 按需判断
|
||||
|
||||
// 若存在 `source._collection` 且 `source._collection.subscriptions` 中的 key 在 `source` 上也存在, 说明输出结果为组合订阅, 但是脚本设置在单条订阅上
|
||||
|
||||
// 若存在 `source._collection` 但 `source._collection.subscriptions` 中的 key 在 `source` 上不存在, 说明输出结果为组合订阅, 脚本设置在组合订阅上
|
||||
|
||||
// 若不存在 `source._collection`, 说明输出结果为单条订阅, 脚本设置在此单条订阅上
|
||||
|
||||
// 1. 输出单条订阅 sub-1 时, 该单条订阅中的脚本上下文为:
|
||||
// {
|
||||
// "source": {
|
||||
// "sub-1": {
|
||||
// "name": "sub-1",
|
||||
// "displayName": "",
|
||||
// "mergeSources": "",
|
||||
// "ignoreFailedRemoteSub": true,
|
||||
// "process": [],
|
||||
// "icon": "",
|
||||
// "source": "local",
|
||||
// "url": "",
|
||||
// "content": "",
|
||||
// "ua": "",
|
||||
// "display-name": "",
|
||||
// "useCacheForFailedRemoteSub": false
|
||||
// }
|
||||
// },
|
||||
// "backend": "Node",
|
||||
// "version": "2.14.198"
|
||||
// }
|
||||
// 2. 输出组合订阅 collection-1 时, 该组合订阅中的脚本上下文为:
|
||||
// {
|
||||
// "source": {
|
||||
// "_collection": {
|
||||
// "name": "collection-1",
|
||||
// "displayName": "",
|
||||
// "mergeSources": "",
|
||||
// "ignoreFailedRemoteSub": false,
|
||||
// "icon": "",
|
||||
// "process": [],
|
||||
// "subscriptions": [
|
||||
// "sub-1"
|
||||
// ],
|
||||
// "display-name": ""
|
||||
// }
|
||||
// },
|
||||
// "backend": "Node",
|
||||
// "version": "2.14.198"
|
||||
// }
|
||||
// 3. 输出组合订阅 collection-1 时, 该组合订阅中的单条订阅 sub-1 中的某个脚本上下文为:
|
||||
// {
|
||||
// "source": {
|
||||
// "sub-1": {
|
||||
// "name": "sub-1",
|
||||
// "displayName": "",
|
||||
// "mergeSources": "",
|
||||
// "ignoreFailedRemoteSub": true,
|
||||
// "icon": "",
|
||||
// "process": [],
|
||||
// "source": "local",
|
||||
// "url": "",
|
||||
// "content": "",
|
||||
// "ua": "",
|
||||
// "display-name": "",
|
||||
// "useCacheForFailedRemoteSub": false
|
||||
// },
|
||||
// "_collection": {
|
||||
// "name": "collection-1",
|
||||
// "displayName": "",
|
||||
// "mergeSources": "",
|
||||
// "ignoreFailedRemoteSub": false,
|
||||
// "icon": "",
|
||||
// "process": [],
|
||||
// "subscriptions": [
|
||||
// "sub-1"
|
||||
// ],
|
||||
// "display-name": ""
|
||||
// }
|
||||
// },
|
||||
// "backend": "Node",
|
||||
// "version": "2.14.198"
|
||||
// }
|
||||
|
||||
// 参数说明
|
||||
// 可参考 https://github.com/sub-store-org/Sub-Store/wiki/%E9%93%BE%E6%8E%A5%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E
|
||||
|
||||
console.log(JSON.stringify(context, null, 2))
|
||||
|
||||
return proxies
|
||||
}
|
||||
Reference in New Issue
Block a user