mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32eb069ab2 | ||
|
|
4c9f8011c7 | ||
|
|
bd26b0a561 | ||
|
|
958d1e52c8 | ||
|
|
e7a2e60963 | ||
|
|
fa6a274f79 | ||
|
|
e40b3f88d5 | ||
|
|
163ad9ee09 | ||
|
|
abb6f2dec1 | ||
|
|
56870bbd5f | ||
|
|
efbc6ecd84 | ||
|
|
c27c589024 | ||
|
|
0efed4f1a0 | ||
|
|
e3a514d1fb | ||
|
|
64478c7a27 | ||
|
|
dc8f19f350 | ||
|
|
b4ccfc7e07 | ||
|
|
3f1940630a | ||
|
|
5a0bdb1276 | ||
|
|
a1b86e26a2 | ||
|
|
6ec8c29f6a | ||
|
|
bbb9602f9f | ||
|
|
6db6153672 | ||
|
|
b66189948a | ||
|
|
2611dccc73 | ||
|
|
25d3cf6ca4 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.252",
|
||||
"version": "2.14.274",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -15,6 +15,7 @@ import $ from '@/core/app';
|
||||
import { FILES_KEY, MODULES_KEY } from '@/constants';
|
||||
import { findByName } from '@/utils/database';
|
||||
import { produceArtifact } from '@/restful/sync';
|
||||
import { getFlag, getISO } from '@/utils/geo';
|
||||
|
||||
function preprocess(raw) {
|
||||
for (const processor of PROXY_PREPROCESSORS) {
|
||||
@@ -159,7 +160,7 @@ async function processFn(proxies, operators = [], targetPlatform, source) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$.info(
|
||||
$.log(
|
||||
`Applying "${item.type}" with arguments:\n >>> ${
|
||||
JSON.stringify(item.args, null, 2) || 'None'
|
||||
}`,
|
||||
@@ -186,6 +187,10 @@ function produce(proxies, targetPlatform, type, opts = {}) {
|
||||
throw new Error(`Target platform: ${targetPlatform} is not supported!`);
|
||||
}
|
||||
|
||||
const sni_off_supported = /Surge|SurgeMac|Shadowrocket/i.test(
|
||||
targetPlatform,
|
||||
);
|
||||
|
||||
// filter unsupported proxies
|
||||
proxies = proxies.filter(
|
||||
(proxy) =>
|
||||
@@ -196,10 +201,22 @@ function produce(proxies, targetPlatform, type, opts = {}) {
|
||||
if (!isNotBlank(proxy.name)) {
|
||||
proxy.name = `${proxy.type} ${proxy.server}:${proxy.port}`;
|
||||
}
|
||||
if (proxy['disable-sni']) {
|
||||
if (sni_off_supported) {
|
||||
proxy.sni = 'off';
|
||||
} else if (!['tuic'].includes(proxy.type)) {
|
||||
$.error(
|
||||
`Target platform ${targetPlatform} does not support sni off. Proxy's fields (sni, tls-fingerprint and skip-cert-verify) will be modified.`,
|
||||
);
|
||||
proxy.sni = '';
|
||||
proxy['skip-cert-verify'] = true;
|
||||
delete proxy['tls-fingerprint'];
|
||||
}
|
||||
}
|
||||
return proxy;
|
||||
});
|
||||
|
||||
$.info(`Producing proxies for target: ${targetPlatform}`);
|
||||
$.log(`Producing proxies for target: ${targetPlatform}`);
|
||||
if (typeof producer.type === 'undefined' || producer.type === 'SINGLE') {
|
||||
let localPort = 10000;
|
||||
const list = proxies
|
||||
@@ -242,6 +259,8 @@ export const ProxyUtils = {
|
||||
isIPv6,
|
||||
isIP,
|
||||
yaml: YAML,
|
||||
getFlag,
|
||||
getISO,
|
||||
};
|
||||
|
||||
function tryParse(parser, line) {
|
||||
@@ -396,6 +415,9 @@ function lastParse(proxy) {
|
||||
proxy.name = `${proxy.type} ${proxy.server}:${proxy.port}`;
|
||||
}
|
||||
}
|
||||
if (['', 'off'].includes(proxy.sni)) {
|
||||
proxy['disable-sni'] = true;
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
@@ -547,6 +547,7 @@ function URI_Hysteria2() {
|
||||
proxy.obfs = params.obfs;
|
||||
}
|
||||
|
||||
proxy.ports = params.mport;
|
||||
proxy['obfs-password'] = params['obfs-password'];
|
||||
proxy['skip-cert-verify'] = /(TRUE)|1/i.test(params.insecure);
|
||||
proxy.tfo = /(TRUE)|1/i.test(params.fastopen);
|
||||
|
||||
@@ -177,7 +177,13 @@ username = & {
|
||||
password = comma match:[^,]+ { proxy.password = match.join(""); }
|
||||
|
||||
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
|
||||
sni = comma "sni" equals sni:domain { proxy.sni = sni; }
|
||||
sni = comma "sni" equals sni:("off"/domain) {
|
||||
if (sni === "off") {
|
||||
proxy["disable-sni"] = true;
|
||||
} else {
|
||||
proxy.sni = sni;
|
||||
}
|
||||
}
|
||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||
tls_fingerprint = comma "server-cert-fingerprint-sha256" equals tls_fingerprint:$[^,]+ { proxy["tls-fingerprint"] = tls_fingerprint.trim(); }
|
||||
|
||||
|
||||
@@ -175,7 +175,13 @@ username = & {
|
||||
password = comma match:[^,]+ { proxy.password = match.join(""); }
|
||||
|
||||
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
|
||||
sni = comma "sni" equals sni:domain { proxy.sni = sni; }
|
||||
sni = comma "sni" equals sni:("off"/domain) {
|
||||
if (sni === "off") {
|
||||
proxy["disable-sni"] = true;
|
||||
} else {
|
||||
proxy.sni = sni;
|
||||
}
|
||||
}
|
||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||
tls_fingerprint = comma "server-cert-fingerprint-sha256" equals tls_fingerprint:$[^,]+ { proxy["tls-fingerprint"] = tls_fingerprint.trim(); }
|
||||
|
||||
|
||||
@@ -617,6 +617,8 @@ function RegionFilter(regions) {
|
||||
SG: '🇸🇬',
|
||||
JP: '🇯🇵',
|
||||
UK: '🇬🇧',
|
||||
DE: '🇩🇪',
|
||||
KR: '🇰🇷',
|
||||
};
|
||||
return {
|
||||
name: 'Region Filter',
|
||||
|
||||
@@ -150,6 +150,13 @@ export default function Clash_Producer() {
|
||||
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
delete proxy.id;
|
||||
delete proxy.resolved;
|
||||
for (const key in proxy) {
|
||||
if (proxy[key] == null) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
|
||||
@@ -165,6 +165,13 @@ export default function ClashMeta_Producer() {
|
||||
}
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
delete proxy.id;
|
||||
delete proxy.resolved;
|
||||
for (const key in proxy) {
|
||||
if (proxy[key] == null) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
|
||||
@@ -13,7 +13,8 @@ import singbox_Producer from './sing-box';
|
||||
|
||||
function JSON_Producer() {
|
||||
const type = 'ALL';
|
||||
const produce = (proxies) => JSON.stringify(proxies, null, 2);
|
||||
const produce = (proxies, type) =>
|
||||
type === 'internal' ? proxies : JSON.stringify(proxies, null, 2);
|
||||
return { type, produce };
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,13 @@ export default function ShadowRocket_Producer() {
|
||||
}
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
delete proxy.id;
|
||||
delete proxy.resolved;
|
||||
for (const key in proxy) {
|
||||
if (proxy[key] == null) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
|
||||
@@ -789,7 +789,9 @@ export default function singbox_Producer() {
|
||||
$.error(e.message ?? e);
|
||||
}
|
||||
});
|
||||
return type === 'internal' ? list : JSON.stringify(list, null, 2);
|
||||
return type === 'internal'
|
||||
? list
|
||||
: JSON.stringify({ outbounds: list }, null, 2);
|
||||
};
|
||||
return { type, produce };
|
||||
}
|
||||
|
||||
@@ -257,6 +257,13 @@ export default function Stash_Producer() {
|
||||
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
delete proxy.id;
|
||||
delete proxy.resolved;
|
||||
for (const key in proxy) {
|
||||
if (proxy[key] == null) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (
|
||||
['grpc'].includes(proxy.network) &&
|
||||
proxy[`${proxy.network}-opts`]
|
||||
|
||||
@@ -8,6 +8,13 @@ export default function URI_Producer() {
|
||||
let result = '';
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
delete proxy.id;
|
||||
delete proxy.resolved;
|
||||
for (const key in proxy) {
|
||||
if (proxy[key] == null) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
if (['trojan', 'tuic', 'hysteria', 'hysteria2'].includes(proxy.type)) {
|
||||
delete proxy.tls;
|
||||
}
|
||||
@@ -274,6 +281,9 @@ export default function URI_Producer() {
|
||||
`sni=${encodeURIComponent(proxy.sni)}`,
|
||||
);
|
||||
}
|
||||
if (proxy.ports) {
|
||||
hysteria2params.push(`mport=${proxy.ports}`);
|
||||
}
|
||||
if (proxy['tls-fingerprint']) {
|
||||
hysteria2params.push(
|
||||
`pinSHA256=${encodeURIComponent(
|
||||
|
||||
@@ -10,6 +10,8 @@ const RULE_TYPES_MAPPING = [
|
||||
[/^PROTOCOL$/, 'PROTOCOL'],
|
||||
[/^IP-CIDR$/i, 'IP-CIDR'],
|
||||
[/^(IP-CIDR6|ip6-cidr|IP6-CIDR)$/, 'IP-CIDR6'],
|
||||
[/^GEOIP$/i, 'GEOIP'],
|
||||
[/^GEOSITE$/i, 'GEOSITE'],
|
||||
];
|
||||
|
||||
function AllRuleParser() {
|
||||
@@ -37,8 +39,7 @@ function AllRuleParser() {
|
||||
content: params[1],
|
||||
};
|
||||
if (
|
||||
rule.type === 'IP-CIDR' ||
|
||||
rule.type === 'IP-CIDR6'
|
||||
['IP-CIDR', 'IP-CIDR6', 'GEOIP'].includes(rule.type)
|
||||
) {
|
||||
rule.options = params.slice(2);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ function QXFilter() {
|
||||
'SRC-IP',
|
||||
'IN-PORT',
|
||||
'PROTOCOL',
|
||||
'GEOSITE',
|
||||
'GEOIP',
|
||||
];
|
||||
if (UNSUPPORTED.indexOf(rule.type) !== -1) return null;
|
||||
|
||||
@@ -29,6 +31,8 @@ function QXFilter() {
|
||||
function SurgeRuleSet() {
|
||||
const type = 'SINGLE';
|
||||
const func = (rule) => {
|
||||
const UNSUPPORTED = ['GEOSITE', 'GEOIP'];
|
||||
if (UNSUPPORTED.indexOf(rule.type) !== -1) return null;
|
||||
let output = `${rule.type},${rule.content}`;
|
||||
if (['IP-CIDR', 'IP-CIDR6'].includes(rule.type)) {
|
||||
output +=
|
||||
@@ -43,7 +47,7 @@ function LoonRules() {
|
||||
const type = 'SINGLE';
|
||||
const func = (rule) => {
|
||||
// skip unsupported rules
|
||||
const UNSUPPORTED = ['DEST-PORT', 'SRC-IP', 'IN-PORT', 'PROTOCOL'];
|
||||
const UNSUPPORTED = ['SRC-IP', 'GEOSITE', 'GEOIP'];
|
||||
if (UNSUPPORTED.indexOf(rule.type) !== -1) return null;
|
||||
if (['IP-CIDR', 'IP-CIDR6'].includes(rule.type) && rule.options) {
|
||||
// Loon only supports the no-resolve option
|
||||
@@ -69,7 +73,7 @@ function ClashRuleProvider() {
|
||||
let output = `${TRANSFORM[rule.type] || rule.type},${
|
||||
rule.content
|
||||
}`;
|
||||
if (['IP-CIDR', 'IP-CIDR6'].includes(rule.type)) {
|
||||
if (['IP-CIDR', 'IP-CIDR6', 'GEOIP'].includes(rule.type)) {
|
||||
if (rule.options) {
|
||||
// Clash only supports the no-resolve option
|
||||
rule.options = rule.options.filter((option) =>
|
||||
|
||||
@@ -128,10 +128,19 @@ async function doSync() {
|
||||
files.map((item) => [item.path, item]),
|
||||
);
|
||||
}
|
||||
const url = files[encodeURIComponent(artifact.name)]?.raw_url;
|
||||
artifact.url = isGitLab
|
||||
? url
|
||||
: url?.replace(/\/raw\/[^/]*\/(.*)/, '/raw/$1');
|
||||
const raw_url =
|
||||
files[encodeURIComponent(artifact.name)]?.raw_url;
|
||||
const new_url = isGitLab
|
||||
? raw_url
|
||||
: raw_url?.replace(/\/raw\/[^/]*\/(.*)/, '/raw/$1');
|
||||
$.info(
|
||||
`上传配置完成\n文件列表: ${Object.keys(files).join(
|
||||
', ',
|
||||
)}\n当前文件: ${encodeURIComponent(
|
||||
artifact.name,
|
||||
)}\n响应返回的原始链接: ${raw_url}\n处理完的新链接: ${new_url}`,
|
||||
);
|
||||
artifact.url = new_url;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { getPlatformFromHeaders } from '@/utils/platform';
|
||||
import {
|
||||
getUserAgentFromHeaders,
|
||||
getPlatformFromUserAgent,
|
||||
} from '@/utils/user-agent';
|
||||
import { COLLECTIONS_KEY, SUBS_KEY } from '@/constants';
|
||||
import { findByName } from '@/utils/database';
|
||||
import { getFlowHeaders } from '@/utils/flow';
|
||||
@@ -16,10 +19,15 @@ async function downloadSubscription(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
|
||||
const platform =
|
||||
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
|
||||
const userAgent = getUserAgentFromHeaders(req.headers);
|
||||
|
||||
const platform =
|
||||
req.query.target || getPlatformFromUserAgent(userAgent) || 'JSON';
|
||||
|
||||
$.info(
|
||||
`正在下载订阅:${name}\ntarget: ${platform}\n来源 User-Agent: ${userAgent.UA}`,
|
||||
);
|
||||
|
||||
$.info(`正在下载订阅:${name}`);
|
||||
let {
|
||||
url,
|
||||
ua,
|
||||
@@ -79,8 +87,7 @@ async function downloadSubscription(req, res) {
|
||||
|
||||
if (
|
||||
sub.source !== 'local' ||
|
||||
['localFirst', 'remoteFirst'].includes(sub.mergeSources) ||
|
||||
url
|
||||
['localFirst', 'remoteFirst'].includes(sub.mergeSources)
|
||||
) {
|
||||
try {
|
||||
url = `${url || sub.url}`
|
||||
@@ -113,7 +120,7 @@ async function downloadSubscription(req, res) {
|
||||
// forward flow headers
|
||||
const flowInfo = await getFlowHeaders(
|
||||
url,
|
||||
undefined,
|
||||
$arguments.flowUserAgent,
|
||||
undefined,
|
||||
sub.proxy,
|
||||
);
|
||||
@@ -173,13 +180,17 @@ async function downloadCollection(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
|
||||
const userAgent = getUserAgentFromHeaders(req.headers);
|
||||
|
||||
const platform =
|
||||
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
|
||||
req.query.target || getPlatformFromUserAgent(userAgent) || 'JSON';
|
||||
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const collection = findByName(allCols, name);
|
||||
|
||||
$.info(`正在下载组合订阅:${name}`);
|
||||
$.info(
|
||||
`正在下载组合订阅:${name}\ntarget: ${platform}\n来源 User-Agent: ${userAgent.UA}`,
|
||||
);
|
||||
|
||||
let { ignoreFailedRemoteSub, produceType, includeUnsupportedProxy } =
|
||||
req.query;
|
||||
@@ -250,7 +261,7 @@ async function downloadCollection(req, res) {
|
||||
if (!$arguments.noFlow) {
|
||||
const flowInfo = await getFlowHeaders(
|
||||
url,
|
||||
undefined,
|
||||
$arguments.flowUserAgent,
|
||||
undefined,
|
||||
sub.proxy,
|
||||
);
|
||||
|
||||
@@ -3,6 +3,8 @@ import { ENV } from '@/vendor/open-api';
|
||||
import { failed, success } from '@/restful/response';
|
||||
import { updateArtifactStore, updateAvatar } from '@/restful/settings';
|
||||
import resourceCache from '@/utils/resource-cache';
|
||||
import scriptResourceCache from '@/utils/script-resource-cache';
|
||||
import headersResourceCache from '@/utils/headers-resource-cache';
|
||||
import {
|
||||
GIST_BACKUP_FILE_NAME,
|
||||
GIST_BACKUP_KEY,
|
||||
@@ -73,6 +75,8 @@ async function refresh(_, res) {
|
||||
|
||||
// 2. clear resource cache
|
||||
resourceCache.revokeAll();
|
||||
scriptResourceCache.revokeAll();
|
||||
headersResourceCache.revokeAll();
|
||||
success(res);
|
||||
}
|
||||
|
||||
@@ -153,11 +157,14 @@ async function gistBackup(req, res) {
|
||||
}
|
||||
success(res);
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`Failed to ${action} gist data.\nReason: ${err.message ?? err}`,
|
||||
);
|
||||
failed(
|
||||
res,
|
||||
new InternalServerError(
|
||||
'BACKUP_FAILED',
|
||||
`Failed to ${action} data to gist!`,
|
||||
`Failed to ${action} gist data!`,
|
||||
`Reason: ${err.message ?? err}`,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -253,11 +253,7 @@ async function compareCollection(req, res) {
|
||||
errors[name] = err;
|
||||
|
||||
$.error(
|
||||
`❌ 处理组合订阅中的子订阅: ${
|
||||
sub.name
|
||||
}时出现错误:${err}!进度--${
|
||||
100 * (processed / subnames.length).toFixed(1)
|
||||
}%`,
|
||||
`❌ 处理组合订阅 ${collection.name} 中的子订阅: ${sub.name}时出现错误:${err}!`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -115,7 +115,7 @@ async function getFlowInfo(req, res) {
|
||||
} else {
|
||||
const flowHeaders = await getFlowHeaders(
|
||||
url,
|
||||
undefined,
|
||||
$arguments.flowUserAgent,
|
||||
undefined,
|
||||
sub.proxy,
|
||||
);
|
||||
|
||||
@@ -563,10 +563,19 @@ async function syncArtifacts() {
|
||||
files.map((item) => [item.path, item]),
|
||||
);
|
||||
}
|
||||
const url = files[encodeURIComponent(artifact.name)]?.raw_url;
|
||||
artifact.url = isGitLab
|
||||
? url
|
||||
: url?.replace(/\/raw\/[^/]*\/(.*)/, '/raw/$1');
|
||||
const raw_url =
|
||||
files[encodeURIComponent(artifact.name)]?.raw_url;
|
||||
const new_url = isGitLab
|
||||
? raw_url
|
||||
: raw_url?.replace(/\/raw\/[^/]*\/(.*)/, '/raw/$1');
|
||||
$.info(
|
||||
`上传配置完成\n文件列表: ${Object.keys(files).join(
|
||||
', ',
|
||||
)}\n当前文件: ${encodeURIComponent(
|
||||
artifact.name,
|
||||
)}\n响应返回的原始链接: ${raw_url}\n处理完的新链接: ${new_url}`,
|
||||
);
|
||||
artifact.url = new_url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,10 +669,18 @@ async function syncArtifact(req, res) {
|
||||
isGitLab = true;
|
||||
files = Object.fromEntries(files.map((item) => [item.path, item]));
|
||||
}
|
||||
const url = files[encodeURIComponent(artifact.name)]?.raw_url;
|
||||
artifact.url = isGitLab
|
||||
? url
|
||||
: url?.replace(/\/raw\/[^/]*\/(.*)/, '/raw/$1');
|
||||
const raw_url = files[encodeURIComponent(artifact.name)]?.raw_url;
|
||||
const new_url = isGitLab
|
||||
? raw_url
|
||||
: raw_url?.replace(/\/raw\/[^/]*\/(.*)/, '/raw/$1');
|
||||
$.info(
|
||||
`上传配置完成\n文件列表: ${Object.keys(files).join(
|
||||
', ',
|
||||
)}\n当前文件: ${encodeURIComponent(
|
||||
artifact.name,
|
||||
)}\n响应返回的原始链接: ${raw_url}\n处理完的新链接: ${new_url}`,
|
||||
);
|
||||
artifact.url = new_url;
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
success(res, artifact);
|
||||
} catch (err) {
|
||||
|
||||
@@ -53,7 +53,7 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
// return item.content;
|
||||
// }
|
||||
|
||||
const { isNode, isStash } = ENV();
|
||||
const { isNode, isStash, isLoon, isShadowRocket, isQX } = ENV();
|
||||
const { defaultUserAgent, defaultTimeout, cacheThreshold } =
|
||||
$.read(SETTINGS_KEY);
|
||||
const userAgent = ua || defaultUserAgent || 'clash.meta';
|
||||
@@ -66,8 +66,10 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
const http = HTTP({
|
||||
headers: {
|
||||
'User-Agent': userAgent,
|
||||
'X-Stash-Selected-Proxy':
|
||||
isStash && proxy ? encodeURIComponent(proxy) : undefined,
|
||||
...(isStash && proxy
|
||||
? { 'X-Stash-Selected-Proxy': encodeURIComponent(proxy) }
|
||||
: {}),
|
||||
...(isShadowRocket && proxy ? { 'X-Surge-Policy': proxy } : {}),
|
||||
},
|
||||
timeout: requestTimeout,
|
||||
});
|
||||
@@ -86,8 +88,10 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
try {
|
||||
const { body, headers } = await http.get({
|
||||
url,
|
||||
proxy,
|
||||
...getPolicyDescriptor(proxy),
|
||||
...(proxy ? { proxy } : {}),
|
||||
...(isLoon && proxy ? { node: proxy } : {}),
|
||||
...(isQX && proxy ? { opts: { policy: proxy } } : {}),
|
||||
...(proxy ? getPolicyDescriptor(proxy) : {}),
|
||||
});
|
||||
|
||||
if (headers) {
|
||||
@@ -125,7 +129,12 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
if ($arguments?.validCheck) {
|
||||
await validCheck(
|
||||
parseFlowHeaders(
|
||||
await getFlowHeaders(url, undefined, undefined, proxy),
|
||||
await getFlowHeaders(
|
||||
url,
|
||||
$arguments.flowUserAgent,
|
||||
undefined,
|
||||
proxy,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export async function getFlowHeaders(rawUrl, ua, timeout, proxy) {
|
||||
if ($arguments?.noFlow) {
|
||||
return;
|
||||
}
|
||||
const { isStash } = ENV();
|
||||
const { isStash, isLoon, isShadowRocket, isQX } = ENV();
|
||||
const cached = headersResourceCache.get(url);
|
||||
let flowInfo;
|
||||
if (!$arguments?.noCache && cached) {
|
||||
@@ -49,7 +49,11 @@ export async function getFlowHeaders(rawUrl, ua, timeout, proxy) {
|
||||
const requestTimeout = timeout || defaultTimeout;
|
||||
const http = HTTP();
|
||||
try {
|
||||
// $.info(`使用 HEAD 方法获取流量信息: ${url}`);
|
||||
$.info(
|
||||
`使用 HEAD 方法获取流量信息: ${url}, User-Agent: ${
|
||||
userAgent || ''
|
||||
}`,
|
||||
);
|
||||
const { headers } = await http.head({
|
||||
url: url
|
||||
.split(/[\r\n]+/)
|
||||
@@ -57,23 +61,36 @@ export async function getFlowHeaders(rawUrl, ua, timeout, proxy) {
|
||||
.filter((i) => i.length)[0],
|
||||
headers: {
|
||||
'User-Agent': userAgent,
|
||||
'X-Stash-Selected-Proxy':
|
||||
isStash && proxy
|
||||
? encodeURIComponent(proxy)
|
||||
: undefined,
|
||||
...(isStash && proxy
|
||||
? {
|
||||
'X-Stash-Selected-Proxy':
|
||||
encodeURIComponent(proxy),
|
||||
}
|
||||
: {}),
|
||||
...(isShadowRocket && proxy
|
||||
? { 'X-Surge-Policy': proxy }
|
||||
: {}),
|
||||
},
|
||||
timeout: requestTimeout,
|
||||
proxy,
|
||||
...getPolicyDescriptor(proxy),
|
||||
...(proxy ? { proxy } : {}),
|
||||
...(isLoon && proxy ? { node: proxy } : {}),
|
||||
...(isQX && proxy ? { opts: { policy: proxy } } : {}),
|
||||
...(proxy ? getPolicyDescriptor(proxy) : {}),
|
||||
});
|
||||
flowInfo = getFlowField(headers);
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`使用 HEAD 方法获取流量信息失败: ${url}: ${e.message ?? e}`,
|
||||
`使用 HEAD 方法获取流量信息失败: ${url}, User-Agent: ${
|
||||
userAgent || ''
|
||||
}: ${e.message ?? e}`,
|
||||
);
|
||||
}
|
||||
if (!flowInfo) {
|
||||
$.info(`使用 GET 方法获取流量信息: ${url}`);
|
||||
$.info(
|
||||
`使用 GET 方法获取流量信息: ${url}, User-Agent: ${
|
||||
userAgent || ''
|
||||
}`,
|
||||
);
|
||||
const { headers } = await http.get({
|
||||
url: url
|
||||
.split(/[\r\n]+/)
|
||||
@@ -121,10 +138,10 @@ export function parseFlowHeaders(flowHeaders) {
|
||||
return { expires, total, usage: { upload, download } };
|
||||
}
|
||||
export function flowTransfer(flow, unit = 'B') {
|
||||
const unitList = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
|
||||
const unitList = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
let unitIndex = unitList.indexOf(unit);
|
||||
|
||||
return flow < 1024
|
||||
return flow < 1024 || unitIndex === unitList.length - 1
|
||||
? { value: flow.toFixed(1), unit: unit }
|
||||
: flowTransfer(flow / 1024, unitList[++unitIndex]);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,105 @@
|
||||
const ISOFlags = {
|
||||
'🏳️🌈': ['EXP', 'BAND'],
|
||||
'🇸🇱': ['TEST', 'SOS'],
|
||||
'🇦🇩': ['AD', 'AND'],
|
||||
'🇦🇪': ['AE', 'ARE'],
|
||||
'🇦🇫': ['AF', 'AFG'],
|
||||
'🇦🇱': ['AL', 'ALB'],
|
||||
'🇦🇲': ['AM', 'ARM'],
|
||||
'🇦🇷': ['AR', 'ARG'],
|
||||
'🇦🇹': ['AT', 'AUT'],
|
||||
'🇦🇺': ['AU', 'AUS'],
|
||||
'🇦🇿': ['AZ', 'AZE'],
|
||||
'🇧🇦': ['BA', 'BIH'],
|
||||
'🇧🇩': ['BD', 'BGD'],
|
||||
'🇧🇪': ['BE', 'BEL'],
|
||||
'🇧🇬': ['BG', 'BGR'],
|
||||
'🇧🇭': ['BH', 'BHR'],
|
||||
'🇧🇷': ['BR', 'BRA'],
|
||||
'🇧🇾': ['BY', 'BLR'],
|
||||
'🇨🇦': ['CA', 'CAN'],
|
||||
'🇨🇭': ['CH', 'CHE'],
|
||||
'🇨🇱': ['CL', 'CHL'],
|
||||
'🇨🇴': ['CO', 'COL'],
|
||||
'🇨🇷': ['CR', 'CRI'],
|
||||
'🇨🇾': ['CY', 'CYP'],
|
||||
'🇨🇿': ['CZ', 'CZE'],
|
||||
'🇩🇪': ['DE', 'DEU'],
|
||||
'🇩🇰': ['DK', 'DNK'],
|
||||
'🇪🇨': ['EC', 'ECU'],
|
||||
'🇪🇪': ['EE', 'EST'],
|
||||
'🇪🇬': ['EG', 'EGY'],
|
||||
'🇪🇸': ['ES', 'ESP'],
|
||||
'🇪🇺': ['EU'],
|
||||
'🇫🇮': ['FI', 'FIN'],
|
||||
'🇫🇷': ['FR', 'FRA'],
|
||||
'🇬🇧': ['GB', 'GBR', 'UK'],
|
||||
'🇬🇪': ['GE', 'GEO'],
|
||||
'🇬🇷': ['GR', 'GRC'],
|
||||
'🇭🇰': ['HK', 'HKG', 'HKT', 'HKBN', 'HGC', 'WTT', 'CMI'],
|
||||
'🇭🇷': ['HR', 'HRV'],
|
||||
'🇭🇺': ['HU', 'HUN'],
|
||||
'🇯🇴': ['JO', 'JOR'],
|
||||
'🇯🇵': ['JP', 'JPN'],
|
||||
'🇰🇪': ['KE', 'KEN'],
|
||||
'🇰🇬': ['KG', 'KGZ'],
|
||||
'🇰🇭': ['KH', 'KGZ'],
|
||||
'🇰🇵': ['KP', 'PRK'],
|
||||
'🇰🇷': ['KR', 'KOR', 'SEL'],
|
||||
'🇰🇿': ['KZ', 'KAZ'],
|
||||
'🇮🇩': ['ID', 'IDN'],
|
||||
'🇮🇪': ['IE', 'IRL'],
|
||||
'🇮🇱': ['IL', 'ISR'],
|
||||
'🇮🇲': ['IM', 'IMN'],
|
||||
'🇮🇳': ['IN', 'IND'],
|
||||
'🇮🇷': ['IR', 'IRN'],
|
||||
'🇮🇸': ['IS', 'ISL'],
|
||||
'🇮🇹': ['IT', 'ITA'],
|
||||
'🇱🇹': ['LT', 'LTU'],
|
||||
'🇱🇺': ['LU', 'LUX'],
|
||||
'🇱🇻': ['LV', 'LVA'],
|
||||
'🇲🇦': ['MA', 'MAR'],
|
||||
'🇲🇩': ['MD', 'MDA'],
|
||||
'🇳🇬': ['NG', 'NGA'],
|
||||
'🇲🇰': ['MK', 'MKD'],
|
||||
'🇲🇳': ['MN', 'MNG'],
|
||||
'🇲🇴': ['MO', 'MAC', 'CTM'],
|
||||
'🇲🇹': ['MT', 'MLT'],
|
||||
'🇲🇽': ['MX', 'MEX'],
|
||||
'🇲🇾': ['MY', 'MYS'],
|
||||
'🇳🇱': ['NL', 'NLD'],
|
||||
'🇳🇴': ['NO', 'NOR'],
|
||||
'🇳🇵': ['NP', 'NPL'],
|
||||
'🇳🇿': ['NZ', 'NZL'],
|
||||
'🇵🇦': ['PA', 'PAN'],
|
||||
'🇵🇪': ['PE', 'PER'],
|
||||
'🇵🇭': ['PH', 'PHL'],
|
||||
'🇵🇰': ['PK', 'PAK'],
|
||||
'🇵🇱': ['PL', 'POL'],
|
||||
'🇵🇷': ['PR', 'PRI'],
|
||||
'🇵🇹': ['PT', 'PRT'],
|
||||
'🇵🇾': ['PY', 'PRY'],
|
||||
'🇷🇴': ['RO', 'ROU'],
|
||||
'🇷🇸': ['RS', 'SRB'],
|
||||
'🇷🇪': ['RE', 'REU'],
|
||||
'🇷🇺': ['RU', 'RUS'],
|
||||
'🇸🇦': ['SA', 'SAU'],
|
||||
'🇸🇪': ['SE', 'SWE'],
|
||||
'🇸🇬': ['SG', 'SGP'],
|
||||
'🇸🇮': ['SI', 'SVN'],
|
||||
'🇸🇰': ['SK', 'SVK'],
|
||||
'🇹🇭': ['TH', 'THA'],
|
||||
'🇹🇳': ['TN', 'TUN'],
|
||||
'🇹🇷': ['TR', 'TUR'],
|
||||
'🇹🇼': ['TW', 'TWN', 'CHT', 'HINET', 'ROC'],
|
||||
'🇺🇦': ['UA', 'UKR'],
|
||||
'🇺🇸': ['US', 'USA', 'LAX', 'SFO', 'SJC'],
|
||||
'🇺🇾': ['UY', 'URY'],
|
||||
'🇻🇪': ['VE', 'VEN'],
|
||||
'🇻🇳': ['VN', 'VNM'],
|
||||
'🇿🇦': ['ZA', 'ZAF', 'JNB'],
|
||||
'🇨🇳': ['CN', 'CHN', 'BACK'],
|
||||
};
|
||||
// get proxy flag according to its name
|
||||
export function getFlag(name) {
|
||||
// flags from @KOP-XIAO: https://github.com/KOP-XIAO/QuantumultX/blob/master/Scripts/resource-parser.js
|
||||
@@ -65,6 +167,7 @@ export function getFlag(name) {
|
||||
'广德',
|
||||
'法兰克福',
|
||||
'Frankfurt',
|
||||
'德意志',
|
||||
],
|
||||
'🇩🇰': ['Denmark', '丹麦', '丹麥'],
|
||||
'🇪🇨': ['Ecuador', '厄瓜多尔'],
|
||||
@@ -283,108 +386,6 @@ export function getFlag(name) {
|
||||
],
|
||||
};
|
||||
|
||||
const ISOFlags = {
|
||||
'🏳️🌈': ['EXP', 'BAND'],
|
||||
'🇸🇱': ['TEST', 'SOS'],
|
||||
'🇦🇩': ['AD', 'AND'],
|
||||
'🇦🇪': ['AE', 'ARE'],
|
||||
'🇦🇫': ['AF', 'AFG'],
|
||||
'🇦🇱': ['AL', 'ALB'],
|
||||
'🇦🇲': ['AM', 'ARM'],
|
||||
'🇦🇷': ['AR', 'ARG'],
|
||||
'🇦🇹': ['AT', 'AUT'],
|
||||
'🇦🇺': ['AU', 'AUS'],
|
||||
'🇦🇿': ['AZ', 'AZE'],
|
||||
'🇧🇦': ['BA', 'BIH'],
|
||||
'🇧🇩': ['BD', 'BGD'],
|
||||
'🇧🇪': ['BE', 'BEL'],
|
||||
'🇧🇬': ['BG', 'BGR'],
|
||||
'🇧🇭': ['BH', 'BHR'],
|
||||
'🇧🇷': ['BR', 'BRA'],
|
||||
'🇧🇾': ['BY', 'BLR'],
|
||||
'🇨🇦': ['CA', 'CAN'],
|
||||
'🇨🇭': ['CH', 'CHE'],
|
||||
'🇨🇱': ['CL', 'CHL'],
|
||||
'🇨🇴': ['CO', 'COL'],
|
||||
'🇨🇷': ['CR', 'CRI'],
|
||||
'🇨🇾': ['CY', 'CYP'],
|
||||
'🇨🇿': ['CZ', 'CZE'],
|
||||
'🇩🇪': ['DE', 'DEU'],
|
||||
'🇩🇰': ['DK', 'DNK'],
|
||||
'🇪🇨': ['EC', 'ECU'],
|
||||
'🇪🇪': ['EE', 'EST'],
|
||||
'🇪🇬': ['EG', 'EGY'],
|
||||
'🇪🇸': ['ES', 'ESP'],
|
||||
'🇪🇺': ['EU'],
|
||||
'🇫🇮': ['FI', 'FIN'],
|
||||
'🇫🇷': ['FR', 'FRA'],
|
||||
'🇬🇧': ['GB', 'GBR', 'UK'],
|
||||
'🇬🇪': ['GE', 'GEO'],
|
||||
'🇬🇷': ['GR', 'GRC'],
|
||||
'🇭🇰': ['HK', 'HKG', 'HKT', 'HKBN', 'HGC', 'WTT', 'CMI'],
|
||||
'🇭🇷': ['HR', 'HRV'],
|
||||
'🇭🇺': ['HU', 'HUN'],
|
||||
'🇯🇴': ['JO', 'JOR'],
|
||||
'🇯🇵': ['JP', 'JPN'],
|
||||
'🇰🇪': ['KE', 'KEN'],
|
||||
'🇰🇬': ['KG', 'KGZ'],
|
||||
'🇰🇭': ['KH', 'KGZ'],
|
||||
'🇰🇵': ['KP', 'PRK'],
|
||||
'🇰🇷': ['KR', 'KOR'],
|
||||
'🇰🇿': ['KZ', 'KAZ'],
|
||||
'🇮🇩': ['ID', 'IDN'],
|
||||
'🇮🇪': ['IE', 'IRL'],
|
||||
'🇮🇱': ['IL', 'ISR'],
|
||||
'🇮🇲': ['IM', 'IMN'],
|
||||
'🇮🇳': ['IN', 'IND'],
|
||||
'🇮🇷': ['IR', 'IRN'],
|
||||
'🇮🇸': ['IS', 'ISL'],
|
||||
'🇮🇹': ['IT', 'ITA'],
|
||||
'🇱🇹': ['LT', 'LTU'],
|
||||
'🇱🇺': ['LU', 'LUX'],
|
||||
'🇱🇻': ['LV', 'LVA'],
|
||||
'🇲🇦': ['MA', 'MAR'],
|
||||
'🇲🇩': ['MD', 'MDA'],
|
||||
'🇳🇬': ['NG', 'NGA'],
|
||||
'🇲🇰': ['MK', 'MKD'],
|
||||
'🇲🇳': ['MN', 'MNG'],
|
||||
'🇲🇴': ['MO', 'MAC', 'CTM'],
|
||||
'🇲🇹': ['MT', 'MLT'],
|
||||
'🇲🇽': ['MX', 'MEX'],
|
||||
'🇲🇾': ['MY', 'MYS'],
|
||||
'🇳🇱': ['NL', 'NLD'],
|
||||
'🇳🇴': ['NO', 'NOR'],
|
||||
'🇳🇵': ['NP', 'NPL'],
|
||||
'🇳🇿': ['NZ', 'NZL'],
|
||||
'🇵🇦': ['PA', 'PAN'],
|
||||
'🇵🇪': ['PE', 'PER'],
|
||||
'🇵🇭': ['PH', 'PHL'],
|
||||
'🇵🇰': ['PK', 'PAK'],
|
||||
'🇵🇱': ['PL', 'POL'],
|
||||
'🇵🇷': ['PR', 'PRI'],
|
||||
'🇵🇹': ['PT', 'PRT'],
|
||||
'🇵🇾': ['PY', 'PRY'],
|
||||
'🇷🇴': ['RO', 'ROU'],
|
||||
'🇷🇸': ['RS', 'SRB'],
|
||||
'🇷🇪': ['RE', 'REU'],
|
||||
'🇷🇺': ['RU', 'RUS'],
|
||||
'🇸🇦': ['SA', 'SAU'],
|
||||
'🇸🇪': ['SE', 'SWE'],
|
||||
'🇸🇬': ['SG', 'SGP'],
|
||||
'🇸🇮': ['SI', 'SVN'],
|
||||
'🇸🇰': ['SK', 'SVK'],
|
||||
'🇹🇭': ['TH', 'THA'],
|
||||
'🇹🇳': ['TN', 'TUN'],
|
||||
'🇹🇷': ['TR', 'TUR'],
|
||||
'🇹🇼': ['TW', 'TWN', 'CHT', 'HINET', 'ROC'],
|
||||
'🇺🇦': ['UA', 'UKR'],
|
||||
'🇺🇸': ['US', 'USA', 'LAX', 'SFO'],
|
||||
'🇺🇾': ['UY', 'URY'],
|
||||
'🇻🇪': ['VE', 'VEN'],
|
||||
'🇻🇳': ['VN', 'VNM'],
|
||||
'🇿🇦': ['ZA', 'ZAF'],
|
||||
'🇨🇳': ['CN', 'CHN', 'BACK'],
|
||||
};
|
||||
// 原旗帜或空
|
||||
let Flag =
|
||||
name.match(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/)?.[0] ||
|
||||
@@ -398,7 +399,9 @@ export function getFlag(name) {
|
||||
// 不精确匹配(只要包含就算,忽略大小写)
|
||||
keywords.some((keyword) => RegExp(`${keyword}`, 'i').test(name))
|
||||
) {
|
||||
//console.log(`newFlag = ${flag}`)
|
||||
if (/内蒙古/.test(name) && ['🇲🇳'].includes(flag)) {
|
||||
return (Flag = '🇨🇳');
|
||||
}
|
||||
return (Flag = flag);
|
||||
}
|
||||
}
|
||||
@@ -416,6 +419,11 @@ export function getFlag(name) {
|
||||
return (Flag = flag);
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(`Final Flag = ${Flag}`)
|
||||
return Flag;
|
||||
}
|
||||
|
||||
export function getISO(name) {
|
||||
return ISOFlags[getFlag(name)]?.[0];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export function getPlatformFromHeaders(headers) {
|
||||
export function getUserAgentFromHeaders(headers) {
|
||||
const keys = Object.keys(headers);
|
||||
let UA = '';
|
||||
let ua = '';
|
||||
@@ -9,6 +9,9 @@ export function getPlatformFromHeaders(headers) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { UA, ua };
|
||||
}
|
||||
export function getPlatformFromUserAgent({ ua, UA }) {
|
||||
if (UA.indexOf('Quantumult%20X') !== -1) {
|
||||
return 'QX';
|
||||
} else if (UA.indexOf('Surfboard') !== -1) {
|
||||
@@ -38,3 +41,7 @@ export function getPlatformFromHeaders(headers) {
|
||||
return 'JSON';
|
||||
}
|
||||
}
|
||||
export function getPlatformFromHeaders(headers) {
|
||||
const { UA, ua } = getUserAgentFromHeaders(headers);
|
||||
return getPlatformFromUserAgent({ ua, UA });
|
||||
}
|
||||
57
backend/src/vendor/open-api.js
vendored
57
backend/src/vendor/open-api.js
vendored
@@ -316,42 +316,53 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
|
||||
url: options.url,
|
||||
headers: options.headers,
|
||||
body: options.body,
|
||||
opts: options.opts,
|
||||
});
|
||||
} else if (isLoon || isSurge || isNode) {
|
||||
worker = new Promise((resolve, reject) => {
|
||||
const request = isNode
|
||||
? eval("require('request')")
|
||||
: $httpClient;
|
||||
request[method.toLowerCase()](
|
||||
JSON.parse(JSON.stringify(options)),
|
||||
(err, response, body) => {
|
||||
// if (err) {
|
||||
// console.log(err);
|
||||
// } else {
|
||||
// console.log({
|
||||
// statusCode:
|
||||
// response.status || response.statusCode,
|
||||
// headers: response.headers,
|
||||
// body,
|
||||
// });
|
||||
// }
|
||||
const opts = JSON.parse(JSON.stringify(options));
|
||||
if (!isNode && opts.timeout) {
|
||||
opts.timeout++;
|
||||
let unit = 'ms';
|
||||
// 这些客户端单位为 s
|
||||
if (isSurge || isStash || isShadowRocket) {
|
||||
opts.timeout = Math.ceil(opts.timeout / 1000);
|
||||
unit = 's';
|
||||
}
|
||||
// Loon 为 ms
|
||||
// console.log(`[httpClient timeout] ${opts.timeout}${unit}`);
|
||||
}
|
||||
request[method.toLowerCase()](opts, (err, response, body) => {
|
||||
// if (err) {
|
||||
// console.log(err);
|
||||
// } else {
|
||||
// console.log({
|
||||
// statusCode:
|
||||
// response.status || response.statusCode,
|
||||
// headers: response.headers,
|
||||
// body,
|
||||
// });
|
||||
// }
|
||||
|
||||
if (err) reject(err);
|
||||
else
|
||||
resolve({
|
||||
statusCode:
|
||||
response.status || response.statusCode,
|
||||
headers: response.headers,
|
||||
body,
|
||||
});
|
||||
},
|
||||
);
|
||||
if (err) reject(err);
|
||||
else
|
||||
resolve({
|
||||
statusCode: response.status || response.statusCode,
|
||||
headers: response.headers,
|
||||
body,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let timeoutid;
|
||||
|
||||
const timer = timeout
|
||||
? new Promise((_, reject) => {
|
||||
// console.log(`[request timeout] ${timeout}ms`);
|
||||
timeoutid = setTimeout(() => {
|
||||
events.onTimeout();
|
||||
return reject(
|
||||
|
||||
@@ -17,4 +17,4 @@ hostname=sub.store
|
||||
http-request ^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))) script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js, requires-body=true, timeout=120, tag=Sub-Store Core
|
||||
http-request ^https?:\/\/sub\.store script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js, requires-body=true, timeout=120, tag=Sub-Store Simple
|
||||
|
||||
cron "55 23 * * *" script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js, tag=Sub-Store Sync
|
||||
cron "55 23 * * *" script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js, timeout=120, tag=Sub-Store Sync
|
||||
@@ -10,6 +10,6 @@ hostname = %APPEND% sub.store
|
||||
[Script]
|
||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120,ability="{{{ability}}}"
|
||||
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true,timeout=120
|
||||
|
||||
{{{sync}}}=type=cron,cronexp="{{{cronexp}}}",wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||
|
||||
@@ -8,6 +8,6 @@ hostname = %APPEND% sub.store
|
||||
[Script]
|
||||
# 主程序 已经去掉 Sub-Store Core 的参数 [,ability=http-client-policy] 不会爆内存,这个参数在 Surge 非常占用内存; 如果不需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 则可以使用此脚本
|
||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true,timeout=120
|
||||
|
||||
Sub-Store Sync=type=cron,cronexp=55 23 * * *,wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||
|
||||
@@ -7,6 +7,6 @@ hostname = %APPEND% sub.store
|
||||
|
||||
[Script]
|
||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120,ability=http-client-policy
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true,timeout=120
|
||||
|
||||
Sub-Store Sync=type=cron,cronexp=55 23 * * *,wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||
|
||||
@@ -7,6 +7,6 @@ hostname = %APPEND% sub.store
|
||||
|
||||
[Script]
|
||||
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true
|
||||
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true,timeout=120
|
||||
|
||||
Sub-Store Sync=type=cron,cronexp=55 23 * * *,wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
|
||||
|
||||
@@ -22,6 +22,9 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
|
||||
// scriptResourceCache 缓存
|
||||
// 可参考 https://t.me/zhetengsha/1003
|
||||
// const cache = scriptResourceCache
|
||||
// cache.set(id, data)
|
||||
// cache.get(id)
|
||||
|
||||
// ProxyUtils 为节点处理工具
|
||||
// 可参考 https://t.me/zhetengsha/1066
|
||||
@@ -33,8 +36,20 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// isIPv6,
|
||||
// isIP,
|
||||
// yaml, // yaml 解析和生成
|
||||
// getFlag, // 获取 emoji 旗帜
|
||||
// getISO, // 获取 ISO 3166-1 alpha-2 代码
|
||||
// }
|
||||
|
||||
// 示例: 给节点名添加前缀
|
||||
// $server.name = `[${ProxyUtils.getISO($server.name)}] ${$server.name}`
|
||||
|
||||
// 示例: 从 sni 文件中读取内容并进行节点操作
|
||||
// const sni = await produceArtifact({
|
||||
// type: 'file',
|
||||
// name: 'sni' // 文件名
|
||||
// });
|
||||
// $server.sni = sni
|
||||
|
||||
// 1. Surge 输出 WireGuard 完整配置
|
||||
|
||||
// let proxies = await produceArtifact({
|
||||
@@ -49,7 +64,10 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
|
||||
// 2. sing-box
|
||||
|
||||
// 但是一般不需要这样用, 可参考 1. https://t.me/zhetengsha/1111 和 2. https://t.me/zhetengsha/1070
|
||||
// 但是一般不需要这样用, 可参考
|
||||
// 1. https://t.me/zhetengsha/1111
|
||||
// 2. https://t.me/zhetengsha/1070
|
||||
// 3. https://t.me/zhetengsha/1241
|
||||
|
||||
// let singboxProxies = await produceArtifact({
|
||||
// type: 'subscription', // type: 'subscription' 或 'collection'
|
||||
@@ -63,24 +81,42 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
|
||||
// 3. clash.meta
|
||||
|
||||
// 但是一般不需要这样用, 可参考 1. https://t.me/zhetengsha/1111 和 2. https://t.me/zhetengsha/1070
|
||||
// 但是一般不需要这样用, 可参考
|
||||
// 1. https://t.me/zhetengsha/1111
|
||||
// 2. https://t.me/zhetengsha/1070
|
||||
// 3. https://t.me/zhetengsha/1234
|
||||
|
||||
// let clashMetaProxies = await produceArtifact({
|
||||
// type: 'subscription',
|
||||
// name: 'sub',
|
||||
// platform: 'ClashMeta',
|
||||
// produceType: 'internal' // 'internal' produces an Array, otherwise produces a String( ProxyUtils.yaml.safeLoad('YAML String').proxies )
|
||||
// }))
|
||||
// })
|
||||
|
||||
// // YAML
|
||||
// ProxyUtils.yaml.load('YAML String')
|
||||
// ProxyUtils.yaml.safeLoad('YAML String')
|
||||
// $content = ProxyUtils.yaml.safeDump({})
|
||||
// $content = ProxyUtils.yaml.dump({})
|
||||
|
||||
// 一个往文件里插入本地节点的例子:
|
||||
// const yaml = ProxyUtils.yaml.safeLoad($content ?? $files[0])
|
||||
// let clashMetaProxies = await produceArtifact({
|
||||
// type: 'collection',
|
||||
// name: '机场',
|
||||
// platform: 'ClashMeta',
|
||||
// produceType: 'internal'
|
||||
// })
|
||||
// yaml.proxies.unshift(...clashMetaProxies)
|
||||
// $content = ProxyUtils.yaml.dump(yaml)
|
||||
|
||||
|
||||
// { $content, $files } will be passed to the next operator
|
||||
// $content is the final content of the file
|
||||
|
||||
// flowUtils 为机场订阅流量信息处理工具
|
||||
// 可参考 https://t.me/zhetengsha/948
|
||||
// https://github.com/sub-store-org/Sub-Store/blob/31b6dd0507a9286d6ab834ec94ad3050f6bdc86b/backend/src/utils/download.js#L104
|
||||
// 可参考:
|
||||
// 1. https://t.me/zhetengsha/948
|
||||
|
||||
// context 为传入的上下文
|
||||
// 有三种情况, 按需判断
|
||||
|
||||
Reference in New Issue
Block a user