feat: 订阅和文件的请求链接支持传入 $options , 可在脚本中使用

This commit is contained in:
xream 2024-09-03 13:58:10 +08:00
parent e1489a3cf7
commit 99d5868cff
No known key found for this signature in database
GPG Key ID: 1D2C5225471789F9
7 changed files with 103 additions and 12 deletions

View File

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

@ -77,7 +77,13 @@ function parse(raw) {
return proxies; return proxies;
} }
async function processFn(proxies, operators = [], targetPlatform, source) { async function processFn(
proxies,
operators = [],
targetPlatform,
source,
$options,
) {
for (const item of operators) { for (const item of operators) {
// process script // process script
let script; let script;
@ -176,6 +182,7 @@ async function processFn(proxies, operators = [], targetPlatform, source) {
targetPlatform, targetPlatform,
$arguments, $arguments,
source, source,
$options,
); );
} else { } else {
processor = PROXY_PROCESSORS[item.type](item.args || {}); processor = PROXY_PROCESSORS[item.type](item.args || {});

View File

@ -316,7 +316,7 @@ function RegexDeleteOperator(regex) {
1. This function name should be `operator`! 1. This function name should be `operator`!
2. Always declare variables before using them! 2. Always declare variables before using them!
*/ */
function ScriptOperator(script, targetPlatform, $arguments, source) { function ScriptOperator(script, targetPlatform, $arguments, source, $options) {
return { return {
name: 'Script Operator', name: 'Script Operator',
func: async (proxies) => { func: async (proxies) => {
@ -326,6 +326,7 @@ function ScriptOperator(script, targetPlatform, $arguments, source) {
'operator', 'operator',
script, script,
$arguments, $arguments,
$options,
); );
output = operator(proxies, targetPlatform, { source, ...env }); output = operator(proxies, targetPlatform, { source, ...env });
})(); })();
@ -338,9 +339,9 @@ function ScriptOperator(script, targetPlatform, $arguments, source) {
'operator', 'operator',
`async function operator(input = []) { `async function operator(input = []) {
if (input && (input.$files || input.$content)) { if (input && (input.$files || input.$content)) {
let { $content, $files } = input let { $content, $files, $options } = input
${script} ${script}
return { $content, $files } return { $content, $files, $options }
} else { } else {
let proxies = input let proxies = input
let list = [] let list = []
@ -352,6 +353,7 @@ function ScriptOperator(script, targetPlatform, $arguments, source) {
} }
}`, }`,
$arguments, $arguments,
$options,
); );
output = operator(proxies, targetPlatform, { source, ...env }); output = operator(proxies, targetPlatform, { source, ...env });
})(); })();
@ -794,7 +796,7 @@ function TypeFilter(types) {
1. This function name should be `filter`! 1. This function name should be `filter`!
2. Always declare variables before using them! 2. Always declare variables before using them!
*/ */
function ScriptFilter(script, targetPlatform, $arguments, source) { function ScriptFilter(script, targetPlatform, $arguments, source, $options) {
return { return {
name: 'Script Filter', name: 'Script Filter',
func: async (proxies) => { func: async (proxies) => {
@ -804,6 +806,7 @@ function ScriptFilter(script, targetPlatform, $arguments, source) {
'filter', 'filter',
script, script,
$arguments, $arguments,
$options,
); );
output = filter(proxies, targetPlatform, { source, ...env }); output = filter(proxies, targetPlatform, { source, ...env });
})(); })();
@ -826,6 +829,7 @@ function ScriptFilter(script, targetPlatform, $arguments, source) {
return list return list
}`, }`,
$arguments, $arguments,
$options,
); );
output = filter(proxies, targetPlatform, { source, ...env }); output = filter(proxies, targetPlatform, { source, ...env });
})(); })();
@ -966,7 +970,7 @@ function clone(object) {
return JSON.parse(JSON.stringify(object)); return JSON.parse(JSON.stringify(object));
} }
function createDynamicFunction(name, script, $arguments) { function createDynamicFunction(name, script, $arguments, $options) {
const flowUtils = { const flowUtils = {
getFlowField, getFlowField,
getFlowHeaders, getFlowHeaders,
@ -978,6 +982,7 @@ function createDynamicFunction(name, script, $arguments) {
if ($.env.isLoon) { if ($.env.isLoon) {
return new Function( return new Function(
'$arguments', '$arguments',
'$options',
'$substore', '$substore',
'lodash', 'lodash',
'$persistentStore', '$persistentStore',
@ -991,6 +996,7 @@ function createDynamicFunction(name, script, $arguments) {
`${script}\n return ${name}`, `${script}\n return ${name}`,
)( )(
$arguments, $arguments,
$options,
$, $,
lodash, lodash,
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
@ -1008,6 +1014,7 @@ function createDynamicFunction(name, script, $arguments) {
} else { } else {
return new Function( return new Function(
'$arguments', '$arguments',
'$options',
'$substore', '$substore',
'lodash', 'lodash',
'ProxyUtils', 'ProxyUtils',
@ -1018,6 +1025,7 @@ function createDynamicFunction(name, script, $arguments) {
`${script}\n return ${name}`, `${script}\n return ${name}`,
)( )(
$arguments, $arguments,
$options,
$, $,
lodash, lodash,
ProxyUtils, ProxyUtils,

View File

@ -70,6 +70,24 @@ async function downloadSubscription(req, res) {
includeUnsupportedProxy, includeUnsupportedProxy,
resultFormat, resultFormat,
} = req.query; } = req.query;
let $options = {};
if (req.query.$options) {
try {
// 支持 `#${encodeURIComponent(JSON.stringify({arg1: "1"}))}`
$options = JSON.parse(decodeURIComponent(req.query.$options));
} catch (e) {
for (const pair of req.query.$options.split('&')) {
const key = pair.split('=')[0];
const value = pair.split('=')[1];
// 部分兼容之前的逻辑 const value = pair.split('=')[1] || true;
$options[key] =
value == null || value === ''
? true
: decodeURIComponent(value);
}
}
$.info(`传入 $options: ${JSON.stringify($options)}`);
}
if (url) { if (url) {
url = decodeURIComponent(url); url = decodeURIComponent(url);
$.info(`指定远程订阅 URL: ${url}`); $.info(`指定远程订阅 URL: ${url}`);
@ -116,6 +134,7 @@ async function downloadSubscription(req, res) {
produceOpts: { produceOpts: {
'include-unsupported-proxy': includeUnsupportedProxy, 'include-unsupported-proxy': includeUnsupportedProxy,
}, },
$options,
}); });
if ( if (
@ -247,6 +266,25 @@ async function downloadCollection(req, res) {
resultFormat, resultFormat,
} = req.query; } = req.query;
let $options = {};
if (req.query.$options) {
try {
// 支持 `#${encodeURIComponent(JSON.stringify({arg1: "1"}))}`
$options = JSON.parse(decodeURIComponent(req.query.$options));
} catch (e) {
for (const pair of req.query.$options.split('&')) {
const key = pair.split('=')[0];
const value = pair.split('=')[1];
// 部分兼容之前的逻辑 const value = pair.split('=')[1] || true;
$options[key] =
value == null || value === ''
? true
: decodeURIComponent(value);
}
}
$.info(`传入 $options: ${JSON.stringify($options)}`);
}
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') { if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
ignoreFailedRemoteSub = decodeURIComponent(ignoreFailedRemoteSub); ignoreFailedRemoteSub = decodeURIComponent(ignoreFailedRemoteSub);
$.info(`指定忽略失败的远程订阅: ${ignoreFailedRemoteSub}`); $.info(`指定忽略失败的远程订阅: ${ignoreFailedRemoteSub}`);
@ -272,6 +310,7 @@ async function downloadCollection(req, res) {
produceOpts: { produceOpts: {
'include-unsupported-proxy': includeUnsupportedProxy, 'include-unsupported-proxy': includeUnsupportedProxy,
}, },
$options,
}); });
// forward flow header from the first subscription in this collection // forward flow header from the first subscription in this collection

View File

@ -60,6 +60,24 @@ async function getFile(req, res) {
mergeSources, mergeSources,
ignoreFailedRemoteFile, ignoreFailedRemoteFile,
} = req.query; } = req.query;
let $options = {};
if (req.query.$options) {
try {
// 支持 `#${encodeURIComponent(JSON.stringify({arg1: "1"}))}`
$options = JSON.parse(decodeURIComponent(req.query.$options));
} catch (e) {
for (const pair of req.query.$options.split('&')) {
const key = pair.split('=')[0];
const value = pair.split('=')[1];
// 部分兼容之前的逻辑 const value = pair.split('=')[1] || true;
$options[key] =
value == null || value === ''
? true
: decodeURIComponent(value);
}
}
$.info(`传入 $options: ${JSON.stringify($options)}`);
}
if (url) { if (url) {
url = decodeURIComponent(url); url = decodeURIComponent(url);
$.info(`指定远程文件 URL: ${url}`); $.info(`指定远程文件 URL: ${url}`);
@ -101,6 +119,7 @@ async function getFile(req, res) {
content, content,
mergeSources, mergeSources,
ignoreFailedRemoteFile, ignoreFailedRemoteFile,
$options,
}); });
try { try {

View File

@ -37,6 +37,7 @@ async function produceArtifact({
produceOpts = {}, produceOpts = {},
subscription, subscription,
awaitCustomCache, awaitCustomCache,
$options,
}) { }) {
platform = platform || 'JSON'; platform = platform || 'JSON';
@ -158,6 +159,7 @@ async function produceArtifact({
sub.process || [], sub.process || [],
platform, platform,
{ [sub.name]: sub }, { [sub.name]: sub },
$options,
); );
if (proxies.length === 0) { if (proxies.length === 0) {
throw new Error(`订阅 ${name} 中不含有效节点`); throw new Error(`订阅 ${name} 中不含有效节点`);
@ -259,7 +261,11 @@ async function produceArtifact({
currentProxies, currentProxies,
sub.process || [], sub.process || [],
platform, platform,
{ [sub.name]: sub, _collection: collection }, {
[sub.name]: sub,
_collection: collection,
$options,
},
); );
results[name] = currentProxies; results[name] = currentProxies;
processed++; processed++;
@ -312,6 +318,7 @@ async function produceArtifact({
collection.process || [], collection.process || [],
platform, platform,
{ _collection: collection }, { _collection: collection },
$options,
); );
if (proxies.length === 0) { if (proxies.length === 0) {
throw new Error(`组合订阅 ${name} 中不含有效节点`); throw new Error(`组合订阅 ${name} 中不含有效节点`);
@ -460,10 +467,10 @@ async function produceArtifact({
const processed = const processed =
Array.isArray(file.process) && file.process.length > 0 Array.isArray(file.process) && file.process.length > 0
? await ProxyUtils.process( ? await ProxyUtils.process(
{ $files: files, $content: filesContent }, { $files: files, $content: filesContent, $options },
file.process, file.process,
) )
: { $content: filesContent, $files: files }; : { $content: filesContent, $files: files, $options };
return processed?.$content ?? ''; return processed?.$content ?? '';
} }

View File

@ -23,6 +23,17 @@ function operator(proxies = [], targetPlatform, context) {
// $arguments 为传入的脚本参数 // $arguments 为传入的脚本参数
// $options 为通过链接传入的参数
// 例如: { arg1: 'a', arg2: 'b' }
// 可这样传:
// 先这样处理 encodeURIComponent(JSON.stringify({ arg1: 'a', arg2: 'b' }))
// /api/file/foo?$options=%7B%22arg1%22%3A%22a%22%2C%22arg2%22%3A%22b%22%7D
// 或这样传:
// 先这样处理 encodeURIComponent('arg1=a&arg2=b')
// /api/file/foo?$options=arg1%3Da%26arg2%3Db
// console.log($options)
// targetPlatform 为输出的目标平台 // targetPlatform 为输出的目标平台
// lodash // lodash
@ -133,7 +144,7 @@ function operator(proxies = [], targetPlatform, context) {
// yaml.proxies.unshift(...clashMetaProxies) // yaml.proxies.unshift(...clashMetaProxies)
// $content = ProxyUtils.yaml.dump(yaml) // $content = ProxyUtils.yaml.dump(yaml)
// { $content, $files } will be passed to the next operator // { $content, $files, $options } will be passed to the next operator
// $content is the final content of the file // $content is the final content of the file
// flowUtils 为机场订阅流量信息处理工具 // flowUtils 为机场订阅流量信息处理工具
@ -141,7 +152,7 @@ function operator(proxies = [], targetPlatform, context) {
// 1. https://t.me/zhetengsha/948 // 1. https://t.me/zhetengsha/948
// context 为传入的上下文 // context 为传入的上下文
// 有三种情况, 按需判断 // 其中 source 为 订阅和组合订阅的数据, 有三种情况, 按需判断
// 若存在 `source._collection` 且 `source._collection.subscriptions` 中的 key 在 `source` 上也存在, 说明输出结果为组合订阅, 但是脚本设置在单条订阅上 // 若存在 `source._collection` 且 `source._collection.subscriptions` 中的 key 在 `source` 上也存在, 说明输出结果为组合订阅, 但是脚本设置在单条订阅上