feat: 支持使用代理/节点/策略获取订阅

This commit is contained in:
xream 2024-03-13 01:55:16 +08:00
parent ca65e4209e
commit 80d46597b4
No known key found for this signature in database
GPG Key ID: 1D2C5225471789F9
8 changed files with 88 additions and 17 deletions

View File

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

View File

@ -111,7 +111,12 @@ async function downloadSubscription(req, res) {
} }
if (!$arguments.noFlow) { if (!$arguments.noFlow) {
// forward flow headers // forward flow headers
const flowInfo = await getFlowHeaders(url); const flowInfo = await getFlowHeaders(
url,
undefined,
undefined,
sub.proxy,
);
if (flowInfo) { if (flowInfo) {
res.set('subscription-userinfo', flowInfo); res.set('subscription-userinfo', flowInfo);
} }
@ -243,7 +248,12 @@ async function downloadCollection(req, res) {
} }
} }
if (!$arguments.noFlow) { if (!$arguments.noFlow) {
const flowInfo = await getFlowHeaders(url); const flowInfo = await getFlowHeaders(
url,
undefined,
undefined,
sub.proxy,
);
if (flowInfo) { if (flowInfo) {
res.set('subscription-userinfo', flowInfo); res.set('subscription-userinfo', flowInfo);
} }

View File

@ -109,7 +109,12 @@ async function compareSub(req, res) {
.filter((i) => i.length) .filter((i) => i.length)
.map(async (url) => { .map(async (url) => {
try { try {
return await download(url, sub.ua); return await download(
url,
sub.ua,
undefined,
sub.proxy,
);
} catch (err) { } catch (err) {
errors[url] = err; errors[url] = err;
$.error( $.error(
@ -195,7 +200,12 @@ async function compareCollection(req, res) {
.filter((i) => i.length) .filter((i) => i.length)
.map(async (url) => { .map(async (url) => {
try { try {
return await download(url, sub.ua); return await download(
url,
sub.ua,
undefined,
sub.proxy,
);
} catch (err) { } catch (err) {
errors[url] = err; errors[url] = err;
$.error( $.error(

View File

@ -113,7 +113,12 @@ async function getFlowInfo(req, res) {
}), }),
}); });
} else { } else {
const flowHeaders = await getFlowHeaders(url); const flowHeaders = await getFlowHeaders(
url,
undefined,
undefined,
sub.proxy,
);
if (!flowHeaders) { if (!flowHeaders) {
failed( failed(
res, res,

View File

@ -62,7 +62,12 @@ async function produceArtifact({
.filter((i) => i.length) .filter((i) => i.length)
.map(async (url) => { .map(async (url) => {
try { try {
return await download(url, ua || sub.ua); return await download(
url,
ua || sub.ua,
undefined,
sub.proxy,
);
} catch (err) { } catch (err) {
errors[url] = err; errors[url] = err;
$.error( $.error(
@ -102,7 +107,12 @@ async function produceArtifact({
.filter((i) => i.length) .filter((i) => i.length)
.map(async (url) => { .map(async (url) => {
try { try {
return await download(url, ua || sub.ua); return await download(
url,
ua || sub.ua,
undefined,
sub.proxy,
);
} catch (err) { } catch (err) {
errors[url] = err; errors[url] = err;
$.error( $.error(
@ -198,7 +208,12 @@ async function produceArtifact({
.filter((i) => i.length) .filter((i) => i.length)
.map(async (url) => { .map(async (url) => {
try { try {
return await download(url, sub.ua); return await download(
url,
sub.ua,
undefined,
sub.proxy,
);
} catch (err) { } catch (err) {
errors[url] = err; errors[url] = err;
$.error( $.error(

View File

@ -1,6 +1,7 @@
import { SETTINGS_KEY } from '@/constants'; import { SETTINGS_KEY } from '@/constants';
import { HTTP, ENV } from '@/vendor/open-api'; import { HTTP, ENV } from '@/vendor/open-api';
import { hex_md5 } from '@/vendor/md5'; import { hex_md5 } from '@/vendor/md5';
import { getPolicyDescriptor } from '@/utils';
import resourceCache from '@/utils/resource-cache'; import resourceCache from '@/utils/resource-cache';
import headersResourceCache from '@/utils/headers-resource-cache'; import headersResourceCache from '@/utils/headers-resource-cache';
import { import {
@ -13,7 +14,7 @@ import $ from '@/core/app';
const tasks = new Map(); const tasks = new Map();
export default async function download(rawUrl, ua, timeout) { export default async function download(rawUrl, ua, timeout, proxy) {
let $arguments = {}; let $arguments = {};
let url = rawUrl.replace(/#noFlow$/, ''); let url = rawUrl.replace(/#noFlow$/, '');
const rawArgs = url.split('#'); const rawArgs = url.split('#');
@ -52,7 +53,7 @@ export default async function download(rawUrl, ua, timeout) {
// return item.content; // return item.content;
// } // }
const { isNode } = ENV(); const { isNode, isStash } = ENV();
const { defaultUserAgent, defaultTimeout, cacheThreshold } = const { defaultUserAgent, defaultTimeout, cacheThreshold } =
$.read(SETTINGS_KEY); $.read(SETTINGS_KEY);
const userAgent = ua || defaultUserAgent || 'clash.meta'; const userAgent = ua || defaultUserAgent || 'clash.meta';
@ -65,6 +66,8 @@ export default async function download(rawUrl, ua, timeout) {
const http = HTTP({ const http = HTTP({
headers: { headers: {
'User-Agent': userAgent, 'User-Agent': userAgent,
'X-Stash-Selected-Proxy':
isStash && proxy ? encodeURIComponent(proxy) : undefined,
}, },
timeout: requestTimeout, timeout: requestTimeout,
}); });
@ -78,10 +81,14 @@ export default async function download(rawUrl, ua, timeout) {
result = cached; result = cached;
} else { } else {
$.info( $.info(
`Downloading...\nUser-Agent: ${userAgent}\nTimeout: ${requestTimeout}\nURL: ${url}`, `Downloading...\nUser-Agent: ${userAgent}\nTimeout: ${requestTimeout}\nProxy: ${proxy}\nURL: ${url}`,
); );
try { try {
const { body, headers } = await http.get(url); const { body, headers } = await http.get({
url,
proxy,
...getPolicyDescriptor(proxy),
});
if (headers) { if (headers) {
const flowInfo = getFlowField(headers); const flowInfo = getFlowField(headers);
@ -116,7 +123,11 @@ export default async function download(rawUrl, ua, timeout) {
// 检查订阅有效性 // 检查订阅有效性
if ($arguments?.validCheck) { if ($arguments?.validCheck) {
await validCheck(parseFlowHeaders(await getFlowHeaders(url))); await validCheck(
parseFlowHeaders(
await getFlowHeaders(url, undefined, undefined, proxy),
),
);
} }
if (!isNode) { if (!isNode) {

View File

@ -1,5 +1,6 @@
import { SETTINGS_KEY } from '@/constants'; import { SETTINGS_KEY } from '@/constants';
import { HTTP } from '@/vendor/open-api'; import { HTTP, ENV } from '@/vendor/open-api';
import { getPolicyDescriptor } from '@/utils';
import $ from '@/core/app'; import $ from '@/core/app';
import headersResourceCache from '@/utils/headers-resource-cache'; import headersResourceCache from '@/utils/headers-resource-cache';
@ -9,7 +10,7 @@ export function getFlowField(headers) {
)[0]; )[0];
return headers[subkey]; return headers[subkey];
} }
export async function getFlowHeaders(rawUrl, ua, timeout) { export async function getFlowHeaders(rawUrl, ua, timeout, proxy) {
let url = rawUrl; let url = rawUrl;
let $arguments = {}; let $arguments = {};
const rawArgs = url.split('#'); const rawArgs = url.split('#');
@ -33,6 +34,7 @@ export async function getFlowHeaders(rawUrl, ua, timeout) {
if ($arguments?.noFlow) { if ($arguments?.noFlow) {
return; return;
} }
const { isStash } = ENV();
const cached = headersResourceCache.get(url); const cached = headersResourceCache.get(url);
let flowInfo; let flowInfo;
if (!$arguments?.noCache && cached) { if (!$arguments?.noCache && cached) {
@ -55,8 +57,14 @@ export async function getFlowHeaders(rawUrl, ua, timeout) {
.filter((i) => i.length)[0], .filter((i) => i.length)[0],
headers: { headers: {
'User-Agent': userAgent, 'User-Agent': userAgent,
'X-Stash-Selected-Proxy':
isStash && proxy
? encodeURIComponent(proxy)
: undefined,
}, },
timeout: requestTimeout, timeout: requestTimeout,
proxy,
...getPolicyDescriptor(proxy),
}); });
flowInfo = getFlowField(headers); flowInfo = getFlowField(headers);
} catch (e) { } catch (e) {
@ -178,7 +186,7 @@ export function getRmainingDays(opt = {}) {
return daysDiff; return daysDiff;
} else { } else {
if (!resetDay) throw new Error('未提供月重置日 resetDay'); if (!resetDay) return;
resetDay = parseInt(resetDay); resetDay = parseInt(resetDay);
if (isNaN(resetDay) || resetDay <= 0 || resetDay > 31) if (isNaN(resetDay) || resetDay <= 0 || resetDay > 31)
throw new Error('月重置日应为 1-31 之间的整数'); throw new Error('月重置日应为 1-31 之间的整数');

View File

@ -35,6 +35,17 @@ function getIfPresent(obj, defaultValue) {
return isPresent(obj) ? obj : defaultValue; return isPresent(obj) ? obj : defaultValue;
} }
function getPolicyDescriptor(str) {
if (!str) return {};
return /^.+?\s*?=\s*?.+?\s*?,.+?/.test(str)
? {
'policy-descriptor': str,
}
: {
policy: str,
};
}
const utf8ArrayToStr = const utf8ArrayToStr =
typeof TextDecoder !== 'undefined' typeof TextDecoder !== 'undefined'
? (v) => new TextDecoder().decode(new Uint8Array(v)) ? (v) => new TextDecoder().decode(new Uint8Array(v))
@ -91,4 +102,5 @@ export {
isPresent, isPresent,
getIfPresent, getIfPresent,
utf8ArrayToStr, utf8ArrayToStr,
getPolicyDescriptor,
}; };