From 3211fbf3575da731ab58079c4201ce0dc59b8965 Mon Sep 17 00:00:00 2001 From: xream Date: Thu, 14 Sep 2023 15:16:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=A8=A1=E5=9D=97=E6=8E=A5=E5=8F=A3;?= =?UTF-8?q?=20=E8=84=9A=E6=9C=AC=E5=8F=82=E6=95=B0=E6=94=AF=E6=8C=81=20JSO?= =?UTF-8?q?N=20=E5=92=8C=20URL=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/package.json | 2 +- backend/src/constants.js | 1 + backend/src/core/proxy-utils/index.js | 19 +++-- backend/src/restful/index.js | 2 + backend/src/restful/module.js | 112 ++++++++++++++++++++++++++ backend/src/utils/download.js | 24 +++--- 6 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 backend/src/restful/module.js diff --git a/backend/package.json b/backend/package.json index 81fbaa3..8ea7343 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "sub-store", - "version": "2.14.53", + "version": "2.14.54", "description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.", "main": "src/main.js", "scripts": { diff --git a/backend/src/constants.js b/backend/src/constants.js index 492305a..7481357 100644 --- a/backend/src/constants.js +++ b/backend/src/constants.js @@ -3,6 +3,7 @@ export const SETTINGS_KEY = 'settings'; export const SUBS_KEY = 'subs'; export const COLLECTIONS_KEY = 'collections'; export const FILES_KEY = 'files'; +export const MODULES_KEY = 'modules'; export const ARTIFACTS_KEY = 'artifacts'; export const RULES_KEY = 'rules'; export const GIST_BACKUP_KEY = 'Auto Generated Sub-Store Backup'; diff --git a/backend/src/core/proxy-utils/index.js b/backend/src/core/proxy-utils/index.js index e27367b..a9c2fae 100644 --- a/backend/src/core/proxy-utils/index.js +++ b/backend/src/core/proxy-utils/index.js @@ -67,7 +67,7 @@ async function process(proxies, operators = [], targetPlatform) { for (const item of operators) { // process script let script; - const $arguments = {}; + let $arguments = {}; if (item.type.indexOf('Script') !== -1) { const { mode, content } = item.args; if (mode === 'link') { @@ -75,10 +75,19 @@ async function process(proxies, operators = [], targetPlatform) { // extract link arguments const rawArgs = url.split('#'); if (rawArgs.length > 1) { - for (const pair of rawArgs[1].split('&')) { - const key = pair.split('=')[0]; - const value = pair.split('=')[1] || true; - $arguments[key] = value; + try { + // 支持 `#${encodeURIComponent(JSON.stringify({arg1: "1"}))}` + $arguments = JSON.parse(decodeURIComponent(rawArgs[1])); + } catch (e) { + for (const pair of rawArgs[1].split('&')) { + const key = pair.split('=')[0]; + const value = pair.split('=')[1]; + // 部分兼容之前的逻辑 const value = pair.split('=')[1] || true; + $arguments[key] = + value == null || value === '' + ? true + : decodeURIComponent(value); + } } } diff --git a/backend/src/restful/index.js b/backend/src/restful/index.js index 7d9de34..8c2f7b5 100644 --- a/backend/src/restful/index.js +++ b/backend/src/restful/index.js @@ -5,6 +5,7 @@ import registerSubscriptionRoutes from './subscriptions'; import registerCollectionRoutes from './collections'; import registerArtifactRoutes from './artifacts'; import registerFileRoutes from './file'; +import registerModuleRoutes from './module'; import registerSyncRoutes from './sync'; import registerDownloadRoutes from './download'; import registerSettingRoutes from './settings'; @@ -25,6 +26,7 @@ export default function serve() { registerSettingRoutes($app); registerArtifactRoutes($app); registerFileRoutes($app); + registerModuleRoutes($app); registerSyncRoutes($app); registerNodeInfoRoutes($app); registerMiscRoutes($app); diff --git a/backend/src/restful/module.js b/backend/src/restful/module.js new file mode 100644 index 0000000..836f2c4 --- /dev/null +++ b/backend/src/restful/module.js @@ -0,0 +1,112 @@ +import { deleteByName, findByName, updateByName } from '@/utils/database'; +import { MODULES_KEY } from '@/constants'; +import { failed, success } from '@/restful/response'; +import $ from '@/core/app'; +import { RequestInvalidError, ResourceNotFoundError } from '@/restful/errors'; +import { hex_md5 } from '@/vendor/md5'; + +export default function register($app) { + if (!$.read(MODULES_KEY)) $.write([], MODULES_KEY); + + $app.route('/api/module/:name') + .get(getModule) + .patch(updateModule) + .delete(deleteModule); + + $app.route('/api/modules') + .get(getAllModules) + .post(createModule) + .put(replaceModule); +} + +// module API +function createModule(req, res) { + const module = req.body; + module.name = module.name ?? hex_md5(JSON.stringify(module)); + $.info(`正在创建模块:${module.name}`); + const allModules = $.read(MODULES_KEY); + if (findByName(allModules, module.name)) { + failed( + res, + new RequestInvalidError( + 'DUPLICATE_KEY', + `已存在相同的模块 请勿重复添加`, + ), + ); + } + allModules.push(module); + $.write(allModules, MODULES_KEY); + success(res, module, 201); +} + +function getModule(req, res) { + let { name } = req.params; + name = decodeURIComponent(name); + const allModules = $.read(MODULES_KEY); + const module = findByName(allModules, name); + if (module) { + success(res, module); + } else { + failed( + res, + new ResourceNotFoundError( + `MODULE_NOT_FOUND`, + `Module ${name} does not exist`, + 404, + ), + ); + } +} + +function updateModule(req, res) { + let { name } = req.params; + name = decodeURIComponent(name); + let module = req.body; + const allModules = $.read(MODULES_KEY); + const oldModule = findByName(allModules, name); + if (oldModule) { + const newModule = { + ...oldModule, + ...module, + }; + $.info(`正在更新模块:${name}...`); + + updateByName(allModules, name, newModule); + $.write(allModules, MODULES_KEY); + success(res, newModule); + } else { + failed( + res, + new ResourceNotFoundError( + 'RESOURCE_NOT_FOUND', + `Module ${name} does not exist!`, + ), + 404, + ); + } +} + +function deleteModule(req, res) { + let { name } = req.params; + name = decodeURIComponent(name); + $.info(`正在删除模块:${name}`); + let allModules = $.read(MODULES_KEY); + deleteByName(allModules, name); + $.write(allModules, MODULES_KEY); + success(res); +} + +function getAllModules(req, res) { + const allModules = $.read(MODULES_KEY); + success( + res, + // eslint-disable-next-line no-unused-vars + allModules.map(({ content, ...rest }) => rest), + ); +} + +function replaceModule(req, res) { + const allModules = req.body; + $.write(allModules, MODULES_KEY); + success(res); +} diff --git a/backend/src/utils/download.js b/backend/src/utils/download.js index 9e9e668..8540bf5 100644 --- a/backend/src/utils/download.js +++ b/backend/src/utils/download.js @@ -1,4 +1,4 @@ -import { FILES_KEY } from '@/constants'; +import { FILES_KEY, MODULES_KEY } from '@/constants'; import { findByName } from '@/utils/database'; import { HTTP, ENV } from '@/vendor/open-api'; import { hex_md5 } from '@/vendor/md5'; @@ -8,19 +8,21 @@ import $ from '@/core/app'; const tasks = new Map(); export default async function download(url, ua) { - const downloadUrlMatch = url.match(/^\/api\/file\/(.+)/); + const downloadUrlMatch = url.match(/^\/api\/(file|module)\/(.+)/); if (downloadUrlMatch) { - let fileName = downloadUrlMatch?.[1]; - if (fileName == null) { - throw new Error(`本地文件 URL 无效: ${url}`); + let type = downloadUrlMatch?.[1]; + let name = downloadUrlMatch?.[2]; + if (name == null) { + throw new Error(`本地 ${type} URL 无效: ${url}`); } - fileName = decodeURIComponent(fileName); - const allFiles = $.read(FILES_KEY); - const file = findByName(allFiles, fileName); - if (!file) { - throw new Error(`找不到本地文件: ${fileName}`); + name = decodeURIComponent(name); + const key = type === 'module' ? MODULES_KEY : FILES_KEY; + const item = findByName($.read(key), name); + if (!item) { + throw new Error(`找不到本地 ${type}: ${name}`); } - return file.content; + + return item.content; } const { isNode } = ENV();