mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2026-03-20 16:52:36 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68cb393a7e | ||
|
|
2e99f28aa5 | ||
|
|
1a18e65e47 | ||
|
|
bbd5341d7a | ||
|
|
623802d73f | ||
|
|
19920dbfa3 | ||
|
|
8764e01d7e | ||
|
|
31b6dd0507 | ||
|
|
a84007d39e | ||
|
|
751e50bf99 | ||
|
|
a91f978042 | ||
|
|
1248e6b32a | ||
|
|
d0f255d9c6 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.192",
|
||||
"version": "2.14.201",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -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';
|
||||
@@ -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']),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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:$[^,]+ {
|
||||
|
||||
@@ -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:$[^,]+ {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -806,6 +807,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}"`,
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -44,6 +44,41 @@ 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');
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import YAML from 'static-js-yaml';
|
||||
import YAML from '@/utils/yaml';
|
||||
|
||||
function QXFilter() {
|
||||
const type = 'SINGLE';
|
||||
|
||||
@@ -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}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
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