From 5217891b17f50e23fb1e3a4c30ad8f53892a0983 Mon Sep 17 00:00:00 2001 From: Virgil Clyne Date: Mon, 21 Oct 2024 11:35:42 +0800 Subject: [PATCH] feat: quantumultx plugin Update template.ts Update template.ejs --- .../plugins/quantumultx/modern.config.ts | 13 ++ .../modkit/plugins/quantumultx/package.json | 27 ++++ .../modkit/plugins/quantumultx/src/index.ts | 45 ++++++ .../plugins/quantumultx/src/template.ts | 150 ++++++++++++++++++ .../modkit/plugins/quantumultx/template.ejs | 21 +++ .../modkit/plugins/quantumultx/tsconfig.json | 8 + pnpm-lock.yaml | 19 +++ 7 files changed, 283 insertions(+) create mode 100644 packages/modkit/plugins/quantumultx/modern.config.ts create mode 100644 packages/modkit/plugins/quantumultx/package.json create mode 100644 packages/modkit/plugins/quantumultx/src/index.ts create mode 100644 packages/modkit/plugins/quantumultx/src/template.ts create mode 100644 packages/modkit/plugins/quantumultx/template.ejs create mode 100644 packages/modkit/plugins/quantumultx/tsconfig.json diff --git a/packages/modkit/plugins/quantumultx/modern.config.ts b/packages/modkit/plugins/quantumultx/modern.config.ts new file mode 100644 index 0000000..590af90 --- /dev/null +++ b/packages/modkit/plugins/quantumultx/modern.config.ts @@ -0,0 +1,13 @@ +import fs from 'node:fs'; +import { dualBuildConfigs } from '@iringo/modkit-config/modern.config.ts'; +import { defineConfig, moduleTools } from '@modern-js/module-tools'; + +export default defineConfig({ + plugins: [moduleTools()], + buildConfig: dualBuildConfigs.map((item) => ({ + ...item, + define: { + 'process.env.TEMP': fs.readFileSync('./template.ejs', 'utf-8'), + }, + })), +}); diff --git a/packages/modkit/plugins/quantumultx/package.json b/packages/modkit/plugins/quantumultx/package.json new file mode 100644 index 0000000..bd35b8b --- /dev/null +++ b/packages/modkit/plugins/quantumultx/package.json @@ -0,0 +1,27 @@ +{ + "name": "@iringo/modkit-plugin-quantumultx", + "version": "0.0.1", + "description": "", + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "scripts": { + "dev": "modern build -w", + "build": "modern build" + }, + "files": ["dist", "types", "CHANGELOG.md"], + "dependencies": { + "@iringo/modkit-shared": "workspace:^" + }, + "devDependencies": { + "@iringo/modkit-config": "workspace:^", + "@modern-js/module-tools": "^2.60.2", + "@types/node": "^20.0.0", + "typescript": "^5.6.2" + }, + "repository": { + "type": "git", + "url": "https://github.com/NSRingo/engineering-solutions", + "directory": "packages/modkit/apps/quantumultx" + } +} diff --git a/packages/modkit/plugins/quantumultx/src/index.ts b/packages/modkit/plugins/quantumultx/src/index.ts new file mode 100644 index 0000000..d387cb4 --- /dev/null +++ b/packages/modkit/plugins/quantumultx/src/index.ts @@ -0,0 +1,45 @@ +import type { ModkitPlugin } from '@iringo/modkit-shared'; +import { QuantumultxTemplate } from './template'; + +export interface QuantumultxPluginOptions { + objectValuesHandler?: (obj: Record) => string; +} + +export const pluginQuantumultx = ({ + objectValuesHandler = (obj) => { + if (Array.isArray(obj)) { + return `"${obj.join()}"`; + } + return `"${JSON.stringify(obj)}"`; + }, +}: QuantumultxPluginOptions = {}): ModkitPlugin => { + return { + name: 'quantumultx', + setup() { + return { + configurePlatform() { + return { + extension: '.snippet', + template: process.env.TEMP || '', + }; + }, + modifySource({ source }) { + source ??= {}; + source.arguments = source.arguments?.filter((item) => { + if (typeof item.type === 'object' && item.type.quantumultx === 'exclude') { + return false; + } + return true; + }); + return source; + }, + templateParameters(params) { + const quantumultxTemplate = new QuantumultxTemplate(params, objectValuesHandler); + return { + quantumultxTemplate, + }; + }, + }; + }, + }; +}; diff --git a/packages/modkit/plugins/quantumultx/src/template.ts b/packages/modkit/plugins/quantumultx/src/template.ts new file mode 100644 index 0000000..30f5531 --- /dev/null +++ b/packages/modkit/plugins/quantumultx/src/template.ts @@ -0,0 +1,150 @@ +import { type RuleType, Template, logger, objectEntries, toKebabCase } from '@iringo/modkit-shared'; + +const ruleTypeMap: Record = { + DOMAIN: 'host', + 'DOMAIN-SUFFIX': 'host-suffix', + 'DOMAIN-KEYWORD': 'host-keyword', + GEOIP: 'geoip', + 'USER-AGENT': 'user-agent', + 'IP-CIDR': 'ip-cidr', + 'IP-CIDR6': 'ip6-cidr', + 'PROCESS-NAME': 'process', +}; + +export class QuantumultxTemplate extends Template { + get Metadata() { + const result: Record = {}; + result.name = this.metadata.name; + result.desc = this.metadata.description; + result.system = this.metadata.system?.join(); + result.version = this.metadata.version; + Object.entries(this.metadata.extra || {}).forEach(([key, value]) => { + result[key] = Array.isArray(value) ? value.join(',') : value; + }); + return this.renderKeyValuePairs(result, { prefix: '#!' }); + } + + get DNS() { + const dnss: string[] = []; + // 没写呢 + return dnss.join('\n').trim(); + } + + get Filter() { + const filters: string[] = []; + this.content.rule?.forEach((rule) => { + if (typeof rule === 'string') { + return rule; + } + const { type, content, policyName, ...rest } = rule; + const options = []; + if (ruleTypeMap[type]) { + options.push(ruleTypeMap[type]); + } else { + logger.warn(`[Quantumult X] Unsupported rule type: ${type}`); + } + options.push(content); + options.push(policyName); + filters.push(options.join(', ')); + }); + return filters.join('\n').trim(); + } + + get Rewrite() { + const rewrites: string[] = []; + this.content.rewrite?.forEach((rewrite) => { + let { type, pattern, mode, content, ...rest } = rewrite; + switch (rewrite.mode) { + case 'header': + logger.warn('[Quantumult X] Unsupported rewrite mode: header'); + break; + case 'header-add': + mode = 'request-header'; + break; + case 'header-del': + mode = 'request-header'; + content += " ''"; + break; + case undefined: + switch (type) { + case 'http-request': + mode = 'request-body'; + break; + case 'http-response': + mode = 'response-body'; + break; + } + break; + } + const options = []; + options.push(pattern); + options.push('url'); + options.push(mode); + options.push(content || ''); + rewrites.push(options.join(' ')); + }); + return `${rewrites.join('\n').trim()}\n${this.#script.rewrite}`; + } + + get #script() { + const rewrites: string[] = []; + const tasks: string[] = []; + this.content.script?.forEach((script, index) => { + let { type, pattern, cronexp, scriptKey, argument, injectArgument, name, ...rest } = script; + switch (type) { + case 'http-request': + type = 'script-request'; + break; + case 'http-response': + type = 'script-response'; + break; + case 'generic': + // 没找到示例 + break; + case 'event': + // 没找到示例 + break; + case 'dns': + logger.warn('[Quantumult X] Unsupported script type: dns'); + break; + } + const parameters: Record = {}; + parameters['script-path'] = this.utils.getScriptPath(scriptKey); + parameters.tag = name || `Script${index}`; + const options = []; + switch (type) { + case 'script-request': + case 'script-response': + options.push(pattern); + options.push('url'); + options.push(type); + options.push(parameters['script-path']); + rewrites.push(`# ${parameters.tag}\n${options.join(' ')}`); + break; + case 'cron': { + options.push(cronexp); + options.push(parameters['script-path']); + const option = [options.join(' ')]; + if (name) { + option.push(`tag = ${name}`); + } + if (imgUrl) { + option.push(`img-url = ${imgUrl}`); + } + if (enabled) { + option.push(`enabled = ${enabled}`); + } + tasks.push(option.join(', ')); + break; + } + } + }); + const rewrite = rewrites.join('\n').trim(); + const task = tasks.join('\n').trim(); + return { rewrite, task }; + } + + get MITM() { + return this.content.mitm?.hostname?.join(', '); + } +} diff --git a/packages/modkit/plugins/quantumultx/template.ejs b/packages/modkit/plugins/quantumultx/template.ejs new file mode 100644 index 0000000..b026b79 --- /dev/null +++ b/packages/modkit/plugins/quantumultx/template.ejs @@ -0,0 +1,21 @@ +<%= quantumultxTemplate.Metadata %> + +<% if (quantumultxTemplate.DNS) { %> +#[dns] +<%= quantumultxTemplate.DNS %> +<% } %> + +<% if (quantumultxTemplate.Filter) { %> +#[filter_local] +<%= quantumultxTemplate.Filter %> +<% } %> + +<% if (quantumultxTemplate.Rewrite) { %> +#[rewrite_local] +<%= quantumultxTemplate.Rewrite %> +<% } %> + +<% if (quantumultxTemplate.MITM) { %> +#[mitm] +<%= quantumultxTemplate.MITM %> +<% } %> diff --git a/packages/modkit/plugins/quantumultx/tsconfig.json b/packages/modkit/plugins/quantumultx/tsconfig.json new file mode 100644 index 0000000..236d32c --- /dev/null +++ b/packages/modkit/plugins/quantumultx/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@iringo/modkit-config/tsconfig", + "compilerOptions": { + "outDir": "./dist", + "baseUrl": "./" + }, + "include": ["src", "types"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9438876..80900c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -228,6 +228,25 @@ importers: specifier: ^5.6.2 version: 5.6.2 + packages/modkit/plugins/quantumultx: + dependencies: + '@iringo/modkit-shared': + specifier: workspace:^ + version: link:../../shared + devDependencies: + '@iringo/modkit-config': + specifier: workspace:^ + version: link:../../config + '@modern-js/module-tools': + specifier: ^2.60.2 + version: 2.60.2(typescript@5.6.2) + '@types/node': + specifier: ^20.0.0 + version: 20.16.10 + typescript: + specifier: ^5.6.2 + version: 5.6.2 + packages/modkit/plugins/surge: dependencies: '@iringo/modkit-shared':