mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2c3956884 | ||
|
|
21c1e11976 | ||
|
|
e0f6b3e692 | ||
|
|
0d2920fadd | ||
|
|
da9b1d8795 | ||
|
|
4c4bda563a | ||
|
|
95f181351a | ||
|
|
3b85063f73 | ||
|
|
7f691c8511 | ||
|
|
55cc7dcd16 | ||
|
|
4f745b0232 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.19.26",
|
||||
"version": "2.19.37",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Buffer } from 'buffer';
|
||||
import rs from '@/utils/rs';
|
||||
import YAML from '@/utils/yaml';
|
||||
import download from '@/utils/download';
|
||||
import download, { downloadFile } from '@/utils/download';
|
||||
import {
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
@@ -330,6 +330,7 @@ export const ProxyUtils = {
|
||||
MMDB,
|
||||
Gist,
|
||||
download,
|
||||
downloadFile,
|
||||
isValidUUID,
|
||||
doh,
|
||||
};
|
||||
|
||||
@@ -128,13 +128,13 @@ function URI_SS() {
|
||||
// parse url
|
||||
let content = line.split('ss://')[1];
|
||||
|
||||
let name = line.split('#')[1];
|
||||
const proxy = {
|
||||
name: decodeURIComponent(line.split('#')[1]),
|
||||
type: 'ss',
|
||||
};
|
||||
content = content.split('#')[0]; // strip proxy name
|
||||
// handle IPV4 and IPV6
|
||||
let serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||
let serverAndPortArray = content.match(/@([^/?]*)(\/|\?|$)/);
|
||||
|
||||
let rawUserInfoStr = decodeURIComponent(content.split('@')[0]); // 其实应该分隔之后, 用户名和密码再 decodeURIComponent. 但是问题不大
|
||||
let userInfoStr;
|
||||
@@ -260,6 +260,10 @@ function URI_SS() {
|
||||
if (/(&|\?)tfo=(1|true)/i.test(query)) {
|
||||
proxy.tfo = true;
|
||||
}
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
proxy.name = name ?? `SS ${proxy.server}:${proxy.port}`;
|
||||
return proxy;
|
||||
};
|
||||
return { name, test, parse };
|
||||
@@ -452,8 +456,12 @@ function URI_VMess() {
|
||||
);
|
||||
}
|
||||
// https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
|
||||
if (proxy.tls && params.sni && params.sni !== '') {
|
||||
proxy.sni = params.sni;
|
||||
if (proxy.tls) {
|
||||
if (params.sni && params.sni !== '') {
|
||||
proxy.sni = params.sni;
|
||||
} else if (params.peer && params.peer !== '') {
|
||||
proxy.sni = params.peer;
|
||||
}
|
||||
}
|
||||
let httpupgrade = false;
|
||||
// handle obfs
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
export default function Clash_Producer() {
|
||||
const type = 'ALL';
|
||||
@@ -46,6 +47,11 @@ export default function Clash_Producer() {
|
||||
proxy['reality-opts']))
|
||||
) {
|
||||
return false;
|
||||
} else if (proxy['underlying-proxy'] || proxy['dialer-proxy']) {
|
||||
$.error(
|
||||
`Clash 不支持前置代理字段. 已过滤节点 ${proxy.name}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
@@ -152,11 +158,6 @@ export default function Clash_Producer() {
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
|
||||
if (proxy['underlying-proxy']) {
|
||||
proxy['dialer-proxy'] = proxy['underlying-proxy'];
|
||||
}
|
||||
delete proxy['underlying-proxy'];
|
||||
|
||||
if (isPresent(proxy, 'tls') && typeof proxy.tls !== 'boolean') {
|
||||
delete proxy.tls;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function QX_Producer() {
|
||||
const produce = (proxy, type, opts = {}) => {
|
||||
switch (proxy.type) {
|
||||
case 'ss':
|
||||
return shadowsocks(proxy, opts['include-unsupported-proxy']);
|
||||
return shadowsocks(proxy);
|
||||
case 'ssr':
|
||||
return shadowsocksr(proxy);
|
||||
case 'trojan':
|
||||
@@ -28,7 +28,7 @@ export default function QX_Producer() {
|
||||
return { produce };
|
||||
}
|
||||
|
||||
function shadowsocks(proxy, includeUnsupportedProxy) {
|
||||
function shadowsocks(proxy) {
|
||||
const result = new Result(proxy);
|
||||
const append = result.append.bind(result);
|
||||
const appendIfPresent = result.appendIfPresent.bind(result);
|
||||
@@ -58,9 +58,8 @@ function shadowsocks(proxy, includeUnsupportedProxy) {
|
||||
'aes-256-gcm',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
...(includeUnsupportedProxy
|
||||
? ['2022-blake3-aes-128-gcm', '2022-blake3-aes-256-gcm']
|
||||
: []),
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
export default function Shadowrocket_Producer() {
|
||||
const type = 'ALL';
|
||||
@@ -10,6 +11,11 @@ export default function Shadowrocket_Producer() {
|
||||
return false;
|
||||
} else if (['mieru', 'anytls'].includes(proxy.type)) {
|
||||
return false;
|
||||
} else if (proxy['underlying-proxy'] || proxy['dialer-proxy']) {
|
||||
$.error(
|
||||
`Shadowrocket 不支持前置代理字段. 已过滤节点 ${proxy.name}. 请使用 App 内的 "代理通过" 功能`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
|
||||
@@ -31,6 +31,21 @@ const smuxParser = (smux, proxy) => {
|
||||
if (smux['min-streams'])
|
||||
proxy.multiplex.min_streams = parseInt(`${smux['min-streams']}`, 10);
|
||||
if (smux.padding) proxy.multiplex.padding = true;
|
||||
if (smux['brutal-opts']?.up || smux['brutal-opts']?.down) {
|
||||
proxy.multiplex.brutal = {
|
||||
enabled: true,
|
||||
};
|
||||
if (smux['brutal-opts']?.up)
|
||||
proxy.multiplex.brutal.up_mbps = parseInt(
|
||||
`${smux['brutal-opts']?.up}`,
|
||||
10,
|
||||
);
|
||||
if (smux['brutal-opts']?.down)
|
||||
proxy.multiplex.brutal.down_mbps = parseInt(
|
||||
`${smux['brutal-opts']?.down}`,
|
||||
10,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const wsParser = (proxy, parsedProxy) => {
|
||||
@@ -552,12 +567,13 @@ const hysteriaParser = (proxy = {}) => {
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const reg = new RegExp('^[0-9]+[ \t]*[KMGT]*[Bb]ps$');
|
||||
if (reg.test(`${proxy.up}`)) {
|
||||
// sing-box 跟文档不一致, 但是懒得全转, 只处理最常见的 Mbps
|
||||
if (reg.test(`${proxy.up}`) && !`${proxy.up}`.endsWith('Mbps')) {
|
||||
parsedProxy.up = `${proxy.up}`;
|
||||
} else {
|
||||
parsedProxy.up_mbps = parseInt(`${proxy.up}`, 10);
|
||||
}
|
||||
if (reg.test(`${proxy.down}`)) {
|
||||
if (reg.test(`${proxy.down}`) && !`${proxy.down}`.endsWith('Mbps')) {
|
||||
parsedProxy.down = `${proxy.down}`;
|
||||
} else {
|
||||
parsedProxy.down_mbps = parseInt(`${proxy.down}`, 10);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
export default function Stash_Producer() {
|
||||
const type = 'ALL';
|
||||
@@ -50,6 +51,11 @@ export default function Stash_Producer() {
|
||||
: proxy.type === 'vless' && proxy['reality-opts'])
|
||||
) {
|
||||
return false;
|
||||
} else if (proxy['underlying-proxy'] || proxy['dialer-proxy']) {
|
||||
$.error(
|
||||
`Stash 暂不支持前置代理字段. 已过滤节点 ${proxy.name}. 请使用 代理的转发链 https://stash.wiki/proxy-protocols/proxy-groups#relay`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
@@ -257,11 +263,6 @@ export default function Stash_Producer() {
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
|
||||
if (proxy['underlying-proxy']) {
|
||||
proxy['dialer-proxy'] = proxy['underlying-proxy'];
|
||||
}
|
||||
delete proxy['underlying-proxy'];
|
||||
|
||||
if (isPresent(proxy, 'tls') && typeof proxy.tls !== 'boolean') {
|
||||
delete proxy.tls;
|
||||
}
|
||||
|
||||
@@ -478,7 +478,7 @@ export default function URI_Producer() {
|
||||
hysteriaParams.push(`obfsParam=${proxy[key]}`);
|
||||
} else if (['sni'].includes(key)) {
|
||||
hysteriaParams.push(`peer=${proxy[key]}`);
|
||||
} else if (proxy[key]) {
|
||||
} else if (proxy[key] && !/^_/i.test(key)) {
|
||||
hysteriaParams.push(
|
||||
`${i}=${encodeURIComponent(proxy[key])}`,
|
||||
);
|
||||
@@ -541,7 +541,7 @@ export default function URI_Producer() {
|
||||
tuicParams.push(
|
||||
`congestion_control=${proxy[key]}`,
|
||||
);
|
||||
} else if (proxy[key]) {
|
||||
} else if (proxy[key] && !/^_/i.test(key)) {
|
||||
tuicParams.push(
|
||||
`${i.replace(
|
||||
/-/g,
|
||||
@@ -593,7 +593,7 @@ export default function URI_Producer() {
|
||||
if (proxy[key]) {
|
||||
anytlsParams.push(`udp=1`);
|
||||
}
|
||||
} else if (proxy[key]) {
|
||||
} else if (proxy[key] && !/^_/i.test(key)) {
|
||||
anytlsParams.push(
|
||||
`${i.replace(/-/g, '_')}=${encodeURIComponent(
|
||||
proxy[key],
|
||||
@@ -630,7 +630,7 @@ export default function URI_Producer() {
|
||||
if (proxy[key]) {
|
||||
wireguardParams.push(`${key}=1`);
|
||||
}
|
||||
} else if (proxy[key]) {
|
||||
} else if (proxy[key] && !/^_/i.test(key)) {
|
||||
wireguardParams.push(
|
||||
`${key}=${encodeURIComponent(proxy[key])}`,
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express from '@/vendor/express';
|
||||
import $ from '@/core/app';
|
||||
import migrate from '@/utils/migration';
|
||||
import download from '@/utils/download';
|
||||
import download, { downloadFile } from '@/utils/download';
|
||||
import { syncArtifacts, produceArtifact } from '@/restful/sync';
|
||||
import { gistBackupAction } from '@/restful/miscs';
|
||||
import { TOKENS_KEY } from '@/constants';
|
||||
@@ -35,7 +35,7 @@ export default function serve() {
|
||||
const fe_be_path = eval('process.env.SUB_STORE_FRONTEND_BACKEND_PATH');
|
||||
const fe_path = eval('process.env.SUB_STORE_FRONTEND_PATH');
|
||||
if (be_prefix || be_merge) {
|
||||
if(!fe_be_path.startsWith('/')){
|
||||
if (!fe_be_path.startsWith('/')) {
|
||||
throw new Error(
|
||||
'SUB_STORE_FRONTEND_BACKEND_PATH should start with /',
|
||||
);
|
||||
@@ -48,15 +48,20 @@ export default function serve() {
|
||||
$app.use((req, res, next) => {
|
||||
if (req.path.startsWith(fe_be_path)) {
|
||||
req.url = req.url.replace(fe_be_path, '') || '/';
|
||||
if(be_merge && req.url.startsWith('/api/')){
|
||||
if (be_merge && req.url.startsWith('/api/')) {
|
||||
req.query['share'] = 'true';
|
||||
}
|
||||
next();
|
||||
return;
|
||||
}
|
||||
const pathname = decodeURIComponent(req._parsedUrl.pathname) || '/';
|
||||
if(be_merge && req.path.startsWith('/share/') && req.query.token){
|
||||
if (req.method.toLowerCase() !== 'get'){
|
||||
const pathname =
|
||||
decodeURIComponent(req._parsedUrl.pathname) || '/';
|
||||
if (
|
||||
be_merge &&
|
||||
req.path.startsWith('/share/') &&
|
||||
req.query.token
|
||||
) {
|
||||
if (req.method.toLowerCase() !== 'get') {
|
||||
res.status(405).send('Method not allowed');
|
||||
return;
|
||||
}
|
||||
@@ -67,14 +72,14 @@ export default function serve() {
|
||||
`/share/${t.type}/${t.name}` === pathname &&
|
||||
(t.exp == null || t.exp > Date.now()),
|
||||
);
|
||||
if (token){
|
||||
if (token) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (be_merge && fe_path && req.path.indexOf('/',1) == -1) {
|
||||
if (req.path.indexOf('.') == -1){
|
||||
req.url = "/index.html"
|
||||
if (be_merge && fe_path && req.path.indexOf('/', 1) == -1) {
|
||||
if (req.path.indexOf('.') == -1) {
|
||||
req.url = '/index.html';
|
||||
}
|
||||
const express_ = eval(`require("express")`);
|
||||
const mime_ = eval(`require("mime-types")`);
|
||||
@@ -85,7 +90,7 @@ export default function serve() {
|
||||
if (type) {
|
||||
res.set('Content-Type', type);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
staticFileMiddleware(req, res, next);
|
||||
return;
|
||||
@@ -230,6 +235,60 @@ export default function serve() {
|
||||
// 'Asia/Shanghai' // timeZone
|
||||
);
|
||||
}
|
||||
const mmdb_cron = eval('process.env.SUB_STORE_MMDB_CRON');
|
||||
const countryFile = eval('process.env.SUB_STORE_MMDB_COUNTRY_PATH');
|
||||
const countryUrl = eval('process.env.SUB_STORE_MMDB_COUNTRY_URL');
|
||||
const asnFile = eval('process.env.SUB_STORE_MMDB_ASN_PATH');
|
||||
const asnUrl = eval('process.env.SUB_STORE_MMDB_ASN_URL');
|
||||
if (mmdb_cron && ((countryFile && countryUrl) || (asnFile && asnUrl))) {
|
||||
$.info(`[MMDB CRON] ${mmdb_cron} enabled`);
|
||||
const { CronJob } = eval(`require("cron")`);
|
||||
new CronJob(
|
||||
mmdb_cron,
|
||||
async function () {
|
||||
try {
|
||||
$.info(`[MMDB CRON] ${mmdb_cron} started`);
|
||||
if (countryFile && countryUrl) {
|
||||
try {
|
||||
$.info(
|
||||
`[MMDB CRON] downloading ${countryUrl} to ${countryFile}`,
|
||||
);
|
||||
await downloadFile(countryUrl, countryFile);
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`[MMDB CRON] ${countryUrl} download failed: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (asnFile && asnUrl) {
|
||||
try {
|
||||
$.info(
|
||||
`[MMDB CRON] downloading ${asnUrl} to ${asnFile}`,
|
||||
);
|
||||
await downloadFile(asnUrl, asnFile);
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`[MMDB CRON] ${asnUrl} download failed: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$.info(`[MMDB CRON] ${mmdb_cron} finished`);
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`[MMDB CRON] ${mmdb_cron} error: ${e.message ?? e}`,
|
||||
);
|
||||
}
|
||||
}, // onTick
|
||||
null, // onComplete
|
||||
true, // start
|
||||
// 'Asia/Shanghai' // timeZone
|
||||
);
|
||||
}
|
||||
const path = eval(`require("path")`);
|
||||
const fs = eval(`require("fs")`);
|
||||
const data_url = eval('process.env.SUB_STORE_DATA_URL');
|
||||
|
||||
@@ -47,15 +47,22 @@ async function previewFile(req, res) {
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
!file.ignoreFailedRemoteFile &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`文件 ${file.name} 的远程文件 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!file.ignoreFailedRemoteFile) {
|
||||
throw new Error(
|
||||
`文件 ${file.name} 的远程文件 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (file.ignoreFailedRemoteFile === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 预览文件失败`,
|
||||
`❌ ${file.name}`,
|
||||
`远程文件 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (file.mergeSources === 'localFirst') {
|
||||
content.unshift(file.content);
|
||||
@@ -136,12 +143,22 @@ async function compareSub(req, res) {
|
||||
}),
|
||||
);
|
||||
|
||||
if (!sub.ignoreFailedRemoteSub && Object.keys(errors).length > 0) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!sub.ignoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (sub.ignoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 预览订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sub.mergeSources === 'localFirst') {
|
||||
content.unshift(sub.content);
|
||||
@@ -244,15 +261,25 @@ async function compareCollection(req, res) {
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (
|
||||
!sub.ignoreFailedRemoteSub &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!sub.ignoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (
|
||||
sub.ignoreFailedRemoteSub === 'enabled'
|
||||
) {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 预览订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sub.mergeSources === 'localFirst') {
|
||||
raw.unshift(sub.content);
|
||||
@@ -284,20 +311,28 @@ async function compareCollection(req, res) {
|
||||
errors[name] = err;
|
||||
|
||||
$.error(
|
||||
`❌ 处理组合订阅 ${collection.name} 中的子订阅: ${sub.name}时出现错误:${err}!`,
|
||||
`❌ 处理组合订阅 ${collection.name} 中的子订阅: ${sub.name} 时出现错误:${err}!`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (
|
||||
!collection.ignoreFailedRemoteSub &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`组合订阅 ${collection.name} 中的子订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!collection.ignoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`组合订阅 ${collection.name} 的子订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (collection.ignoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 预览组合订阅失败`,
|
||||
`❌ ${collection.name}`,
|
||||
`子订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
// merge proxies with the original order
|
||||
const original = Array.prototype.concat.apply(
|
||||
|
||||
@@ -89,12 +89,23 @@ async function produceArtifact({
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
subIgnoreFailedRemoteSub = ignoreFailedRemoteSub;
|
||||
}
|
||||
if (!subIgnoreFailedRemoteSub && Object.keys(errors).length > 0) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!subIgnoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (subIgnoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (mergeSources === 'localFirst') {
|
||||
raw.unshift(content);
|
||||
@@ -138,12 +149,23 @@ async function produceArtifact({
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
subIgnoreFailedRemoteSub = ignoreFailedRemoteSub;
|
||||
}
|
||||
if (!subIgnoreFailedRemoteSub && Object.keys(errors).length > 0) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!subIgnoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (subIgnoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sub.mergeSources === 'localFirst') {
|
||||
raw.unshift(sub.content);
|
||||
@@ -264,15 +286,25 @@ async function produceArtifact({
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (
|
||||
!sub.ignoreFailedRemoteSub &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!sub.ignoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (
|
||||
sub.ignoreFailedRemoteSub === 'enabled'
|
||||
) {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sub.mergeSources === 'localFirst') {
|
||||
raw.unshift(sub.content);
|
||||
@@ -327,15 +359,23 @@ async function produceArtifact({
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
collectionIgnoreFailedRemoteSub = ignoreFailedRemoteSub;
|
||||
}
|
||||
if (
|
||||
!collectionIgnoreFailedRemoteSub &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`组合订阅 ${name} 中的子订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!collectionIgnoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`组合订阅 ${collection.name} 的子订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (collectionIgnoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理组合订阅失败`,
|
||||
`❌ ${collection.name}`,
|
||||
`子订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// merge proxies with the original order
|
||||
@@ -505,15 +545,23 @@ async function produceArtifact({
|
||||
) {
|
||||
fileIgnoreFailedRemoteFile = ignoreFailedRemoteFile;
|
||||
}
|
||||
if (
|
||||
!fileIgnoreFailedRemoteFile &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`文件 ${file.name} 的远程文件 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!fileIgnoreFailedRemoteFile) {
|
||||
throw new Error(
|
||||
`文件 ${file.name} 的远程文件 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (fileIgnoreFailedRemoteFile === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理文件失败`,
|
||||
`❌ ${file.name}`,
|
||||
`远程文件 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (file.mergeSources === 'localFirst') {
|
||||
raw.unshift(file.content);
|
||||
|
||||
@@ -273,3 +273,25 @@ export default async function download(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function downloadFile(url, file) {
|
||||
const undici = eval("require('undici')");
|
||||
const fs = eval("require('fs')");
|
||||
const { pipeline } = eval("require('stream/promises')");
|
||||
const { Agent, interceptors, request } = undici;
|
||||
$.info(`Downloading file...\nURL: ${url}\nFile: ${file}`);
|
||||
const { body, statusCode } = await request(url, {
|
||||
dispatcher: new Agent().compose(
|
||||
interceptors.redirect({
|
||||
maxRedirections: 3,
|
||||
throwOnRedirect: true,
|
||||
}),
|
||||
),
|
||||
});
|
||||
if (statusCode !== 200)
|
||||
throw new Error(`Failed to download file from ${url}`);
|
||||
const fileStream = fs.createWriteStream(file);
|
||||
await pipeline(body, fileStream);
|
||||
$.info(`File downloaded from ${url} to ${file}`);
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// 只给 mihomo 输出的话, `dialer-proxy` 也行
|
||||
// 只给 sing-box 输出的话, `detour` 也行
|
||||
// 只给 egern 输出的话, `prev_hop` 也行
|
||||
// 输出到 Clash/Stash/Shadowrocket 时, 会过滤掉配置了前置代理的节点, 并提示使用对应的功能.
|
||||
// 9. `trojan`, `tuic`, `hysteria`, `hysteria2`, `juicity` 会在解析时设置 `tls`: true (会使用 tls 类协议的通用逻辑), 输出时删除
|
||||
// 10. `sni` 在某些协议里会自动与 `servername` 转换
|
||||
// 11. 读取节点的 ca-str 和 _ca (后端文件路径) 字段, 自动计算 fingerprint (参考 https://t.me/zhetengsha/1512)
|
||||
@@ -116,6 +117,7 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// getISO, // 获取 ISO 3166-1 alpha-2 代码
|
||||
// Gist, // Gist 类
|
||||
// download, // 内部的下载方法, 见 backend/src/utils/download.js
|
||||
// downloadFile, // 下载二进制文件, 见 backend/src/utils/download.js
|
||||
// MMDB, // Node.js 环境 可用于模拟 Surge/Loon 的 $utils.ipasn, $utils.ipaso, $utils.geoip. 具体见 https://t.me/zhetengsha/1269
|
||||
// isValidUUID, // 辅助判断是否为有效的 UUID
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user