Compare commits

..

9 Commits

Author SHA1 Message Date
xream
dda8113a42 feat: 增加 subscription-userinfo 兼容性 2024-12-04 00:56:53 +08:00
xream
f16b2d34f1 feat: geo 更新 2024-11-30 13:58:04 +08:00
xream
5b28e1a4c9 feat: 支持禁用节点操作 2024-11-29 21:03:29 +08:00
xream
8d0a71d983 feat: VMess URI 输出支持 alterId; Trojan 支持 fp 和 alpn 2024-11-28 16:04:52 +08:00
xream
815552d470 feat: 找不到资源时不通知, 仅保留日志 2024-11-28 15:44:55 +08:00
xream
9d90369594 feat: Trojan URI 支持省略端口号 2024-11-28 13:15:22 +08:00
xream
6aece471aa feat: Stash 使用 includeUnsupportedProxy 参数开启 Shadowsocks 2022 2024-11-27 15:20:20 +08:00
xream
99396773f6 ci: 去除 GitLab Sync 2024-11-26 15:01:04 +08:00
xream
e229408a2d feat: 默认缓存阈值 1024KB 2024-11-24 12:31:18 +08:00
13 changed files with 89 additions and 22 deletions

View File

@@ -76,8 +76,8 @@ jobs:
git commit -m "release: ${{ steps.tag.outputs.release_tag }}"
git remote add origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}"
git push -f -u origin release
- name: Sync to GitLab
env:
GITLAB_PIPELINE_TOKEN: ${{ secrets.GITLAB_PIPELINE_TOKEN }}
run: |
curl -X POST --fail -F token=$GITLAB_PIPELINE_TOKEN -F ref=master https://gitlab.com/api/v4/projects/48891296/trigger/pipeline
# - name: Sync to GitLab
# env:
# GITLAB_PIPELINE_TOKEN: ${{ secrets.GITLAB_PIPELINE_TOKEN }}
# run: |
# curl -X POST --fail -F token=$GITLAB_PIPELINE_TOKEN -F ref=master https://gitlab.com/api/v4/projects/48891296/trigger/pipeline

View File

@@ -1,6 +1,6 @@
{
"name": "sub-store",
"version": "2.14.426",
"version": "2.14.434",
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
"main": "src/main.js",
"scripts": {

View File

@@ -86,6 +86,16 @@ async function processFn(
$options,
) {
for (const item of operators) {
if (item.disabled) {
$.log(
`Skipping disabled operator: "${
item.type
}" with arguments:\n >>> ${
JSON.stringify(item.args, null, 2) || 'None'
}`,
);
continue;
}
// process script
let script;
let $arguments = {};

View File

@@ -842,6 +842,11 @@ function URI_Trojan() {
};
const parse = (line) => {
const matched = /^(trojan:\/\/.*?@.*?)(:(\d+))?\/?(\?.*?)?$/.exec(line);
const port = matched?.[2];
if (!port) {
line = line.replace(matched[1], `${matched[1]}:443`);
}
let [newLine, name] = line.split(/#(.+)/, 2);
const parser = getTrojanURIParser();
const proxy = parser.parse(newLine);

View File

@@ -82,6 +82,8 @@ port = digits:[0-9]+ {
params = "?" head:param tail:("&"@param)* {
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
proxy.sni = params["sni"] || params["peer"];
proxy['client-fingerprint'] = params.fp;
proxy.alpn = params.alpn ? decodeURIComponent(params.alpn).split(',') : undefined;
if (toBool(params["ws"])) {
proxy.network = "ws";

View File

@@ -80,6 +80,8 @@ port = digits:[0-9]+ {
params = "?" head:param tail:("&"@param)* {
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
proxy.sni = params["sni"] || params["peer"];
proxy['client-fingerprint'] = params.fp;
proxy.alpn = params.alpn ? decodeURIComponent(params.alpn).split(',') : undefined;
if (toBool(params["ws"])) {
proxy.network = "ws";

View File

@@ -6,7 +6,6 @@ export default function Stash_Producer() {
// https://stash.wiki/proxy-protocols/proxy-types#shadowsocks
const list = proxies
.filter((proxy) => {
if (opts['include-unsupported-proxy']) return true;
if (
![
'ss',
@@ -40,6 +39,12 @@ export default function Stash_Producer() {
'xchacha20',
'chacha20-ietf-poly1305',
'xchacha20-ietf-poly1305',
...(opts['include-unsupported-proxy']
? [
'2022-blake3-aes-128-gcm',
'2022-blake3-aes-256-gcm',
]
: []),
].includes(proxy.cipher)) ||
(proxy.type === 'snell' && String(proxy.version) === '4') ||
(proxy.type === 'vless' && proxy['reality-opts'])

View File

@@ -102,7 +102,7 @@ export default function URI_Producer() {
port: proxy.port,
id: proxy.uuid,
type,
aid: 0,
aid: proxy.alterId || 0,
net,
tls: proxy.tls ? 'tls' : '',
};
@@ -290,11 +290,27 @@ export default function URI_Producer() {
)}`;
}
}
let trojanFp = '';
if (proxy['client-fingerprint']) {
trojanFp = `&fp=${encodeURIComponent(
proxy['client-fingerprint'],
)}`;
}
let trojanAlpn = '';
if (proxy.alpn) {
trojanAlpn = `&alpn=${encodeURIComponent(
Array.isArray(proxy.alpn)
? proxy.alpn
: proxy.alpn.join(','),
)}`;
}
result = `trojan://${proxy.password}@${proxy.server}:${
proxy.port
}?sni=${encodeURIComponent(proxy.sni || proxy.server)}${
proxy['skip-cert-verify'] ? '&allowInsecure=1' : ''
}${trojanTransport}#${encodeURIComponent(proxy.name)}`;
}${trojanTransport}${trojanAlpn}${trojanFp}#${encodeURIComponent(
proxy.name,
)}`;
break;
case 'hysteria2':
let hysteria2params = [];

View File

@@ -255,7 +255,7 @@ async function downloadSubscription(req, res) {
);
}
} else {
$.notify(`🌍 Sub-Store 下载订阅失败`, `❌ 未找到订阅:${name}`);
$.error(`🌍 Sub-Store 下载订阅失败`, `❌ 未找到订阅:${name}`);
failed(
res,
new ResourceNotFoundError(
@@ -457,7 +457,7 @@ async function downloadCollection(req, res) {
);
}
} else {
$.notify(
$.error(
`🌍 Sub-Store 下载组合订阅失败`,
`❌ 未找到组合订阅:${name}`,
);

View File

@@ -179,7 +179,7 @@ async function getFile(req, res) {
);
}
} else {
$.notify(`🌍 Sub-Store 下载文件失败`, `❌ 未找到文件:${name}`);
$.error(`🌍 Sub-Store 下载文件失败`, `❌ 未找到文件:${name}`);
failed(
res,
new ResourceNotFoundError(

View File

@@ -44,8 +44,13 @@ export default async function download(
}
}
const { isNode, isStash, isLoon, isShadowRocket, isQX } = ENV();
const { defaultProxy, defaultUserAgent, defaultTimeout, cacheThreshold } =
$.read(SETTINGS_KEY);
const {
defaultProxy,
defaultUserAgent,
defaultTimeout,
cacheThreshold: defaultCacheThreshold,
} = $.read(SETTINGS_KEY);
const cacheThreshold = defaultCacheThreshold || 1024;
let proxy = customProxy || defaultProxy;
if ($.env.isNode) {
proxy = proxy || eval('process.env.SUB_STORE_BACKEND_DEFAULT_PROXY');

View File

@@ -18,7 +18,9 @@ export function getFlowField(headers) {
}
}
return `${sub || ''}${webPage ? `;app_url=${webPage}` : ''}`;
return `${sub || ''}${
webPage ? `; app_url=${encodeURIComponent(webPage)}` : ''
}`;
}
export async function getFlowHeaders(
rawUrl,

View File

@@ -151,6 +151,7 @@ export function getFlag(name) {
'滑铁卢',
'多伦多',
'Waterloo',
'Toronto',
],
'🇨🇭': ['Switzerland', '瑞士', '苏黎世', 'Zurich'],
'🇨🇱': ['Chile', '智利'],
@@ -245,7 +246,7 @@ export function getFlag(name) {
'🇮🇪': ['Ireland', '爱尔兰', '愛爾蘭', '都柏林'],
'🇮🇱': ['Israel', '以色列'],
'🇮🇲': ['Isle of Man', '马恩岛', '馬恩島'],
'🇮🇳': ['India', '印度', '孟买', 'MFumbai'],
'🇮🇳': ['India', '印度', '孟买', 'MFumbai', 'Mumbai'],
'🇮🇷': ['Iran', '伊朗'],
'🇮🇸': ['Iceland', '冰岛', '冰島'],
'🇮🇹': ['Italy', '意大利', '義大利', '米兰', 'Nachash'],
@@ -261,7 +262,14 @@ export function getFlag(name) {
'🇲🇹': ['Malta', '马耳他'],
'🇲🇽': ['Mexico', '墨西哥'],
'🇲🇾': ['Malaysia', '马来', '馬來', '吉隆坡', '大馬'],
'🇳🇱': ['Netherlands', '荷兰', '荷蘭', '尼德蘭', '阿姆斯特丹'],
'🇳🇱': [
'Netherlands',
'荷兰',
'荷蘭',
'尼德蘭',
'阿姆斯特丹',
'Amsterdam',
],
'🇳🇴': ['Norway', '挪威'],
'🇳🇵': ['Nepal', '尼泊尔'],
'🇳🇿': ['New Zealand', '新西兰', '新西蘭'],
@@ -269,7 +277,7 @@ export function getFlag(name) {
'🇵🇪': ['Peru', '秘鲁', '祕魯'],
'🇵🇭': ['Philippines', '菲律宾', '菲律賓'],
'🇵🇰': ['Pakistan', '巴基斯坦'],
'🇵🇱': ['Poland', '波兰', '波蘭'],
'🇵🇱': ['Poland', '波兰', '波蘭', '华沙', 'Warsaw'],
'🇵🇷': ['Puerto Rico', '波多黎各'],
'🇵🇹': ['Portugal', '葡萄牙'],
'🇵🇾': ['Paraguay', '巴拉圭'],
@@ -294,7 +302,7 @@ export function getFlag(name) {
'Moscow',
],
'🇸🇦': ['Saudi', '沙特阿拉伯', '沙特', 'Riyadh', '利雅得'],
'🇸🇪': ['Sweden', '瑞典'],
'🇸🇪': ['Sweden', '瑞典', '斯德哥尔摩', 'Stockholm'],
'🇸🇬': [
'Singapore',
'新加坡',
@@ -314,7 +322,7 @@ export function getFlag(name) {
'🇸🇰': ['Slovakia', '斯洛伐克'],
'🇹🇭': ['Thailand', '泰国', '泰國', '曼谷'],
'🇹🇳': ['Tunisia', '突尼斯'],
'🇹🇷': ['Turkey', '土耳其', '伊斯坦布尔'],
'🇹🇷': ['Turkey', '土耳其', '伊斯坦布尔', 'Istanbul'],
'🇹🇼': [
'Taiwan',
'台湾',
@@ -341,6 +349,7 @@ export function getFlag(name) {
'波特兰',
'达拉斯',
'俄勒冈',
'Oregon',
'凤凰城',
'费利蒙',
'硅谷',
@@ -354,10 +363,17 @@ export function getFlag(name) {
'沪美',
'哥伦布',
'纽约',
'New York',
'Los Angeles',
'San Jose',
'Sillicon Valley',
'Michigan',
'俄亥俄',
'Ohio',
'马纳萨斯',
'Manassas',
'弗吉尼亚',
'Virginia',
],
'🇺🇾': ['Uruguay', '乌拉圭'],
'🇻🇪': ['Venezuela', '委内瑞拉'],
@@ -418,8 +434,12 @@ export function getFlag(name) {
RegExp(`(^|[^a-zA-Z])${keyword}([^a-zA-Z]|$)`).test(name),
)
) {
//console.log(`ISOFlag = ${flag}`)
return (Flag = flag);
const isCN2 =
flag == '🇨🇳' &&
RegExp(`(^|[^a-zA-Z])CN2([^a-zA-Z]|$)`).test(name);
if (!isCN2) {
return (Flag = flag);
}
}
}