mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9d120bac3 | ||
|
|
72a445ae33 | ||
|
|
5e2a87e250 | ||
|
|
71fc9affbf | ||
|
|
6f82294c49 | ||
|
|
7c398ba51c | ||
|
|
7002eee88d | ||
|
|
bd21d58fe7 | ||
|
|
2ea46dcbf1 | ||
|
|
4a2a2297f6 | ||
|
|
07d5a913f0 | ||
|
|
421df8f0d4 | ||
|
|
e14944dd19 | ||
|
|
bf18c51f6a | ||
|
|
23e8fbd1b7 | ||
|
|
b94b3c366b |
@@ -26,13 +26,13 @@ Core functionalities:
|
||||
|
||||
### Supported Input Formats
|
||||
|
||||
> ⚠️ Do not use `Shadowrocket` to export URI and then import it as input. It is not a standard URI.
|
||||
> ⚠️ Do not use `Shadowrocket` or `NekoBox` to export URI and then import it as input. The URIs exported in this way may not be standard URIs.
|
||||
|
||||
- [x] Proxy URI Scheme(`socks5`, `socks5+tls`, `http`, `https`(it's ok))
|
||||
|
||||
example: `socks5+tls://user:pass@ip:port#name`
|
||||
|
||||
- [x] URI(SS, SSR, VMess, VLESS, Trojan, Hysteria, Hysteria 2, TUIC v5, WireGuard)
|
||||
- [x] URI(SOCKS, SS, SSR, VMess, VLESS, Trojan, Hysteria, Hysteria 2, TUIC v5, WireGuard)
|
||||
- [x] Clash Proxies YAML
|
||||
- [x] Clash Proxy JSON(single line)
|
||||
- [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5, VLESS)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* ╚════██║██║ ██║██╔══██╗╚════╝╚════██║ ██║ ██║ ██║██╔══██╗██╔══╝
|
||||
* ███████║╚██████╔╝██████╔╝ ███████║ ██║ ╚██████╔╝██║ ██║███████╗
|
||||
* ╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
|
||||
* Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket!
|
||||
* Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket!
|
||||
* @updated: <%= updated %>
|
||||
* @version: <%= pkg.version %>
|
||||
* @author: Peng-YM
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.16.26",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"version": "2.16.40",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
isValidPortNumber,
|
||||
isValidUUID,
|
||||
isNotBlank,
|
||||
ipAddress,
|
||||
getRandomPort,
|
||||
@@ -21,6 +22,7 @@ import { findByName } from '@/utils/database';
|
||||
import { produceArtifact } from '@/restful/sync';
|
||||
import { getFlag, removeFlag, getISO, MMDB } from '@/utils/geo';
|
||||
import Gist from '@/utils/gist';
|
||||
import { isPresent } from './producers/utils';
|
||||
|
||||
function preprocess(raw) {
|
||||
for (const processor of PROXY_PREPROCESSORS) {
|
||||
@@ -75,7 +77,16 @@ function parse(raw) {
|
||||
$.error(`Failed to parse line: ${line}`);
|
||||
}
|
||||
}
|
||||
return proxies;
|
||||
return proxies.filter((proxy) => {
|
||||
if (['vless', 'vmess'].includes(proxy.type)) {
|
||||
const isProxyUUIDValid = isValidUUID(proxy.uuid);
|
||||
if (!isProxyUUIDValid) {
|
||||
$.error(`UUID is invalid: ${proxy.name} ${proxy.uuid}`);
|
||||
}
|
||||
return isProxyUUIDValid;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
async function processFn(
|
||||
@@ -214,10 +225,22 @@ function produce(proxies, targetPlatform, type, opts = {}) {
|
||||
);
|
||||
|
||||
// filter unsupported proxies
|
||||
proxies = proxies.filter(
|
||||
(proxy) =>
|
||||
!(proxy.supported && proxy.supported[targetPlatform] === false),
|
||||
);
|
||||
proxies = proxies.filter((proxy) => {
|
||||
// 检查代理是否支持目标平台
|
||||
if (proxy.supported && proxy.supported[targetPlatform] === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 对于 vless 和 vmess 代理,需要额外验证 UUID
|
||||
if (['vless', 'vmess'].includes(proxy.type)) {
|
||||
const isProxyUUIDValid = isValidUUID(proxy.uuid);
|
||||
if (!isProxyUUIDValid)
|
||||
$.error(`UUID is invalid: ${proxy.name} ${proxy.uuid}`);
|
||||
return isProxyUUIDValid;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
proxies = proxies.map((proxy) => {
|
||||
proxy._resolved = proxy.resolved;
|
||||
@@ -572,6 +595,20 @@ function lastParse(proxy) {
|
||||
if (!proxy['tls-fingerprint'] && caStr) {
|
||||
proxy['tls-fingerprint'] = rs.generateFingerprint(caStr);
|
||||
}
|
||||
if (
|
||||
['shadowsocks'].includes(proxy.type) &&
|
||||
isPresent(proxy, 'shadow-tls-password')
|
||||
) {
|
||||
proxy.plugin = 'shadow-tls';
|
||||
proxy['plugin-opts'] = {
|
||||
host: proxy['shadow-tls-sni'],
|
||||
password: proxy['shadow-tls-password'],
|
||||
version: proxy['shadow-tls-version'],
|
||||
};
|
||||
delete proxy['shadow-tls-sni'];
|
||||
delete proxy['shadow-tls-password'];
|
||||
delete proxy['shadow-tls-version'];
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import getSurgeParser from './peggy/surge';
|
||||
import getLoonParser from './peggy/loon';
|
||||
import getQXParser from './peggy/qx';
|
||||
import getTrojanURIParser from './peggy/trojan-uri';
|
||||
import $ from '@/core/app';
|
||||
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
@@ -40,8 +41,21 @@ function URI_PROXY() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, type, tls, username, password, server, port, query, name] =
|
||||
line.match(
|
||||
/^(socks5|http|http)(\+tls|s)?:\/\/(?:(.*?):(.*?)@)?(.*?):(\d+?)(\?.*?)?(?:#(.*?))?$/,
|
||||
/^(socks5|http|http)(\+tls|s)?:\/\/(?:(.*?):(.*?)@)?(.*?)(?::(\d+?))?(\?.*?)?(?:#(.*?))?$/,
|
||||
);
|
||||
if (port) {
|
||||
port = parseInt(port, 10);
|
||||
} else {
|
||||
if (tls) {
|
||||
port = 443;
|
||||
} else if (type === 'http') {
|
||||
port = 80;
|
||||
} else {
|
||||
$.error(`port is not present in line: ${line}`);
|
||||
throw new Error(`port is not present in line: ${line}`);
|
||||
}
|
||||
$.info(`port is not present in line: ${line}, set to ${port}`);
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
name:
|
||||
@@ -62,7 +76,46 @@ function URI_PROXY() {
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
function URI_SOCKS() {
|
||||
const name = 'URI SOCKS Parser';
|
||||
const test = (line) => {
|
||||
return /^socks:\/\//.test(line);
|
||||
};
|
||||
const parse = (line) => {
|
||||
// parse url
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, type, auth, server, port, query, name] = line.match(
|
||||
/^(socks)?:\/\/(?:(.*)@)?(.*?)(?::(\d+?))?(\?.*?)?(?:#(.*?))?$/,
|
||||
);
|
||||
if (port) {
|
||||
port = parseInt(port, 10);
|
||||
} else {
|
||||
$.error(`port is not present in line: ${line}`);
|
||||
throw new Error(`port is not present in line: ${line}`);
|
||||
}
|
||||
let username, password;
|
||||
if (auth) {
|
||||
const parsed = Base64.decode(decodeURIComponent(auth)).split(':');
|
||||
username = parsed[0];
|
||||
password = parsed[1];
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
name:
|
||||
name != null
|
||||
? decodeURIComponent(name)
|
||||
: `${type} ${server}:${port}`,
|
||||
type: 'socks5',
|
||||
server,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
};
|
||||
|
||||
return proxy;
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
|
||||
// reference: https://github.com/shadowsocks/shadowsocks-org/wiki/SIP002-URI-Scheme
|
||||
function URI_SS() {
|
||||
@@ -99,6 +152,7 @@ function URI_SS() {
|
||||
query = parsed[2];
|
||||
}
|
||||
content = Base64.decode(content);
|
||||
|
||||
if (query) {
|
||||
if (/(&|\?)v2ray-plugin=/.test(query)) {
|
||||
const parsed = query.match(/(&|\?)v2ray-plugin=(.*?)(&|$)/);
|
||||
@@ -112,8 +166,11 @@ function URI_SS() {
|
||||
}
|
||||
content = `${content}${query}`;
|
||||
}
|
||||
userInfoStr = content.split('@')[0];
|
||||
serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||
userInfoStr = content.match(/(^.*)@/)?.[1];
|
||||
serverAndPortArray = content.match(/@([^/@]*)(\/|$)/);
|
||||
} else if (content.includes('?')) {
|
||||
const parsed = content.match(/(\?.*)$/);
|
||||
query = parsed[1];
|
||||
}
|
||||
|
||||
const serverAndPort = serverAndPortArray[1];
|
||||
@@ -132,11 +189,10 @@ function URI_SS() {
|
||||
// }
|
||||
|
||||
// handle obfs
|
||||
const idx = content.indexOf('?plugin=');
|
||||
if (idx !== -1) {
|
||||
const pluginMatch = content.match(/[?&]plugin=([^&]+)/);
|
||||
if (pluginMatch) {
|
||||
const pluginInfo = (
|
||||
'plugin=' +
|
||||
decodeURIComponent(content.split('?plugin=')[1].split('&')[0])
|
||||
'plugin=' + decodeURIComponent(pluginMatch[1])
|
||||
).split(';');
|
||||
const params = {};
|
||||
for (const item of pluginInfo) {
|
||||
@@ -161,6 +217,16 @@ function URI_SS() {
|
||||
tls: getIfPresent(params.tls),
|
||||
};
|
||||
break;
|
||||
case 'shadow-tls': {
|
||||
proxy.plugin = 'shadow-tls';
|
||||
const version = getIfNotBlank(params['version']);
|
||||
proxy['plugin-opts'] = {
|
||||
host: getIfNotBlank(params['host']),
|
||||
password: getIfNotBlank(params['password']),
|
||||
version: version ? parseInt(version, 10) : undefined,
|
||||
};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported plugin option: ${params.plugin}`,
|
||||
@@ -773,8 +839,11 @@ function URI_TUIC() {
|
||||
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);
|
||||
let [__, auth, server, port, addons = '', name] =
|
||||
/^(.*?)@(.*?)(?::(\d+))?\/?(?:\?(.*?))?(?:#(.*?))?$/.exec(line);
|
||||
auth = decodeURIComponent(auth);
|
||||
let [uuid, ...passwordParts] = auth.split(':');
|
||||
let password = passwordParts.join(':');
|
||||
port = parseInt(`${port}`, 10);
|
||||
if (isNaN(port)) {
|
||||
port = 443;
|
||||
@@ -1441,6 +1510,7 @@ function isIP(ip) {
|
||||
|
||||
export default [
|
||||
URI_PROXY(),
|
||||
URI_SOCKS(),
|
||||
URI_SS(),
|
||||
URI_SSR(),
|
||||
URI_VMess(),
|
||||
|
||||
@@ -16,6 +16,7 @@ function Base64Encoded() {
|
||||
const keys = [
|
||||
'dm1lc3M', // vmess
|
||||
'c3NyOi8v', // ssr://
|
||||
'c29ja3M6Ly', // socks://
|
||||
'dHJvamFu', // trojan
|
||||
'c3M6Ly', // ss:/
|
||||
'c3NkOi8v', // ssd://
|
||||
|
||||
@@ -365,7 +365,9 @@ function ScriptOperator(script, targetPlatform, $arguments, source, $options) {
|
||||
if (output?.$file?.type === 'mihomoProfile') {
|
||||
try {
|
||||
let patch = YAML.safeLoad(script);
|
||||
if (typeof patch !== 'object') patch = {};
|
||||
// if (typeof patch !== 'object') patch = {};
|
||||
if (typeof patch !== 'object')
|
||||
throw new Error('patch is not an object');
|
||||
output.$content = ProxyUtils.yaml.safeDump(
|
||||
deepMerge(
|
||||
{
|
||||
|
||||
@@ -32,9 +32,10 @@ export default function ClashMeta_Producer() {
|
||||
isPresent(proxy, 'cipher') &&
|
||||
![
|
||||
'auto',
|
||||
'none',
|
||||
'zero',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
'none',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
proxy.cipher = 'auto';
|
||||
|
||||
@@ -48,23 +48,12 @@ export default function Egern_Producer() {
|
||||
'salsa20',
|
||||
'chacha20',
|
||||
'chacha20-ietf',
|
||||
...(opts['include-unsupported-proxy']
|
||||
? [
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
]
|
||||
: []),
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
].includes(proxy.cipher))) ||
|
||||
(proxy.type === 'vmess' &&
|
||||
(![
|
||||
'auto',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
'none',
|
||||
'zero',
|
||||
].includes(proxy.cipher) ||
|
||||
(!['http', 'ws', 'tcp'].includes(proxy.network) &&
|
||||
proxy.network))) ||
|
||||
!['http', 'ws', 'tcp'].includes(proxy.network) &&
|
||||
proxy.network) ||
|
||||
(proxy.type === 'trojan' &&
|
||||
!['http', 'ws', 'tcp'].includes(proxy.network) &&
|
||||
proxy.network) ||
|
||||
@@ -195,6 +184,20 @@ export default function Egern_Producer() {
|
||||
websocket: proxy.websocket,
|
||||
};
|
||||
} else if (proxy.type === 'vmess') {
|
||||
// Egern:传输层,支持 ws/wss/http1/http2/tls,不配置则为 tcp
|
||||
let security = proxy.cipher;
|
||||
if (
|
||||
security &&
|
||||
![
|
||||
'auto',
|
||||
'none',
|
||||
'zero',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
].includes(security)
|
||||
) {
|
||||
security = 'auto';
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
proxy.transport = {
|
||||
[proxy.tls ? 'wss' : 'ws']: {
|
||||
@@ -210,7 +213,7 @@ export default function Egern_Producer() {
|
||||
};
|
||||
} else if (proxy.network === 'http') {
|
||||
proxy.transport = {
|
||||
http: {
|
||||
http1: {
|
||||
method: proxy['http-opts']?.method,
|
||||
path: proxy['http-opts']?.path,
|
||||
headers: {
|
||||
@@ -223,9 +226,27 @@ export default function Egern_Producer() {
|
||||
skip_tls_verify: proxy['skip-cert-verify'],
|
||||
},
|
||||
};
|
||||
} else if (proxy.network === 'tcp' || !proxy.network) {
|
||||
} else if (proxy.network === 'h2') {
|
||||
proxy.transport = {
|
||||
[proxy.tls ? 'tls' : 'tcp']: {
|
||||
http2: {
|
||||
method: proxy['h2-opts']?.method,
|
||||
path: proxy['h2-opts']?.path,
|
||||
headers: {
|
||||
Host: Array.isArray(
|
||||
proxy['h2-opts']?.headers?.Host,
|
||||
)
|
||||
? proxy['h2-opts']?.headers?.Host[0]
|
||||
: proxy['h2-opts']?.headers?.Host,
|
||||
},
|
||||
skip_tls_verify: proxy['skip-cert-verify'],
|
||||
},
|
||||
};
|
||||
} else if (
|
||||
(proxy.network === 'tcp' || !proxy.network) &&
|
||||
proxy.tls
|
||||
) {
|
||||
proxy.transport = {
|
||||
tls: {
|
||||
sni: proxy.tls ? proxy.sni : undefined,
|
||||
skip_tls_verify: proxy.tls
|
||||
? proxy['skip-cert-verify']
|
||||
@@ -239,7 +260,7 @@ export default function Egern_Producer() {
|
||||
server: proxy.server,
|
||||
port: proxy.port,
|
||||
user_id: proxy.uuid,
|
||||
security: proxy.cipher,
|
||||
security,
|
||||
tfo: proxy.tfo || proxy['fast-open'],
|
||||
legacy: proxy.legacy,
|
||||
udp_relay:
|
||||
@@ -249,8 +270,6 @@ export default function Egern_Producer() {
|
||||
// sni: proxy.sni,
|
||||
// skip_tls_verify: proxy['skip-cert-verify'],
|
||||
};
|
||||
} else if (proxy.type === 'snell' && proxy.version < 3) {
|
||||
delete proxy.udp;
|
||||
} else if (proxy.type === 'vless') {
|
||||
if (proxy.network === 'ws') {
|
||||
proxy.transport = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
|
||||
export default function ShadowRocket_Producer() {
|
||||
export default function Shadowrocket_Producer() {
|
||||
const type = 'ALL';
|
||||
const produce = (proxies, type, opts = {}) => {
|
||||
const list = proxies
|
||||
@@ -32,9 +32,10 @@ export default function ShadowRocket_Producer() {
|
||||
isPresent(proxy, 'cipher') &&
|
||||
![
|
||||
'auto',
|
||||
'none',
|
||||
'zero',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305',
|
||||
'none',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
proxy.cipher = 'auto';
|
||||
|
||||
@@ -27,6 +27,11 @@ export default function URI_Producer() {
|
||||
proxy.server = `[${proxy.server}]`;
|
||||
}
|
||||
switch (proxy.type) {
|
||||
case 'socks5':
|
||||
result = `socks://${encodeURIComponent(
|
||||
Base64.encode(`${proxy.username}:${proxy.password}`),
|
||||
)}@${proxy.server}:${proxy.port}#${proxy.name}`;
|
||||
break;
|
||||
case 'ss':
|
||||
const userinfo = `${proxy.cipher}:${proxy.password}`;
|
||||
result = `ss://${
|
||||
@@ -54,6 +59,11 @@ export default function URI_Producer() {
|
||||
}${opts.tls ? ';tls' : ''}`,
|
||||
);
|
||||
break;
|
||||
case 'shadow-tls':
|
||||
result += encodeURIComponent(
|
||||
`shadow-tls;host=${opts.host};password=${opts.password};version=${opts.version}`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported plugin option: ${proxy.plugin}`,
|
||||
|
||||
@@ -84,6 +84,7 @@ async function doSync() {
|
||||
const files = {};
|
||||
|
||||
try {
|
||||
const valid = [];
|
||||
const invalid = [];
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
@@ -156,19 +157,26 @@ async function doSync() {
|
||||
files[encodeURIComponent(artifact.name)] = {
|
||||
content: output,
|
||||
};
|
||||
|
||||
valid.push(artifact.name);
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`同步配置 ${artifact.name} 发生错误: ${e.message ?? e}`,
|
||||
`生成同步配置 ${artifact.name} 发生错误: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
invalid.push(artifact.name);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if (invalid.length > 0) {
|
||||
$.info(`${valid.length} 个同步配置生成成功: ${valid.join(', ')}`);
|
||||
$.info(`${invalid.length} 个同步配置生成失败: ${invalid.join(', ')}`);
|
||||
|
||||
if (valid.length === 0) {
|
||||
throw new Error(
|
||||
`同步配置 ${invalid.join(', ')} 发生错误 详情请查看日志`,
|
||||
`同步配置 ${invalid.join(', ')} 生成失败 详情请查看日志`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -184,7 +192,11 @@ async function doSync() {
|
||||
$.info(JSON.stringify(body, null, 2));
|
||||
|
||||
for (const artifact of allArtifacts) {
|
||||
if (artifact.sync) {
|
||||
if (
|
||||
artifact.sync &&
|
||||
artifact.source &&
|
||||
valid.includes(artifact.name)
|
||||
) {
|
||||
artifact.updated = new Date().getTime();
|
||||
// extract real url from gist
|
||||
let files = body.files;
|
||||
@@ -212,9 +224,18 @@ async function doSync() {
|
||||
}
|
||||
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
$.notify('🌍 Sub-Store', '全部订阅同步成功!');
|
||||
$.info('上传配置成功');
|
||||
|
||||
if (invalid.length > 0) {
|
||||
$.notify(
|
||||
'🌍 Sub-Store',
|
||||
`同步配置成功 ${valid.length} 个, 失败 ${invalid.length} 个, 详情请查看日志`,
|
||||
);
|
||||
} else {
|
||||
$.notify('🌍 Sub-Store', '同步配置完成');
|
||||
}
|
||||
} catch (e) {
|
||||
$.notify('🌍 Sub-Store', '同步订阅失败', `原因:${e.message ?? e}`);
|
||||
$.error(`无法同步订阅配置到 Gist,原因:${e}`);
|
||||
$.notify('🌍 Sub-Store', '同步配置失败', `原因:${e.message ?? e}`);
|
||||
$.error(`无法同步配置到 Gist,原因:${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,6 +540,7 @@ async function syncArtifacts() {
|
||||
const files = {};
|
||||
|
||||
try {
|
||||
const valid = [];
|
||||
const invalid = [];
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
@@ -614,19 +615,26 @@ async function syncArtifacts() {
|
||||
files[encodeURIComponent(artifact.name)] = {
|
||||
content: output,
|
||||
};
|
||||
|
||||
valid.push(artifact.name);
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`同步配置 ${artifact.name} 发生错误: ${e.message ?? e}`,
|
||||
`生成同步配置 ${artifact.name} 发生错误: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
invalid.push(artifact.name);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if (invalid.length > 0) {
|
||||
$.info(`${valid.length} 个同步配置生成成功: ${valid.join(', ')}`);
|
||||
$.info(`${invalid.length} 个同步配置生成失败: ${invalid.join(', ')}`);
|
||||
|
||||
if (valid.length === 0) {
|
||||
throw new Error(
|
||||
`同步配置 ${invalid.join(', ')} 发生错误 详情请查看日志`,
|
||||
`同步配置 ${invalid.join(', ')} 生成失败 详情请查看日志`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -643,7 +651,11 @@ async function syncArtifacts() {
|
||||
$.info(JSON.stringify(body, null, 2));
|
||||
|
||||
for (const artifact of allArtifacts) {
|
||||
if (artifact.sync) {
|
||||
if (
|
||||
artifact.sync &&
|
||||
artifact.source &&
|
||||
valid.includes(artifact.name)
|
||||
) {
|
||||
artifact.updated = new Date().getTime();
|
||||
// extract real url from gist
|
||||
let files = body.files;
|
||||
@@ -671,9 +683,17 @@ async function syncArtifacts() {
|
||||
}
|
||||
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
$.info('全部订阅同步成功!');
|
||||
$.info('上传配置成功');
|
||||
|
||||
if (invalid.length > 0) {
|
||||
throw new Error(
|
||||
`同步配置成功 ${valid.length} 个, 失败 ${invalid.length} 个, 详情请查看日志`,
|
||||
);
|
||||
} else {
|
||||
$.info(`同步配置成功 ${valid.length} 个`);
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(`同步订阅失败,原因:${e.message ?? e}`);
|
||||
$.error(`同步配置失败,原因:${e.message ?? e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -683,7 +703,7 @@ async function syncAllArtifacts(_, res) {
|
||||
await syncArtifacts();
|
||||
success(res);
|
||||
} catch (e) {
|
||||
$.error(`同步订阅失败,原因:${e.message ?? e}`);
|
||||
$.error(`同步配置失败,原因:${e.message ?? e}`);
|
||||
failed(
|
||||
res,
|
||||
new InternalServerError(
|
||||
|
||||
@@ -117,7 +117,17 @@ function numberToString(value) {
|
||||
: BigInt(value).toString();
|
||||
}
|
||||
|
||||
function isValidUUID(uuid) {
|
||||
return (
|
||||
typeof uuid === 'string' &&
|
||||
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(
|
||||
uuid,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
isValidUUID,
|
||||
ipAddress,
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
|
||||
Reference in New Issue
Block a user