Compare commits

..

6 Commits

Author SHA1 Message Date
xream
bbb9602f9f release: backend version 2.14.256 2024-03-15 08:12:06 +08:00
xream
6db6153672 Merge pull request #295 from makaspacex/master
[Feat]规则转换增加对GEOIP与GEOSITE的支持
2024-03-15 08:10:30 +08:00
makabaka
b66189948a 规则转换增加对GEOIP与GEOSITE的支持 2024-03-14 22:07:45 +08:00
xream
2611dccc73 feat: 支持设置查询远程订阅流量信息时的 User-Agent 2024-03-14 19:45:39 +08:00
xream
25d3cf6ca4 feat: 通过代理/节点/策略获取订阅 现已支持 Surge, Loon, Stash, Shadowrocket, QX, Node.js 2024-03-14 01:54:07 +08:00
xream
3637c5eb74 feat: SSH 协议跟进 clash.meta(mihomo) 的修改 2024-03-13 16:24:30 +08:00
8 changed files with 56 additions and 20 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "sub-store", "name": "sub-store",
"version": "2.14.251", "version": "2.14.256",
"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

@@ -231,6 +231,10 @@ const sshParser = (proxy = {}) => {
// https://wiki.metacubex.one/config/proxies/ssh // https://wiki.metacubex.one/config/proxies/ssh
// https://sing-box.sagernet.org/zh/configuration/outbound/ssh // https://sing-box.sagernet.org/zh/configuration/outbound/ssh
if (proxy['privateKey']) parsedProxy.private_key_path = proxy['privateKey']; if (proxy['privateKey']) parsedProxy.private_key_path = proxy['privateKey'];
if (proxy['private-key'])
parsedProxy.private_key_path = proxy['private-key'];
if (proxy['private-key-passphrase'])
parsedProxy.private_key_passphrase = proxy['private-key-passphrase'];
if (proxy['server-fingerprint']) { if (proxy['server-fingerprint']) {
parsedProxy.host_key = [proxy['server-fingerprint']]; parsedProxy.host_key = [proxy['server-fingerprint']];
// https://manual.nssurge.com/policy/ssh.html // https://manual.nssurge.com/policy/ssh.html
@@ -240,6 +244,9 @@ const sshParser = (proxy = {}) => {
proxy['server-fingerprint'].split(' ')[0], proxy['server-fingerprint'].split(' ')[0],
]; ];
} }
if (proxy['host-key']) parsedProxy.host_key = proxy['host-key'];
if (proxy['host-key-algorithms'])
parsedProxy.host_key_algorithms = proxy['host-key-algorithms'];
if (proxy['fast-open']) parsedProxy.udp_fragment = true; if (proxy['fast-open']) parsedProxy.udp_fragment = true;
tfoParser(proxy, parsedProxy); tfoParser(proxy, parsedProxy);
return parsedProxy; return parsedProxy;

View File

@@ -10,6 +10,8 @@ const RULE_TYPES_MAPPING = [
[/^PROTOCOL$/, 'PROTOCOL'], [/^PROTOCOL$/, 'PROTOCOL'],
[/^IP-CIDR$/i, 'IP-CIDR'], [/^IP-CIDR$/i, 'IP-CIDR'],
[/^(IP-CIDR6|ip6-cidr|IP6-CIDR)$/, 'IP-CIDR6'], [/^(IP-CIDR6|ip6-cidr|IP6-CIDR)$/, 'IP-CIDR6'],
[/^GEOIP$/i, 'GEOIP'],
[/^GEOSITE$/i, 'GEOSITE'],
]; ];
function AllRuleParser() { function AllRuleParser() {

View File

@@ -113,7 +113,7 @@ async function downloadSubscription(req, res) {
// forward flow headers // forward flow headers
const flowInfo = await getFlowHeaders( const flowInfo = await getFlowHeaders(
url, url,
undefined, $arguments.flowUserAgent,
undefined, undefined,
sub.proxy, sub.proxy,
); );
@@ -250,7 +250,7 @@ async function downloadCollection(req, res) {
if (!$arguments.noFlow) { if (!$arguments.noFlow) {
const flowInfo = await getFlowHeaders( const flowInfo = await getFlowHeaders(
url, url,
undefined, $arguments.flowUserAgent,
undefined, undefined,
sub.proxy, sub.proxy,
); );

View File

@@ -115,7 +115,7 @@ async function getFlowInfo(req, res) {
} else { } else {
const flowHeaders = await getFlowHeaders( const flowHeaders = await getFlowHeaders(
url, url,
undefined, $arguments.flowUserAgent,
undefined, undefined,
sub.proxy, sub.proxy,
); );

View File

@@ -53,7 +53,7 @@ export default async function download(rawUrl, ua, timeout, proxy) {
// return item.content; // return item.content;
// } // }
const { isNode, isStash } = ENV(); const { isNode, isStash, isLoon, isShadowRocket, isQX } = 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';
@@ -66,8 +66,10 @@ export default async function download(rawUrl, ua, timeout, proxy) {
const http = HTTP({ const http = HTTP({
headers: { headers: {
'User-Agent': userAgent, 'User-Agent': userAgent,
'X-Stash-Selected-Proxy': ...(isStash && proxy
isStash && proxy ? encodeURIComponent(proxy) : undefined, ? { 'X-Stash-Selected-Proxy': encodeURIComponent(proxy) }
: {}),
...(isShadowRocket && proxy ? { 'X-Surge-Policy': proxy } : {}),
}, },
timeout: requestTimeout, timeout: requestTimeout,
}); });
@@ -86,8 +88,10 @@ export default async function download(rawUrl, ua, timeout, proxy) {
try { try {
const { body, headers } = await http.get({ const { body, headers } = await http.get({
url, url,
proxy, ...(proxy ? { proxy } : {}),
...getPolicyDescriptor(proxy), ...(isLoon && proxy ? { node: proxy } : {}),
...(isQX && proxy ? { opts: { policy: proxy } } : {}),
...(proxy ? getPolicyDescriptor(proxy) : {}),
}); });
if (headers) { if (headers) {
@@ -125,7 +129,12 @@ export default async function download(rawUrl, ua, timeout, proxy) {
if ($arguments?.validCheck) { if ($arguments?.validCheck) {
await validCheck( await validCheck(
parseFlowHeaders( parseFlowHeaders(
await getFlowHeaders(url, undefined, undefined, proxy), await getFlowHeaders(
url,
$arguments.flowUserAgent,
undefined,
proxy,
),
), ),
); );
} }

View File

@@ -34,7 +34,7 @@ export async function getFlowHeaders(rawUrl, ua, timeout, proxy) {
if ($arguments?.noFlow) { if ($arguments?.noFlow) {
return; return;
} }
const { isStash } = ENV(); const { isStash, isLoon, isShadowRocket, isQX } = ENV();
const cached = headersResourceCache.get(url); const cached = headersResourceCache.get(url);
let flowInfo; let flowInfo;
if (!$arguments?.noCache && cached) { if (!$arguments?.noCache && cached) {
@@ -49,7 +49,11 @@ export async function getFlowHeaders(rawUrl, ua, timeout, proxy) {
const requestTimeout = timeout || defaultTimeout; const requestTimeout = timeout || defaultTimeout;
const http = HTTP(); const http = HTTP();
try { try {
// $.info(`使用 HEAD 方法获取流量信息: ${url}`); $.info(
`使用 HEAD 方法获取流量信息: ${url}, User-Agent: ${
userAgent || ''
}`,
);
const { headers } = await http.head({ const { headers } = await http.head({
url: url url: url
.split(/[\r\n]+/) .split(/[\r\n]+/)
@@ -57,23 +61,36 @@ export async function getFlowHeaders(rawUrl, ua, timeout, proxy) {
.filter((i) => i.length)[0], .filter((i) => i.length)[0],
headers: { headers: {
'User-Agent': userAgent, 'User-Agent': userAgent,
...(isStash && proxy
? {
'X-Stash-Selected-Proxy': 'X-Stash-Selected-Proxy':
isStash && proxy encodeURIComponent(proxy),
? encodeURIComponent(proxy) }
: undefined, : {}),
...(isShadowRocket && proxy
? { 'X-Surge-Policy': proxy }
: {}),
}, },
timeout: requestTimeout, timeout: requestTimeout,
proxy, ...(proxy ? { proxy } : {}),
...getPolicyDescriptor(proxy), ...(isLoon && proxy ? { node: proxy } : {}),
...(isQX && proxy ? { opts: { policy: proxy } } : {}),
...(proxy ? getPolicyDescriptor(proxy) : {}),
}); });
flowInfo = getFlowField(headers); flowInfo = getFlowField(headers);
} catch (e) { } catch (e) {
$.error( $.error(
`使用 HEAD 方法获取流量信息失败: ${url}: ${e.message ?? e}`, `使用 HEAD 方法获取流量信息失败: ${url}, User-Agent: ${
userAgent || ''
}: ${e.message ?? e}`,
); );
} }
if (!flowInfo) { if (!flowInfo) {
$.info(`使用 GET 方法获取流量信息: ${url}`); $.info(
`使用 GET 方法获取流量信息: ${url}, User-Agent: ${
userAgent || ''
}`,
);
const { headers } = await http.get({ const { headers } = await http.get({
url: url url: url
.split(/[\r\n]+/) .split(/[\r\n]+/)

View File

@@ -316,6 +316,7 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
url: options.url, url: options.url,
headers: options.headers, headers: options.headers,
body: options.body, body: options.body,
opts: options.opts,
}); });
} else if (isLoon || isSurge || isNode) { } else if (isLoon || isSurge || isNode) {
worker = new Promise((resolve, reject) => { worker = new Promise((resolve, reject) => {