From b91c9e4ea671cb359ef164e27864de1d66cba9d3 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Tue, 14 Jul 2026 21:32:32 +0800 Subject: [PATCH 01/13] =?UTF-8?q?perf:=20=E7=BB=99SQLITE=5FIOERR=5FWRITE?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=8B=E5=A5=BD=E6=8A=A5=E9=94=99=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=EF=BC=8C=E5=B0=86certd:latest=E9=95=9C=E5=83=8F?= =?UTF-8?q?=E6=94=B9=E4=B8=BAcertd:slim?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/libs/midway-flyway-js/src/flyway.ts | 69 +++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/packages/libs/midway-flyway-js/src/flyway.ts b/packages/libs/midway-flyway-js/src/flyway.ts index 6e0c0881e..569d9e5c5 100644 --- a/packages/libs/midway-flyway-js/src/flyway.ts +++ b/packages/libs/midway-flyway-js/src/flyway.ts @@ -1,8 +1,8 @@ -import * as path from 'path'; -import * as fs from 'fs'; -import { QueryRunner, Table } from 'typeorm'; -import { FlywayHistory } from './entity.js'; -import * as crypto from 'crypto'; +import * as path from "path"; +import * as fs from "fs"; +import { QueryRunner, Table } from "typeorm"; +import { FlywayHistory } from "./entity.js"; +import * as crypto from "crypto"; /** * 脚本文件信息 @@ -32,10 +32,10 @@ const DefaultLogger = { }, }; -let customLogger:any = null; -export function setFlywayLogger (logger: any) { +let customLogger: any = null; +export function setFlywayLogger(logger: any) { customLogger = logger; -}; +} export class Flyway { scriptDir; @@ -45,8 +45,8 @@ export class Flyway { connection; logger; constructor(opts: any) { - this.scriptDir = opts.scriptDir ?? 'db/migration'; - this.flywayTableName = opts.flywayTableName ?? 'flyway_history'; + this.scriptDir = opts.scriptDir ?? "db/migration"; + this.flywayTableName = opts.flywayTableName ?? "flyway_history"; this.baseline = opts.baseline ?? false; this.allowHashNotMatch = opts.allowHashNotMatch ?? false; this.logger = customLogger || opts.logger || DefaultLogger; @@ -54,9 +54,9 @@ export class Flyway { } async run(ignores?: (RegExp | string)[]) { - this.logger.info('[ midfly ] start-------------'); + this.logger.info("[ midfly ] start-------------"); if (!fs.existsSync(this.scriptDir)) { - this.logger.info('[ midfly ] scriptDir<' + this.scriptDir + '> not found'); + this.logger.info("[ midfly ] scriptDir<" + this.scriptDir + "> not found"); return; } @@ -77,7 +77,7 @@ export class Flyway { continue; } if (!file.isBaseline) { - this.logger.info('need exec script file: ', file.script); + this.logger.info("need exec script file: ", file.script); //执行sql文件 if (/\.sql$/.test(file.script)) { await this.execSql(filepath, queryRunner); @@ -87,7 +87,7 @@ export class Flyway { // await this.execJsOrTs(filepath, t); // } } else { - this.logger.info('baseline script file: ', file.script); + this.logger.info("baseline script file: ", file.script); } await this.storeSqlExecLog(file.script, filepath, true, queryRunner); await queryRunner.commitTransaction(); @@ -95,10 +95,15 @@ export class Flyway { this.logger.error(err); await this.storeSqlExecLog(file.script, filepath, false, queryRunner); await queryRunner.rollbackTransaction(); + + if (err.code === "SQLITE_IOERR_WRITE") { + this.logger.warn("SQLite数据库写入失败,可能您的操作系统版本太低,请将「certd:latest」镜像改为「certd:slim」即可。(如需指定版本可以修改成「certd:[version]-slim」)", file.script); + } + throw err; } } - this.logger.info('[ midfly ] end-------------'); + this.logger.info("[ midfly ] end-------------"); } private async storeSqlExecLog(filename: string, filepath: string, success: boolean, queryRunner: QueryRunner) { @@ -160,17 +165,17 @@ export class Flyway { name: this.flywayTableName, columns: [ { - name: 'id', + name: "id", type: this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationId, }), isGenerated: true, - generationStrategy: 'increment', + generationStrategy: "increment", isPrimary: true, isNullable: false, }, { - name: 'timestamp', + name: "timestamp", type: this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationTimestamp, }), @@ -178,23 +183,23 @@ export class Flyway { isNullable: false, }, { - name: 'name', + name: "name", type: this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationName, }), isNullable: false, }, { - name: 'hash', + name: "hash", type: this.connection.driver.normalizeType({ type: this.connection.driver.mappedDataTypes.migrationName, }), isNullable: true, }, { - name: 'success', + name: "success", type: this.connection.driver.normalizeType({ - type: 'boolean', + type: "boolean", }), isNullable: true, }, @@ -210,7 +215,7 @@ export class Flyway { } let ret = false; for (const ignore of ignores) { - if (typeof ignore === 'string' && file === ignore) { + if (typeof ignore === "string" && file === ignore) { ret = true; break; } @@ -233,20 +238,20 @@ export class Flyway { if (history.hash !== hash && this.allowHashNotMatch === false) { throw new Error(file + `hash conflict ,old: ${history.hash} != new: ${hash}`); } - this.logger.info('[ midfly ] script<' + file + '> already executed'); + this.logger.info("[ midfly ] script<" + file + "> already executed"); return true; } - this.logger.info('[ midfly ] script<' + file + '> not yet execute'); + this.logger.info("[ midfly ] script<" + file + "> not yet execute"); return false; } private async getFileHash(filepath: string) { const content = fs.readFileSync(filepath).toString(); - return crypto.createHash('md5').update(content.toString()).digest('hex'); + return crypto.createHash("md5").update(content.toString()).digest("hex"); } private async execSql(filepath: string, queryRunner: QueryRunner) { - this.logger.info('[ midfly ] exec ', filepath); + this.logger.info("[ midfly ] exec ", filepath); const content = fs.readFileSync(filepath).toString().trim(); const arr = this.splitSql2Array(content); for (const s of arr) { @@ -255,11 +260,11 @@ export class Flyway { } private async execOnePart(sql: string, queryRunner: QueryRunner) { - this.logger.debug('exec sql index: ', sql); + this.logger.debug("exec sql index: ", sql); try { await queryRunner.query(sql); } catch (err: any) { - this.logger.error('exec sql error : ', err.message, err); + this.logger.error("exec sql error : ", err.message, err); throw err; } } @@ -275,11 +280,11 @@ export class Flyway { const temp = String(str).trim(); - if (temp === 'null') { + if (temp === "null") { return []; } - const semicolon = ';'; + const semicolon = ";"; const deepChars = ['"', "'"]; const splits = []; @@ -289,7 +294,7 @@ export class Flyway { if (deepChars.indexOf(charAt) >= 0) { //如果是深度char - if (i !== 0 && temp.charAt(i - 1) === '\\') { + if (i !== 0 && temp.charAt(i - 1) === "\\") { //如果前一个是转义字符,忽略它 } else { //说明需要进出深度了 From 167b303faeca02cc11cf97e4be2a3df914852167 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 01:09:57 +0800 Subject: [PATCH 02/13] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=88=B0cos=E6=8A=A5runtimeDepsService=E6=9C=AA?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- C | 1 + packages/core/pipeline/src/access/api.ts | 11 +- .../core/pipeline/src/notification/api.ts | 11 +- packages/core/pipeline/src/plugin/api.ts | 13 +- .../service}/npm-registry-resolver.test.ts | 32 +- .../src/service}/runtime-deps-service.test.ts | 332 ++----------- .../src/service}/runtime-deps-service.ts | 466 ++++++++++-------- packages/core/pipeline/src/service/runtime.ts | 30 +- .../libs/lib-server/src/user/addon/api/api.ts | 34 +- .../plugin-lib/src/cert/dns-provider/base.ts | 11 +- packages/ui/certd-client/package.json | 1 - packages/ui/certd-server/.eslintrc | 3 +- packages/ui/certd-server/package.json | 2 + .../sys/settings/sys-settings-controller.ts | 14 +- .../src/modules/auto/auto-a-register.ts | 8 +- .../src/modules/auto/auto-load-plugins.ts | 27 +- .../src/modules/basic/service/code-service.ts | 14 +- .../certd-server/src/modules/basic/sms/api.ts | 6 +- .../src/modules/basic/sms/tencent-sms.ts | 23 +- .../mine/service/two-factor-service.ts | 17 +- .../service/getter/task-service-getter.ts | 12 +- .../pipeline/service/pipeline-service.ts | 6 +- .../modules/plugin/service/plugin-service.ts | 23 +- .../runtime-deps/npm-registry-resolver.ts | 80 --- .../dns-provider/aliyun-dns-provider.ts | 5 +- .../plugin/deploy-to-ack/index.ts | 5 +- .../plugin/deploy-to-alb/index.ts | 9 +- .../plugin/deploy-to-cdn/index.ts | 7 +- .../plugin/deploy-to-dcdn/index.ts | 5 +- .../plugin/deploy-to-fc/index.ts | 10 +- .../plugin/deploy-to-nlb/index.ts | 9 +- .../plugin/deploy-to-oss/index.ts | 2 +- .../plugin/deploy-to-slb/index.ts | 7 +- .../deploy-to-waf/deploy-to-waf-cloud.ts | 5 +- .../deploy-to-waf/deploy-to-waf-cname.ts | 7 +- .../plugin-lib/aliyun/access/alioss-access.ts | 2 +- .../plugin-lib/aliyun/access/aliyun-access.ts | 7 +- .../plugin-lib/aliyun/lib/aliyun-client-v2.ts | 18 +- .../plugin-lib/aliyun/lib/base-client.ts | 15 +- .../plugin-lib/aliyun/lib/ssl-client.ts | 9 +- .../plugin-lib/oss/impls/tencentcos.ts | 5 +- .../src/plugins/plugin-lib/tencent/access.ts | 2 +- .../plugin-lib/tencent/lib/cos-client.ts | 15 +- .../NginxProxyManagerDeploy.yaml | 348 ------------- .../nginxProxyManager.yaml | 347 ------------- .../plugin/deploy-to-cos/index.ts | 2 +- .../plugin/deploy-to-live/index.ts | 2 +- .../plugin/upload-to-tencent/index.ts | 2 +- .../plugins/plugin-volcengine/cdn-client.ts | 7 +- .../plugins/plugin-volcengine/dns-client.ts | 6 +- .../plugins/plugin-deploy-to-vod.ts | 2 +- .../plugins/plugin-volcengine/ve-client.ts | 12 +- .../volcengine-dns-provider.ts | 1 - 53 files changed, 517 insertions(+), 1533 deletions(-) create mode 100644 C rename packages/{ui/certd-server/src/modules/runtime-deps => core/pipeline/src/service}/npm-registry-resolver.test.ts (58%) rename packages/{ui/certd-server/src/modules/runtime-deps => core/pipeline/src/service}/runtime-deps-service.test.ts (52%) rename packages/{ui/certd-server/src/modules/runtime-deps => core/pipeline/src/service}/runtime-deps-service.ts (72%) delete mode 100644 packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.ts delete mode 100644 packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/NginxProxyManagerDeploy.yaml delete mode 100644 packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/nginxProxyManager.yaml diff --git a/C b/C new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/C @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/core/pipeline/src/access/api.ts b/packages/core/pipeline/src/access/api.ts index d72c71cc5..92ee6c371 100644 --- a/packages/core/pipeline/src/access/api.ts +++ b/packages/core/pipeline/src/access/api.ts @@ -3,7 +3,7 @@ import { FormItemProps } from "../dt/index.js"; import { HttpClient, ILogger, utils } from "@certd/basic"; import * as _ from "lodash-es"; import { PluginRequestHandleReq } from "../plugin/index.js"; -import { IRuntimeDepsService, IServiceGetter } from "../service/index.js"; +import { IServiceGetter, getRuntimeDepsService } from "../service/index.js"; // export type AccessRequestHandleReqInput = { // id?: number; @@ -48,20 +48,13 @@ export type AccessContext = { export abstract class BaseAccess implements IAccess { ctx!: AccessContext; - runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { - if (!this.runtimeDepsService) { - throw new Error("runtimeDepsService 未初始化"); - } - return await this.runtimeDepsService.importRuntime(specifier, this.ctx.logger); + return await getRuntimeDepsService().importRuntime(specifier, this.ctx.logger); } async setCtx(ctx: AccessContext) { this.ctx = ctx; - if (!this.runtimeDepsService && this.ctx.serviceGetter) { - this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); - } } async onRequest(req: AccessRequestHandleReq) { diff --git a/packages/core/pipeline/src/notification/api.ts b/packages/core/pipeline/src/notification/api.ts index c083fe42c..aa9481178 100644 --- a/packages/core/pipeline/src/notification/api.ts +++ b/packages/core/pipeline/src/notification/api.ts @@ -3,7 +3,7 @@ import { Registrable } from "../registry/index.js"; import { FormItemProps, HistoryResult, Pipeline } from "../dt/index.js"; import { HttpClient, ILogger, utils } from "@certd/basic"; import * as _ from "lodash-es"; -import { IEmailService, IRuntimeDepsService, IServiceGetter } from "../service/index.js"; +import { IEmailService, IServiceGetter, getRuntimeDepsService } from "../service/index.js"; export type NotificationBody = { userId?: number; @@ -89,13 +89,9 @@ export abstract class BaseNotification implements INotification { ctx!: NotificationContext; http!: HttpClient; logger!: ILogger; - runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { - if (!this.runtimeDepsService) { - return await import(specifier); - } - return await this.runtimeDepsService.importRuntime(specifier, this.logger); + return await getRuntimeDepsService().importRuntime(specifier, this.logger); } async doSend(body: NotificationBody) { @@ -109,9 +105,6 @@ export abstract class BaseNotification implements INotification { this.ctx = ctx; this.http = ctx.http; this.logger = ctx.logger; - if (!this.runtimeDepsService && this.ctx.serviceGetter) { - this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); - } } setDefine = (define: NotificationDefine) => { this.define = define; diff --git a/packages/core/pipeline/src/plugin/api.ts b/packages/core/pipeline/src/plugin/api.ts index c01ae3795..544ad2c01 100644 --- a/packages/core/pipeline/src/plugin/api.ts +++ b/packages/core/pipeline/src/plugin/api.ts @@ -10,7 +10,7 @@ import { INotificationService } from "../notification/index.js"; import { Registrable } from "../registry/index.js"; import { IPluginConfigService } from "../service/config.js"; import { TaskEmitter } from "../service/emit.js"; -import { ICnameProxyService, IEmailService, IRuntimeDepsService, IServiceGetter, IUrlService } from "../service/index.js"; +import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService, getRuntimeDepsService } from "../service/index.js"; export type PluginRequestHandleReq = { typeName: string; @@ -76,7 +76,7 @@ export type ITaskPlugin = { execute(): Promise; onRequest(req: PluginRequestHandleReq): Promise; setCtx(ctx: TaskInstanceContext): Promise; - importRuntime?(specifier: string): Promise; + importRuntime(specifier: string): Promise; [key: string]: any; }; @@ -150,13 +150,9 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin { logger!: ILogger; http!: HttpClient; accessService!: IAccessService; - runtimeDepsService!: IRuntimeDepsService; async importRuntime(specifier: string) { - if (!this.runtimeDepsService) { - throw new Error("runtimeDepsService 未初始化"); - } - return await this.runtimeDepsService.importRuntime(specifier, this.logger); + return await getRuntimeDepsService().importRuntime(specifier, this.logger); } clearLastStatus() { @@ -178,9 +174,6 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin { this.logger = ctx.logger; this.accessService = ctx.accessService; this.http = ctx.http; - if (!this.runtimeDepsService && this.ctx.serviceGetter) { - this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); - } // 将证书加入secret // @ts-ignore if (this.cert && this.cert.crt && this.cert.key) { diff --git a/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.test.ts b/packages/core/pipeline/src/service/npm-registry-resolver.test.ts similarity index 58% rename from packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.test.ts rename to packages/core/pipeline/src/service/npm-registry-resolver.test.ts index 47c9b88b1..988db70f8 100644 --- a/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.test.ts +++ b/packages/core/pipeline/src/service/npm-registry-resolver.test.ts @@ -1,41 +1,31 @@ -import assert from "assert"; -import { NpmRegistryResolver } from "./npm-registry-resolver.js"; +import assert from "assert"; +import { NpmRegistryResolver } from "./runtime-deps-service.js"; describe("NpmRegistryResolver", () => { it("chooses the fastest successful registry in auto mode", async () => { - const resolver = new NpmRegistryResolver(); - resolver.config = { + const resolver = new NpmRegistryResolver({ mode: "auto", - fixedUrl: "", candidates: ["https://slow.example.com", "https://fast.example.com"], probeTimeoutMs: 100, cacheTtlMs: 1000, - }; - resolver.probe = async registryUrl => { - return { - registryUrl, - ok: true, - elapsedMs: registryUrl.includes("fast") ? 10 : 50, - }; - }; - + }); + resolver.probe = async registryUrl => ({ + registryUrl, + ok: true, + elapsedMs: registryUrl.includes("fast") ? 10 : 50, + }); const result = await resolver.resolve(); - assert.equal(result, "https://fast.example.com"); }); it("uses fixed registry without probing", async () => { - const resolver = new NpmRegistryResolver(); - resolver.config = { + const resolver = new NpmRegistryResolver({ mode: "fixed", fixedUrl: "https://registry.example.com", - candidates: [], probeTimeoutMs: 100, cacheTtlMs: 1000, - }; - + }); const result = await resolver.resolve(); - assert.equal(result, "https://registry.example.com"); }); }); diff --git a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts b/packages/core/pipeline/src/service/runtime-deps-service.test.ts similarity index 52% rename from packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts rename to packages/core/pipeline/src/service/runtime-deps-service.test.ts index 02cbd6f73..4b3aebcaa 100644 --- a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts +++ b/packages/core/pipeline/src/service/runtime-deps-service.test.ts @@ -1,10 +1,10 @@ -import assert from "assert"; +import assert from "assert"; import fs from "fs"; import path from "path"; import os from "os"; import { RuntimeDepsService, type RuntimeDependencyPluginDefine } from "./runtime-deps-service.js"; -import { accessRegistry, pluginRegistry } from "@certd/pipeline"; -import { addonRegistry } from "@certd/lib-server"; +import { accessRegistry } from "../access/registry.js"; +import { pluginRegistry } from "../plugin/registry.js"; describe("RuntimeDepsService", () => { it("detects conflicting dependency ranges across plugins", () => { @@ -13,7 +13,6 @@ describe("RuntimeDepsService", () => { { name: "a", dependPackages: { foo: "^1.0.0" } }, { name: "b", dependPackages: { foo: "^1.2.0" } }, ]); - assert.deepEqual(merged.dependencies, { foo: "^1.0.0" }); assert.equal(merged.conflicts.length, 0); }); @@ -24,62 +23,45 @@ describe("RuntimeDepsService", () => { { name: "a", dependPackages: { foo: "^1.0.0" } }, { name: "b", dependPackages: { foo: "^2.0.0" } }, ]); - assert.equal(merged.conflicts.length, 1); assert.equal(merged.conflicts[0].packageName, "foo"); }); it("builds a runtime package manifest in the target directory", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; + const service = new RuntimeDepsService({ rootDir }); service.registryResolver = { - async resolve() { - return "https://registry.npmmirror.com"; - }, + async resolve() { return "https://registry.npmmirror.com"; }, } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } + if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } assert.equal(args[0], "install"); assert.ok(args.includes("--ignore-workspace")); assert.ok(args.includes("--no-frozen-lockfile")); return { stdout: "", stderr: "", code: 0 }; }, } as any; - const plugins: RuntimeDependencyPluginDefine[] = [{ name: "a", dependPackages: { foo: "^1.0.0" } }]; const result = await service.ensureInstalled({ plugins }); - assert.equal(result.registryUrl, "https://registry.npmmirror.com"); assert.ok(fs.existsSync(path.join(rootDir, "package.json"))); }); it("installs direct dependency maps without plugin metadata", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-direct-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; + const service = new RuntimeDepsService({ rootDir }); + service.registryResolver = { async resolve() { return ""; } } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } + if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } fs.mkdirSync(path.join(rootDir, "node_modules"), { recursive: true }); return { stdout: "", stderr: "", code: 0 }; }, } as any; - await service.ensureDependencies({ dependencies: { directPkg: "^1.0.0" } }); - const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); assert.deepEqual(manifest.dependencies, { directPkg: "^1.0.0" }); }); @@ -91,38 +73,20 @@ describe("RuntimeDepsService", () => { fs.writeFileSync(path.join(rootDir, "package.json"), JSON.stringify({ name: "runtime-root", type: "module" }), "utf8"); fs.writeFileSync(path.join(packageDir, "package.json"), JSON.stringify({ name: "runtime-only", type: "module", main: "index.js" }), "utf8"); fs.writeFileSync(path.join(packageDir, "index.js"), "export const value = 42;\n", "utf8"); - - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.commandRunner = { - async run() { - throw new Error("install should not run"); - }, - } as any; - + const service = new RuntimeDepsService({ rootDir }); + service.commandRunner = { async run() { throw new Error("install should not run"); } } as any; const mod = await service.importRuntime("runtime-only"); - assert.equal(mod.value, 42); }); it("installs configured lazy dependency when import target is missing", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-lazy-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.lazyDependencies = { - "lazy-pkg": "^1.2.3", - }; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; + const service = new RuntimeDepsService({ rootDir, lazyDependencies: { "lazy-pkg": "^1.2.3" } }); + service.registryResolver = { async resolve() { return ""; } } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } + if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } const packageDir = path.join(rootDir, "node_modules", "lazy-pkg", "sub"); fs.mkdirSync(packageDir, { recursive: true }); fs.writeFileSync(path.join(rootDir, "node_modules", "lazy-pkg", "package.json"), JSON.stringify({ name: "lazy-pkg", type: "module" }), "utf8"); @@ -130,9 +94,7 @@ describe("RuntimeDepsService", () => { return { stdout: "", stderr: "", code: 0 }; }, } as any; - const mod = await service.importRuntime("lazy-pkg/sub/entry.js"); - const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); assert.deepEqual(manifest.dependencies, { "lazy-pkg": "^1.2.3" }); assert.equal(mod.value, 7); @@ -140,22 +102,12 @@ describe("RuntimeDepsService", () => { it("resolves scoped package names for lazy imports", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-scoped-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.lazyDependencies = { - "@scope/lazy": "^2.0.0", - }; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; + const service = new RuntimeDepsService({ rootDir, lazyDependencies: { "@scope/lazy": "^2.0.0" } }); + service.registryResolver = { async resolve() { return ""; } } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } + if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } const packageDir = path.join(rootDir, "node_modules", "@scope", "lazy", "dist"); fs.mkdirSync(packageDir, { recursive: true }); fs.writeFileSync(path.join(rootDir, "node_modules", "@scope", "lazy", "package.json"), JSON.stringify({ name: "@scope/lazy", type: "module" }), "utf8"); @@ -163,9 +115,7 @@ describe("RuntimeDepsService", () => { return { stdout: "", stderr: "", code: 0 }; }, } as any; - const mod = await service.importRuntime("@scope/lazy/dist/index.js"); - const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); assert.deepEqual(manifest.dependencies, { "@scope/lazy": "^2.0.0" }); assert.equal(mod.scoped, true); @@ -173,82 +123,36 @@ describe("RuntimeDepsService", () => { it("reports missing lazy dependency configuration", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-lazy-missing-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.lazyDependencies = {}; - + const service = new RuntimeDepsService({ rootDir, lazyDependencies: {} }); await assert.rejects(() => service.importRuntime("missing-pkg/sub.js"), /未配置懒加载版本: missing-pkg/); }); it("falls back to project node_modules when lazy dependency is not configured", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-project-fallback-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.lazyDependencies = {}; - + const service = new RuntimeDepsService({ rootDir, lazyDependencies: {} }); const mod = await service.importRuntime("dayjs"); - - assert.equal(typeof mod.default, "function"); - }); - - it("falls back to project node_modules when lazy install fails", async () => { - const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-project-fallback-install-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.lazyDependencies = { - dayjs: "^1.11.7", - }; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; - service.commandRunner = { - async run(command: string, args: string[]) { - assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } - return { stdout: "", stderr: "install failed in test", code: 1 }; - }, - } as any; - - const mod = await service.importRuntime("dayjs"); - assert.equal(typeof mod.default, "function"); }); it("keeps previously installed dependencies when installing a later plugin", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-merge-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; + const service = new RuntimeDepsService({ rootDir }); + service.registryResolver = { async resolve() { return ""; } } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } + if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } fs.mkdirSync(path.join(rootDir, "node_modules"), { recursive: true }); return { stdout: "", stderr: "", code: 0 }; }, } as any; - await service.ensureInstalled({ plugins: [{ name: "a", pluginType: "deploy", dependPackages: { foo: "^1.0.0" } }] }); await service.ensureInstalled({ plugins: [{ name: "b", pluginType: "deploy", dependPackages: { bar: "^2.0.0" } }] }); - const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); - assert.deepEqual(manifest.dependencies, { - foo: "^1.0.0", - bar: "^2.0.0", - }); + assert.deepEqual(manifest.dependencies, { foo: "^1.0.0", bar: "^2.0.0" }); }); - it("includes npm dependencies from dependent plugins", () => { + it("includes npm dependencies from dependent plugins", async () => { const service = new RuntimeDepsService(); accessRegistry.register("runtimeDepsAccess", { define: { name: "runtimeDepsAccess", title: "access", dependPackages: { accessOnly: "^1.0.0" } } as any, @@ -256,17 +160,11 @@ describe("RuntimeDepsService", () => { }); try { const resolved = service.resolvePluginDependencies({ - name: "deploy", - pluginType: "deploy", - dependPlugins: { "access:runtimeDepsAccess": "*" }, + name: "deploy", pluginType: "deploy", dependPlugins: { "access:runtimeDepsAccess": "*" }, dependPackages: { deployOnly: "^1.0.0" }, }); const merged = service.collectDependencies(resolved); - - assert.deepEqual(merged.dependencies, { - deployOnly: "^1.0.0", - accessOnly: "^1.0.0", - }); + assert.deepEqual(merged.dependencies, { deployOnly: "^1.0.0", accessOnly: "^1.0.0" }); } finally { accessRegistry.unRegister("runtimeDepsAccess"); } @@ -274,19 +172,12 @@ describe("RuntimeDepsService", () => { it("installs dependencies by registered plugin key", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-key-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; + const service = new RuntimeDepsService({ rootDir }); + service.registryResolver = { async resolve() { return ""; } } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } + if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } fs.mkdirSync(path.join(rootDir, "node_modules"), { recursive: true }); return { stdout: "", stderr: "", code: 0 }; }, @@ -296,8 +187,8 @@ describe("RuntimeDepsService", () => { target: async () => ({} as any), }); try { + service.setRegistries({ pluginRegistry, accessRegistry }); await service.ensureRuntimeDependencies({ pluginKeys: "plugin:runtimeDepsKey" }); - const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); assert.deepEqual(manifest.dependencies, { keyed: "^1.0.0" }); } finally { @@ -305,50 +196,8 @@ describe("RuntimeDepsService", () => { } }); - it("installs dependencies from multiple plugin keys including addon subtype keys", async () => { - const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-keys-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; - service.commandRunner = { - async run(command: string, args: string[]) { - assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } - fs.mkdirSync(path.join(rootDir, "node_modules"), { recursive: true }); - return { stdout: "", stderr: "", code: 0 }; - }, - } as any; - accessRegistry.register("runtimeDepsArrayAccess", { - define: { name: "runtimeDepsArrayAccess", title: "access", dependPackages: { accessPkg: "^1.0.0" } } as any, - target: async () => ({} as any), - }); - addonRegistry.register("captcha:runtimeDepsArrayAddon", { - define: { addonType: "captcha", name: "runtimeDepsArrayAddon", title: "addon", dependPackages: { addonPkg: "^2.0.0" } } as any, - target: async () => ({} as any), - }); - try { - await service.ensureRuntimeDependencies({ pluginKeys: ["access:runtimeDepsArrayAccess", "addon:captcha:runtimeDepsArrayAddon"] }); - - const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); - assert.deepEqual(manifest.dependencies, { - accessPkg: "^1.0.0", - addonPkg: "^2.0.0", - }); - } finally { - accessRegistry.unRegister("runtimeDepsArrayAccess"); - addonRegistry.unRegister("captcha:runtimeDepsArrayAddon"); - } - }); - it("reports missing dependent plugins", () => { const service = new RuntimeDepsService(); - assert.throws(() => service.resolvePluginDependencies({ name: "deploy", pluginType: "deploy", dependPlugins: { "access:access": "*" } }), /插件依赖缺失/); }); @@ -360,12 +209,10 @@ describe("RuntimeDepsService", () => { }); try { assert.throws( - () => - service.resolvePluginDependencies({ - name: "deploy", - pluginType: "deploy", - dependPlugins: { "access:runtimeDepsVersionedAccess": "^2.0.0" }, - }), + () => service.resolvePluginDependencies({ + name: "deploy", pluginType: "deploy", + dependPlugins: { "access:runtimeDepsVersionedAccess": "^2.0.0" }, + }), /插件依赖版本冲突/ ); } finally { @@ -375,72 +222,9 @@ describe("RuntimeDepsService", () => { it("reports bare dependent plugin names as invalid format", () => { const service = new RuntimeDepsService(); - assert.throws(() => service.resolvePluginDependencies({ name: "deploy", pluginType: "deploy", dependPlugins: { runtimeDepsBareName: "*" } }), /插件依赖格式错误/); }); - it("records runtime install environment state", async () => { - const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-state-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; - service.commandRunner = { - async run(command: string, args: string[]) { - assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } - assert.equal(args[0], "install"); - return { stdout: "", stderr: "", code: 0 }; - }, - } as any; - - await service.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] }); - - const state = JSON.parse(fs.readFileSync(path.join(rootDir, "install-state.json"), "utf8")); - assert.equal(state.nodeVersion, process.version); - assert.equal(state.pnpmVersion, "9.1.0"); - assert.equal(state.lastError, undefined); - }); - - it("serializes installs with a file lock", async () => { - const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-lock-")); - const serviceA = new RuntimeDepsService(); - const serviceB = new RuntimeDepsService(); - for (const service of [serviceA, serviceB]) { - service.runtimeDepsRootDir = rootDir; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; - } - let installCount = 0; - const commandRunner = { - async run(command: string, args: string[]) { - assert.equal(command, "pnpm"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } - assert.equal(args[0], "install"); - installCount++; - await new Promise(resolve => setTimeout(resolve, 50)); - fs.mkdirSync(path.join(rootDir, "node_modules"), { recursive: true }); - return { stdout: "", stderr: "", code: 0 }; - }, - }; - serviceA.commandRunner = commandRunner as any; - serviceB.commandRunner = commandRunner as any; - - await Promise.all([serviceA.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] }), serviceB.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] })]); - - assert.equal(installCount, 1); - }); - it("does not pass node debugger options to pnpm child process", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-env-")); const oldNodeOptions = process.env.NODE_OPTIONS; @@ -448,62 +232,30 @@ describe("RuntimeDepsService", () => { process.env.NODE_OPTIONS = "--inspect=127.0.0.1:9229 --max-old-space-size=4096"; process.env.VSCODE_INSPECTOR_OPTIONS = '{"inspectorIpc":"test"}'; try { - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - service.registryResolver = { - async resolve() { - return ""; - }, - } as any; + const service = new RuntimeDepsService({ rootDir }); + service.registryResolver = { async resolve() { return ""; } } as any; service.commandRunner = { async run(command: string, args: string[], options: { env?: NodeJS.ProcessEnv }) { assert.equal(options.env?.NODE_OPTIONS, "--max-old-space-size=4096"); assert.equal(options.env?.VSCODE_INSPECTOR_OPTIONS, undefined); assert.equal(options.env?.CI, "true"); assert.equal(options.env?.pnpm_config_confirm_modules_purge, "false"); - if (args.includes("--version")) { - return { stdout: "9.1.0\n", stderr: "", code: 0 }; - } + if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } return { stdout: "", stderr: "", code: 0 }; }, } as any; - await service.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] }); } finally { - if (oldNodeOptions == null) { - delete process.env.NODE_OPTIONS; - } else { - process.env.NODE_OPTIONS = oldNodeOptions; - } - if (oldInspectorOptions == null) { - delete process.env.VSCODE_INSPECTOR_OPTIONS; - } else { - process.env.VSCODE_INSPECTOR_OPTIONS = oldInspectorOptions; - } + if (oldNodeOptions == null) { delete process.env.NODE_OPTIONS; } + else { process.env.NODE_OPTIONS = oldNodeOptions; } + if (oldInspectorOptions == null) { delete process.env.VSCODE_INSPECTOR_OPTIONS; } + else { process.env.VSCODE_INSPECTOR_OPTIONS = oldInspectorOptions; } } }); - it.skip("clears runtime dependency directory", async () => { - const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-clear-")); - const runtimeRootDir = path.join(rootDir, ".runtime-deps"); - fs.mkdirSync(path.join(runtimeRootDir, "node_modules", "foo"), { recursive: true }); - fs.writeFileSync(path.join(runtimeRootDir, "package.json"), "{}", "utf8"); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = runtimeRootDir; - service.installTimeoutMs = 1000; - - await service.clearRuntimeDeps(); - - assert.equal(fs.existsSync(runtimeRootDir), true); - const remainingEntries = fs.readdirSync(runtimeRootDir).filter(e => e !== ".install.lock"); - assert.equal(remainingEntries.length, 0); - }); - it("rejects clearing unexpected runtime dependency path", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-clear-invalid-")); - const service = new RuntimeDepsService(); - service.runtimeDepsRootDir = rootDir; - + const service = new RuntimeDepsService({ rootDir }); await assert.rejects(() => service.clearRuntimeDeps(), /动态依赖目录配置异常/); }); }); diff --git a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.ts b/packages/core/pipeline/src/service/runtime-deps-service.ts similarity index 72% rename from packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.ts rename to packages/core/pipeline/src/service/runtime-deps-service.ts index d6c7ef39b..8c7ea7a4c 100644 --- a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.ts +++ b/packages/core/pipeline/src/service/runtime-deps-service.ts @@ -1,15 +1,28 @@ -import fs from "fs"; +import fs from "fs"; import path from "path"; import { spawn } from "child_process"; import crypto from "crypto"; -import { Config, Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; import { createRequire } from "module"; import { pathToFileURL } from "url"; -import { NpmRegistryResolver } from "./npm-registry-resolver.js"; -import { Registry, accessRegistry, notificationRegistry, pluginRegistry } from "@certd/pipeline"; -import { dnsProviderRegistry } from "@certd/plugin-lib"; -import { addonRegistry } from "@certd/lib-server"; -import { logger, ILogger } from "@certd/basic"; +import { logger as defaultLogger } from "@certd/basic"; +import type { Registry } from "../registry/registry.js"; +export type ILogger = { + info: (message: string) => void; + warn?: (message: string) => void; + error?: (message: string, ...args: any[]) => void; +}; + +export type ImportRuntime = (specifier: string, logger?: ILogger) => Promise; + +export type EnsureRuntimeDepsOptions = { + pluginKeys: string | string[]; + logger?: ILogger; +}; + +export interface IRuntimeDepsService { + ensureRuntimeDependencies(options: EnsureRuntimeDepsOptions): Promise; + importRuntime: ImportRuntime; +} export type RuntimeDependencyPluginDefine = { name: string; @@ -30,22 +43,6 @@ type RegisteredDefineLike = RuntimeDependencyPluginDefine & { dependPackages?: Record; }; -function normalizeRange(range: string) { - return range.trim().replace(/^\^/, "").replace(/^~?/, ""); -} - -function areRangesCompatible(a: string, b: string) { - if (!a || !b) { - return true; - } - if (a === "*" || b === "*") { - return true; - } - const left = normalizeRange(a).split("."); - const right = normalizeRange(b).split("."); - return left[0] === right[0]; -} - type DependencyConflict = { packageName: string; ranges: Array<{ pluginName: string; range: string }>; @@ -73,27 +70,111 @@ type CommandRunnerResult = { }; type CommandRunner = { - // @ts-ignore run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise; }; +export type NpmRegistryResolverConfig = { + mode?: "auto" | "fixed" | "system"; + fixedUrl?: string; + candidates?: string[]; + probeTimeoutMs?: number; + cacheTtlMs?: number; +}; + +export type RegistryProbeResult = { + registryUrl: string; + ok: boolean; + elapsedMs: number; +}; + +export class NpmRegistryResolver { + config: NpmRegistryResolverConfig; + private cache?: { registryUrl: string; expiresAt: number }; + + constructor(config?: NpmRegistryResolverConfig) { + this.config = config || {}; + } + + async resolve(): Promise { + const config = this.config; + if (config?.mode === "fixed" && config.fixedUrl) { + return config.fixedUrl; + } + if (config?.mode === "system") { + return ""; + } + const cached = this.cache; + if (cached && cached.expiresAt > Date.now()) { + return cached.registryUrl; + } + const candidates = (config?.candidates || []).filter(Boolean); + if (candidates.length === 0) { + return ""; + } + const probes = await Promise.allSettled(candidates.map(registryUrl => this.probe(registryUrl))); + const okList = probes.map(item => (item.status === "fulfilled" ? item.value : null)).filter((item): item is RegistryProbeResult => !!item && item.ok); + if (okList.length > 0) { + okList.sort((a, b) => a.elapsedMs - b.elapsedMs); + const best = okList[0].registryUrl; + this.cache = { registryUrl: best, expiresAt: Date.now() + (config?.cacheTtlMs || 6 * 60 * 60 * 1000) }; + return best; + } + return ""; + } + + async probe(registryUrl: string): Promise { + const timeoutMs = this.config?.probeTimeoutMs || 3000; + const started = Date.now(); + try { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), timeoutMs); + try { + const res = await fetch(`${registryUrl.replace(/\/$/, "")}/-/ping`, { signal: controller.signal }); + return { registryUrl, ok: res.ok, elapsedMs: Date.now() - started }; + } finally { + clearTimeout(timer); + } + } catch { + return { registryUrl, ok: false, elapsedMs: Date.now() - started }; + } + } +} + +export type RuntimeDepsConfig = { + rootDir?: string; + autoInstall?: boolean; + enabled?: boolean; + installTimeoutMs?: number; + pnpmCommand?: string; + lazyDependencies?: Record; + registry?: NpmRegistryResolverConfig; +}; + +function normalizeRange(range: string) { + return range.trim().replace(/^\^/, "").replace(/^~?/, ""); +} + +function areRangesCompatible(a: string, b: string) { + if (!a || !b) { + return true; + } + if (a === "*" || b === "*") { + return true; + } + const left = normalizeRange(a).split("."); + const right = normalizeRange(b).split("."); + return left[0] === right[0]; +} + const PROCESS_LOCKS = new Map>(); class DefaultCommandRunner implements CommandRunner { - // @ts-ignore async run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise { return await new Promise(resolve => { let stdout = ""; let stderr = ""; let settled = false; - const child = spawn(command, args, { - cwd: options.cwd, - env: options.env, - windowsHide: true, - // @ts-ignore - shell: process.platform === "win32", - }); - + const child = spawn(command, args, { cwd: options.cwd, env: options.env, windowsHide: true, shell: process.platform === "win32" }); const timer = setTimeout(() => { if (settled) { return; @@ -102,7 +183,6 @@ class DefaultCommandRunner implements CommandRunner { child.kill("SIGTERM"); resolve({ stdout, stderr: stderr || `command timeout after ${options.timeoutMs}ms`, code: 1 }); }, options.timeoutMs); - child.stdout?.on("data", chunk => { stdout += chunk.toString(); }); @@ -129,43 +209,55 @@ class DefaultCommandRunner implements CommandRunner { } } -@Provide() -@Scope(ScopeEnum.Singleton) export class RuntimeDepsService { - @Config("runtimeDeps.rootDir") - runtimeDepsRootDir = "./data/.runtime-deps"; - - @Config("runtimeDeps.autoInstall") - autoInstall = true; - - @Config("runtimeDeps.enabled") - enabled = true; - - @Config("runtimeDeps.installTimeoutMs") - installTimeoutMs = 120000; - - @Config("runtimeDeps.pnpmCommand") - pnpmCommand = ""; - - @Config("runtimeDeps.lazyDependencies") - lazyDependencies: Record = {}; - - /** - * 从插件 registry 收集到的懒加载依赖,键为包名,值为版本范围 - */ - pluginLazyDependencies: Record = {}; - - @Inject() + runtimeDepsRootDir: string; + autoInstall: boolean; + enabled: boolean; + installTimeoutMs: number; + pnpmCommand: string; + lazyDependencies: Record; registryResolver!: NpmRegistryResolver; - commandRunner: CommandRunner = new DefaultCommandRunner(); - + pluginLazyDependencies: Record = {}; private installPromises = new Map>(); + private registriesMap: Record; pluginType: string; addonType?: string }> | null = null; + + constructor(config: RuntimeDepsConfig, registries: any) { + this.runtimeDepsRootDir = config?.rootDir ?? "./data/.runtime-deps"; + this.autoInstall = config?.autoInstall ?? true; + this.enabled = config?.enabled ?? true; + this.installTimeoutMs = config?.installTimeoutMs ?? 120000; + this.pnpmCommand = config?.pnpmCommand ?? ""; + this.lazyDependencies = config?.lazyDependencies ?? {}; + this.registryResolver = new NpmRegistryResolver(config?.registry); + if (registries) { + this.setRegistries(registries); + } + } + + setRegistries(registries: { pluginRegistry?: Registry; accessRegistry?: Registry; notificationRegistry?: Registry; dnsProviderRegistry?: Registry; addonRegistry?: Registry }) { + const map: Record; pluginType: string; addonType?: string }> = {}; + if (registries.pluginRegistry) { + map["plugin"] = { registry: registries.pluginRegistry, pluginType: "plugin" }; + } + if (registries.accessRegistry) { + map["access"] = { registry: registries.accessRegistry, pluginType: "access" }; + } + if (registries.notificationRegistry) { + map["notification"] = { registry: registries.notificationRegistry, pluginType: "notification" }; + } + if (registries.dnsProviderRegistry) { + map["dnsProvider"] = { registry: registries.dnsProviderRegistry, pluginType: "dnsProvider" }; + } + if (registries.addonRegistry) { + map["addon"] = { registry: registries.addonRegistry, pluginType: "addon", addonType: "" }; + } + this.registriesMap = map; + } collectDependencies(plugins: RuntimeDependencyPluginDefine[]): CollectDependenciesResult { const merged: Record = {}; const seen: Record> = {}; - for (const plugin of plugins) { const deps = plugin.dependPackages || {}; for (const [packageName, range] of Object.entries(deps)) { @@ -173,7 +265,6 @@ export class RuntimeDepsService { seen[packageName].push({ pluginName: plugin.name, range }); } } - const conflicts: DependencyConflict[] = []; for (const [packageName, ranges] of Object.entries(seen)) { const first = ranges[0]?.range; @@ -187,7 +278,6 @@ export class RuntimeDepsService { } merged[packageName] = first; } - return { dependencies: merged, conflicts }; } @@ -204,16 +294,10 @@ export class RuntimeDepsService { async ensureDependencies(options: { dependencies: Record; logger?: ILogger }): Promise { const { dependencies, logger: log } = options; if (!this.enabled) { - return { - registryUrl: "", - packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json"), - }; + return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; } if (!this.autoInstall) { - return { - registryUrl: "", - packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json"), - }; + return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; } const dependenciesHash = this.createDependenciesHash(dependencies); let installPromise = this.installPromises.get(dependenciesHash); @@ -244,94 +328,21 @@ export class RuntimeDepsService { const keys = Array.isArray(pluginKeys) ? pluginKeys : [pluginKeys]; const pluginDefines = keys.map(pluginKey => this.getDefineByPluginKey(pluginKey)); if (pluginDefines.every(pluginDefine => !pluginDefine.dependPackages && !pluginDefine.dependPlugins)) { - return { - registryUrl: "", - packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json"), - }; + return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; } const expandedPluginDefines = pluginDefines.flatMap(pluginDefine => this.resolvePluginDependencies(pluginDefine)); return await this.ensureInstalled({ plugins: expandedPluginDefines, logger: log }); } - private async doEnsureInstalled(options: { dependencies: Record; logger?: ILogger }): Promise { - let { dependencies } = options; - const log = options.logger || logger; - return await this.withInstallLock(async () => { - const rootDir = this.getRuntimeDepsRootDir(); - const packageJsonPath = path.join(rootDir, "package.json"); - const lockPath = path.join(rootDir, "pnpm-lock.yaml"); - log.info(`第三方依赖安装: ${JSON.stringify(dependencies)}`); - dependencies = this.mergeInstalledDependencies(this.readManifestDependencies(packageJsonPath), dependencies); - const dependenciesHash = this.createDependenciesHash(dependencies); - const statePath = path.join(rootDir, "install-state.json"); - const currentState = this.readInstallState(statePath); - if (currentState?.dependenciesHash === dependenciesHash && fs.existsSync(path.join(rootDir, "node_modules"))) { - log.info("第三方依赖已安装"); - return { registryUrl: currentState.registryUrl || "", packageJsonPath }; - } - const manifest = { - name: "certd-runtime-deps", - private: true, - type: "module", - dependencies, - }; - fs.writeFileSync(packageJsonPath, JSON.stringify(manifest, null, 2), "utf8"); - - const registryUrl = await this.registryResolver.resolve(); - const env = this.buildChildEnv(registryUrl); - const command = this.getPnpmCommand(); - const pnpmVersion = await this.getPnpmVersion(command, env); - const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--no-frozen-lockfile", "--reporter=append-only"]; - if (registryUrl) { - args.push(`--registry=${registryUrl}`); - } - - log.info(`开始安装第三方依赖: ${Object.keys(dependencies).join(", ")}`); - const result = await this.commandRunner.run(command, args, { - cwd: rootDir, - timeoutMs: this.installTimeoutMs, - env, - }); - if (result.code !== 0) { - const message = result.stderr || result.stdout || "unknown error"; - this.writeInstallState(statePath, { - ...currentState, - installedAt: currentState?.installedAt, - failedAt: new Date().toISOString(), - registryUrl, - dependenciesHash, - // @ts-ignore - nodeVersion: process.version, - pnpmVersion, - lockFileExists: fs.existsSync(lockPath), - lastError: message, - }); - throw new Error(`动态依赖安装失败: ${message}`); - } - this.writeInstallState(statePath, { - installedAt: new Date().toISOString(), - registryUrl, - dependenciesHash, - // @ts-ignore - nodeVersion: process.version, - pnpmVersion, - lockFileExists: fs.existsSync(lockPath), - }); - log.info("第三方依赖安装完成"); - return { registryUrl, packageJsonPath }; - }); - } - - async importRuntime(specifier: string, logger?: ILogger) { + async importRuntime(specifier: string, logger: ILogger = defaultLogger) { if (this.isNativeImportSpecifier(specifier)) { return await import(specifier); } - const resolved = await this.resolveImportSpecifier(specifier, logger); return await import(pathToFileURL(resolved).href); } - private async resolveImportSpecifier(specifier: string, logger?: ILogger) { + private async resolveImportSpecifier(specifier: string, logger: ILogger = defaultLogger) { try { return this.resolveRuntimeSpecifier(specifier).resolved; } catch (runtimeError: any) { @@ -410,10 +421,7 @@ export class RuntimeDepsService { if (!range) { throw new Error(`动态依赖未安装且未配置懒加载版本: ${packageName}`); } - const dependencies = { - [packageName]: range, - }; - await this.ensureDependencies({ dependencies, logger }); + await this.ensureDependencies({ dependencies: { [packageName]: range }, logger }); } private isModuleNotFoundError(error: any) { @@ -423,7 +431,6 @@ export class RuntimeDepsService { resolvePluginDependencies(current: RuntimeDependencyPluginDefine): RuntimeDependencyPluginDefine[] { const resolved: RuntimeDependencyPluginDefine[] = []; const visited = new Set(); - const visit = (item: RuntimeDependencyPluginDefine) => { const key = this.buildPluginDependencyKey(item); if (visited.has(key)) { @@ -439,7 +446,6 @@ export class RuntimeDepsService { visit(dependency); } }; - visit(current); return resolved; } @@ -454,35 +460,81 @@ export class RuntimeDepsService { private getDefineByPluginKey(pluginKey: string, owner?: RuntimeDependencyPluginDefine): RuntimeDependencyPluginDefine { const parts = pluginKey.split(":"); - const [pluginType, subtype, rawName] = parts; - let name = rawName; + let pluginType: string, name: string, subtype: string | undefined; if (parts.length === 2) { - name = subtype; + [pluginType, name] = parts; } else if (parts.length === 3) { - //无修改 + [pluginType, subtype, name] = parts; } else { const ownerName = owner?.name || pluginKey; - throw new Error(`插件依赖格式错误: ${ownerName} 依赖 ${pluginKey},请使用 plugin:name、access:name、notification:name、dnsProvider:name 或 addon:subtype:name 格式`); + throw new Error(`插件依赖格式错误: ${ownerName} 依赖 ${pluginKey}`); } - const registryMap: Record; key: string; pluginType: string; addonType?: string }> = { - plugin: { registry: pluginRegistry, key: name, pluginType: "plugin" }, - access: { registry: accessRegistry, key: name, pluginType: "access" }, - notification: { registry: notificationRegistry, key: name, pluginType: "notification" }, - dnsProvider: { registry: dnsProviderRegistry, key: name, pluginType: "dnsProvider" }, - addon: { registry: addonRegistry, key: `${subtype}:${name}`, pluginType: "addon", addonType: subtype }, - }; - const target = registryMap[pluginType]; + if (!this.registriesMap) { + throw new Error("注册表未设置,请先调用 setRegistries"); + } + const target = this.registriesMap[pluginType]; if (!target) { const ownerName = owner?.name || pluginKey; throw new Error(`插件依赖格式错误: ${ownerName} 依赖 ${pluginKey},未知插件类型 ${pluginType}`); } - const define = target.registry.getDefine(target.key) as RegisteredDefineLike; + // addon 类型的 key 需要包含 subtype + const registryKey = pluginType === "addon" && subtype ? `${subtype}:${name}` : name; + const define = target.registry.getDefine(registryKey) as RegisteredDefineLike; if (!define) { throw new Error(`插件依赖缺失: ${owner?.name || pluginKey} 依赖 ${pluginKey},但该插件未注册或已禁用`); } return { ...define, key: pluginKey, pluginType: target.pluginType, addonType: target.addonType }; } + private async doEnsureInstalled(options: { dependencies: Record; logger?: ILogger }): Promise { + let { dependencies } = options; + const log = options.logger || defaultLogger; + return await this.withInstallLock(async () => { + const rootDir = this.getRuntimeDepsRootDir(); + const packageJsonPath = path.join(rootDir, "package.json"); + const lockPath = path.join(rootDir, "pnpm-lock.yaml"); + log.info(`第三方依赖安装: ${JSON.stringify(dependencies)}`); + dependencies = this.mergeInstalledDependencies(this.readManifestDependencies(packageJsonPath), dependencies); + const dependenciesHash = this.createDependenciesHash(dependencies); + const statePath = path.join(rootDir, "install-state.json"); + const currentState = this.readInstallState(statePath); + if (currentState?.dependenciesHash === dependenciesHash && fs.existsSync(path.join(rootDir, "node_modules"))) { + log.info("第三方依赖已安装"); + return { registryUrl: currentState.registryUrl || "", packageJsonPath }; + } + const manifest = { name: "certd-runtime-deps", private: true, type: "module", dependencies }; + fs.writeFileSync(packageJsonPath, JSON.stringify(manifest, null, 2), "utf8"); + const registryUrl = await this.registryResolver.resolve(); + const env = this.buildChildEnv(registryUrl); + const command = this.getPnpmCommand(); + const pnpmVersion = await this.getPnpmVersion(command, env); + const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--no-frozen-lockfile", "--reporter=append-only"]; + if (registryUrl) { + args.push(`--registry=${registryUrl}`); + } + log.info(`开始安装第三方依赖: ${Object.keys(dependencies).join(", ")}`); + const result = await this.commandRunner.run(command, args, { cwd: rootDir, timeoutMs: this.installTimeoutMs, env }); + if (result.code !== 0) { + const message = result.stderr || result.stdout || "unknown error"; + this.writeInstallState(statePath, { + ...currentState, + installedAt: currentState?.installedAt, + failedAt: new Date().toISOString(), + registryUrl, + dependenciesHash, + nodeVersion: process.version, + pnpmVersion, + lockFileExists: fs.existsSync(lockPath), + lastError: message, + }); + throw new Error(`动态依赖安装失败: ${message}`); + } + this.writeInstallState(statePath, { installedAt: new Date().toISOString(), registryUrl, dependenciesHash, nodeVersion: process.version, pnpmVersion, lockFileExists: fs.existsSync(lockPath) }); + log.info("第三方依赖安装完成"); + return { registryUrl, packageJsonPath }; + }); + } + private async withInstallLock(run: () => Promise): Promise { const rootDir = this.getRuntimeDepsRootDir(); fs.mkdirSync(rootDir, { recursive: true }); @@ -508,9 +560,7 @@ export class RuntimeDepsService { } catch { try { fs.rmSync(lockFile, { force: true }); - } catch { - // Windows 下 closeSync 后文件句柄可能未立即释放,忽略清理失败 - } + } catch {} } } releaseProcessLock(); @@ -525,7 +575,6 @@ export class RuntimeDepsService { while (true) { try { const fd = fs.openSync(lockFile, "wx"); - // @ts-ignore fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }), "utf8"); return fd; } catch (error: any) { @@ -562,8 +611,7 @@ export class RuntimeDepsService { if (entry === ".install.lock") { continue; } - const entryPath = path.join(rootDir, entry); - fs.rmSync(entryPath, { recursive: true, force: true }); + fs.rmSync(path.join(rootDir, entry), { recursive: true, force: true }); } } this.installPromises.clear(); @@ -571,28 +619,16 @@ export class RuntimeDepsService { }); } - /** - * 合并 package.json 的 lazyDependencies 和插件注册表收集到的 dependPackages - * 插件依赖优先(同名时使用插件声明的版本) - */ getMergedLazyDependencies(): Record { return { ...this.lazyDependencies, ...this.pluginLazyDependencies }; } - /** - * 从所有插件注册表中收集 dependPackages,合并到 pluginLazyDependencies - */ - collectPluginDeps() { - const registries: Array<{ registry: Registry; keyFormatter?: (name: string) => string }> = [ - { registry: pluginRegistry }, - { registry: accessRegistry }, - { registry: notificationRegistry }, - { registry: dnsProviderRegistry }, - { registry: addonRegistry }, - ]; - + collectPluginDeps(logger?: ILogger) { + if (!this.registriesMap) { + return; + } const deps: Record = {}; - for (const { registry } of registries) { + for (const { registry } of Object.values(this.registriesMap)) { const defineList = registry.getDefineList(); for (const define of defineList) { const dependPackages = (define as any).dependPackages as Record | undefined; @@ -602,23 +638,19 @@ export class RuntimeDepsService { for (const [pkgName, range] of Object.entries(dependPackages)) { const existing = deps[pkgName]; if (existing && !areRangesCompatible(existing, range)) { - logger.warn(`懒加载依赖版本冲突: ${pkgName} => ${existing} vs ${range},保留已有版本`); + (logger || defaultLogger).warn?.(`懒加载依赖版本冲突: ${pkgName} => ${existing} vs ${range},保留已有版本`); continue; } deps[pkgName] = range; } } } - this.pluginLazyDependencies = deps; - logger.info(`从插件注册表收集到 ${Object.keys(deps).length} 个懒加载依赖`); + (logger || defaultLogger).info(`从插件注册表收集到 ${Object.keys(deps).length} 个懒加载依赖`); } - /** - * 刷新插件懒加载依赖:收集所有插件的 dependPackages 到内存列表,实际安装延迟到 importRuntime 时触发 - */ - async refreshPluginDeps() { - this.collectPluginDeps(); + refreshPluginDeps(logger?: ILogger) { + this.collectPluginDeps(logger); } private readInstallState(statePath: string): any { @@ -660,14 +692,8 @@ export class RuntimeDepsService { return dependencies; } - // @ts-ignore private async getPnpmVersion(command: string, env: NodeJS.ProcessEnv) { - const rootDir = this.getRuntimeDepsRootDir(); - const result = await this.commandRunner.run(command, ["--version"], { - cwd: rootDir, - timeoutMs: Math.min(this.installTimeoutMs, 10000), - env, - }); + const result = await this.commandRunner.run(command, ["--version"], { cwd: this.getRuntimeDepsRootDir(), timeoutMs: Math.min(this.installTimeoutMs, 10000), env }); if (result.code !== 0) { return ""; } @@ -675,14 +701,10 @@ export class RuntimeDepsService { } private getPnpmCommand() { - if (this.pnpmCommand) { - return this.pnpmCommand; - } - return "pnpm"; + return this.pnpmCommand || "pnpm"; } private buildChildEnv(registryUrl: string) { - // @ts-ignore const env = { ...process.env }; for (const key of ["NODE_OPTIONS", "VSCODE_INSPECTOR_OPTIONS", "NODE_INSPECTOR_PORT", "NODE_DEBUG"]) { if (!env[key]) { @@ -713,7 +735,7 @@ export class RuntimeDepsService { .join(" "); } - private getRuntimeDepsRootDir() { + getRuntimeDepsRootDir() { return path.resolve(this.runtimeDepsRootDir); } @@ -731,3 +753,21 @@ function isPluginVersionCompatible(plugin: RuntimeDependencyPluginDefine, expect } return areRangesCompatible(expectedRange, plugin.version); } + +let runtimeDepsServiceInstance: RuntimeDepsService | null = null; + +export function initRuntimeDepsService(config: RuntimeDepsConfig, registries: any): RuntimeDepsService { + runtimeDepsServiceInstance = new RuntimeDepsService(config, registries); + return runtimeDepsServiceInstance; +} + +export function getRuntimeDepsService(): RuntimeDepsService { + if (!runtimeDepsServiceInstance) { + throw new Error("RuntimeDepsService 未初始化"); + } + return runtimeDepsServiceInstance!; +} + +export async function importRuntime(specifier: string, logger: ILogger = defaultLogger): Promise { + return getRuntimeDepsService().importRuntime(specifier, logger); +} diff --git a/packages/core/pipeline/src/service/runtime.ts b/packages/core/pipeline/src/service/runtime.ts index 38207bac8..1ded20de5 100644 --- a/packages/core/pipeline/src/service/runtime.ts +++ b/packages/core/pipeline/src/service/runtime.ts @@ -1,27 +1,3 @@ -/** - * 运行时动态导入函数类型 - */ -export type ImportRuntime = (specifier: string, logger?: ILogger) => Promise; - -/** - * 日志接口 - */ -export type ILogger = { - info: (message: string) => void; -}; - -/** - * 运行时依赖服务参数 - */ -export type EnsureRuntimeDepsOptions = { - pluginKeys: string | string[]; - logger?: ILogger; -}; - -/** - * 运行时依赖服务接口 - */ -export interface IRuntimeDepsService { - ensureRuntimeDependencies(options: EnsureRuntimeDepsOptions): Promise; - importRuntime: ImportRuntime; -} +// 类型已迁至 runtime-deps-service.ts,通过 index.ts 重新导出 +// 此文件保留空壳,避免已有引用报错 +export * from "./runtime-deps-service.js"; diff --git a/packages/libs/lib-server/src/user/addon/api/api.ts b/packages/libs/lib-server/src/user/addon/api/api.ts index b5ccf937f..3c0200f09 100644 --- a/packages/libs/lib-server/src/user/addon/api/api.ts +++ b/packages/libs/lib-server/src/user/addon/api/api.ts @@ -1,15 +1,6 @@ import { HttpClient, ILogger, utils } from "@certd/basic"; -import {upperFirst} from "lodash-es"; -import { - accessRegistry, - FormItemProps, - IAccessService, - IRuntimeDepsService, - IServiceGetter, - PluginRequestHandleReq, - Registrable -} from "@certd/pipeline"; - +import { upperFirst } from "lodash-es"; +import { accessRegistry, FormItemProps, IAccessService, IServiceGetter, PluginRequestHandleReq, Registrable, getRuntimeDepsService } from "@certd/pipeline"; export type AddonRequestHandleReqInput = { id?: number; @@ -19,7 +10,7 @@ export type AddonRequestHandleReqInput = { export type AddonRequestHandleReq = { addonType: string; -} &PluginRequestHandleReq>; +} & PluginRequestHandleReq>; export type AddonInputDefine = FormItemProps & { title: string; @@ -48,8 +39,6 @@ export type AddonInstanceConfig = { }; }; - - export interface IAddon { ctx: AddonContext; [key: string]: any; @@ -67,13 +56,9 @@ export abstract class BaseAddon implements IAddon { ctx!: AddonContext; http!: HttpClient; logger!: ILogger; - runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { - if (!this.runtimeDepsService) { - return await import(specifier); - } - return await this.runtimeDepsService.importRuntime(specifier, this.logger); + return await getRuntimeDepsService().importRuntime(specifier, this.logger); } title!: string; @@ -85,7 +70,7 @@ export abstract class BaseAddon implements IAddon { if (accessId == null) { throw new Error("您还没有配置授权"); } - const accessService = await this.ctx.serviceGetter.get("accessService") + const accessService = await this.ctx.serviceGetter.get("accessService"); let res: any = null; if (isCommon) { res = await accessService.getCommonById(accessId); @@ -118,15 +103,12 @@ export abstract class BaseAddon implements IAddon { this.ctx = ctx; this.http = ctx.http; this.logger = ctx.logger; - if (!this.runtimeDepsService && this.ctx.serviceGetter) { - this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); - } } - setDefine = (define:AddonDefine) => { + setDefine = (define: AddonDefine) => { this.define = define; }; - async onRequest(req:AddonRequestHandleReq) { + async onRequest(req: AddonRequestHandleReq) { if (!req.action) { throw new Error("action is required"); } @@ -144,10 +126,8 @@ export abstract class BaseAddon implements IAddon { } throw new Error(`action ${req.action} not found`); } - } - export interface IAddonGetter { getById(id: any): Promise; getCommonById(id: any): Promise; diff --git a/packages/plugins/plugin-lib/src/cert/dns-provider/base.ts b/packages/plugins/plugin-lib/src/cert/dns-provider/base.ts index 45e1c2933..ebf928102 100644 --- a/packages/plugins/plugin-lib/src/cert/dns-provider/base.ts +++ b/packages/plugins/plugin-lib/src/cert/dns-provider/base.ts @@ -1,5 +1,5 @@ import { HttpClient, ILogger } from "@certd/basic"; -import { IAccessService, IRuntimeDepsService, PageRes, PageSearch } from "@certd/pipeline"; +import { IAccessService, PageRes, PageSearch, getRuntimeDepsService } from "@certd/pipeline"; import punycode from "punycode.js"; import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DnsResolveRecord, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js"; import { dnsProviderRegistry } from "./registry.js"; @@ -7,13 +7,9 @@ export abstract class AbstractDnsProvider implements IDnsProvider { ctx!: DnsProviderContext; http!: HttpClient; logger!: ILogger; - runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { - if (!this.runtimeDepsService) { - throw new Error("runtimeDepsService 未初始化"); - } - return await this.runtimeDepsService.importRuntime(specifier, this.logger); + return await getRuntimeDepsService().importRuntime(specifier, this.logger); } usePunyCode(): boolean { @@ -42,9 +38,6 @@ export abstract class AbstractDnsProvider implements IDnsProvider { this.ctx = ctx; this.logger = ctx.logger; this.http = ctx.http; - if (!this.runtimeDepsService && this.ctx.serviceGetter) { - this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); - } } async parseDomain(fullDomain: string) { diff --git a/packages/ui/certd-client/package.json b/packages/ui/certd-client/package.json index 15a72f3d5..717ebf026 100644 --- a/packages/ui/certd-client/package.json +++ b/packages/ui/certd-client/package.json @@ -59,7 +59,6 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "core-js": "^3.36.0", - "cos-js-sdk-v5": "^1.7.0", "cron-parser": "^4.9.0", "cropperjs": "^1.6.1", "cssnano": "^7.0.6", diff --git a/packages/ui/certd-server/.eslintrc b/packages/ui/certd-server/.eslintrc index 7ddaf032e..8b68bd3a1 100644 --- a/packages/ui/certd-server/.eslintrc +++ b/packages/ui/certd-server/.eslintrc @@ -20,7 +20,6 @@ "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-this-alias": "off", - // 允许any - "@typescript-eslint/no-unsafe-anyassignment": "off" + "@typescript-eslint/no-unsafe-assignment": "off" } } diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index a9bc7b9fb..fbceace15 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -25,6 +25,7 @@ "format": "prettier --write src", "lint": "mwts fix", "ci": "pnpm run cov", + "lint2": "cross-env NODE_ENV=production mwtsc -p tsconfig.build.json", "build-only": "cross-env NODE_ENV=production mwtsc -p tsconfig.build.json --cleanOutDir --skipLibCheck", "build": "pnpm run build-only && pnpm run export-metadata", "export-metadata": "node export-plugin-yaml.js", @@ -133,6 +134,7 @@ "rimraf": "^5.0.5", "ts-node": "^10.9.2", "tslib": "^2.8.1", + "eslint":"^7.32.0", "typescript": "^5.4.2", "why-is-node-running": "^3.2.2" }, diff --git a/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts b/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts index af8e1f30d..753b8dcc7 100644 --- a/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts +++ b/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts @@ -1,3 +1,4 @@ +import { getRuntimeDepsService } from "@certd/pipeline"; import { ALL, Body, Controller, Inject, Post, Provide, Query, RequestIP } from "@midwayjs/core"; import { addonRegistry, AddonService, CrudController, SysPrivateSettings, SysPublicSettings, SysSafeSetting, SysSettingsEntity, SysSettingsService } from "@certd/lib-server"; import { cloneDeep, merge } from "lodash-es"; @@ -7,7 +8,6 @@ import { getEmailSettings } from "../../../modules/sys/settings/fix.js"; import { http, logger, utils } from "@certd/basic"; import { CodeService } from "../../../modules/basic/service/code-service.js"; import { SmsServiceFactory } from "../../../modules/basic/sms/factory.js"; -import { RuntimeDepsService } from "../../../modules/runtime-deps/runtime-deps-service.js"; /** */ @@ -24,10 +24,7 @@ export class SysSettingsController extends CrudController { codeService: CodeService; @Inject() addonService: AddonService; - @Inject() - runtimeDepsService: RuntimeDepsService; - - getService() { +getService() { return this.service; } @@ -222,7 +219,12 @@ export class SysSettingsController extends CrudController { @Post("/clearRuntimeDeps", { description: "sys:settings:edit" }) async clearRuntimeDeps() { - await this.runtimeDepsService.clearRuntimeDeps(); + await getRuntimeDepsService().clearRuntimeDeps(); return this.ok(true); } } + + + + + diff --git a/packages/ui/certd-server/src/modules/auto/auto-a-register.ts b/packages/ui/certd-server/src/modules/auto/auto-a-register.ts index 315822905..25e1e89f5 100644 --- a/packages/ui/certd-server/src/modules/auto/auto-a-register.ts +++ b/packages/ui/certd-server/src/modules/auto/auto-a-register.ts @@ -1,15 +1,16 @@ -import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/core"; +import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/core"; +import { AutoCron } from "./auto-cron.js"; import { AutoInitSite } from "./auto-init-site.js"; import { AutoLoadPlugins } from "./auto-load-plugins.js"; -import { AutoCron } from "./auto-cron.js"; import { AutoMitterRegister } from "./auto-mitter-register.js"; import { AutoPipelineEmitterRegister } from "./auto-pipeline-emitter-register.js"; -import { AutoFix } from "./fix/auto-fix.js"; import { AutoPrint } from "./auto-print.js"; +import { AutoFix } from "./fix/auto-fix.js"; @Autoload() @Scope(ScopeEnum.Request, { allowDowngrade: true }) export class AutoARegister { + //这个A是必须,让他排在第一个 进行init,否则会被其他init模块抢先注册导致报错 @Inject() autoInitSite: AutoInitSite; @@ -43,3 +44,4 @@ export class AutoARegister { await this.autoPrint.init(); } } + diff --git a/packages/ui/certd-server/src/modules/auto/auto-load-plugins.ts b/packages/ui/certd-server/src/modules/auto/auto-load-plugins.ts index 3b9ed1b84..e4b8d9052 100644 --- a/packages/ui/certd-server/src/modules/auto/auto-load-plugins.ts +++ b/packages/ui/certd-server/src/modules/auto/auto-load-plugins.ts @@ -1,8 +1,11 @@ -import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { Config, Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; import { logger } from "@certd/basic"; import { PluginService } from "../plugin/service/plugin-service.js"; import { registerPaymentProviders } from "../suite/payments/index.js"; -import { RuntimeDepsService } from "../runtime-deps/runtime-deps-service.js"; +import { getRuntimeDepsService, initRuntimeDepsService } from "@certd/pipeline"; +import { pluginRegistry, accessRegistry, notificationRegistry } from "@certd/pipeline"; +import { dnsProviderRegistry } from "@certd/plugin-lib"; +import { addonRegistry } from "@certd/lib-server"; @Provide() @Scope(ScopeEnum.Request, { allowDowngrade: true }) @@ -10,8 +13,8 @@ export class AutoLoadPlugins { @Inject() pluginService: PluginService; - @Inject() - runtimeDepsService: RuntimeDepsService; + @Config('runtimeDeps') + runtimeDepsConfig: any; async init() { logger.info(`加载插件开始,加载模式:${process.env.certd_plugin_loadmode}`); @@ -31,11 +34,23 @@ export class AutoLoadPlugins { } // await import("../../plugins/index.js") await this.pluginService.registerFromDb(); - + await registerPaymentProviders(); logger.info(`加载插件完成,加载模式:${process.env.certd_plugin_loadmode}`); + //初始化第三方依赖服务 + initRuntimeDepsService(this.runtimeDepsConfig,{ + pluginRegistry, + accessRegistry, + notificationRegistry, + dnsProviderRegistry, + addonRegistry, + }); // 收集插件 dependPackages 并安装 - await this.runtimeDepsService.refreshPluginDeps(); + const service = getRuntimeDepsService(); + service.refreshPluginDeps(); } } + + + diff --git a/packages/ui/certd-server/src/modules/basic/service/code-service.ts b/packages/ui/certd-server/src/modules/basic/service/code-service.ts index 76b2486ae..e3f8b3016 100644 --- a/packages/ui/certd-server/src/modules/basic/service/code-service.ts +++ b/packages/ui/certd-server/src/modules/basic/service/code-service.ts @@ -1,4 +1,4 @@ -import { cache, isDev, randomNumber, simpleNanoId } from "@certd/basic"; +import { cache, isDev, randomNumber, simpleNanoId } from "@certd/basic"; import { AccessService, AccessSysGetter, CodeErrorException, SysSettingsService } from "@certd/lib-server"; import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; import { ISmsService } from "../sms/api.js"; @@ -6,7 +6,7 @@ import { SmsServiceFactory } from "../sms/factory.js"; import { CaptchaService } from "./captcha-service.js"; import { EmailService } from "./email-service.js"; import { CaptchaRequest } from "../../../plugins/plugin-captcha/api.js"; -import { RuntimeDepsService } from "../../runtime-deps/runtime-deps-service.js"; + // {data: '', text: 'abcd'} /** @@ -25,10 +25,7 @@ export class CodeService { @Inject() captchaService: CaptchaService; - @Inject() - runtimeDepsService: RuntimeDepsService; - - async checkCaptcha(body: any, req: CaptchaRequest) { +async checkCaptcha(body: any, req: CaptchaRequest) { return await this.captchaService.doValidate({ form: body, req }); } /** @@ -60,7 +57,6 @@ export class CodeService { await sender.setCtx({ accessService: accessGetter, config: smsConfig, - runtimeDepsService: this.runtimeDepsService, }); const smsCode = randomNumber(verificationCodeLength); await sender.sendSmsCode({ @@ -200,3 +196,7 @@ export class CodeService { return cache.get(this.buildValidationValueKey(code)); } } + + + + diff --git a/packages/ui/certd-server/src/modules/basic/sms/api.ts b/packages/ui/certd-server/src/modules/basic/sms/api.ts index 930c665e6..1dde3e055 100644 --- a/packages/ui/certd-server/src/modules/basic/sms/api.ts +++ b/packages/ui/certd-server/src/modules/basic/sms/api.ts @@ -1,9 +1,8 @@ -import { FormItemProps, IAccessService } from "@certd/pipeline"; -import type { RuntimeDepsService } from "../../runtime-deps/runtime-deps-service.js"; +import { FormItemProps, IAccessService } from "@certd/pipeline"; export interface ISmsService { sendSmsCode(opts: { mobile: string; code: string; phoneCode: string }): Promise; - setCtx(ctx: { accessService: IAccessService; config: { [key: string]: any }; runtimeDepsService?: RuntimeDepsService }): Promise; + setCtx(ctx: { accessService: IAccessService; config: { [key: string]: any } }): Promise; } export type PluginInputs = { @@ -13,5 +12,4 @@ export type PluginInputs = { export type SmsPluginCtx = { accessService: IAccessService; config: T; - runtimeDepsService?: RuntimeDepsService; }; diff --git a/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts b/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts index fba25b27f..e24fb3d7b 100644 --- a/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts +++ b/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts @@ -1,5 +1,6 @@ -import { TencentAccess } from "../../../plugins/plugin-lib/tencent/access.js"; -import { ISmsService, PluginInputs, SmsPluginCtx } from "./api.js"; +import { TencentAccess } from "../../../plugins/plugin-lib/tencent/access.js"; +import { importRuntime } from "@certd/pipeline"; +import { ISmsService, PluginInputs } from "./api.js"; export type TencentSmsConfig = { accessId: string; @@ -66,26 +67,16 @@ export class TencentSmsService implements ISmsService { }; } - ctx: SmsPluginCtx; + ctx: { accessService: any; config: TencentSmsConfig }; async setCtx(ctx: any) { this.ctx = ctx; - if (this.ctx.runtimeDepsService) { - await this.ctx.runtimeDepsService.ensureDependencies({ - dependencies: { - "tencentcloud-sdk-nodejs": "^4.1.112", - }, - }); - } } async getClient() { - if (!this.ctx.runtimeDepsService) { - throw new Error("动态依赖服务未初始化,无法加载腾讯云短信SDK"); - } - const sdk = await this.ctx.runtimeDepsService.importRuntime("tencentcloud-sdk-nodejs/tencentcloud/services/sms/v20210111/index.js"); + const sdk = await importRuntime("tencentcloud-sdk-nodejs/tencentcloud/services/sms/v20210111/index.js"); const client = sdk.v20210111.Client; - const access = await this.ctx.accessService.getById(this.ctx.config.accessId); + const access: TencentAccess = await this.ctx.accessService.getById(this.ctx.config.accessId); // const region = this.region; const clientConfig = { @@ -126,3 +117,5 @@ export class TencentSmsService implements ISmsService { } } } + + diff --git a/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts b/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts index 8075afc62..2947efdb9 100644 --- a/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts +++ b/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts @@ -1,8 +1,9 @@ +import { importRuntime } from "@certd/pipeline"; import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; import { UserSettingsService } from "./user-settings-service.js"; import { UserTwoFactorSetting } from "./models.js"; import { UserService } from "../../sys/authority/service/user-service.js"; -import { RuntimeDepsService } from "../../runtime-deps/runtime-deps-service.js"; + /** * 授权 @@ -14,15 +15,12 @@ export class TwoFactorService { userSettingsService: UserSettingsService; @Inject() userService: UserService; - @Inject() - runtimeDepsService: RuntimeDepsService; - - async getAuthenticatorQrCode(userId: any) { +async getAuthenticatorQrCode(userId: any) { const setting = await this.getSetting(userId); const authenticatorSetting = setting.authenticator; if (!authenticatorSetting.secret) { - const { authenticator } = await this.runtimeDepsService.importRuntime("otplib"); + const { authenticator } = await importRuntime("otplib"); authenticatorSetting.secret = authenticator.generateSecret(); await this.userSettingsService.saveSetting(userId, null, setting); @@ -41,7 +39,7 @@ export class TwoFactorService { async saveAuthenticator(req: { userId: any; verifyCode: any }) { const userId = req.userId; - const { authenticator } = await this.runtimeDepsService.importRuntime("otplib"); + const { authenticator } = await importRuntime("otplib"); const setting = await this.getSetting(userId); const authenticatorSetting = setting.authenticator; @@ -80,7 +78,7 @@ export class TwoFactorService { } async verifyAuthenticatorCode(userId: any, verifyCode: string) { - const { authenticator } = await this.runtimeDepsService.importRuntime("otplib"); + const { authenticator } = await importRuntime("otplib"); const setting = await this.getSetting(userId); if (!setting.authenticator.enabled) { throw new Error("authenticator 未开启"); @@ -91,3 +89,6 @@ export class TwoFactorService { return true; } } + + + diff --git a/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts index 2f60acb82..a0e986317 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts @@ -1,4 +1,4 @@ -import { IServiceGetter } from "@certd/pipeline"; +import { IServiceGetter } from "@certd/pipeline"; import { ApplicationContext, IMidwayContainer, Provide, Scope, ScopeEnum } from "@midwayjs/core"; import { AccessGetter, AccessService } from "@certd/lib-server"; import { CnameProxyService } from "./cname-proxy-service.js"; @@ -13,7 +13,7 @@ import { CertInfoGetter } from "./cert-info-getter.js"; import { CertInfoService } from "../../../monitor/index.js"; import { ICertInfoGetter } from "@certd/plugin-lib"; import { CnameProviderService } from "../../../cname/service/cname-provider-service.js"; -import { RuntimeDepsService } from "../../../runtime-deps/runtime-deps-service.js"; + const serviceNames = ["ocrService"]; export class TaskServiceGetter implements IServiceGetter { @@ -39,8 +39,6 @@ export class TaskServiceGetter implements IServiceGetter { return (await this.getDomainVerifierGetter()) as T; } else if (serviceName === "certInfoGetter") { return (await this.getCertInfoGetter()) as T; - } else if (serviceName === "runtimeDepsService") { - return (await this.getRuntimeDepsService()) as T; } else { if (!serviceNames.includes(serviceName)) { throw new Error(`${serviceName} not in whitelist`); @@ -84,10 +82,6 @@ export class TaskServiceGetter implements IServiceGetter { const domainService: DomainService = await this.appCtx.getAsync("domainService"); return new DomainVerifierGetter(this.userId, this.projectId, domainService); } - - async getRuntimeDepsService(): Promise { - return await this.appCtx.getAsync("runtimeDepsService"); - } } @Provide() @Scope(ScopeEnum.Request, { allowDowngrade: true }) @@ -106,3 +100,5 @@ export type TaskServiceCreateReq = { userId: number; projectId?: number; }; + + diff --git a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts index 362eff8d8..8e3acabfe 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts @@ -962,7 +962,7 @@ export class PipelineService extends BaseService { return result; } - async batchDelete(ids: number[], userId?: number, projectId?: number):Promise { + async batchDelete(ids: number[], userId?: number, projectId?: number): Promise { if (!isPlus()) { throw new NeedVIPException("此功能需要升级Certd专业版"); } @@ -974,9 +974,9 @@ export class PipelineService extends BaseService { await this.checkUserId(id, projectId, "projectId"); } await this.delete(id); - ids.push(id); + ids.push(id); } - return ids.length + return ids.length; } async batchUpdateGroup(ids: number[], groupId: number, userId: any, projectId?: number) { diff --git a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts index b7a2f4676..69905af0a 100644 --- a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts +++ b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts @@ -1,4 +1,4 @@ -import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; import { addonRegistry, BaseService, PageReq } from "@certd/lib-server"; import { PluginEntity } from "../entity/plugin.js"; import { InjectEntityModel } from "@midwayjs/typeorm"; @@ -6,14 +6,13 @@ import { IsNull, Not, Repository } from "typeorm"; import { isComm } from "@certd/plus-core"; import { BuiltInPluginService } from "../../pipeline/service/builtin-plugin-service.js"; import { merge } from "lodash-es"; -import { accessRegistry, notificationRegistry, pluginRegistry } from "@certd/pipeline"; import { dnsProviderRegistry } from "@certd/plugin-cert"; import { logger } from "@certd/basic"; import yaml from "js-yaml"; import { getDefaultAccessPlugin, getDefaultDeployPlugin, getDefaultDnsPlugin } from "./default-plugin.js"; import fs from "fs"; import path from "path"; -import { RuntimeDepsService } from "../../runtime-deps/runtime-deps-service.js"; +import { importRuntime as importRuntimeDirect, getRuntimeDepsService, pluginRegistry, accessRegistry, notificationRegistry } from "@certd/pipeline"; export type PluginImportReq = { content: string; @@ -49,10 +48,7 @@ export class PluginService extends BaseService { @Inject() builtInPluginService: BuiltInPluginService; - @Inject() - runtimeDepsService: RuntimeDepsService; - - //@ts-ignore +//@ts-ignore getRepository() { return this.repository; } @@ -270,7 +266,7 @@ export class PluginService extends BaseService { return; } await this.registerPlugin(item); - await this.runtimeDepsService.refreshPluginDeps(); + await this.refreshPluginDeps(); } async unRegisterById(id: any) { @@ -298,7 +294,12 @@ export class PluginService extends BaseService { } else { logger.warn(`不支持的插件类型:${item.pluginType}`); } - await this.runtimeDepsService.refreshPluginDeps(); + await this.refreshPluginDeps(); + } + + async refreshPluginDeps(){ + const service = getRuntimeDepsService(); + service.refreshPluginDeps(); } async update(param: any) { @@ -334,7 +335,7 @@ export class PluginService extends BaseService { if (!isBareModuleSpecifier(modulePath)) { return await importLocalModule(modulePath); } - return await this.runtimeDepsService.importRuntime(modulePath, logger); + return await importRuntimeDirect(modulePath, logger); } private async getPluginClassFromFile(item: any) { @@ -565,3 +566,5 @@ export class PluginService extends BaseService { } } } + + diff --git a/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.ts b/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.ts deleted file mode 100644 index 3247cfdad..000000000 --- a/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Config, Provide, Scope, ScopeEnum } from "@midwayjs/core"; - -export type NpmRegistryResolverConfig = { - mode: "auto" | "fixed" | "system"; - fixedUrl: string; - candidates: string[]; - probeTimeoutMs: number; - cacheTtlMs: number; -}; - -export type RegistryProbeResult = { - registryUrl: string; - ok: boolean; - elapsedMs: number; -}; - -@Provide() -@Scope(ScopeEnum.Singleton) -export class NpmRegistryResolver { - @Config("runtimeDeps.registry") - config!: NpmRegistryResolverConfig; - - private cache?: { registryUrl: string; expiresAt: number }; - - async resolve(): Promise { - const config = this.config; - if (config?.mode === "fixed" && config.fixedUrl) { - return config.fixedUrl; - } - if (config?.mode === "system") { - return ""; - } - - const cached = this.cache; - if (cached && cached.expiresAt > Date.now()) { - return cached.registryUrl; - } - - const candidates = (config?.candidates || []).filter(Boolean); - const probes = await Promise.allSettled(candidates.map(registryUrl => this.probe(registryUrl))); - const okList = probes.map(item => (item.status === "fulfilled" ? item.value : null)).filter((item): item is RegistryProbeResult => !!item && item.ok); - - if (okList.length > 0) { - okList.sort((a, b) => a.elapsedMs - b.elapsedMs); - const best = okList[0].registryUrl; - this.cache = { - registryUrl: best, - expiresAt: Date.now() + (config?.cacheTtlMs || 0), - }; - return best; - } - - return ""; - } - - async probe(registryUrl: string): Promise { - const timeoutMs = this.config?.probeTimeoutMs || 3000; - const started = Date.now(); - try { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeoutMs); - try { - const res = await fetch(`${registryUrl.replace(/\/$/, "")}/-/ping`, { signal: controller.signal }); - return { - registryUrl, - ok: res.ok, - elapsedMs: Date.now() - started, - }; - } finally { - clearTimeout(timer); - } - } catch { - return { - registryUrl, - ok: false, - elapsedMs: Date.now() - started, - }; - } - } -} diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts index 86d4c8589..df7cf0846 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts @@ -1,4 +1,4 @@ -import { AbstractDnsProvider, CreateRecordOptions, DomainRecord, DnsResolveRecord, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert"; +import { AbstractDnsProvider, CreateRecordOptions, DomainRecord, DnsResolveRecord, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert"; import { AliyunAccess } from "../../plugin-lib/aliyun/access/aliyun-access.js"; import { AliyunClient } from "../../plugin-lib/aliyun/index.js"; import { Pager, PageRes, PageSearch } from "@certd/pipeline"; @@ -16,7 +16,7 @@ export class AliyunDnsProvider extends AbstractDnsProvider { async onInstance() { const access: AliyunAccess = this.ctx.access as AliyunAccess; - this.client = new AliyunClient({ logger: this.logger, importRuntime: this.importRuntime.bind(this) }); + this.client = new AliyunClient({ logger: this.logger }); await this.client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, @@ -209,3 +209,4 @@ export class AliyunDnsProvider extends AbstractDnsProvider { } new AliyunDnsProvider(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts index 71167bf48..e022b0b49 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts @@ -1,4 +1,4 @@ -import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { utils } from "@certd/basic"; import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; @@ -241,7 +241,7 @@ export class DeployCertToAliyunAckPlugin extends AbstractTaskPlugin { } async getClient(aliyunProvider: any, regionId: string) { - const client = new AliyunClient({ logger: this.logger, useROAClient: true, importRuntime: aliyunProvider.importRuntime.bind(aliyunProvider) }); + const client = new AliyunClient({ logger: this.logger, useROAClient: true }); await client.init({ accessKeyId: aliyunProvider.accessKeyId, accessKeySecret: aliyunProvider.accessKeySecret, @@ -275,3 +275,4 @@ export class DeployCertToAliyunAckPlugin extends AbstractTaskPlugin { } new DeployCertToAliyunAckPlugin(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts index 04ab71ecc..06da96221 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts @@ -1,4 +1,4 @@ -import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { CertApplyPluginNames, CertInfo, CertReader } from "@certd/plugin-cert"; import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; import { AliyunAccess } from "../../../plugin-lib/aliyun/access/index.js"; @@ -128,7 +128,7 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin { async onInstance() {} async getLBClient(access: AliyunAccess, region: string) { - const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); + const client = new AliyunClient({ logger: this.logger }); const version = "2020-06-16"; await client.init({ @@ -239,7 +239,7 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin { access: client.access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); const certIds = []; for (const item of list) { @@ -295,7 +295,7 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); if (certInfo.crt) { const certName = this.buildCertName(CertReader.getMainDomain(certInfo.crt)); @@ -404,3 +404,4 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin { } new AliyunDeployCertToALB(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts index d4aef9121..a9cc28239 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts @@ -1,4 +1,4 @@ -import { optionsUtils } from "@certd/basic"; +import { optionsUtils } from "@certd/basic"; import { AbstractTaskPlugin, CertTargetItem, IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; import { CertApplyPluginNames, CertReader } from "@certd/plugin-cert"; import { CertInfo, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; @@ -136,7 +136,7 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.endpoint || "cas.aliyuncs.com", - }); + }); if (this.domainMatchMode === "auto") { const { result, deployedList } = await this.autoMatchedDeploy({ @@ -209,7 +209,7 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin { } async getClient(access: AliyunAccess) { - const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); + const client = new AliyunClient({ logger: this.logger }); await client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, @@ -293,3 +293,4 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin { } } new DeployCertToAliyunCDN(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts index 0ee34e4d4..239a86795 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts @@ -1,4 +1,4 @@ -import { AbstractTaskPlugin, CertTargetItem, IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; +import { AbstractTaskPlugin, CertTargetItem, IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; import dayjs from "dayjs"; import { AliyunAccess } from "../../../plugin-lib/aliyun/access/index.js"; @@ -143,7 +143,7 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { } async getClient(access: AliyunAccess) { - const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); + const client = new AliyunClient({ logger: this.logger }); await client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, @@ -224,3 +224,4 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { } } new DeployCertToAliyunDCDN(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts index 29caf9fed..9168b025c 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts @@ -137,8 +137,8 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { const client = await this.getClient(access); - const $Util = await access.importRuntime("@alicloud/tea-util"); - const $OpenApi = await access.importRuntime("@alicloud/openapi-client"); + const $Util = await this.importRuntime("@alicloud/tea-util"); + const $OpenApi = await this.importRuntime("@alicloud/openapi-client"); let privateKey = this.cert.key; try { @@ -204,7 +204,7 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { } async getClient(access: AliyunAccess) { - const $OpenApi = await access.importRuntime("@alicloud/openapi-client"); + const $OpenApi = await this.importRuntime("@alicloud/openapi-client"); const config = new $OpenApi.Config({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, @@ -221,8 +221,8 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { const access = await this.getAccess(this.accessId); const client = await this.getClient(access); - const $OpenApi = await access.importRuntime("@alicloud/openapi-client"); - const $Util = await access.importRuntime("@alicloud/tea-util"); + const $OpenApi = await this.importRuntime("@alicloud/openapi-client"); + const $Util = await this.importRuntime("@alicloud/tea-util"); const params = new $OpenApi.Params({ // 接口名称 action: "ListCustomDomains", diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts index 28f937f8e..12c32bd77 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts @@ -1,4 +1,4 @@ -import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { CertInfo, CertReader } from "@certd/plugin-cert"; import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; import { CertApplyPluginNames } from "@certd/plugin-cert"; @@ -117,7 +117,7 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin { async onInstance() {} async getLBClient(access: AliyunAccess, region: string) { - const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); + const client = new AliyunClient({ logger: this.logger }); const version = "2022-04-30"; await client.init({ @@ -221,7 +221,7 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin { access: client.access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); const certIds = []; for (const item of list) { @@ -286,7 +286,7 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); const certName = this.buildCertName(CertReader.getMainDomain(certInfo.crt)); @@ -389,3 +389,4 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin { } new AliyunDeployCertToNLB(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-oss/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-oss/index.ts index 1691d02dc..02a09d8de 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-oss/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-oss/index.ts @@ -196,7 +196,7 @@ export class DeployCertToAliyunOSS extends AbstractTaskPlugin { async getClient(access: AliyunAccess) { // @ts-ignore - const OSS = await access.importRuntime("ali-oss"); + const OSS = await this.importRuntime("ali-oss"); return new OSS.default({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts index 399a4f852..8b77ef60d 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts @@ -1,4 +1,4 @@ -import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { CertInfo } from "@certd/plugin-cert"; import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; import { CertApplyPluginNames } from "@certd/plugin-cert"; @@ -130,7 +130,7 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin { async onInstance() {} async getLBClient(access: AliyunAccess, region: string) { - const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); + const client = new AliyunClient({ logger: this.logger }); const version = "2014-05-15"; await client.init({ accessKeyId: access.accessKeyId, @@ -234,7 +234,7 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); if (typeof this.cert === "object") { const name = this.appendTimeSuffix("certd"); @@ -412,3 +412,4 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin { } new AliyunDeployCertToSLB(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts index 6320f5518..96db7b90e 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts @@ -1,4 +1,4 @@ -import { AbstractTaskPlugin, IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { AbstractTaskPlugin, IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { CertApplyPluginNames, CertInfo, CertReader } from "@certd/plugin-cert"; import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; import { AliyunAccess } from "../../../plugin-lib/aliyun/access/index.js"; @@ -103,7 +103,7 @@ export class AliyunDeployCertToWafCloud extends AbstractTaskPlugin { async onInstance() {} async getWafClient(access: AliyunAccess) { - const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); + const client = new AliyunClient({ logger: this.logger }); await client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, @@ -316,3 +316,4 @@ export class AliyunDeployCertToWafCloud extends AbstractTaskPlugin { } new AliyunDeployCertToWafCloud(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts index a655798de..bf7e07397 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts @@ -1,4 +1,4 @@ -import { AbstractTaskPlugin, IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { AbstractTaskPlugin, IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { CertApplyPluginNames, CertInfo, CertReader } from "@certd/plugin-cert"; import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; import { AliyunAccess } from "../../../plugin-lib/aliyun/access/index.js"; @@ -115,7 +115,7 @@ export class AliyunDeployCertToWaf extends AbstractTaskPlugin { async onInstance() {} async getWafClient(access: AliyunAccess) { - const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); + const client = new AliyunClient({ logger: this.logger }); await client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, @@ -144,7 +144,7 @@ export class AliyunDeployCertToWaf extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); const cert = this.cert as CertInfo; const casCert = this.cert as CasCertInfo; @@ -250,3 +250,4 @@ export class AliyunDeployCertToWaf extends AbstractTaskPlugin { } new AliyunDeployCertToWaf(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/alioss-access.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/alioss-access.ts index 372325e78..c18d34ade 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/alioss-access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/alioss-access.ts @@ -101,7 +101,7 @@ export class AliossAccess extends BaseAccess { async getClient(access: AliyunAccess) { // @ts-ignore - const OSS = await access.importRuntime("ali-oss"); + const OSS = await this.importRuntime("ali-oss"); return new OSS.default({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts index 090cb7dbd..727c01960 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts @@ -1,4 +1,4 @@ -import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline"; +import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline"; import { AliyunClientV2 } from "../lib/aliyun-client-v2.js"; import { AliyunSslClient } from "../lib/ssl-client.js"; @IsAccess({ @@ -85,7 +85,7 @@ export class AliyunAccess extends BaseAccess { access: this, logger: this.ctx.logger, endpoint, - }); + }); return client; } @@ -94,8 +94,9 @@ export class AliyunAccess extends BaseAccess { access: this, logger: this.ctx.logger, endpoint: endpoint, - }); + }); } } new AliyunAccess(); + diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts index 11e8a869f..5d08bc625 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts @@ -1,5 +1,6 @@ -import { ILogger } from "@certd/basic"; +import { ILogger } from "@certd/basic"; import { AliyunAccess } from "../access/aliyun-access.js"; +import { importRuntime as importRuntimeDirect } from "@certd/pipeline"; export type AliyunClientV2Req = { action: string; @@ -19,18 +20,24 @@ export class AliyunClientV2 { logger: ILogger; endpoint: string; + client: any; constructor(opts: { access: AliyunAccess; logger: ILogger; endpoint: string }) { this.access = opts.access; this.logger = opts.logger; this.endpoint = opts.endpoint; + + } + + async importRuntime(name: string) { + return await importRuntimeDirect(name, this.logger); } async getClient() { if (this.client) { return this.client; } - const $OpenApi = await this.access.importRuntime("@alicloud/openapi-client"); + const $OpenApi = await this.importRuntime("@alicloud/openapi-client"); // const Credential = await import("@alicloud/credentials"); // //@ts-ignore // const credential = new Credential.default.default({ @@ -52,9 +59,9 @@ export class AliyunClientV2 { async doRequest(req: AliyunClientV2Req) { const client = await this.getClient(); - const $OpenApi = await this.access.importRuntime("@alicloud/openapi-client"); - const $Util = await this.access.importRuntime("@alicloud/tea-util"); - const OpenApiUtil = await this.access.importRuntime("@alicloud/openapi-util"); + const $OpenApi = await this.importRuntime("@alicloud/openapi-client"); + const $Util = await this.importRuntime("@alicloud/tea-util"); + const OpenApiUtil = await this.importRuntime("@alicloud/openapi-util"); const params = new $OpenApi.Params({ // 接口名称 action: req.action, @@ -89,3 +96,4 @@ export class AliyunClientV2 { return res?.body; } } + diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts index b2950ddc4..83dd2f607 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts @@ -1,21 +1,25 @@ -import { getGlobalAgents, ILogger } from "@certd/basic"; -import { ImportRuntime } from "@certd/pipeline"; +import { getGlobalAgents, ILogger } from "@certd/basic"; +import { importRuntime as importRuntimeDirect } from "@certd/pipeline"; export class AliyunClient { client: any; logger: ILogger; agent: any; useROAClient: boolean; - importRuntime: ImportRuntime; - constructor(opts: { logger: ILogger; useROAClient?: boolean; importRuntime?: ImportRuntime }) { + + constructor(opts: { logger: ILogger; useROAClient?: boolean }) { this.logger = opts.logger; this.useROAClient = opts.useROAClient || false; - this.importRuntime = opts.importRuntime || (async (specifier: string) => await import(specifier)); + const agents = getGlobalAgents(); this.agent = agents.httpsAgent; } + async importRuntime(specifier: string) { + return await importRuntimeDirect(specifier, this.logger); + } + async getSdk() { if (this.useROAClient) { return await this.getROAClient(); @@ -78,3 +82,4 @@ export class AliyunClient { return res; } } + diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts index 4c26223ad..352de9bba 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts @@ -1,4 +1,4 @@ -import { ILogger, utils } from "@certd/basic"; +import { ILogger, utils } from "@certd/basic"; import { AliyunAccess } from "../access/index.js"; import { AliyunClient } from "./index.js"; import { CertInfo, CertReader, SimpleCertDetail } from "@certd/plugin-lib"; @@ -12,6 +12,7 @@ export type AliyunSslClientOpts = { logger: ILogger; endpoint?: string; region?: string; + }; export type AliyunSslGetResourceListReq = { @@ -42,9 +43,11 @@ export type CasCertId = { export class AliyunSslClient { opts: AliyunSslClientOpts; logger: ILogger; + constructor(opts: AliyunSslClientOpts) { this.opts = opts; this.logger = opts.logger; + } checkRet(ret: any) { @@ -55,7 +58,7 @@ export class AliyunSslClient { async getClient() { const access = this.opts.access; - const client = new AliyunClient({ logger: this.opts.logger, importRuntime: access.importRuntime.bind(access) }); + const client = new AliyunClient({ logger: this.opts.logger }); let endpoint = this.opts.endpoint || "cas.aliyuncs.com"; if (this.opts.endpoint == null && this.opts.region) { @@ -251,3 +254,5 @@ export class AliyunSslClient { } } } + + diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/tencentcos.ts b/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/tencentcos.ts index 5656f3e64..1f581d627 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/tencentcos.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/tencentcos.ts @@ -1,4 +1,4 @@ -import dayjs from "dayjs"; +import dayjs from "dayjs"; import { TencentAccess, TencentCosAccess, TencentCosClient } from "../../tencent/index.js"; import { BaseOssClient, OssFileItem } from "../api.js"; @@ -19,7 +19,7 @@ export default class TencentOssClientImpl extends BaseOssClient { const key = this.join(this.rootDir, filePath); @@ -52,3 +52,4 @@ export default class TencentOssClientImpl extends BaseOssClient await import(specifier)); + + } + + async importRuntime(specifier: string) { + return await importRuntimeDirect(specifier, this.logger); } async getCosClient() { @@ -118,3 +122,4 @@ export class TencentCosClient { }); } } + diff --git a/packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/NginxProxyManagerDeploy.yaml b/packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/NginxProxyManagerDeploy.yaml deleted file mode 100644 index c01567fd7..000000000 --- a/packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/NginxProxyManagerDeploy.yaml +++ /dev/null @@ -1,348 +0,0 @@ -name: NginxProxyManagerDeploy -icon: logos:nginx -title: Nginx Proxy Manager-部署到主机 -group: panel -desc: 上传自定义证书到 Nginx Proxy Manager,并绑定到所选主机。 -setting: null -sysSetting: null -type: custom -disabled: false -version: 1.0.0 -pluginType: deploy -author: samler -input: - cert: - title: 域名证书 - helper: 请选择前置任务产出的证书。 - component: - name: output-selector - from: - - ":cert:" - required: true - order: 0 - certDomains: - title: 证书域名 - component: - name: cert-domains-getter - mergeScript: | - return { - component: { - inputKey: ctx.compute(({ form }) => { - return form.cert; - }), - }, - } - required: false - order: 0 - accessId: - title: NPM授权 - component: - name: access-selector - type: samler/nginxProxyManager - helper: 选择用于部署的 Nginx Proxy Manager 授权。 - required: true - order: 0 - proxyHostIds: - title: 代理主机 - component: - name: remote-select - vModel: value - mode: tags - type: plugin - action: onGetProxyHostOptions - search: true - pager: false - multi: true - watches: - - certDomains - - accessId - required: true - helper: 选择要绑定此证书的一个或多个代理主机。 - mergeScript: | - return { - component: { - form: ctx.compute(({ form }) => { - return form; - }), - }, - } - order: 0 - certificateLabel: - title: 证书标识 - component: - name: a-input - allowClear: true - placeholder: certd_npm_example_com - helper: 可选。留空时默认使用 certd_npm_<主域名规范化>. - required: false - order: 0 - cleanupMatchingCertificates: - title: 自动清理未使用证书 - component: - name: a-switch - vModel: checked - helper: 部署成功后,自动删除除当前证书外所有未被任何主机引用的证书。 - required: false - order: 0 -output: {} -default: - strategy: - runStrategy: 1 -showRunStrategy: false -content: | - const { AbstractTaskPlugin } = await import("@certd/pipeline"); - const { CertReader } = await import("@certd/plugin-cert"); - - function normalizeDomain(domain) { - return String(domain ?? "").trim().toLowerCase(); - } - - function wildcardMatches(pattern, candidate) { - if (!pattern.startsWith("*.")) { - return false; - } - - const suffix = pattern.slice(1).toLowerCase(); - return candidate.endsWith(suffix); - } - - function isDomainMatch(left, right) { - const normalizedLeft = normalizeDomain(left); - const normalizedRight = normalizeDomain(right); - - return ( - normalizedLeft === normalizedRight || - wildcardMatches(normalizedLeft, normalizedRight) || - wildcardMatches(normalizedRight, normalizedLeft) - ); - } - - function normalizeDomainIdentity(domain) { - return normalizeDomain(domain).replace(/^\*\./, ""); - } - - function certificateHasBindings(certificate) { - return ( - (certificate.proxy_hosts?.length ?? 0) > 0 || - (certificate.redirection_hosts?.length ?? 0) > 0 || - (certificate.dead_hosts?.length ?? 0) > 0 || - (certificate.streams?.length ?? 0) > 0 - ); - } - - function sanitizeDomainSegment(value) { - const sanitized = String(value ?? "") - .trim() - .toLowerCase() - .replace(/[^a-z0-9]+/g, "_") - .replace(/^_+|_+$/g, "") - .replace(/_+/g, "_"); - - return sanitized || "unknown"; - } - - function buildDefaultCertificateLabel(cert) { - const mainDomain = CertReader.getMainDomain(cert.crt); - return `certd_npm_${sanitizeDomainSegment(mainDomain)}`; - } - - function normalizeStringList(input) { - if (Array.isArray(input)) { - return input; - } - - if (input == null || input === "") { - return []; - } - - return [input]; - } - - function resolveCertificateDomains(cert, configuredDomains) { - const configured = normalizeStringList(configuredDomains) - .map((value) => String(value).trim()) - .filter(Boolean); - - if (configured.length > 0) { - return Array.from(new Set(configured)); - } - - return new CertReader(cert).getAllDomains(); - } - - function buildProxyHostLabel(host) { - const domains = host.domain_names?.length ? host.domain_names.join(", ") : "(no domains)"; - return `${domains} <#${host.id}>`; - } - - function hasAnyCertDomainMatch(host, certDomains) { - if (!certDomains.length) { - return false; - } - - const hostDomains = host.domain_names ?? []; - return hostDomains.some((hostDomain) => certDomains.some((certDomain) => isDomainMatch(hostDomain, certDomain))); - } - - function buildProxyHostOptions(hosts, certDomains) { - const sortedHosts = [...hosts].sort((left, right) => { - return buildProxyHostLabel(left).localeCompare(buildProxyHostLabel(right)); - }); - - const matched = []; - const unmatched = []; - - for (const host of sortedHosts) { - const option = { - label: buildProxyHostLabel(host), - value: String(host.id), - domain: host.domain_names?.[0] ?? "", - }; - - if (hasAnyCertDomainMatch(host, certDomains)) { - matched.push(option); - } else { - unmatched.push(option); - } - } - - if (matched.length && unmatched.length) { - return [ - { - label: "匹配证书域名的主机", - options: matched, - }, - { - label: "其他代理主机", - options: unmatched, - }, - ]; - } - - return matched.length ? matched : unmatched; - } - - function normalizeProxyHostIds(proxyHostIds) { - return Array.from( - new Set( - normalizeStringList(proxyHostIds) - .map((value) => Number.parseInt(String(value), 10)) - .filter((value) => Number.isInteger(value) && value > 0), - ), - ); - } - - return class NpmDeployToProxyHosts extends AbstractTaskPlugin { - cert; - certDomains; - accessId; - proxyHostIds; - certificateLabel; - cleanupMatchingCertificates = false; - - async execute() { - const access = await this.getAccess(this.accessId); - const client = access.createClient(); - const proxyHostIds = normalizeProxyHostIds(this.proxyHostIds); - - if (proxyHostIds.length === 0) { - throw new Error("请至少选择一个 Nginx Proxy Manager 代理主机"); - } - - const certificateLabel = this.certificateLabel?.trim() || buildDefaultCertificateLabel(this.cert); - const certificateDomains = resolveCertificateDomains(this.cert, this.certDomains); - - let certificate = await client.findCustomCertificateByNiceName(certificateLabel); - if (!certificate) { - this.logger.info(`在 Nginx Proxy Manager 中创建自定义证书 "${certificateLabel}"`); - certificate = await client.createCustomCertificate(certificateLabel, certificateDomains); - } else { - this.logger.info(`复用已有自定义证书 "${certificateLabel}" (#${certificate.id})`); - } - - await client.uploadCertificate(certificate.id, { - certificate: this.cert.crt, - certificateKey: this.cert.key, - intermediateCertificate: this.cert.ic, - }); - this.logger.info(`证书内容已上传到 Nginx Proxy Manager 证书 #${certificate.id}`); - - for (const proxyHostId of proxyHostIds) { - this.logger.info(`将证书 #${certificate.id} 绑定到代理主机 #${proxyHostId}`); - try { - await client.assignCertificateToProxyHost(proxyHostId, certificate.id); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - throw new Error(`为代理主机 #${proxyHostId} 绑定证书失败:${message}`); - } - } - - if (this.cleanupMatchingCertificates === true) { - await this.cleanupOldCertificates(client, certificate.id); - } - - this.logger.info(`部署完成,共更新 ${proxyHostIds.length} 个代理主机`); - } - - async onGetProxyHostOptions(req = {}) { - if (!this.accessId) { - throw new Error("请先选择 Nginx Proxy Manager 授权"); - } - - const access = await this.getAccess(this.accessId); - const proxyHosts = await access.getProxyHostList(req); - return buildProxyHostOptions(proxyHosts, normalizeStringList(this.certDomains)); - } - - async cleanupOldCertificates(client, currentCertificateId) { - const certificates = await client.getCertificatesWithExpand(undefined, [ - "proxy_hosts", - "redirection_hosts", - "dead_hosts", - "streams", - ]); - - const candidates = certificates.filter((certificate) => { - return certificate.id !== currentCertificateId; - }); - - if (candidates.length === 0) { - this.logger.info("未发现可自动清理的旧证书"); - return; - } - - const deletedIds = []; - const skippedInUse = []; - const failedDeletes = []; - - for (const candidate of candidates) { - if (certificateHasBindings(candidate)) { - skippedInUse.push(`#${candidate.id} ${candidate.nice_name}`); - continue; - } - - this.logger.info(`自动清理旧证书 #${candidate.id} ${candidate.nice_name}`); - try { - await client.deleteCertificate(candidate.id); - deletedIds.push(candidate.id); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - failedDeletes.push(`#${candidate.id} ${candidate.nice_name}: ${message}`); - } - } - - if (deletedIds.length > 0) { - this.logger.info(`自动清理完成,共删除 ${deletedIds.length} 张旧证书:${deletedIds.map((id) => `#${id}`).join(", ")}`); - } else { - this.logger.info("未删除任何旧证书"); - } - - if (skippedInUse.length > 0) { - this.logger.info(`以下旧证书仍被其他资源引用,已跳过清理:${skippedInUse.join(", ")}`); - } - - if (failedDeletes.length > 0) { - this.logger.warn(`以下旧证书清理失败,已跳过:${failedDeletes.join(", ")}`); - } - } - }; diff --git a/packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/nginxProxyManager.yaml b/packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/nginxProxyManager.yaml deleted file mode 100644 index 59932d6df..000000000 --- a/packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/nginxProxyManager.yaml +++ /dev/null @@ -1,347 +0,0 @@ -name: nginxProxyManager -icon: logos:nginx -title: Nginx Proxy Manager 授权 -group: null -desc: 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 -setting: null -sysSetting: null -type: custom -disabled: false -version: 1.0.0 -pluginType: access -author: samler -input: - endpoint: - title: NPM 地址 - component: - name: a-input - allowClear: true - placeholder: https://npm.example.com - helper: 请输入 Nginx Proxy Manager 根地址,不要带 /api 后缀。 - required: true - email: - title: 邮箱 - component: - name: a-input - allowClear: true - placeholder: admin@example.com - required: true - password: - title: 密码 - component: - name: a-input-password - allowClear: true - placeholder: 请输入密码 - required: true - encrypt: true - totpSecret: - title: TOTP 密钥 - component: - name: a-input-password - allowClear: true - placeholder: Optional base32 TOTP secret - helper: 当 Nginx Proxy Manager 账号开启 2FA 时必填。 - required: false - encrypt: true - ignoreTls: - title: 忽略无效 TLS - component: - name: a-switch - vModel: checked - helper: 仅在 Nginx Proxy Manager 使用自签 HTTPS 证书时开启。 - required: false - testRequest: - title: 测试 - component: - name: api-test - action: TestRequest - helper: 测试登录并拉取代理主机列表。 -content: | - const runtimeImport = import; - const { BaseAccess } = await runtimeImport("@certd/pipeline"); - const httpsModule = await runtimeImport("node:https"); - const { URL } = await runtimeImport("node:url"); - const axiosModule = await runtimeImport("axios"); - const formDataModule = await runtimeImport("form-data"); - - const https = httpsModule.default ?? httpsModule; - const axios = axiosModule.default ?? axiosModule; - const FormData = formDataModule.default ?? formDataModule; - - function normalizeEndpoint(endpoint) { - const trimmed = String(endpoint ?? "").trim(); - if (!trimmed) { - throw new Error("Nginx Proxy Manager 地址不能为空"); - } - - const withoutTrailingSlash = trimmed.replace(/\/+$/, ""); - return withoutTrailingSlash.endsWith("/api") - ? withoutTrailingSlash.slice(0, -4) - : withoutTrailingSlash; - } - - function buildHttpsAgent(endpoint, ignoreTls) { - const url = new URL(endpoint); - if (url.protocol !== "https:") { - return undefined; - } - - return new https.Agent({ - rejectUnauthorized: !ignoreTls, - }); - } - - function describeError(error, action) { - if (axios.isAxiosError(error)) { - const status = error.response?.status; - const data = error.response?.data; - const message = - data?.error?.message || - data?.error || - data?.message || - (typeof data === "string" ? data : null) || - error.message; - return new Error(`${action} failed${status ? ` (${status})` : ""}: ${message}`); - } - - if (error instanceof Error) { - return new Error(`${action} failed: ${error.message}`); - } - - return new Error(`${action} failed`); - } - - class NginxProxyManagerClient { - constructor(options) { - this.options = options; - this.importFn = options.importFn; - this.endpoint = normalizeEndpoint(options.endpoint); - this.apiBaseUrl = `${this.endpoint}/api`; - this.token = undefined; - this.tokenPromise = undefined; - this.httpClient = axios.create({ - baseURL: this.apiBaseUrl, - timeout: 30000, - maxBodyLength: Infinity, - maxContentLength: Infinity, - httpsAgent: buildHttpsAgent(this.endpoint, options.ignoreTls === true), - headers: { - Accept: "application/json", - }, - }); - } - - async verifyAccess() { - const proxyHosts = await this.getProxyHosts(); - return { - proxyHostCount: proxyHosts.length, - }; - } - - async getProxyHosts(searchQuery) { - return await this.requestWithAuth({ - method: "GET", - url: "/nginx/proxy-hosts", - params: { - expand: "certificate", - ...(searchQuery ? { query: searchQuery } : {}), - }, - }); - } - - async getCertificates(searchQuery) { - return await this.requestWithAuth({ - method: "GET", - url: "/nginx/certificates", - params: searchQuery ? { query: searchQuery } : undefined, - }); - } - - async getCertificatesWithExpand(searchQuery, expand = []) { - return await this.requestWithAuth({ - method: "GET", - url: "/nginx/certificates", - params: { - ...(searchQuery ? { query: searchQuery } : {}), - ...(expand.length > 0 ? { expand: expand.join(",") } : {}), - }, - }); - } - - async findCustomCertificateByNiceName(niceName) { - const certificates = await this.getCertificates(niceName); - return certificates.find((certificate) => { - return certificate.provider === "other" && certificate.nice_name === niceName; - }); - } - - async createCustomCertificate(niceName, domainNames = []) { - return await this.requestWithAuth({ - method: "POST", - url: "/nginx/certificates", - data: { - provider: "other", - nice_name: niceName, - domain_names: domainNames, - }, - }); - } - - async deleteCertificate(certificateId) { - await this.requestWithAuth({ - method: "DELETE", - url: `/nginx/certificates/${certificateId}`, - }); - } - - async uploadCertificate(certificateId, payload) { - const form = new FormData(); - form.append("certificate", Buffer.from(payload.certificate, "utf8"), { - filename: "fullchain.pem", - contentType: "application/x-pem-file", - }); - form.append("certificate_key", Buffer.from(payload.certificateKey, "utf8"), { - filename: "privkey.pem", - contentType: "application/x-pem-file", - }); - if (payload.intermediateCertificate) { - form.append("intermediate_certificate", Buffer.from(payload.intermediateCertificate, "utf8"), { - filename: "chain.pem", - contentType: "application/x-pem-file", - }); - } - - await this.requestWithAuth({ - method: "POST", - url: `/nginx/certificates/${certificateId}/upload`, - data: form, - headers: form.getHeaders(), - }); - } - - async assignCertificateToProxyHost(hostId, certificateId) { - await this.requestWithAuth({ - method: "PUT", - url: `/nginx/proxy-hosts/${hostId}`, - data: { - certificate_id: certificateId, - }, - }); - } - - async login() { - if (this.token) { - return this.token; - } - - if (!this.tokenPromise) { - this.tokenPromise = this.performLogin().finally(() => { - this.tokenPromise = undefined; - }); - } - - this.token = await this.tokenPromise; - return this.token; - } - - async performLogin() { - const initialLogin = await this.request({ - method: "POST", - url: "/tokens", - data: { - identity: this.options.email, - secret: this.options.password, - }, - }); - - if (initialLogin.token) { - return initialLogin.token; - } - - if (!initialLogin.requires_2fa || !initialLogin.challenge_token) { - throw new Error("登录失败:Nginx Proxy Manager 未返回访问令牌"); - } - - if (!this.options.totpSecret) { - throw new Error("登录失败:该 Nginx Proxy Manager 账号启用了 2FA,但未配置 totpSecret"); - } - - let code; - try { - const { authenticator } = await this.importFn("otplib"); - code = authenticator.generate(this.options.totpSecret); - } catch (error) { - throw describeError(error, "Generating TOTP code"); - } - - const completedLogin = await this.request({ - method: "POST", - url: "/tokens/2fa", - data: { - challenge_token: initialLogin.challenge_token, - code, - }, - }); - - if (!completedLogin.token) { - throw new Error("2FA 登录失败:Nginx Proxy Manager 未返回访问令牌"); - } - - return completedLogin.token; - } - - async requestWithAuth(config) { - const token = await this.login(); - const headers = { - ...(config.headers ?? {}), - Authorization: `Bearer ${token}`, - }; - - return await this.request({ - ...config, - headers, - }); - } - - async request(config) { - const action = `${config.method ?? "GET"} ${config.url ?? "/"}`; - try { - const response = await this.httpClient.request(config); - return response.data; - } catch (error) { - throw describeError(error, action); - } - } - } - - return class NginxProxyManagerAccess extends BaseAccess { - endpoint = ""; - email = ""; - password = ""; - totpSecret = ""; - ignoreTls = false; - testRequest = true; - - createClient() { - return new NginxProxyManagerClient({ - endpoint: this.endpoint, - email: this.email, - password: this.password, - totpSecret: this.totpSecret || undefined, - ignoreTls: this.ignoreTls === true, - importFn: this.importRuntime.bind(this), - }); - } - - async onTestRequest() { - const client = this.createClient(); - const result = await client.verifyAccess(); - this.ctx.logger.info(`Nginx Proxy Manager 授权验证成功,找到 ${result.proxyHostCount} 个代理主机`); - return `成功(${result.proxyHostCount} 个代理主机)`; - } - - async getProxyHostList(req = {}) { - const client = this.createClient(); - return await client.getProxyHosts(req.searchKey); - } - }; diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cos/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cos/index.ts index ab84df6f2..d48fb58cd 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cos/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cos/index.ts @@ -142,7 +142,7 @@ export class DeployCertToTencentCosPlugin extends AbstractTaskPlugin { async onGetDomainList(data: any) { const access = await this.getAccess(this.accessId); - const cosv5 = await (this as any).importRuntime("cos-nodejs-sdk-v5"); + const cosv5 = await this.importRuntime("cos-nodejs-sdk-v5"); const cos = new cosv5.default({ SecretId: access.secretId, SecretKey: access.secretKey, diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-live/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-live/index.ts index 4d11fc8c2..53116a070 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-live/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-live/index.ts @@ -95,7 +95,7 @@ export class TencentDeployCertToLive extends AbstractTaskPlugin { async getLiveClient() { const accessProvider = await this.getAccess(this.accessId); - const sdk = await (this as any).importRuntime("tencentcloud-sdk-nodejs/tencentcloud/services/live/v20180801/index.js"); + const sdk = await this.importRuntime("tencentcloud-sdk-nodejs/tencentcloud/services/live/v20180801/index.js"); const CssClient = sdk.v20180801.Client; const clientConfig = { diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/upload-to-tencent/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/upload-to-tencent/index.ts index 03b25bc1a..314bcf573 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/upload-to-tencent/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/upload-to-tencent/index.ts @@ -53,7 +53,7 @@ export class UploadCertToTencent extends AbstractTaskPlugin { Client: any; async onInstance() { - const sdk = await (this as any).importRuntime("tencentcloud-sdk-nodejs/tencentcloud/services/ssl/v20191205/index.js"); + const sdk = await this.importRuntime("tencentcloud-sdk-nodejs/tencentcloud/services/ssl/v20191205/index.js"); this.Client = sdk.v20191205.Client; } diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/cdn-client.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/cdn-client.ts index 3678ede0e..14740e89f 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/cdn-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/cdn-client.ts @@ -10,12 +10,15 @@ export class VolcengineCdnClient { this.opts = opts; } + async importRuntime(packageName: string) { + return this.opts.access.importRuntime(packageName); + } + async getCdnClient() { if (this.service) { return this.service; } - const importRuntime = this.opts.importRuntime || this.opts.access.importRuntime.bind(this.opts.access); - const { cdn } = await importRuntime("@volcengine/openapi"); + const { cdn } = await this.importRuntime("@volcengine/openapi"); const service = new cdn.CdnService(); // 设置ak、sk service.setAccessKeyId(this.opts.access.accessKeyId); diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/dns-client.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/dns-client.ts index 68cd49738..8f81c1a0a 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/dns-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/dns-client.ts @@ -19,10 +19,12 @@ export class VolcengineDnsClient { constructor(opts: VolcengineOpts) { this.opts = opts; } + async importRuntime(packageName: string) { + return this.opts.access.importRuntime(packageName); + } async doRequest(req: VolcengineReq) { - const importRuntime = this.opts.importRuntime || this.opts.access.importRuntime.bind(this.opts.access); - const { Signer } = await importRuntime("@volcengine/openapi"); + const { Signer } = await this.importRuntime("@volcengine/openapi"); // http request data const openApiRequestData: any = { diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vod.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vod.ts index 31bbc6be4..4fb474856 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vod.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vod.ts @@ -243,7 +243,7 @@ export class VolcengineDeployToVOD extends AbstractTaskPlugin { return { value: item.Domain, label: item.Domain, - domain : item.Domain, + domain: item.Domain, }; }); return this.ctx.utils.options.buildGroupOptions(list, this.certDomains); diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/ve-client.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/ve-client.ts index 2bd2725ff..48f3b7490 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/ve-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/ve-client.ts @@ -1,12 +1,10 @@ import { VolcengineAccess } from "./access.js"; import { HttpClient, ILogger } from "@certd/basic"; -import { ImportRuntime } from "@certd/pipeline"; export type VolcengineOpts = { access: VolcengineAccess; logger: ILogger; http: HttpClient; - importRuntime?: ImportRuntime; }; export class VolcengineClient { @@ -17,6 +15,10 @@ export class VolcengineClient { this.opts = opts; } + async importRuntime(packageName: string) { + return this.opts.access.importRuntime(packageName); + } + async getCertCenterService() { const CommonService = await this.getServiceCls(); @@ -142,8 +144,7 @@ export class VolcengineClient { } async getTOSService(opts: { region?: string }) { - const importRuntime = this.opts.importRuntime || this.opts.access.importRuntime.bind(this.opts.access); - const { TosClient } = await importRuntime("@volcengine/tos-sdk"); + const { TosClient } = await this.importRuntime("@volcengine/tos-sdk"); const client = new TosClient({ accessKeyId: this.opts.access.accessKeyId, @@ -172,8 +173,7 @@ export class VolcengineClient { if (this.CommonService) { return this.CommonService; } - const importRuntime = this.opts.importRuntime || this.opts.access.importRuntime.bind(this.opts.access); - const { Service } = await importRuntime("@volcengine/openapi"); + const { Service } = await this.importRuntime("@volcengine/openapi"); class CommonService extends Service { Generic: any; diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/volcengine-dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/volcengine-dns-provider.ts index 762a74ae8..3bf5b35ee 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/volcengine-dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/volcengine-dns-provider.ts @@ -22,7 +22,6 @@ export class VolcengineDnsProvider extends AbstractDnsProvider { access: this.access, logger: this.logger, http: this.http, - importRuntime: this.importRuntime.bind(this), }); } From 6cc74a1c0abddeeba5ef10d7f538c10b0d10c111 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 01:25:55 +0800 Subject: [PATCH 03/13] chore: lint --- package.json | 1 + packages/libs/lib-huawei/package.json | 3 ++- packages/libs/lib-jdcloud/package.json | 3 ++- .../sys/settings/sys-settings-controller.ts | 7 +------ .../src/modules/auto/auto-a-register.ts | 2 -- .../src/modules/auto/auto-load-plugins.ts | 21 ++++++++----------- .../src/modules/basic/service/code-service.ts | 7 +------ .../src/modules/basic/sms/tencent-sms.ts | 2 -- .../mine/service/two-factor-service.ts | 6 +----- .../service/getter/task-service-getter.ts | 3 --- .../modules/plugin/service/plugin-service.ts | 6 ++---- .../dns-provider/aliyun-dns-provider.ts | 1 - .../plugin/deploy-to-ack/index.ts | 1 - .../plugin/deploy-to-alb/index.ts | 5 ++--- .../plugin/deploy-to-cdn/index.ts | 3 +-- .../plugin/deploy-to-dcdn/index.ts | 1 - .../plugin/deploy-to-nlb/index.ts | 5 ++--- .../plugin/deploy-to-slb/index.ts | 3 +-- .../deploy-to-waf/deploy-to-waf-cloud.ts | 1 - .../deploy-to-waf/deploy-to-waf-cname.ts | 3 +-- .../plugin-lib/aliyun/access/aliyun-access.ts | 5 ++--- .../plugin-lib/aliyun/lib/aliyun-client-v2.ts | 3 --- .../plugin-lib/aliyun/lib/base-client.ts | 2 -- .../plugin-lib/aliyun/lib/ssl-client.ts | 4 ---- .../plugin-lib/oss/impls/tencentcos.ts | 3 +-- .../plugin-lib/tencent/lib/cos-client.ts | 3 --- 26 files changed, 29 insertions(+), 75 deletions(-) diff --git a/package.json b/package.json index ed0169e9a..28f818c71 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "test:unit": "cross-env NODE_ENV=unittest pnpm -r --workspace-concurrency=1 run test:unit", "pub": "echo 1", "dev": "pnpm run -r --parallel compile ", + "lint_all": "pnpm run -r --parallel lint ", "pub_all": "node ./scripts/pub-all.js", "release": "time /t >trigger/release.trigger && git add trigger/release.trigger && git commit -m \"build: release\" && git push", "publish_to_atomgit": "node --experimental-json-modules ./scripts/publish-atomgit.js", diff --git a/packages/libs/lib-huawei/package.json b/packages/libs/lib-huawei/package.json index 5ece06357..176b959ad 100644 --- a/packages/libs/lib-huawei/package.json +++ b/packages/libs/lib-huawei/package.json @@ -28,7 +28,8 @@ "cross-env": "^7.0.3", "esmock": "^2.7.5", "prettier": "3.3.3", - "tslib": "^2.8.1" + "tslib": "^2.8.1", + "eslint": "^8.57.0" }, "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" } diff --git a/packages/libs/lib-jdcloud/package.json b/packages/libs/lib-jdcloud/package.json index 12ecab32b..476e34f7d 100644 --- a/packages/libs/lib-jdcloud/package.json +++ b/packages/libs/lib-jdcloud/package.json @@ -39,7 +39,8 @@ "js-yaml": "^3.11.0", "mocha": "^10.6.0", "prettier": "3.3.3", - "tslib": "^2.8.1" + "tslib": "^2.8.1", + "eslint": "^8.57.0" }, "engines": { "node": ">= 8.6.0", diff --git a/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts b/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts index 753b8dcc7..350161a81 100644 --- a/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts +++ b/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts @@ -24,7 +24,7 @@ export class SysSettingsController extends CrudController { codeService: CodeService; @Inject() addonService: AddonService; -getService() { + getService() { return this.service; } @@ -223,8 +223,3 @@ getService() { return this.ok(true); } } - - - - - diff --git a/packages/ui/certd-server/src/modules/auto/auto-a-register.ts b/packages/ui/certd-server/src/modules/auto/auto-a-register.ts index 25e1e89f5..d92ecf91f 100644 --- a/packages/ui/certd-server/src/modules/auto/auto-a-register.ts +++ b/packages/ui/certd-server/src/modules/auto/auto-a-register.ts @@ -10,7 +10,6 @@ import { AutoFix } from "./fix/auto-fix.js"; @Autoload() @Scope(ScopeEnum.Request, { allowDowngrade: true }) export class AutoARegister { - //这个A是必须,让他排在第一个 进行init,否则会被其他init模块抢先注册导致报错 @Inject() autoInitSite: AutoInitSite; @@ -44,4 +43,3 @@ export class AutoARegister { await this.autoPrint.init(); } } - diff --git a/packages/ui/certd-server/src/modules/auto/auto-load-plugins.ts b/packages/ui/certd-server/src/modules/auto/auto-load-plugins.ts index e4b8d9052..de99e848f 100644 --- a/packages/ui/certd-server/src/modules/auto/auto-load-plugins.ts +++ b/packages/ui/certd-server/src/modules/auto/auto-load-plugins.ts @@ -13,7 +13,7 @@ export class AutoLoadPlugins { @Inject() pluginService: PluginService; - @Config('runtimeDeps') + @Config("runtimeDeps") runtimeDepsConfig: any; async init() { @@ -34,23 +34,20 @@ export class AutoLoadPlugins { } // await import("../../plugins/index.js") await this.pluginService.registerFromDb(); - + await registerPaymentProviders(); logger.info(`加载插件完成,加载模式:${process.env.certd_plugin_loadmode}`); //初始化第三方依赖服务 - initRuntimeDepsService(this.runtimeDepsConfig,{ - pluginRegistry, - accessRegistry, - notificationRegistry, - dnsProviderRegistry, - addonRegistry, - }); + initRuntimeDepsService(this.runtimeDepsConfig, { + pluginRegistry, + accessRegistry, + notificationRegistry, + dnsProviderRegistry, + addonRegistry, + }); // 收集插件 dependPackages 并安装 const service = getRuntimeDepsService(); service.refreshPluginDeps(); } } - - - diff --git a/packages/ui/certd-server/src/modules/basic/service/code-service.ts b/packages/ui/certd-server/src/modules/basic/service/code-service.ts index e3f8b3016..f24177afa 100644 --- a/packages/ui/certd-server/src/modules/basic/service/code-service.ts +++ b/packages/ui/certd-server/src/modules/basic/service/code-service.ts @@ -7,7 +7,6 @@ import { CaptchaService } from "./captcha-service.js"; import { EmailService } from "./email-service.js"; import { CaptchaRequest } from "../../../plugins/plugin-captcha/api.js"; - // {data: '', text: 'abcd'} /** */ @@ -25,7 +24,7 @@ export class CodeService { @Inject() captchaService: CaptchaService; -async checkCaptcha(body: any, req: CaptchaRequest) { + async checkCaptcha(body: any, req: CaptchaRequest) { return await this.captchaService.doValidate({ form: body, req }); } /** @@ -196,7 +195,3 @@ async checkCaptcha(body: any, req: CaptchaRequest) { return cache.get(this.buildValidationValueKey(code)); } } - - - - diff --git a/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts b/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts index e24fb3d7b..71080b15b 100644 --- a/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts +++ b/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts @@ -117,5 +117,3 @@ export class TencentSmsService implements ISmsService { } } } - - diff --git a/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts b/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts index 2947efdb9..040017ae1 100644 --- a/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts +++ b/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts @@ -4,7 +4,6 @@ import { UserSettingsService } from "./user-settings-service.js"; import { UserTwoFactorSetting } from "./models.js"; import { UserService } from "../../sys/authority/service/user-service.js"; - /** * 授权 */ @@ -15,7 +14,7 @@ export class TwoFactorService { userSettingsService: UserSettingsService; @Inject() userService: UserService; -async getAuthenticatorQrCode(userId: any) { + async getAuthenticatorQrCode(userId: any) { const setting = await this.getSetting(userId); const authenticatorSetting = setting.authenticator; @@ -89,6 +88,3 @@ async getAuthenticatorQrCode(userId: any) { return true; } } - - - diff --git a/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts index a0e986317..5e144d2e8 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts @@ -14,7 +14,6 @@ import { CertInfoService } from "../../../monitor/index.js"; import { ICertInfoGetter } from "@certd/plugin-lib"; import { CnameProviderService } from "../../../cname/service/cname-provider-service.js"; - const serviceNames = ["ocrService"]; export class TaskServiceGetter implements IServiceGetter { private userId: number; @@ -100,5 +99,3 @@ export type TaskServiceCreateReq = { userId: number; projectId?: number; }; - - diff --git a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts index 69905af0a..ca9dea670 100644 --- a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts +++ b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts @@ -48,7 +48,7 @@ export class PluginService extends BaseService { @Inject() builtInPluginService: BuiltInPluginService; -//@ts-ignore + //@ts-ignore getRepository() { return this.repository; } @@ -297,7 +297,7 @@ export class PluginService extends BaseService { await this.refreshPluginDeps(); } - async refreshPluginDeps(){ + async refreshPluginDeps() { const service = getRuntimeDepsService(); service.refreshPluginDeps(); } @@ -566,5 +566,3 @@ export class PluginService extends BaseService { } } } - - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts index df7cf0846..63bf0afc0 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts @@ -209,4 +209,3 @@ export class AliyunDnsProvider extends AbstractDnsProvider { } new AliyunDnsProvider(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts index e022b0b49..26284b59a 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts @@ -275,4 +275,3 @@ export class DeployCertToAliyunAckPlugin extends AbstractTaskPlugin { } new DeployCertToAliyunAckPlugin(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts index 06da96221..2c9be0752 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts @@ -239,7 +239,7 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin { access: client.access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); const certIds = []; for (const item of list) { @@ -295,7 +295,7 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); if (certInfo.crt) { const certName = this.buildCertName(CertReader.getMainDomain(certInfo.crt)); @@ -404,4 +404,3 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin { } new AliyunDeployCertToALB(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts index a9cc28239..1450b2d84 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts @@ -136,7 +136,7 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.endpoint || "cas.aliyuncs.com", - }); + }); if (this.domainMatchMode === "auto") { const { result, deployedList } = await this.autoMatchedDeploy({ @@ -293,4 +293,3 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin { } } new DeployCertToAliyunCDN(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts index 239a86795..e65be98b0 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts @@ -224,4 +224,3 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { } } new DeployCertToAliyunDCDN(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts index 12c32bd77..ed151e3a8 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts @@ -221,7 +221,7 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin { access: client.access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); const certIds = []; for (const item of list) { @@ -286,7 +286,7 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); const certName = this.buildCertName(CertReader.getMainDomain(certInfo.crt)); @@ -389,4 +389,3 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin { } new AliyunDeployCertToNLB(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts index 8b77ef60d..afe17c013 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts @@ -234,7 +234,7 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); if (typeof this.cert === "object") { const name = this.appendTimeSuffix("certd"); @@ -412,4 +412,3 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin { } new AliyunDeployCertToSLB(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts index 96db7b90e..185a2b223 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts @@ -316,4 +316,3 @@ export class AliyunDeployCertToWafCloud extends AbstractTaskPlugin { } new AliyunDeployCertToWafCloud(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts index bf7e07397..058f44331 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts @@ -144,7 +144,7 @@ export class AliyunDeployCertToWaf extends AbstractTaskPlugin { access, logger: this.logger, endpoint: this.casEndpoint, - }); + }); const cert = this.cert as CertInfo; const casCert = this.cert as CasCertInfo; @@ -250,4 +250,3 @@ export class AliyunDeployCertToWaf extends AbstractTaskPlugin { } new AliyunDeployCertToWaf(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts index 727c01960..79e62c4f5 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts @@ -85,7 +85,7 @@ export class AliyunAccess extends BaseAccess { access: this, logger: this.ctx.logger, endpoint, - }); + }); return client; } @@ -94,9 +94,8 @@ export class AliyunAccess extends BaseAccess { access: this, logger: this.ctx.logger, endpoint: endpoint, - }); + }); } } new AliyunAccess(); - diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts index 5d08bc625..554c0a2e7 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts @@ -20,13 +20,11 @@ export class AliyunClientV2 { logger: ILogger; endpoint: string; - client: any; constructor(opts: { access: AliyunAccess; logger: ILogger; endpoint: string }) { this.access = opts.access; this.logger = opts.logger; this.endpoint = opts.endpoint; - } async importRuntime(name: string) { @@ -96,4 +94,3 @@ export class AliyunClientV2 { return res?.body; } } - diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts index 83dd2f607..c66733b18 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts @@ -7,7 +7,6 @@ export class AliyunClient { agent: any; useROAClient: boolean; - constructor(opts: { logger: ILogger; useROAClient?: boolean }) { this.logger = opts.logger; this.useROAClient = opts.useROAClient || false; @@ -82,4 +81,3 @@ export class AliyunClient { return res; } } - diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts index 352de9bba..319a81eab 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts @@ -12,7 +12,6 @@ export type AliyunSslClientOpts = { logger: ILogger; endpoint?: string; region?: string; - }; export type AliyunSslGetResourceListReq = { @@ -47,7 +46,6 @@ export class AliyunSslClient { constructor(opts: AliyunSslClientOpts) { this.opts = opts; this.logger = opts.logger; - } checkRet(ret: any) { @@ -254,5 +252,3 @@ export class AliyunSslClient { } } } - - diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/tencentcos.ts b/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/tencentcos.ts index 1f581d627..33bc1bfd4 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/tencentcos.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/tencentcos.ts @@ -19,7 +19,7 @@ export default class TencentOssClientImpl extends BaseOssClient { const key = this.join(this.rootDir, filePath); @@ -52,4 +52,3 @@ export default class TencentOssClientImpl extends BaseOssClient Date: Wed, 15 Jul 2026 23:00:42 +0800 Subject: [PATCH 04/13] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Ddingtalk?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E6=A0=BC=E5=BC=8F=E6=B2=A1=E6=9C=89=E6=8D=A2?= =?UTF-8?q?=E8=A1=8C=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugins/plugin-notification/dingtalk/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/dingtalk/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/dingtalk/index.ts index b63ebf4ce..8bba66f27 100644 --- a/packages/ui/certd-server/src/plugins/plugin-notification/dingtalk/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-notification/dingtalk/index.ts @@ -121,7 +121,7 @@ export class DingTalkNotification extends BaseNotification { at.isAtAll = true; } - const color = body.errorMessage ? "red" : "green"; + const color = body.errorMessage ? "#CC0000": "#00CC66"; const res = await this.http.request({ url: webhook, method: "POST", From 604fa5be634d099d797bfee5c2b0f26ce0ac8461 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:01:32 +0800 Subject: [PATCH 05/13] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96vke=20keubconfi?= =?UTF-8?q?g=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F=EF=BC=8C=E6=94=B9?= =?UTF-8?q?=E6=88=90=E5=85=88=E6=9F=A5=E8=AF=A2=EF=BC=8C=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=86=8D=E5=88=9B=E5=BB=BA=E4=B8=B4=E6=97=B6?= =?UTF-8?q?config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/pipeline/src/notification/api.ts | 4 + packages/ui/certd-server/.eslintrc | 25 ---- .../src/plugins/plugin-volcengine/access.ts | 18 ++- .../plugins/plugin-deploy-to-vke.ts | 122 ++++++++++-------- 4 files changed, 85 insertions(+), 84 deletions(-) delete mode 100644 packages/ui/certd-server/.eslintrc diff --git a/packages/core/pipeline/src/notification/api.ts b/packages/core/pipeline/src/notification/api.ts index aa9481178..c79d309a4 100644 --- a/packages/core/pipeline/src/notification/api.ts +++ b/packages/core/pipeline/src/notification/api.ts @@ -95,6 +95,10 @@ export abstract class BaseNotification implements INotification { } async doSend(body: NotificationBody) { + if (body.content) { + const content = body.content?.replace(/\n/g, " \n"); + body.content = content; + } return await this.send(body); } abstract send(body: NotificationBody): Promise; diff --git a/packages/ui/certd-server/.eslintrc b/packages/ui/certd-server/.eslintrc deleted file mode 100644 index 8b68bd3a1..000000000 --- a/packages/ui/certd-server/.eslintrc +++ /dev/null @@ -1,25 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ], - "ignorePatterns": ["dist"], - "extends": [ - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended", - "prettier" - ], - "env": { - "mocha": true - }, - "rules": { - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/ban-ts-ignore": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-this-alias": "off", - "@typescript-eslint/no-unsafe-assignment": "off" - } -} diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/access.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/access.ts index 8c1a3ae96..818b1582b 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/access.ts @@ -43,7 +43,11 @@ export class VolcengineAccess extends BaseAccess { testRequest = true; async onTestRequest() { - await this.getCallerIdentity(); + const result = await this.getCallerIdentity(); + this.ctx.logger.info("✅ 密钥有效!"); + this.ctx.logger.info(` 账户ID: ${result.accountId}`); + this.ctx.logger.info(` ARN: ${result.arn}`); + this.ctx.logger.info(` 用户ID: ${result.userId}`); return "ok"; } @@ -60,18 +64,18 @@ export class VolcengineAccess extends BaseAccess { }); const result = res.Result || {}; - this.ctx.logger.info("✅ 密钥有效!"); - this.ctx.logger.info(` 账户ID: ${result.AccountId}`); - this.ctx.logger.info(` ARN: ${result.Trn}`); - this.ctx.logger.info(` 用户ID: ${result.IdentityId}`); - return { valid: true, accountId: result.AccountId, arn: result.Trn, - userId: result.IdentityId, + userId: parseInt(result.IdentityId), }; } + + async getUserId() { + const { userId } = await this.getCallerIdentity(); + return userId; + } } new VolcengineAccess(); diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.ts index 7cf98c127..fe58bf23e 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.ts @@ -208,10 +208,10 @@ export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin { this.logger.info("使用已有证书中心ID:" + certId); } - const kubeconfigId = await this.createKubeconfig(vkeService); - - try { - const kubeconfig = await this.getKubeconfig(vkeService, kubeconfigId); + const userId = await access.getUserId(); + this.logger.info(`当前用户ID:${userId}`); + const kubeconfig = await this.getOrCreateKubeconfig({ vkeService, userId }); + try{ const k8sClient = new this.K8sClient({ kubeConfigStr: kubeconfig, logger: this.logger, @@ -224,9 +224,7 @@ export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin { throw new Error(this.formatK8sError(e.response.body)); } throw e; - } finally { - await this.deleteKubeconfig(vkeService, kubeconfigId); - } + } await utils.sleep(5000); this.logger.info("VKE证书替换完成"); @@ -250,7 +248,19 @@ export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin { return await client.getVkeService({ region: this.regionId }); } - private async createKubeconfig(vkeService: any) { + async getOrCreateKubeconfig(opts: {vkeService: any, userId: number }) : Promise { + // 先查询 + let kubeconfig = await this.getKubeconfig(opts); + // 如果不存在,再创建 + if (!kubeconfig) { + return await this.createKubeconfig(opts); + } + return kubeconfig; + } + + + private async createKubeconfig(opts: {vkeService: any}) { + const {vkeService} = opts; const clusterId = this.getClusterId(); const res = await vkeService.request({ action: "CreateKubeconfig", @@ -266,25 +276,38 @@ export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin { throw new Error(`生成VKE Kubeconfig失败:${JSON.stringify(res)}`); } this.logger.info(`已生成临时Kubeconfig:${kubeconfigId}`); - return kubeconfigId; + + const config = await this.getKubeconfig({ vkeService, kubeconfigId }); + return config; } - private async getKubeconfig(vkeService: any, kubeconfigId: string) { + private async getKubeconfig(opts: {vkeService: any, kubeconfigId?: string, userId?:number }) { + const {vkeService, kubeconfigId, userId} = opts; const clusterId = this.getClusterId(); + const query: any = { + ClusterIds: [clusterId], + Types: [this.kubeconfigType], + } + if (kubeconfigId) { + query.Ids = [kubeconfigId]; + } + if (userId) { + query.UserIds = [userId]; + } const res = await vkeService.request({ action: "ListKubeconfigs", method: "POST", body: { - Filter: { - ClusterIds: [clusterId], - Ids: [kubeconfigId], - Types: [this.kubeconfigType], - }, + Filter: query, PageNumber: 1, PageSize: 10, }, }); const items = res.Result?.Items || res.Items || []; + if (items.length === 0) { + return null; + } + const item = items.find((it: any) => it.Id === kubeconfigId) || items[0]; const kubeconfig = item?.Kubeconfig; if (!kubeconfig) { @@ -293,25 +316,25 @@ export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin { return this.decodeKubeconfig(kubeconfig); } - private async deleteKubeconfig(vkeService: any, kubeconfigId?: string) { - if (!kubeconfigId) { - return; - } - const clusterId = this.getClusterId(); - try { - await vkeService.request({ - action: "DeleteKubeconfigs", - method: "POST", - body: { - ClusterId: clusterId, - Ids: [kubeconfigId], - }, - }); - this.logger.info(`已删除临时Kubeconfig:${kubeconfigId}`); - } catch (e) { - this.logger.warn(`删除临时Kubeconfig失败:${e.message || e}`); - } - } + // private async deleteKubeconfig(vkeService: any, kubeconfigId?: string) { + // if (!kubeconfigId) { + // return; + // } + // const clusterId = this.getClusterId(); + // try { + // await vkeService.request({ + // action: "DeleteKubeconfigs", + // method: "POST", + // body: { + // ClusterId: clusterId, + // Ids: [kubeconfigId], + // }, + // }); + // this.logger.info(`已删除临时Kubeconfig:${kubeconfigId}`); + // } catch (e) { + // this.logger.warn(`删除临时Kubeconfig失败:${e.message || e}`); + // } + // } private getClusterId() { if (!this.clusterId) { @@ -476,24 +499,19 @@ export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin { } const access = await this.getAccess(this.accessId); const vkeService = await this.getVkeService(access); - const kubeconfigId = await this.createKubeconfig(vkeService); - - try { - const kubeconfig = await this.getKubeconfig(vkeService, kubeconfigId); - const k8sClient = new this.K8sClient({ - kubeConfigStr: kubeconfig, - logger: this.logger, - skipTLSVerify: this.skipTLSVerify, - }); - const res = await k8sClient.getSecrets({ namespace: this.namespace || "default" }); - const list = res.body?.items || res.items || []; - return list.map((item: any) => ({ - label: item.metadata.name, - value: item.metadata.name, - })); - } finally { - await this.deleteKubeconfig(vkeService, kubeconfigId); - } + const userId = await access.getUserId(); + const kubeconfig = await this.getOrCreateKubeconfig({ vkeService, userId }); + const k8sClient = new this.K8sClient({ + kubeConfigStr: kubeconfig, + logger: this.logger, + skipTLSVerify: this.skipTLSVerify, + }); + const res = await k8sClient.getSecrets({ namespace: this.namespace || "default" }); + const list = res.body?.items || res.items || []; + return list.map((item: any) => ({ + label: item.metadata.name, + value: item.metadata.name, + })); } } From 4a88f795e1d8ffa20e62d5fc6ce6c6e8148a1b96 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:10:58 +0800 Subject: [PATCH 06/13] =?UTF-8?q?chore:=20vke=20kubeconfig=20=E6=9C=89?= =?UTF-8?q?=E6=95=88=E6=9C=9F=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugins/plugin-deploy-to-vke.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.ts index fe58bf23e..cc1dc61ad 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.ts @@ -268,7 +268,7 @@ export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin { body: { ClusterId: clusterId, Type: this.kubeconfigType, - ValidDuration: 3600, + ValidDuration: 26280, //3年 }, }); const kubeconfigId = res.Result?.Id || res.Id; @@ -303,7 +303,18 @@ export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin { PageSize: 10, }, }); - const items = res.Result?.Items || res.Items || []; + let items = res.Result?.Items || res.Items || []; + if (items.length === 0) { + return null; + } + const now = new Date(); + items = items.filter((it: any) => { + if (!it.ExpireTime) { + return true; + } + const expireTime = new Date(it.ExpireTime); + return expireTime > now; + }); if (items.length === 0) { return null; } From 21e5aed3f318d0fbbe3a8f459596a80a54e52eb1 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:25:28 +0800 Subject: [PATCH 07/13] =?UTF-8?q?chore:=20=E4=BF=AE=E5=A4=8D=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/service/npm-registry-resolver.test.ts | 31 - .../src/service/runtime-deps-service.ts | 773 ----------------- ...e-deps-service.test.ts => runtime.test.ts} | 173 +++- packages/core/pipeline/src/service/runtime.ts | 776 +++++++++++++++++- 4 files changed, 901 insertions(+), 852 deletions(-) delete mode 100644 packages/core/pipeline/src/service/npm-registry-resolver.test.ts delete mode 100644 packages/core/pipeline/src/service/runtime-deps-service.ts rename packages/core/pipeline/src/service/{runtime-deps-service.test.ts => runtime.test.ts} (71%) diff --git a/packages/core/pipeline/src/service/npm-registry-resolver.test.ts b/packages/core/pipeline/src/service/npm-registry-resolver.test.ts deleted file mode 100644 index 988db70f8..000000000 --- a/packages/core/pipeline/src/service/npm-registry-resolver.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import assert from "assert"; -import { NpmRegistryResolver } from "./runtime-deps-service.js"; - -describe("NpmRegistryResolver", () => { - it("chooses the fastest successful registry in auto mode", async () => { - const resolver = new NpmRegistryResolver({ - mode: "auto", - candidates: ["https://slow.example.com", "https://fast.example.com"], - probeTimeoutMs: 100, - cacheTtlMs: 1000, - }); - resolver.probe = async registryUrl => ({ - registryUrl, - ok: true, - elapsedMs: registryUrl.includes("fast") ? 10 : 50, - }); - const result = await resolver.resolve(); - assert.equal(result, "https://fast.example.com"); - }); - - it("uses fixed registry without probing", async () => { - const resolver = new NpmRegistryResolver({ - mode: "fixed", - fixedUrl: "https://registry.example.com", - probeTimeoutMs: 100, - cacheTtlMs: 1000, - }); - const result = await resolver.resolve(); - assert.equal(result, "https://registry.example.com"); - }); -}); diff --git a/packages/core/pipeline/src/service/runtime-deps-service.ts b/packages/core/pipeline/src/service/runtime-deps-service.ts deleted file mode 100644 index 8c7ea7a4c..000000000 --- a/packages/core/pipeline/src/service/runtime-deps-service.ts +++ /dev/null @@ -1,773 +0,0 @@ -import fs from "fs"; -import path from "path"; -import { spawn } from "child_process"; -import crypto from "crypto"; -import { createRequire } from "module"; -import { pathToFileURL } from "url"; -import { logger as defaultLogger } from "@certd/basic"; -import type { Registry } from "../registry/registry.js"; -export type ILogger = { - info: (message: string) => void; - warn?: (message: string) => void; - error?: (message: string, ...args: any[]) => void; -}; - -export type ImportRuntime = (specifier: string, logger?: ILogger) => Promise; - -export type EnsureRuntimeDepsOptions = { - pluginKeys: string | string[]; - logger?: ILogger; -}; - -export interface IRuntimeDepsService { - ensureRuntimeDependencies(options: EnsureRuntimeDepsOptions): Promise; - importRuntime: ImportRuntime; -} - -export type RuntimeDependencyPluginDefine = { - name: string; - key?: string; - title?: string; - version?: string; - pluginType?: string; - addonType?: string; - dependPlugins?: Record; - dependPackages?: Record; -}; - -type RegisteredDefineLike = RuntimeDependencyPluginDefine & { - key?: string; - pluginType?: string; - addonType?: string; - dependPlugins?: Record; - dependPackages?: Record; -}; - -type DependencyConflict = { - packageName: string; - ranges: Array<{ pluginName: string; range: string }>; -}; - -type CollectDependenciesResult = { - dependencies: Record; - conflicts: DependencyConflict[]; -}; - -type InstallResult = { - registryUrl: string; - packageJsonPath: string; -}; - -type RuntimeImportResolveResult = { - resolved: string; - packageName: string; -}; - -type CommandRunnerResult = { - stdout: string; - stderr: string; - code: number; -}; - -type CommandRunner = { - run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise; -}; - -export type NpmRegistryResolverConfig = { - mode?: "auto" | "fixed" | "system"; - fixedUrl?: string; - candidates?: string[]; - probeTimeoutMs?: number; - cacheTtlMs?: number; -}; - -export type RegistryProbeResult = { - registryUrl: string; - ok: boolean; - elapsedMs: number; -}; - -export class NpmRegistryResolver { - config: NpmRegistryResolverConfig; - private cache?: { registryUrl: string; expiresAt: number }; - - constructor(config?: NpmRegistryResolverConfig) { - this.config = config || {}; - } - - async resolve(): Promise { - const config = this.config; - if (config?.mode === "fixed" && config.fixedUrl) { - return config.fixedUrl; - } - if (config?.mode === "system") { - return ""; - } - const cached = this.cache; - if (cached && cached.expiresAt > Date.now()) { - return cached.registryUrl; - } - const candidates = (config?.candidates || []).filter(Boolean); - if (candidates.length === 0) { - return ""; - } - const probes = await Promise.allSettled(candidates.map(registryUrl => this.probe(registryUrl))); - const okList = probes.map(item => (item.status === "fulfilled" ? item.value : null)).filter((item): item is RegistryProbeResult => !!item && item.ok); - if (okList.length > 0) { - okList.sort((a, b) => a.elapsedMs - b.elapsedMs); - const best = okList[0].registryUrl; - this.cache = { registryUrl: best, expiresAt: Date.now() + (config?.cacheTtlMs || 6 * 60 * 60 * 1000) }; - return best; - } - return ""; - } - - async probe(registryUrl: string): Promise { - const timeoutMs = this.config?.probeTimeoutMs || 3000; - const started = Date.now(); - try { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeoutMs); - try { - const res = await fetch(`${registryUrl.replace(/\/$/, "")}/-/ping`, { signal: controller.signal }); - return { registryUrl, ok: res.ok, elapsedMs: Date.now() - started }; - } finally { - clearTimeout(timer); - } - } catch { - return { registryUrl, ok: false, elapsedMs: Date.now() - started }; - } - } -} - -export type RuntimeDepsConfig = { - rootDir?: string; - autoInstall?: boolean; - enabled?: boolean; - installTimeoutMs?: number; - pnpmCommand?: string; - lazyDependencies?: Record; - registry?: NpmRegistryResolverConfig; -}; - -function normalizeRange(range: string) { - return range.trim().replace(/^\^/, "").replace(/^~?/, ""); -} - -function areRangesCompatible(a: string, b: string) { - if (!a || !b) { - return true; - } - if (a === "*" || b === "*") { - return true; - } - const left = normalizeRange(a).split("."); - const right = normalizeRange(b).split("."); - return left[0] === right[0]; -} - -const PROCESS_LOCKS = new Map>(); - -class DefaultCommandRunner implements CommandRunner { - async run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise { - return await new Promise(resolve => { - let stdout = ""; - let stderr = ""; - let settled = false; - const child = spawn(command, args, { cwd: options.cwd, env: options.env, windowsHide: true, shell: process.platform === "win32" }); - const timer = setTimeout(() => { - if (settled) { - return; - } - settled = true; - child.kill("SIGTERM"); - resolve({ stdout, stderr: stderr || `command timeout after ${options.timeoutMs}ms`, code: 1 }); - }, options.timeoutMs); - child.stdout?.on("data", chunk => { - stdout += chunk.toString(); - }); - child.stderr?.on("data", chunk => { - stderr += chunk.toString(); - }); - child.on("error", error => { - if (settled) { - return; - } - settled = true; - clearTimeout(timer); - resolve({ stdout, stderr: error.message, code: 1 }); - }); - child.on("close", code => { - if (settled) { - return; - } - settled = true; - clearTimeout(timer); - resolve({ stdout, stderr, code: code || 0 }); - }); - }); - } -} - -export class RuntimeDepsService { - runtimeDepsRootDir: string; - autoInstall: boolean; - enabled: boolean; - installTimeoutMs: number; - pnpmCommand: string; - lazyDependencies: Record; - registryResolver!: NpmRegistryResolver; - commandRunner: CommandRunner = new DefaultCommandRunner(); - pluginLazyDependencies: Record = {}; - private installPromises = new Map>(); - private registriesMap: Record; pluginType: string; addonType?: string }> | null = null; - - constructor(config: RuntimeDepsConfig, registries: any) { - this.runtimeDepsRootDir = config?.rootDir ?? "./data/.runtime-deps"; - this.autoInstall = config?.autoInstall ?? true; - this.enabled = config?.enabled ?? true; - this.installTimeoutMs = config?.installTimeoutMs ?? 120000; - this.pnpmCommand = config?.pnpmCommand ?? ""; - this.lazyDependencies = config?.lazyDependencies ?? {}; - this.registryResolver = new NpmRegistryResolver(config?.registry); - if (registries) { - this.setRegistries(registries); - } - } - - setRegistries(registries: { pluginRegistry?: Registry; accessRegistry?: Registry; notificationRegistry?: Registry; dnsProviderRegistry?: Registry; addonRegistry?: Registry }) { - const map: Record; pluginType: string; addonType?: string }> = {}; - if (registries.pluginRegistry) { - map["plugin"] = { registry: registries.pluginRegistry, pluginType: "plugin" }; - } - if (registries.accessRegistry) { - map["access"] = { registry: registries.accessRegistry, pluginType: "access" }; - } - if (registries.notificationRegistry) { - map["notification"] = { registry: registries.notificationRegistry, pluginType: "notification" }; - } - if (registries.dnsProviderRegistry) { - map["dnsProvider"] = { registry: registries.dnsProviderRegistry, pluginType: "dnsProvider" }; - } - if (registries.addonRegistry) { - map["addon"] = { registry: registries.addonRegistry, pluginType: "addon", addonType: "" }; - } - this.registriesMap = map; - } - - collectDependencies(plugins: RuntimeDependencyPluginDefine[]): CollectDependenciesResult { - const merged: Record = {}; - const seen: Record> = {}; - for (const plugin of plugins) { - const deps = plugin.dependPackages || {}; - for (const [packageName, range] of Object.entries(deps)) { - seen[packageName] ||= []; - seen[packageName].push({ pluginName: plugin.name, range }); - } - } - const conflicts: DependencyConflict[] = []; - for (const [packageName, ranges] of Object.entries(seen)) { - const first = ranges[0]?.range; - if (!first) { - continue; - } - const conflict = ranges.some(item => !areRangesCompatible(first, item.range)); - if (conflict) { - conflicts.push({ packageName, ranges }); - continue; - } - merged[packageName] = first; - } - return { dependencies: merged, conflicts }; - } - - async ensureInstalled(options: { plugins: RuntimeDependencyPluginDefine[]; logger?: ILogger }): Promise { - const { plugins, logger: log } = options; - const { dependencies, conflicts } = this.resolveDependenciesFromPlugins(plugins); - if (conflicts.length > 0) { - const conflict = conflicts[0]; - throw new Error(`动态依赖版本冲突: ${conflict.packageName} => ${conflict.ranges.map(item => `${item.pluginName}:${item.range}`).join(", ")}`); - } - return await this.ensureDependencies({ dependencies, logger: log }); - } - - async ensureDependencies(options: { dependencies: Record; logger?: ILogger }): Promise { - const { dependencies, logger: log } = options; - if (!this.enabled) { - return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; - } - if (!this.autoInstall) { - return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; - } - const dependenciesHash = this.createDependenciesHash(dependencies); - let installPromise = this.installPromises.get(dependenciesHash); - if (installPromise) { - const nodeModulesPath = path.join(this.getRuntimeDepsRootDir(), "node_modules"); - if (!fs.existsSync(nodeModulesPath)) { - this.installPromises.delete(dependenciesHash); - installPromise = undefined; - } - } - if (!installPromise) { - installPromise = this.doEnsureInstalled({ dependencies, logger: log }).catch(error => { - this.installPromises.delete(dependenciesHash); - throw error; - }); - this.installPromises.set(dependenciesHash, installPromise); - } - return await installPromise; - } - - resolveDependenciesFromPlugins(plugins: RuntimeDependencyPluginDefine[]): CollectDependenciesResult { - const expandedPlugins = plugins.flatMap(plugin => this.resolvePluginDependencies(plugin)); - return this.collectDependencies(expandedPlugins); - } - - async ensureRuntimeDependencies(options: { pluginKeys: string | string[]; logger?: ILogger }): Promise { - const { pluginKeys, logger: log } = options; - const keys = Array.isArray(pluginKeys) ? pluginKeys : [pluginKeys]; - const pluginDefines = keys.map(pluginKey => this.getDefineByPluginKey(pluginKey)); - if (pluginDefines.every(pluginDefine => !pluginDefine.dependPackages && !pluginDefine.dependPlugins)) { - return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; - } - const expandedPluginDefines = pluginDefines.flatMap(pluginDefine => this.resolvePluginDependencies(pluginDefine)); - return await this.ensureInstalled({ plugins: expandedPluginDefines, logger: log }); - } - - async importRuntime(specifier: string, logger: ILogger = defaultLogger) { - if (this.isNativeImportSpecifier(specifier)) { - return await import(specifier); - } - const resolved = await this.resolveImportSpecifier(specifier, logger); - return await import(pathToFileURL(resolved).href); - } - - private async resolveImportSpecifier(specifier: string, logger: ILogger = defaultLogger) { - try { - return this.resolveRuntimeSpecifier(specifier).resolved; - } catch (runtimeError: any) { - if (!this.isModuleNotFoundError(runtimeError)) { - throw runtimeError; - } - return await this.resolveMissingRuntimeSpecifier(specifier, runtimeError, logger); - } - } - - private async resolveMissingRuntimeSpecifier(specifier: string, runtimeError: any, logger?: ILogger) { - const packageName = this.parsePackageName(specifier); - const mergedDeps = this.getMergedLazyDependencies(); - const lazyRange = mergedDeps[packageName]; - if (!lazyRange) { - try { - return this.resolveProjectSpecifier(specifier, runtimeError).resolved; - } catch { - throw new Error(`动态依赖未安装且未配置懒加载版本: ${packageName}`); - } - } - try { - await this.ensureLazyDependency(packageName, logger); - return this.resolveRuntimeSpecifier(specifier).resolved; - } catch (lazyError: any) { - return this.resolveProjectSpecifier(specifier, lazyError).resolved; - } - } - - private isNativeImportSpecifier(specifier: string) { - return specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("file:") || specifier.startsWith("node:"); - } - - private resolveRuntimeSpecifier(specifier: string): RuntimeImportResolveResult { - const packageName = this.parsePackageName(specifier); - const packageJsonPath = path.join(this.getRuntimeDepsRootDir(), "package.json"); - const require = createRequire(packageJsonPath); - const resolved = require.resolve(specifier); - return { packageName, resolved }; - } - - private resolveProjectSpecifier(specifier: string, cause?: any): RuntimeImportResolveResult { - try { - const packageName = this.parsePackageName(specifier); - const packageJsonPath = path.resolve("package.json"); - const require = createRequire(packageJsonPath); - const resolved = require.resolve(specifier); - return { packageName, resolved }; - } catch (projectError: any) { - if (cause) { - projectError.cause = cause; - } - throw projectError; - } - } - - private parsePackageName(specifier: string) { - if (!specifier || specifier.trim() !== specifier) { - throw new Error(`动态依赖导入路径无效: ${specifier}`); - } - const parts = specifier.split("/"); - if (specifier.startsWith("@")) { - if (parts.length < 2 || !parts[0] || !parts[1]) { - throw new Error(`动态依赖导入路径无效: ${specifier}`); - } - return `${parts[0]}/${parts[1]}`; - } - if (!parts[0]) { - throw new Error(`动态依赖导入路径无效: ${specifier}`); - } - return parts[0]; - } - - private async ensureLazyDependency(packageName: string, logger?: ILogger) { - const range = this.lazyDependencies?.[packageName]; - if (!range) { - throw new Error(`动态依赖未安装且未配置懒加载版本: ${packageName}`); - } - await this.ensureDependencies({ dependencies: { [packageName]: range }, logger }); - } - - private isModuleNotFoundError(error: any) { - return error?.code === "MODULE_NOT_FOUND" || error?.code === "ERR_MODULE_NOT_FOUND"; - } - - resolvePluginDependencies(current: RuntimeDependencyPluginDefine): RuntimeDependencyPluginDefine[] { - const resolved: RuntimeDependencyPluginDefine[] = []; - const visited = new Set(); - const visit = (item: RuntimeDependencyPluginDefine) => { - const key = this.buildPluginDependencyKey(item); - if (visited.has(key)) { - return; - } - visited.add(key); - resolved.push(item); - for (const [dependencyName, expectedRange] of Object.entries(item.dependPlugins || {})) { - const dependency = this.getDefineByPluginKey(dependencyName, item); - if (!isPluginVersionCompatible(dependency, expectedRange)) { - throw new Error(`插件依赖版本冲突: ${item.name} 依赖 ${dependencyName}@${expectedRange},当前版本为 ${dependency.version || "未声明"}`); - } - visit(dependency); - } - }; - visit(current); - return resolved; - } - - private buildPluginDependencyKey(plugin: RuntimeDependencyPluginDefine) { - if (plugin.pluginType === "addon" && plugin.addonType) { - return `addon:${plugin.addonType}:${plugin.name}`; - } - const pluginType = plugin.pluginType === "deploy" ? "plugin" : plugin.pluginType || "unknown"; - return `${pluginType}:${plugin.name}`; - } - - private getDefineByPluginKey(pluginKey: string, owner?: RuntimeDependencyPluginDefine): RuntimeDependencyPluginDefine { - const parts = pluginKey.split(":"); - let pluginType: string, name: string, subtype: string | undefined; - if (parts.length === 2) { - [pluginType, name] = parts; - } else if (parts.length === 3) { - [pluginType, subtype, name] = parts; - } else { - const ownerName = owner?.name || pluginKey; - throw new Error(`插件依赖格式错误: ${ownerName} 依赖 ${pluginKey}`); - } - if (!this.registriesMap) { - throw new Error("注册表未设置,请先调用 setRegistries"); - } - const target = this.registriesMap[pluginType]; - if (!target) { - const ownerName = owner?.name || pluginKey; - throw new Error(`插件依赖格式错误: ${ownerName} 依赖 ${pluginKey},未知插件类型 ${pluginType}`); - } - // addon 类型的 key 需要包含 subtype - const registryKey = pluginType === "addon" && subtype ? `${subtype}:${name}` : name; - const define = target.registry.getDefine(registryKey) as RegisteredDefineLike; - if (!define) { - throw new Error(`插件依赖缺失: ${owner?.name || pluginKey} 依赖 ${pluginKey},但该插件未注册或已禁用`); - } - return { ...define, key: pluginKey, pluginType: target.pluginType, addonType: target.addonType }; - } - - private async doEnsureInstalled(options: { dependencies: Record; logger?: ILogger }): Promise { - let { dependencies } = options; - const log = options.logger || defaultLogger; - return await this.withInstallLock(async () => { - const rootDir = this.getRuntimeDepsRootDir(); - const packageJsonPath = path.join(rootDir, "package.json"); - const lockPath = path.join(rootDir, "pnpm-lock.yaml"); - log.info(`第三方依赖安装: ${JSON.stringify(dependencies)}`); - dependencies = this.mergeInstalledDependencies(this.readManifestDependencies(packageJsonPath), dependencies); - const dependenciesHash = this.createDependenciesHash(dependencies); - const statePath = path.join(rootDir, "install-state.json"); - const currentState = this.readInstallState(statePath); - if (currentState?.dependenciesHash === dependenciesHash && fs.existsSync(path.join(rootDir, "node_modules"))) { - log.info("第三方依赖已安装"); - return { registryUrl: currentState.registryUrl || "", packageJsonPath }; - } - const manifest = { name: "certd-runtime-deps", private: true, type: "module", dependencies }; - fs.writeFileSync(packageJsonPath, JSON.stringify(manifest, null, 2), "utf8"); - const registryUrl = await this.registryResolver.resolve(); - const env = this.buildChildEnv(registryUrl); - const command = this.getPnpmCommand(); - const pnpmVersion = await this.getPnpmVersion(command, env); - const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--no-frozen-lockfile", "--reporter=append-only"]; - if (registryUrl) { - args.push(`--registry=${registryUrl}`); - } - log.info(`开始安装第三方依赖: ${Object.keys(dependencies).join(", ")}`); - const result = await this.commandRunner.run(command, args, { cwd: rootDir, timeoutMs: this.installTimeoutMs, env }); - if (result.code !== 0) { - const message = result.stderr || result.stdout || "unknown error"; - this.writeInstallState(statePath, { - ...currentState, - installedAt: currentState?.installedAt, - failedAt: new Date().toISOString(), - registryUrl, - dependenciesHash, - nodeVersion: process.version, - pnpmVersion, - lockFileExists: fs.existsSync(lockPath), - lastError: message, - }); - throw new Error(`动态依赖安装失败: ${message}`); - } - this.writeInstallState(statePath, { installedAt: new Date().toISOString(), registryUrl, dependenciesHash, nodeVersion: process.version, pnpmVersion, lockFileExists: fs.existsSync(lockPath) }); - log.info("第三方依赖安装完成"); - return { registryUrl, packageJsonPath }; - }); - } - - private async withInstallLock(run: () => Promise): Promise { - const rootDir = this.getRuntimeDepsRootDir(); - fs.mkdirSync(rootDir, { recursive: true }); - const lockFile = path.join(rootDir, ".install.lock"); - const previous = PROCESS_LOCKS.get(lockFile); - if (previous) { - await previous.catch(() => undefined); - } - let releaseProcessLock!: () => void; - const current = new Promise(resolve => { - releaseProcessLock = resolve; - }); - PROCESS_LOCKS.set(lockFile, current); - let fd: number | undefined; - try { - fd = await this.acquireFileLock(lockFile); - return await run(); - } finally { - if (fd != null) { - fs.closeSync(fd); - try { - fs.rmSync(lockFile, { force: true }); - } catch { - try { - fs.rmSync(lockFile, { force: true }); - } catch {} - } - } - releaseProcessLock(); - if (PROCESS_LOCKS.get(lockFile) === current) { - PROCESS_LOCKS.delete(lockFile); - } - } - } - - private async acquireFileLock(lockFile: string) { - const deadline = Date.now() + this.installTimeoutMs; - while (true) { - try { - const fd = fs.openSync(lockFile, "wx"); - fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }), "utf8"); - return fd; - } catch (error: any) { - if (error?.code !== "EEXIST") { - throw error; - } - if (Date.now() > deadline) { - throw new Error(`动态依赖安装锁等待超时: ${lockFile}`); - } - await this.waitForExternalLock(lockFile, deadline); - } - } - } - - private async waitForExternalLock(lockFile: string, deadline: number) { - while (fs.existsSync(lockFile)) { - if (Date.now() > deadline) { - throw new Error(`动态依赖安装锁等待超时: ${lockFile}`); - } - await new Promise(resolve => setTimeout(resolve, 300)); - } - } - - async clearRuntimeDeps() { - const rootDir = this.getRuntimeDepsRootDir(); - const normalizedRootDir = path.normalize(rootDir); - if (!normalizedRootDir.endsWith(path.normalize(".runtime-deps"))) { - throw new Error(`动态依赖目录配置异常,拒绝清理: ${rootDir}`); - } - await this.withInstallLock(async () => { - if (fs.existsSync(rootDir)) { - const entries = fs.readdirSync(rootDir); - for (const entry of entries) { - if (entry === ".install.lock") { - continue; - } - fs.rmSync(path.join(rootDir, entry), { recursive: true, force: true }); - } - } - this.installPromises.clear(); - return undefined; - }); - } - - getMergedLazyDependencies(): Record { - return { ...this.lazyDependencies, ...this.pluginLazyDependencies }; - } - - collectPluginDeps(logger?: ILogger) { - if (!this.registriesMap) { - return; - } - const deps: Record = {}; - for (const { registry } of Object.values(this.registriesMap)) { - const defineList = registry.getDefineList(); - for (const define of defineList) { - const dependPackages = (define as any).dependPackages as Record | undefined; - if (!dependPackages) { - continue; - } - for (const [pkgName, range] of Object.entries(dependPackages)) { - const existing = deps[pkgName]; - if (existing && !areRangesCompatible(existing, range)) { - (logger || defaultLogger).warn?.(`懒加载依赖版本冲突: ${pkgName} => ${existing} vs ${range},保留已有版本`); - continue; - } - deps[pkgName] = range; - } - } - } - this.pluginLazyDependencies = deps; - (logger || defaultLogger).info(`从插件注册表收集到 ${Object.keys(deps).length} 个懒加载依赖`); - } - - refreshPluginDeps(logger?: ILogger) { - this.collectPluginDeps(logger); - } - - private readInstallState(statePath: string): any { - if (!fs.existsSync(statePath)) { - return null; - } - try { - return JSON.parse(fs.readFileSync(statePath, "utf8")); - } catch { - return null; - } - } - - private writeInstallState(statePath: string, state: any) { - fs.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf8"); - } - - private readManifestDependencies(packageJsonPath: string): Record { - if (!fs.existsSync(packageJsonPath)) { - return {}; - } - try { - const manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); - return manifest.dependencies || {}; - } catch { - return {}; - } - } - - private mergeInstalledDependencies(installed: Record, requested: Record) { - const dependencies = { ...installed }; - for (const [packageName, range] of Object.entries(requested)) { - const installedRange = dependencies[packageName]; - if (installedRange && !areRangesCompatible(installedRange, range)) { - throw new Error(`动态依赖版本冲突: ${packageName} => installed:${installedRange}, requested:${range}`); - } - dependencies[packageName] = installedRange || range; - } - return dependencies; - } - - private async getPnpmVersion(command: string, env: NodeJS.ProcessEnv) { - const result = await this.commandRunner.run(command, ["--version"], { cwd: this.getRuntimeDepsRootDir(), timeoutMs: Math.min(this.installTimeoutMs, 10000), env }); - if (result.code !== 0) { - return ""; - } - return (result.stdout || result.stderr || "").trim(); - } - - private getPnpmCommand() { - return this.pnpmCommand || "pnpm"; - } - - private buildChildEnv(registryUrl: string) { - const env = { ...process.env }; - for (const key of ["NODE_OPTIONS", "VSCODE_INSPECTOR_OPTIONS", "NODE_INSPECTOR_PORT", "NODE_DEBUG"]) { - if (!env[key]) { - continue; - } - if (key === "NODE_OPTIONS") { - env[key] = this.stripDebugNodeOptions(env[key] as string); - } else { - delete env[key]; - } - } - if (registryUrl) { - env.npm_config_registry = registryUrl; - env.pnpm_config_registry = registryUrl; - } - env.CI = env.CI || "true"; - env.npm_config_confirm_modules_purge = "false"; - env.pnpm_config_confirm_modules_purge = "false"; - return env; - } - - private stripDebugNodeOptions(value: string) { - return value - .split(/\s+/) - .filter(Boolean) - .filter(item => !/^--inspect(-brk|-port)?(=|$)/.test(item)) - .filter(item => !/^--debug(=|$)/.test(item)) - .join(" "); - } - - getRuntimeDepsRootDir() { - return path.resolve(this.runtimeDepsRootDir); - } - - private createDependenciesHash(dependencies: Record) { - return crypto.createHash("sha256").update(JSON.stringify(dependencies)).digest("hex"); - } -} - -function isPluginVersionCompatible(plugin: RuntimeDependencyPluginDefine, expectedRange: string) { - if (!expectedRange || expectedRange === "*") { - return true; - } - if (!plugin.version) { - return false; - } - return areRangesCompatible(expectedRange, plugin.version); -} - -let runtimeDepsServiceInstance: RuntimeDepsService | null = null; - -export function initRuntimeDepsService(config: RuntimeDepsConfig, registries: any): RuntimeDepsService { - runtimeDepsServiceInstance = new RuntimeDepsService(config, registries); - return runtimeDepsServiceInstance; -} - -export function getRuntimeDepsService(): RuntimeDepsService { - if (!runtimeDepsServiceInstance) { - throw new Error("RuntimeDepsService 未初始化"); - } - return runtimeDepsServiceInstance!; -} - -export async function importRuntime(specifier: string, logger: ILogger = defaultLogger): Promise { - return getRuntimeDepsService().importRuntime(specifier, logger); -} diff --git a/packages/core/pipeline/src/service/runtime-deps-service.test.ts b/packages/core/pipeline/src/service/runtime.test.ts similarity index 71% rename from packages/core/pipeline/src/service/runtime-deps-service.test.ts rename to packages/core/pipeline/src/service/runtime.test.ts index 4b3aebcaa..1947840a3 100644 --- a/packages/core/pipeline/src/service/runtime-deps-service.test.ts +++ b/packages/core/pipeline/src/service/runtime.test.ts @@ -2,13 +2,13 @@ import fs from "fs"; import path from "path"; import os from "os"; -import { RuntimeDepsService, type RuntimeDependencyPluginDefine } from "./runtime-deps-service.js"; +import { RuntimeDepsService, NpmRegistryResolver, type RuntimeDependencyPluginDefine } from "./runtime.js"; import { accessRegistry } from "../access/registry.js"; import { pluginRegistry } from "../plugin/registry.js"; describe("RuntimeDepsService", () => { it("detects conflicting dependency ranges across plugins", () => { - const service = new RuntimeDepsService(); + const service = new RuntimeDepsService({}, null); const merged = service.collectDependencies([ { name: "a", dependPackages: { foo: "^1.0.0" } }, { name: "b", dependPackages: { foo: "^1.2.0" } }, @@ -18,7 +18,7 @@ describe("RuntimeDepsService", () => { }); it("reports incompatible dependency ranges", () => { - const service = new RuntimeDepsService(); + const service = new RuntimeDepsService({}, null); const merged = service.collectDependencies([ { name: "a", dependPackages: { foo: "^1.0.0" } }, { name: "b", dependPackages: { foo: "^2.0.0" } }, @@ -29,14 +29,18 @@ describe("RuntimeDepsService", () => { it("builds a runtime package manifest in the target directory", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-")); - const service = new RuntimeDepsService({ rootDir }); + const service = new RuntimeDepsService({ rootDir }, null); service.registryResolver = { - async resolve() { return "https://registry.npmmirror.com"; }, + async resolve() { + return "https://registry.npmmirror.com"; + }, } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } + if (args.includes("--version")) { + return { stdout: "9.1.0\n", stderr: "", code: 0 }; + } assert.equal(args[0], "install"); assert.ok(args.includes("--ignore-workspace")); assert.ok(args.includes("--no-frozen-lockfile")); @@ -51,12 +55,18 @@ describe("RuntimeDepsService", () => { it("installs direct dependency maps without plugin metadata", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-direct-")); - const service = new RuntimeDepsService({ rootDir }); - service.registryResolver = { async resolve() { return ""; } } as any; + const service = new RuntimeDepsService({ rootDir }, null); + service.registryResolver = { + async resolve() { + return ""; + }, + } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } + if (args.includes("--version")) { + return { stdout: "9.1.0\n", stderr: "", code: 0 }; + } fs.mkdirSync(path.join(rootDir, "node_modules"), { recursive: true }); return { stdout: "", stderr: "", code: 0 }; }, @@ -73,20 +83,30 @@ describe("RuntimeDepsService", () => { fs.writeFileSync(path.join(rootDir, "package.json"), JSON.stringify({ name: "runtime-root", type: "module" }), "utf8"); fs.writeFileSync(path.join(packageDir, "package.json"), JSON.stringify({ name: "runtime-only", type: "module", main: "index.js" }), "utf8"); fs.writeFileSync(path.join(packageDir, "index.js"), "export const value = 42;\n", "utf8"); - const service = new RuntimeDepsService({ rootDir }); - service.commandRunner = { async run() { throw new Error("install should not run"); } } as any; + const service = new RuntimeDepsService({ rootDir }, null); + service.commandRunner = { + async run() { + throw new Error("install should not run"); + }, + } as any; const mod = await service.importRuntime("runtime-only"); assert.equal(mod.value, 42); }); it("installs configured lazy dependency when import target is missing", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-lazy-")); - const service = new RuntimeDepsService({ rootDir, lazyDependencies: { "lazy-pkg": "^1.2.3" } }); - service.registryResolver = { async resolve() { return ""; } } as any; + const service = new RuntimeDepsService({ rootDir, lazyDependencies: { "lazy-pkg": "^1.2.3" } }, null); + service.registryResolver = { + async resolve() { + return ""; + }, + } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } + if (args.includes("--version")) { + return { stdout: "9.1.0\n", stderr: "", code: 0 }; + } const packageDir = path.join(rootDir, "node_modules", "lazy-pkg", "sub"); fs.mkdirSync(packageDir, { recursive: true }); fs.writeFileSync(path.join(rootDir, "node_modules", "lazy-pkg", "package.json"), JSON.stringify({ name: "lazy-pkg", type: "module" }), "utf8"); @@ -102,12 +122,18 @@ describe("RuntimeDepsService", () => { it("resolves scoped package names for lazy imports", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-scoped-")); - const service = new RuntimeDepsService({ rootDir, lazyDependencies: { "@scope/lazy": "^2.0.0" } }); - service.registryResolver = { async resolve() { return ""; } } as any; + const service = new RuntimeDepsService({ rootDir, lazyDependencies: { "@scope/lazy": "^2.0.0" } }, null); + service.registryResolver = { + async resolve() { + return ""; + }, + } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } + if (args.includes("--version")) { + return { stdout: "9.1.0\n", stderr: "", code: 0 }; + } const packageDir = path.join(rootDir, "node_modules", "@scope", "lazy", "dist"); fs.mkdirSync(packageDir, { recursive: true }); fs.writeFileSync(path.join(rootDir, "node_modules", "@scope", "lazy", "package.json"), JSON.stringify({ name: "@scope/lazy", type: "module" }), "utf8"); @@ -123,25 +149,31 @@ describe("RuntimeDepsService", () => { it("reports missing lazy dependency configuration", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-lazy-missing-")); - const service = new RuntimeDepsService({ rootDir, lazyDependencies: {} }); + const service = new RuntimeDepsService({ rootDir, lazyDependencies: {} }, null); await assert.rejects(() => service.importRuntime("missing-pkg/sub.js"), /未配置懒加载版本: missing-pkg/); }); it("falls back to project node_modules when lazy dependency is not configured", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-project-fallback-")); - const service = new RuntimeDepsService({ rootDir, lazyDependencies: {} }); + const service = new RuntimeDepsService({ rootDir, lazyDependencies: {} }, null); const mod = await service.importRuntime("dayjs"); assert.equal(typeof mod.default, "function"); }); it("keeps previously installed dependencies when installing a later plugin", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-merge-")); - const service = new RuntimeDepsService({ rootDir }); - service.registryResolver = { async resolve() { return ""; } } as any; + const service = new RuntimeDepsService({ rootDir }, null); + service.registryResolver = { + async resolve() { + return ""; + }, + } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } + if (args.includes("--version")) { + return { stdout: "9.1.0\n", stderr: "", code: 0 }; + } fs.mkdirSync(path.join(rootDir, "node_modules"), { recursive: true }); return { stdout: "", stderr: "", code: 0 }; }, @@ -153,14 +185,16 @@ describe("RuntimeDepsService", () => { }); it("includes npm dependencies from dependent plugins", async () => { - const service = new RuntimeDepsService(); + const service = new RuntimeDepsService({}, { accessRegistry, pluginRegistry }); accessRegistry.register("runtimeDepsAccess", { define: { name: "runtimeDepsAccess", title: "access", dependPackages: { accessOnly: "^1.0.0" } } as any, - target: async () => ({} as any), + target: async () => ({}) as any, }); try { const resolved = service.resolvePluginDependencies({ - name: "deploy", pluginType: "deploy", dependPlugins: { "access:runtimeDepsAccess": "*" }, + name: "deploy", + pluginType: "deploy", + dependPlugins: { "access:runtimeDepsAccess": "*" }, dependPackages: { deployOnly: "^1.0.0" }, }); const merged = service.collectDependencies(resolved); @@ -172,19 +206,25 @@ describe("RuntimeDepsService", () => { it("installs dependencies by registered plugin key", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-key-")); - const service = new RuntimeDepsService({ rootDir }); - service.registryResolver = { async resolve() { return ""; } } as any; + const service = new RuntimeDepsService({ rootDir }, { pluginRegistry, accessRegistry }); + service.registryResolver = { + async resolve() { + return ""; + }, + } as any; service.commandRunner = { async run(command: string, args: string[]) { assert.equal(command, "pnpm"); - if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } + if (args.includes("--version")) { + return { stdout: "9.1.0\n", stderr: "", code: 0 }; + } fs.mkdirSync(path.join(rootDir, "node_modules"), { recursive: true }); return { stdout: "", stderr: "", code: 0 }; }, } as any; pluginRegistry.register("runtimeDepsKey", { define: { name: "runtimeDepsKey", title: "key", dependPackages: { keyed: "^1.0.0" } } as any, - target: async () => ({} as any), + target: async () => ({}) as any, }); try { service.setRegistries({ pluginRegistry, accessRegistry }); @@ -197,22 +237,24 @@ describe("RuntimeDepsService", () => { }); it("reports missing dependent plugins", () => { - const service = new RuntimeDepsService(); + const service = new RuntimeDepsService({}, { accessRegistry, pluginRegistry }); assert.throws(() => service.resolvePluginDependencies({ name: "deploy", pluginType: "deploy", dependPlugins: { "access:access": "*" } }), /插件依赖缺失/); }); it("reports incompatible dependent plugin versions", () => { - const service = new RuntimeDepsService(); + const service = new RuntimeDepsService({}, { accessRegistry, pluginRegistry }); accessRegistry.register("runtimeDepsVersionedAccess", { define: { name: "runtimeDepsVersionedAccess", title: "access", version: "1.4.0", dependPackages: { accessOnly: "^1.0.0" } } as any, - target: async () => ({} as any), + target: async () => ({}) as any, }); try { assert.throws( - () => service.resolvePluginDependencies({ - name: "deploy", pluginType: "deploy", - dependPlugins: { "access:runtimeDepsVersionedAccess": "^2.0.0" }, - }), + () => + service.resolvePluginDependencies({ + name: "deploy", + pluginType: "deploy", + dependPlugins: { "access:runtimeDepsVersionedAccess": "^2.0.0" }, + }), /插件依赖版本冲突/ ); } finally { @@ -221,7 +263,7 @@ describe("RuntimeDepsService", () => { }); it("reports bare dependent plugin names as invalid format", () => { - const service = new RuntimeDepsService(); + const service = new RuntimeDepsService({}, null); assert.throws(() => service.resolvePluginDependencies({ name: "deploy", pluginType: "deploy", dependPlugins: { runtimeDepsBareName: "*" } }), /插件依赖格式错误/); }); @@ -232,30 +274,71 @@ describe("RuntimeDepsService", () => { process.env.NODE_OPTIONS = "--inspect=127.0.0.1:9229 --max-old-space-size=4096"; process.env.VSCODE_INSPECTOR_OPTIONS = '{"inspectorIpc":"test"}'; try { - const service = new RuntimeDepsService({ rootDir }); - service.registryResolver = { async resolve() { return ""; } } as any; + const service = new RuntimeDepsService({ rootDir }, null); + service.registryResolver = { + async resolve() { + return ""; + }, + } as any; service.commandRunner = { async run(command: string, args: string[], options: { env?: NodeJS.ProcessEnv }) { assert.equal(options.env?.NODE_OPTIONS, "--max-old-space-size=4096"); assert.equal(options.env?.VSCODE_INSPECTOR_OPTIONS, undefined); assert.equal(options.env?.CI, "true"); assert.equal(options.env?.pnpm_config_confirm_modules_purge, "false"); - if (args.includes("--version")) { return { stdout: "9.1.0\n", stderr: "", code: 0 }; } + if (args.includes("--version")) { + return { stdout: "9.1.0\n", stderr: "", code: 0 }; + } return { stdout: "", stderr: "", code: 0 }; }, } as any; await service.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] }); } finally { - if (oldNodeOptions == null) { delete process.env.NODE_OPTIONS; } - else { process.env.NODE_OPTIONS = oldNodeOptions; } - if (oldInspectorOptions == null) { delete process.env.VSCODE_INSPECTOR_OPTIONS; } - else { process.env.VSCODE_INSPECTOR_OPTIONS = oldInspectorOptions; } + if (oldNodeOptions == null) { + delete process.env.NODE_OPTIONS; + } else { + process.env.NODE_OPTIONS = oldNodeOptions; + } + if (oldInspectorOptions == null) { + delete process.env.VSCODE_INSPECTOR_OPTIONS; + } else { + process.env.VSCODE_INSPECTOR_OPTIONS = oldInspectorOptions; + } } }); it("rejects clearing unexpected runtime dependency path", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-clear-invalid-")); - const service = new RuntimeDepsService({ rootDir }); + const service = new RuntimeDepsService({ rootDir }, null); await assert.rejects(() => service.clearRuntimeDeps(), /动态依赖目录配置异常/); }); }); + +describe("NpmRegistryResolver", () => { + it("chooses the fastest successful registry in auto mode", async () => { + const resolver = new NpmRegistryResolver({ + mode: "auto", + candidates: ["https://slow.example.com", "https://fast.example.com"], + probeTimeoutMs: 100, + cacheTtlMs: 1000, + }); + resolver.probe = async (registryUrl: string) => ({ + registryUrl, + ok: true, + elapsedMs: registryUrl.includes("fast") ? 10 : 50, + }); + const result = await resolver.resolve(); + assert.equal(result, "https://fast.example.com"); + }); + + it("uses fixed registry without probing", async () => { + const resolver = new NpmRegistryResolver({ + mode: "fixed", + fixedUrl: "https://registry.example.com", + probeTimeoutMs: 100, + cacheTtlMs: 1000, + }); + const result = await resolver.resolve(); + assert.equal(result, "https://registry.example.com"); + }); +}); diff --git a/packages/core/pipeline/src/service/runtime.ts b/packages/core/pipeline/src/service/runtime.ts index 1ded20de5..8c7ea7a4c 100644 --- a/packages/core/pipeline/src/service/runtime.ts +++ b/packages/core/pipeline/src/service/runtime.ts @@ -1,3 +1,773 @@ -// 类型已迁至 runtime-deps-service.ts,通过 index.ts 重新导出 -// 此文件保留空壳,避免已有引用报错 -export * from "./runtime-deps-service.js"; +import fs from "fs"; +import path from "path"; +import { spawn } from "child_process"; +import crypto from "crypto"; +import { createRequire } from "module"; +import { pathToFileURL } from "url"; +import { logger as defaultLogger } from "@certd/basic"; +import type { Registry } from "../registry/registry.js"; +export type ILogger = { + info: (message: string) => void; + warn?: (message: string) => void; + error?: (message: string, ...args: any[]) => void; +}; + +export type ImportRuntime = (specifier: string, logger?: ILogger) => Promise; + +export type EnsureRuntimeDepsOptions = { + pluginKeys: string | string[]; + logger?: ILogger; +}; + +export interface IRuntimeDepsService { + ensureRuntimeDependencies(options: EnsureRuntimeDepsOptions): Promise; + importRuntime: ImportRuntime; +} + +export type RuntimeDependencyPluginDefine = { + name: string; + key?: string; + title?: string; + version?: string; + pluginType?: string; + addonType?: string; + dependPlugins?: Record; + dependPackages?: Record; +}; + +type RegisteredDefineLike = RuntimeDependencyPluginDefine & { + key?: string; + pluginType?: string; + addonType?: string; + dependPlugins?: Record; + dependPackages?: Record; +}; + +type DependencyConflict = { + packageName: string; + ranges: Array<{ pluginName: string; range: string }>; +}; + +type CollectDependenciesResult = { + dependencies: Record; + conflicts: DependencyConflict[]; +}; + +type InstallResult = { + registryUrl: string; + packageJsonPath: string; +}; + +type RuntimeImportResolveResult = { + resolved: string; + packageName: string; +}; + +type CommandRunnerResult = { + stdout: string; + stderr: string; + code: number; +}; + +type CommandRunner = { + run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise; +}; + +export type NpmRegistryResolverConfig = { + mode?: "auto" | "fixed" | "system"; + fixedUrl?: string; + candidates?: string[]; + probeTimeoutMs?: number; + cacheTtlMs?: number; +}; + +export type RegistryProbeResult = { + registryUrl: string; + ok: boolean; + elapsedMs: number; +}; + +export class NpmRegistryResolver { + config: NpmRegistryResolverConfig; + private cache?: { registryUrl: string; expiresAt: number }; + + constructor(config?: NpmRegistryResolverConfig) { + this.config = config || {}; + } + + async resolve(): Promise { + const config = this.config; + if (config?.mode === "fixed" && config.fixedUrl) { + return config.fixedUrl; + } + if (config?.mode === "system") { + return ""; + } + const cached = this.cache; + if (cached && cached.expiresAt > Date.now()) { + return cached.registryUrl; + } + const candidates = (config?.candidates || []).filter(Boolean); + if (candidates.length === 0) { + return ""; + } + const probes = await Promise.allSettled(candidates.map(registryUrl => this.probe(registryUrl))); + const okList = probes.map(item => (item.status === "fulfilled" ? item.value : null)).filter((item): item is RegistryProbeResult => !!item && item.ok); + if (okList.length > 0) { + okList.sort((a, b) => a.elapsedMs - b.elapsedMs); + const best = okList[0].registryUrl; + this.cache = { registryUrl: best, expiresAt: Date.now() + (config?.cacheTtlMs || 6 * 60 * 60 * 1000) }; + return best; + } + return ""; + } + + async probe(registryUrl: string): Promise { + const timeoutMs = this.config?.probeTimeoutMs || 3000; + const started = Date.now(); + try { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), timeoutMs); + try { + const res = await fetch(`${registryUrl.replace(/\/$/, "")}/-/ping`, { signal: controller.signal }); + return { registryUrl, ok: res.ok, elapsedMs: Date.now() - started }; + } finally { + clearTimeout(timer); + } + } catch { + return { registryUrl, ok: false, elapsedMs: Date.now() - started }; + } + } +} + +export type RuntimeDepsConfig = { + rootDir?: string; + autoInstall?: boolean; + enabled?: boolean; + installTimeoutMs?: number; + pnpmCommand?: string; + lazyDependencies?: Record; + registry?: NpmRegistryResolverConfig; +}; + +function normalizeRange(range: string) { + return range.trim().replace(/^\^/, "").replace(/^~?/, ""); +} + +function areRangesCompatible(a: string, b: string) { + if (!a || !b) { + return true; + } + if (a === "*" || b === "*") { + return true; + } + const left = normalizeRange(a).split("."); + const right = normalizeRange(b).split("."); + return left[0] === right[0]; +} + +const PROCESS_LOCKS = new Map>(); + +class DefaultCommandRunner implements CommandRunner { + async run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise { + return await new Promise(resolve => { + let stdout = ""; + let stderr = ""; + let settled = false; + const child = spawn(command, args, { cwd: options.cwd, env: options.env, windowsHide: true, shell: process.platform === "win32" }); + const timer = setTimeout(() => { + if (settled) { + return; + } + settled = true; + child.kill("SIGTERM"); + resolve({ stdout, stderr: stderr || `command timeout after ${options.timeoutMs}ms`, code: 1 }); + }, options.timeoutMs); + child.stdout?.on("data", chunk => { + stdout += chunk.toString(); + }); + child.stderr?.on("data", chunk => { + stderr += chunk.toString(); + }); + child.on("error", error => { + if (settled) { + return; + } + settled = true; + clearTimeout(timer); + resolve({ stdout, stderr: error.message, code: 1 }); + }); + child.on("close", code => { + if (settled) { + return; + } + settled = true; + clearTimeout(timer); + resolve({ stdout, stderr, code: code || 0 }); + }); + }); + } +} + +export class RuntimeDepsService { + runtimeDepsRootDir: string; + autoInstall: boolean; + enabled: boolean; + installTimeoutMs: number; + pnpmCommand: string; + lazyDependencies: Record; + registryResolver!: NpmRegistryResolver; + commandRunner: CommandRunner = new DefaultCommandRunner(); + pluginLazyDependencies: Record = {}; + private installPromises = new Map>(); + private registriesMap: Record; pluginType: string; addonType?: string }> | null = null; + + constructor(config: RuntimeDepsConfig, registries: any) { + this.runtimeDepsRootDir = config?.rootDir ?? "./data/.runtime-deps"; + this.autoInstall = config?.autoInstall ?? true; + this.enabled = config?.enabled ?? true; + this.installTimeoutMs = config?.installTimeoutMs ?? 120000; + this.pnpmCommand = config?.pnpmCommand ?? ""; + this.lazyDependencies = config?.lazyDependencies ?? {}; + this.registryResolver = new NpmRegistryResolver(config?.registry); + if (registries) { + this.setRegistries(registries); + } + } + + setRegistries(registries: { pluginRegistry?: Registry; accessRegistry?: Registry; notificationRegistry?: Registry; dnsProviderRegistry?: Registry; addonRegistry?: Registry }) { + const map: Record; pluginType: string; addonType?: string }> = {}; + if (registries.pluginRegistry) { + map["plugin"] = { registry: registries.pluginRegistry, pluginType: "plugin" }; + } + if (registries.accessRegistry) { + map["access"] = { registry: registries.accessRegistry, pluginType: "access" }; + } + if (registries.notificationRegistry) { + map["notification"] = { registry: registries.notificationRegistry, pluginType: "notification" }; + } + if (registries.dnsProviderRegistry) { + map["dnsProvider"] = { registry: registries.dnsProviderRegistry, pluginType: "dnsProvider" }; + } + if (registries.addonRegistry) { + map["addon"] = { registry: registries.addonRegistry, pluginType: "addon", addonType: "" }; + } + this.registriesMap = map; + } + + collectDependencies(plugins: RuntimeDependencyPluginDefine[]): CollectDependenciesResult { + const merged: Record = {}; + const seen: Record> = {}; + for (const plugin of plugins) { + const deps = plugin.dependPackages || {}; + for (const [packageName, range] of Object.entries(deps)) { + seen[packageName] ||= []; + seen[packageName].push({ pluginName: plugin.name, range }); + } + } + const conflicts: DependencyConflict[] = []; + for (const [packageName, ranges] of Object.entries(seen)) { + const first = ranges[0]?.range; + if (!first) { + continue; + } + const conflict = ranges.some(item => !areRangesCompatible(first, item.range)); + if (conflict) { + conflicts.push({ packageName, ranges }); + continue; + } + merged[packageName] = first; + } + return { dependencies: merged, conflicts }; + } + + async ensureInstalled(options: { plugins: RuntimeDependencyPluginDefine[]; logger?: ILogger }): Promise { + const { plugins, logger: log } = options; + const { dependencies, conflicts } = this.resolveDependenciesFromPlugins(plugins); + if (conflicts.length > 0) { + const conflict = conflicts[0]; + throw new Error(`动态依赖版本冲突: ${conflict.packageName} => ${conflict.ranges.map(item => `${item.pluginName}:${item.range}`).join(", ")}`); + } + return await this.ensureDependencies({ dependencies, logger: log }); + } + + async ensureDependencies(options: { dependencies: Record; logger?: ILogger }): Promise { + const { dependencies, logger: log } = options; + if (!this.enabled) { + return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; + } + if (!this.autoInstall) { + return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; + } + const dependenciesHash = this.createDependenciesHash(dependencies); + let installPromise = this.installPromises.get(dependenciesHash); + if (installPromise) { + const nodeModulesPath = path.join(this.getRuntimeDepsRootDir(), "node_modules"); + if (!fs.existsSync(nodeModulesPath)) { + this.installPromises.delete(dependenciesHash); + installPromise = undefined; + } + } + if (!installPromise) { + installPromise = this.doEnsureInstalled({ dependencies, logger: log }).catch(error => { + this.installPromises.delete(dependenciesHash); + throw error; + }); + this.installPromises.set(dependenciesHash, installPromise); + } + return await installPromise; + } + + resolveDependenciesFromPlugins(plugins: RuntimeDependencyPluginDefine[]): CollectDependenciesResult { + const expandedPlugins = plugins.flatMap(plugin => this.resolvePluginDependencies(plugin)); + return this.collectDependencies(expandedPlugins); + } + + async ensureRuntimeDependencies(options: { pluginKeys: string | string[]; logger?: ILogger }): Promise { + const { pluginKeys, logger: log } = options; + const keys = Array.isArray(pluginKeys) ? pluginKeys : [pluginKeys]; + const pluginDefines = keys.map(pluginKey => this.getDefineByPluginKey(pluginKey)); + if (pluginDefines.every(pluginDefine => !pluginDefine.dependPackages && !pluginDefine.dependPlugins)) { + return { registryUrl: "", packageJsonPath: path.join(this.getRuntimeDepsRootDir(), "package.json") }; + } + const expandedPluginDefines = pluginDefines.flatMap(pluginDefine => this.resolvePluginDependencies(pluginDefine)); + return await this.ensureInstalled({ plugins: expandedPluginDefines, logger: log }); + } + + async importRuntime(specifier: string, logger: ILogger = defaultLogger) { + if (this.isNativeImportSpecifier(specifier)) { + return await import(specifier); + } + const resolved = await this.resolveImportSpecifier(specifier, logger); + return await import(pathToFileURL(resolved).href); + } + + private async resolveImportSpecifier(specifier: string, logger: ILogger = defaultLogger) { + try { + return this.resolveRuntimeSpecifier(specifier).resolved; + } catch (runtimeError: any) { + if (!this.isModuleNotFoundError(runtimeError)) { + throw runtimeError; + } + return await this.resolveMissingRuntimeSpecifier(specifier, runtimeError, logger); + } + } + + private async resolveMissingRuntimeSpecifier(specifier: string, runtimeError: any, logger?: ILogger) { + const packageName = this.parsePackageName(specifier); + const mergedDeps = this.getMergedLazyDependencies(); + const lazyRange = mergedDeps[packageName]; + if (!lazyRange) { + try { + return this.resolveProjectSpecifier(specifier, runtimeError).resolved; + } catch { + throw new Error(`动态依赖未安装且未配置懒加载版本: ${packageName}`); + } + } + try { + await this.ensureLazyDependency(packageName, logger); + return this.resolveRuntimeSpecifier(specifier).resolved; + } catch (lazyError: any) { + return this.resolveProjectSpecifier(specifier, lazyError).resolved; + } + } + + private isNativeImportSpecifier(specifier: string) { + return specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("file:") || specifier.startsWith("node:"); + } + + private resolveRuntimeSpecifier(specifier: string): RuntimeImportResolveResult { + const packageName = this.parsePackageName(specifier); + const packageJsonPath = path.join(this.getRuntimeDepsRootDir(), "package.json"); + const require = createRequire(packageJsonPath); + const resolved = require.resolve(specifier); + return { packageName, resolved }; + } + + private resolveProjectSpecifier(specifier: string, cause?: any): RuntimeImportResolveResult { + try { + const packageName = this.parsePackageName(specifier); + const packageJsonPath = path.resolve("package.json"); + const require = createRequire(packageJsonPath); + const resolved = require.resolve(specifier); + return { packageName, resolved }; + } catch (projectError: any) { + if (cause) { + projectError.cause = cause; + } + throw projectError; + } + } + + private parsePackageName(specifier: string) { + if (!specifier || specifier.trim() !== specifier) { + throw new Error(`动态依赖导入路径无效: ${specifier}`); + } + const parts = specifier.split("/"); + if (specifier.startsWith("@")) { + if (parts.length < 2 || !parts[0] || !parts[1]) { + throw new Error(`动态依赖导入路径无效: ${specifier}`); + } + return `${parts[0]}/${parts[1]}`; + } + if (!parts[0]) { + throw new Error(`动态依赖导入路径无效: ${specifier}`); + } + return parts[0]; + } + + private async ensureLazyDependency(packageName: string, logger?: ILogger) { + const range = this.lazyDependencies?.[packageName]; + if (!range) { + throw new Error(`动态依赖未安装且未配置懒加载版本: ${packageName}`); + } + await this.ensureDependencies({ dependencies: { [packageName]: range }, logger }); + } + + private isModuleNotFoundError(error: any) { + return error?.code === "MODULE_NOT_FOUND" || error?.code === "ERR_MODULE_NOT_FOUND"; + } + + resolvePluginDependencies(current: RuntimeDependencyPluginDefine): RuntimeDependencyPluginDefine[] { + const resolved: RuntimeDependencyPluginDefine[] = []; + const visited = new Set(); + const visit = (item: RuntimeDependencyPluginDefine) => { + const key = this.buildPluginDependencyKey(item); + if (visited.has(key)) { + return; + } + visited.add(key); + resolved.push(item); + for (const [dependencyName, expectedRange] of Object.entries(item.dependPlugins || {})) { + const dependency = this.getDefineByPluginKey(dependencyName, item); + if (!isPluginVersionCompatible(dependency, expectedRange)) { + throw new Error(`插件依赖版本冲突: ${item.name} 依赖 ${dependencyName}@${expectedRange},当前版本为 ${dependency.version || "未声明"}`); + } + visit(dependency); + } + }; + visit(current); + return resolved; + } + + private buildPluginDependencyKey(plugin: RuntimeDependencyPluginDefine) { + if (plugin.pluginType === "addon" && plugin.addonType) { + return `addon:${plugin.addonType}:${plugin.name}`; + } + const pluginType = plugin.pluginType === "deploy" ? "plugin" : plugin.pluginType || "unknown"; + return `${pluginType}:${plugin.name}`; + } + + private getDefineByPluginKey(pluginKey: string, owner?: RuntimeDependencyPluginDefine): RuntimeDependencyPluginDefine { + const parts = pluginKey.split(":"); + let pluginType: string, name: string, subtype: string | undefined; + if (parts.length === 2) { + [pluginType, name] = parts; + } else if (parts.length === 3) { + [pluginType, subtype, name] = parts; + } else { + const ownerName = owner?.name || pluginKey; + throw new Error(`插件依赖格式错误: ${ownerName} 依赖 ${pluginKey}`); + } + if (!this.registriesMap) { + throw new Error("注册表未设置,请先调用 setRegistries"); + } + const target = this.registriesMap[pluginType]; + if (!target) { + const ownerName = owner?.name || pluginKey; + throw new Error(`插件依赖格式错误: ${ownerName} 依赖 ${pluginKey},未知插件类型 ${pluginType}`); + } + // addon 类型的 key 需要包含 subtype + const registryKey = pluginType === "addon" && subtype ? `${subtype}:${name}` : name; + const define = target.registry.getDefine(registryKey) as RegisteredDefineLike; + if (!define) { + throw new Error(`插件依赖缺失: ${owner?.name || pluginKey} 依赖 ${pluginKey},但该插件未注册或已禁用`); + } + return { ...define, key: pluginKey, pluginType: target.pluginType, addonType: target.addonType }; + } + + private async doEnsureInstalled(options: { dependencies: Record; logger?: ILogger }): Promise { + let { dependencies } = options; + const log = options.logger || defaultLogger; + return await this.withInstallLock(async () => { + const rootDir = this.getRuntimeDepsRootDir(); + const packageJsonPath = path.join(rootDir, "package.json"); + const lockPath = path.join(rootDir, "pnpm-lock.yaml"); + log.info(`第三方依赖安装: ${JSON.stringify(dependencies)}`); + dependencies = this.mergeInstalledDependencies(this.readManifestDependencies(packageJsonPath), dependencies); + const dependenciesHash = this.createDependenciesHash(dependencies); + const statePath = path.join(rootDir, "install-state.json"); + const currentState = this.readInstallState(statePath); + if (currentState?.dependenciesHash === dependenciesHash && fs.existsSync(path.join(rootDir, "node_modules"))) { + log.info("第三方依赖已安装"); + return { registryUrl: currentState.registryUrl || "", packageJsonPath }; + } + const manifest = { name: "certd-runtime-deps", private: true, type: "module", dependencies }; + fs.writeFileSync(packageJsonPath, JSON.stringify(manifest, null, 2), "utf8"); + const registryUrl = await this.registryResolver.resolve(); + const env = this.buildChildEnv(registryUrl); + const command = this.getPnpmCommand(); + const pnpmVersion = await this.getPnpmVersion(command, env); + const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--no-frozen-lockfile", "--reporter=append-only"]; + if (registryUrl) { + args.push(`--registry=${registryUrl}`); + } + log.info(`开始安装第三方依赖: ${Object.keys(dependencies).join(", ")}`); + const result = await this.commandRunner.run(command, args, { cwd: rootDir, timeoutMs: this.installTimeoutMs, env }); + if (result.code !== 0) { + const message = result.stderr || result.stdout || "unknown error"; + this.writeInstallState(statePath, { + ...currentState, + installedAt: currentState?.installedAt, + failedAt: new Date().toISOString(), + registryUrl, + dependenciesHash, + nodeVersion: process.version, + pnpmVersion, + lockFileExists: fs.existsSync(lockPath), + lastError: message, + }); + throw new Error(`动态依赖安装失败: ${message}`); + } + this.writeInstallState(statePath, { installedAt: new Date().toISOString(), registryUrl, dependenciesHash, nodeVersion: process.version, pnpmVersion, lockFileExists: fs.existsSync(lockPath) }); + log.info("第三方依赖安装完成"); + return { registryUrl, packageJsonPath }; + }); + } + + private async withInstallLock(run: () => Promise): Promise { + const rootDir = this.getRuntimeDepsRootDir(); + fs.mkdirSync(rootDir, { recursive: true }); + const lockFile = path.join(rootDir, ".install.lock"); + const previous = PROCESS_LOCKS.get(lockFile); + if (previous) { + await previous.catch(() => undefined); + } + let releaseProcessLock!: () => void; + const current = new Promise(resolve => { + releaseProcessLock = resolve; + }); + PROCESS_LOCKS.set(lockFile, current); + let fd: number | undefined; + try { + fd = await this.acquireFileLock(lockFile); + return await run(); + } finally { + if (fd != null) { + fs.closeSync(fd); + try { + fs.rmSync(lockFile, { force: true }); + } catch { + try { + fs.rmSync(lockFile, { force: true }); + } catch {} + } + } + releaseProcessLock(); + if (PROCESS_LOCKS.get(lockFile) === current) { + PROCESS_LOCKS.delete(lockFile); + } + } + } + + private async acquireFileLock(lockFile: string) { + const deadline = Date.now() + this.installTimeoutMs; + while (true) { + try { + const fd = fs.openSync(lockFile, "wx"); + fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }), "utf8"); + return fd; + } catch (error: any) { + if (error?.code !== "EEXIST") { + throw error; + } + if (Date.now() > deadline) { + throw new Error(`动态依赖安装锁等待超时: ${lockFile}`); + } + await this.waitForExternalLock(lockFile, deadline); + } + } + } + + private async waitForExternalLock(lockFile: string, deadline: number) { + while (fs.existsSync(lockFile)) { + if (Date.now() > deadline) { + throw new Error(`动态依赖安装锁等待超时: ${lockFile}`); + } + await new Promise(resolve => setTimeout(resolve, 300)); + } + } + + async clearRuntimeDeps() { + const rootDir = this.getRuntimeDepsRootDir(); + const normalizedRootDir = path.normalize(rootDir); + if (!normalizedRootDir.endsWith(path.normalize(".runtime-deps"))) { + throw new Error(`动态依赖目录配置异常,拒绝清理: ${rootDir}`); + } + await this.withInstallLock(async () => { + if (fs.existsSync(rootDir)) { + const entries = fs.readdirSync(rootDir); + for (const entry of entries) { + if (entry === ".install.lock") { + continue; + } + fs.rmSync(path.join(rootDir, entry), { recursive: true, force: true }); + } + } + this.installPromises.clear(); + return undefined; + }); + } + + getMergedLazyDependencies(): Record { + return { ...this.lazyDependencies, ...this.pluginLazyDependencies }; + } + + collectPluginDeps(logger?: ILogger) { + if (!this.registriesMap) { + return; + } + const deps: Record = {}; + for (const { registry } of Object.values(this.registriesMap)) { + const defineList = registry.getDefineList(); + for (const define of defineList) { + const dependPackages = (define as any).dependPackages as Record | undefined; + if (!dependPackages) { + continue; + } + for (const [pkgName, range] of Object.entries(dependPackages)) { + const existing = deps[pkgName]; + if (existing && !areRangesCompatible(existing, range)) { + (logger || defaultLogger).warn?.(`懒加载依赖版本冲突: ${pkgName} => ${existing} vs ${range},保留已有版本`); + continue; + } + deps[pkgName] = range; + } + } + } + this.pluginLazyDependencies = deps; + (logger || defaultLogger).info(`从插件注册表收集到 ${Object.keys(deps).length} 个懒加载依赖`); + } + + refreshPluginDeps(logger?: ILogger) { + this.collectPluginDeps(logger); + } + + private readInstallState(statePath: string): any { + if (!fs.existsSync(statePath)) { + return null; + } + try { + return JSON.parse(fs.readFileSync(statePath, "utf8")); + } catch { + return null; + } + } + + private writeInstallState(statePath: string, state: any) { + fs.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf8"); + } + + private readManifestDependencies(packageJsonPath: string): Record { + if (!fs.existsSync(packageJsonPath)) { + return {}; + } + try { + const manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); + return manifest.dependencies || {}; + } catch { + return {}; + } + } + + private mergeInstalledDependencies(installed: Record, requested: Record) { + const dependencies = { ...installed }; + for (const [packageName, range] of Object.entries(requested)) { + const installedRange = dependencies[packageName]; + if (installedRange && !areRangesCompatible(installedRange, range)) { + throw new Error(`动态依赖版本冲突: ${packageName} => installed:${installedRange}, requested:${range}`); + } + dependencies[packageName] = installedRange || range; + } + return dependencies; + } + + private async getPnpmVersion(command: string, env: NodeJS.ProcessEnv) { + const result = await this.commandRunner.run(command, ["--version"], { cwd: this.getRuntimeDepsRootDir(), timeoutMs: Math.min(this.installTimeoutMs, 10000), env }); + if (result.code !== 0) { + return ""; + } + return (result.stdout || result.stderr || "").trim(); + } + + private getPnpmCommand() { + return this.pnpmCommand || "pnpm"; + } + + private buildChildEnv(registryUrl: string) { + const env = { ...process.env }; + for (const key of ["NODE_OPTIONS", "VSCODE_INSPECTOR_OPTIONS", "NODE_INSPECTOR_PORT", "NODE_DEBUG"]) { + if (!env[key]) { + continue; + } + if (key === "NODE_OPTIONS") { + env[key] = this.stripDebugNodeOptions(env[key] as string); + } else { + delete env[key]; + } + } + if (registryUrl) { + env.npm_config_registry = registryUrl; + env.pnpm_config_registry = registryUrl; + } + env.CI = env.CI || "true"; + env.npm_config_confirm_modules_purge = "false"; + env.pnpm_config_confirm_modules_purge = "false"; + return env; + } + + private stripDebugNodeOptions(value: string) { + return value + .split(/\s+/) + .filter(Boolean) + .filter(item => !/^--inspect(-brk|-port)?(=|$)/.test(item)) + .filter(item => !/^--debug(=|$)/.test(item)) + .join(" "); + } + + getRuntimeDepsRootDir() { + return path.resolve(this.runtimeDepsRootDir); + } + + private createDependenciesHash(dependencies: Record) { + return crypto.createHash("sha256").update(JSON.stringify(dependencies)).digest("hex"); + } +} + +function isPluginVersionCompatible(plugin: RuntimeDependencyPluginDefine, expectedRange: string) { + if (!expectedRange || expectedRange === "*") { + return true; + } + if (!plugin.version) { + return false; + } + return areRangesCompatible(expectedRange, plugin.version); +} + +let runtimeDepsServiceInstance: RuntimeDepsService | null = null; + +export function initRuntimeDepsService(config: RuntimeDepsConfig, registries: any): RuntimeDepsService { + runtimeDepsServiceInstance = new RuntimeDepsService(config, registries); + return runtimeDepsServiceInstance; +} + +export function getRuntimeDepsService(): RuntimeDepsService { + if (!runtimeDepsServiceInstance) { + throw new Error("RuntimeDepsService 未初始化"); + } + return runtimeDepsServiceInstance!; +} + +export async function importRuntime(specifier: string, logger: ILogger = defaultLogger): Promise { + return getRuntimeDepsService().importRuntime(specifier, logger); +} From 5589da182266891a202a13d353f18d9fff4a326e Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:30:49 +0800 Subject: [PATCH 08/13] =?UTF-8?q?chore:=20=E4=BF=AE=E5=A4=8D=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugins/plugin-deploy-to-vke.test.ts | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.test.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.test.ts index 32bd28a07..eadf82872 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.test.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-vke.test.ts @@ -19,16 +19,23 @@ describe("VolcengineDeployToVKE", () => { plugin.kubeconfigType = "Public"; plugin.logger = { info: () => undefined } as any; - let requestBody: any; - const kubeconfigId = await (plugin as any).createKubeconfig({ + let createRequestBody: any; + const kubeconfig = ["apiVersion: v1", "clusters:", "- cluster:", " server: https://example.com", " name: vke", "contexts: []", "current-context: vke"].join("\n"); + const vkeService = { request: async (req: any) => { - requestBody = req.body; - return { Result: { Id: "kc-123" } }; + if (req.action === "CreateKubeconfig") { + createRequestBody = req.body; + return { Result: { Id: "kc-123" } }; + } else if (req.action === "ListKubeconfigs") { + return { Result: { Items: [{ Id: "kc-123", Kubeconfig: Buffer.from(kubeconfig).toString("base64") }] } }; + } + return {}; }, - }); + }; + const kubeconfigStr = await (plugin as any).createKubeconfig({ vkeService }); - assert.equal(kubeconfigId, "kc-123"); - assert.equal(requestBody.ClusterId, "cc1234567890123456789"); + assert.equal(createRequestBody.ClusterId, "cc1234567890123456789"); + assert.equal(kubeconfigStr, kubeconfig); }); it("decodes the base64 kubeconfig returned by VKE", async () => { @@ -38,16 +45,15 @@ describe("VolcengineDeployToVKE", () => { const kubeconfig = ["apiVersion: v1", "clusters:", "- cluster:", " server: https://example.com", " name: vke", "contexts: []", "current-context: vke"].join("\n"); - const result = await (plugin as any).getKubeconfig( - { - request: async () => ({ - Result: { - Items: [{ Id: "kc-123", Kubeconfig: Buffer.from(kubeconfig).toString("base64") }], - }, - }), - }, - "kc-123" - ); + const vkeService = { + request: async () => ({ + Result: { + Items: [{ Id: "kc-123", Kubeconfig: Buffer.from(kubeconfig).toString("base64") }], + }, + }), + }; + + const result = await (plugin as any).getKubeconfig({ vkeService, kubeconfigId: "kc-123" }); assert.equal(result, kubeconfig); }); From 5108416904912485da3cf0703cac397eb0188b5e Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:31:35 +0800 Subject: [PATCH 09/13] build: prepare to build --- packages/core/basic/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/basic/build.md b/packages/core/basic/build.md index 376926ba0..e2c91fe7d 100644 --- a/packages/core/basic/build.md +++ b/packages/core/basic/build.md @@ -1 +1 @@ -02:45 +23:31 From 83495b32138cf831b4ea02054d12981d9787ebf0 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:34:16 +0800 Subject: [PATCH 10/13] v1.42.5 --- CHANGELOG.md | 12 +++++++++ lerna.json | 2 +- packages/core/acme-client/CHANGELOG.md | 4 +++ packages/core/acme-client/package.json | 4 +-- packages/core/basic/CHANGELOG.md | 4 +++ packages/core/basic/package.json | 2 +- packages/core/pipeline/CHANGELOG.md | 10 +++++++ packages/core/pipeline/package.json | 6 ++--- packages/libs/lib-huawei/CHANGELOG.md | 4 +++ packages/libs/lib-huawei/package.json | 6 ++--- packages/libs/lib-iframe/CHANGELOG.md | 4 +++ packages/libs/lib-iframe/package.json | 2 +- packages/libs/lib-jdcloud/CHANGELOG.md | 4 +++ packages/libs/lib-jdcloud/package.json | 6 ++--- packages/libs/lib-k8s/CHANGELOG.md | 4 +++ packages/libs/lib-k8s/package.json | 4 +-- packages/libs/lib-server/CHANGELOG.md | 6 +++++ packages/libs/lib-server/package.json | 12 ++++----- packages/libs/midway-flyway-js/CHANGELOG.md | 6 +++++ packages/libs/midway-flyway-js/package.json | 2 +- packages/plugins/plugin-cert/CHANGELOG.md | 4 +++ packages/plugins/plugin-cert/package.json | 4 +-- packages/plugins/plugin-lib/CHANGELOG.md | 6 +++++ packages/plugins/plugin-lib/package.json | 8 +++--- packages/ui/certd-client/CHANGELOG.md | 6 +++++ packages/ui/certd-client/package.json | 6 ++--- packages/ui/certd-server/CHANGELOG.md | 11 ++++++++ packages/ui/certd-server/package.json | 30 ++++++++++----------- 28 files changed, 132 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb638d909..e9a5b57a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +### Bug Fixes + +* 修复上传到cos报runtimeDepsService未初始化的问题 ([167b303](https://github.com/certd/certd/commit/167b303faeca02cc11cf97e4be2a3df914852167)) +* 修复dingtalk通知格式没有换行的bug ([7ed1be9](https://github.com/certd/certd/commit/7ed1be994f8b4b74cdeb38743060c912c027248b)) + +### Performance Improvements + +* 给SQLITE_IOERR_WRITE增加友好报错提示,将certd:latest镜像改为certd:slim ([b91c9e4](https://github.com/certd/certd/commit/b91c9e4ea671cb359ef164e27864de1d66cba9d3)) +* 优化vke keubconfig获取方式,改成先查询,如果没有再创建临时config ([604fa5b](https://github.com/certd/certd/commit/604fa5be634d099d797bfee5c2b0f26ce0ac8461)) + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 12ec730e2..99c74acca 100644 --- a/lerna.json +++ b/lerna.json @@ -9,5 +9,5 @@ } }, "npmClient": "pnpm", - "version": "1.42.4" + "version": "1.42.5" } diff --git a/packages/core/acme-client/CHANGELOG.md b/packages/core/acme-client/CHANGELOG.md index 8b01a4871..0583e7eb8 100644 --- a/packages/core/acme-client/CHANGELOG.md +++ b/packages/core/acme-client/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/publishlab/node-acme-client/compare/v1.42.4...v1.42.5) (2026-07-15) + +**Note:** Version bump only for package @certd/acme-client + ## [1.42.4](https://github.com/publishlab/node-acme-client/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/acme-client diff --git a/packages/core/acme-client/package.json b/packages/core/acme-client/package.json index c1fa8a21a..8f2b6592e 100644 --- a/packages/core/acme-client/package.json +++ b/packages/core/acme-client/package.json @@ -3,7 +3,7 @@ "description": "Simple and unopinionated ACME client", "private": false, "author": "nmorsman", - "version": "1.42.4", + "version": "1.42.5", "type": "module", "module": "./dist/index.js", "main": "./dist/index.js", @@ -18,7 +18,7 @@ "types" ], "dependencies": { - "@certd/basic": "^1.42.4", + "@certd/basic": "^1.42.5", "@peculiar/x509": "^1.11.0", "asn1js": "^3.0.5", "axios": "^1.9.0", diff --git a/packages/core/basic/CHANGELOG.md b/packages/core/basic/CHANGELOG.md index 802c41db0..7e26ded27 100644 --- a/packages/core/basic/CHANGELOG.md +++ b/packages/core/basic/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +**Note:** Version bump only for package @certd/basic + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/basic diff --git a/packages/core/basic/package.json b/packages/core/basic/package.json index 8c7e91941..0bc93b32b 100644 --- a/packages/core/basic/package.json +++ b/packages/core/basic/package.json @@ -1,7 +1,7 @@ { "name": "@certd/basic", "private": false, - "version": "1.42.4", + "version": "1.42.5", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/packages/core/pipeline/CHANGELOG.md b/packages/core/pipeline/CHANGELOG.md index 09aa981e2..023e40e68 100644 --- a/packages/core/pipeline/CHANGELOG.md +++ b/packages/core/pipeline/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +### Bug Fixes + +* 修复上传到cos报runtimeDepsService未初始化的问题 ([167b303](https://github.com/certd/certd/commit/167b303faeca02cc11cf97e4be2a3df914852167)) + +### Performance Improvements + +* 优化vke keubconfig获取方式,改成先查询,如果没有再创建临时config ([604fa5b](https://github.com/certd/certd/commit/604fa5be634d099d797bfee5c2b0f26ce0ac8461)) + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) ### Bug Fixes diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json index 218b20c3e..f37b4c6fb 100644 --- a/packages/core/pipeline/package.json +++ b/packages/core/pipeline/package.json @@ -1,7 +1,7 @@ { "name": "@certd/pipeline", "private": false, - "version": "1.42.4", + "version": "1.42.5", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -21,8 +21,8 @@ "lint": "eslint --fix" }, "dependencies": { - "@certd/basic": "^1.42.4", - "@certd/plus-core": "^1.42.4", + "@certd/basic": "^1.42.5", + "@certd/plus-core": "^1.42.5", "dayjs": "^1.11.7", "lodash-es": "^4.17.21", "reflect-metadata": "^0.2.2" diff --git a/packages/libs/lib-huawei/CHANGELOG.md b/packages/libs/lib-huawei/CHANGELOG.md index 83f932c7e..89efeba72 100644 --- a/packages/libs/lib-huawei/CHANGELOG.md +++ b/packages/libs/lib-huawei/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +**Note:** Version bump only for package @certd/lib-huawei + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/lib-huawei diff --git a/packages/libs/lib-huawei/package.json b/packages/libs/lib-huawei/package.json index 176b959ad..fe0b3ffbd 100644 --- a/packages/libs/lib-huawei/package.json +++ b/packages/libs/lib-huawei/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-huawei", "private": false, - "version": "1.42.4", + "version": "1.42.5", "main": "./dist/bundle.js", "module": "./dist/bundle.js", "types": "./dist/d/index.d.ts", @@ -26,10 +26,10 @@ "@typescript-eslint/eslint-plugin": "^8.26.1", "@typescript-eslint/parser": "^8.26.1", "cross-env": "^7.0.3", + "eslint": "^8.57.0", "esmock": "^2.7.5", "prettier": "3.3.3", - "tslib": "^2.8.1", - "eslint": "^8.57.0" + "tslib": "^2.8.1" }, "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" } diff --git a/packages/libs/lib-iframe/CHANGELOG.md b/packages/libs/lib-iframe/CHANGELOG.md index 31324ea78..28e35d779 100644 --- a/packages/libs/lib-iframe/CHANGELOG.md +++ b/packages/libs/lib-iframe/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +**Note:** Version bump only for package @certd/lib-iframe + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/lib-iframe diff --git a/packages/libs/lib-iframe/package.json b/packages/libs/lib-iframe/package.json index 1b033f740..2e3154cd3 100644 --- a/packages/libs/lib-iframe/package.json +++ b/packages/libs/lib-iframe/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-iframe", "private": false, - "version": "1.42.4", + "version": "1.42.5", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/packages/libs/lib-jdcloud/CHANGELOG.md b/packages/libs/lib-jdcloud/CHANGELOG.md index f2fe238bf..dc18b8c78 100644 --- a/packages/libs/lib-jdcloud/CHANGELOG.md +++ b/packages/libs/lib-jdcloud/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +**Note:** Version bump only for package @certd/jdcloud + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/jdcloud diff --git a/packages/libs/lib-jdcloud/package.json b/packages/libs/lib-jdcloud/package.json index 476e34f7d..c47948afd 100644 --- a/packages/libs/lib-jdcloud/package.json +++ b/packages/libs/lib-jdcloud/package.json @@ -1,6 +1,6 @@ { "name": "@certd/jdcloud", - "version": "1.42.4", + "version": "1.42.5", "description": "jdcloud openApi sdk", "main": "./dist/bundle.js", "module": "./dist/bundle.js", @@ -35,12 +35,12 @@ "chai": "^5.1.0", "config": "^1.30.0", "cross-env": "^7.0.3", + "eslint": "^8.57.0", "esmock": "^2.7.5", "js-yaml": "^3.11.0", "mocha": "^10.6.0", "prettier": "3.3.3", - "tslib": "^2.8.1", - "eslint": "^8.57.0" + "tslib": "^2.8.1" }, "engines": { "node": ">= 8.6.0", diff --git a/packages/libs/lib-k8s/CHANGELOG.md b/packages/libs/lib-k8s/CHANGELOG.md index 7fe0455e0..864a19912 100644 --- a/packages/libs/lib-k8s/CHANGELOG.md +++ b/packages/libs/lib-k8s/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +**Note:** Version bump only for package @certd/lib-k8s + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/lib-k8s diff --git a/packages/libs/lib-k8s/package.json b/packages/libs/lib-k8s/package.json index 884c0e94c..3fb18e7d5 100644 --- a/packages/libs/lib-k8s/package.json +++ b/packages/libs/lib-k8s/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-k8s", "private": false, - "version": "1.42.4", + "version": "1.42.5", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -21,7 +21,7 @@ "lint": "eslint --fix" }, "dependencies": { - "@certd/basic": "^1.42.4", + "@certd/basic": "^1.42.5", "@kubernetes/client-node": "0.21.0" }, "devDependencies": { diff --git a/packages/libs/lib-server/CHANGELOG.md b/packages/libs/lib-server/CHANGELOG.md index 8eacd7fc9..a67c6677c 100644 --- a/packages/libs/lib-server/CHANGELOG.md +++ b/packages/libs/lib-server/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +### Bug Fixes + +* 修复上传到cos报runtimeDepsService未初始化的问题 ([167b303](https://github.com/certd/certd/commit/167b303faeca02cc11cf97e4be2a3df914852167)) + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) ### Bug Fixes diff --git a/packages/libs/lib-server/package.json b/packages/libs/lib-server/package.json index cf5d3a727..249d5ef53 100644 --- a/packages/libs/lib-server/package.json +++ b/packages/libs/lib-server/package.json @@ -1,6 +1,6 @@ { "name": "@certd/lib-server", - "version": "1.42.4", + "version": "1.42.5", "description": "midway with flyway, sql upgrade way ", "private": false, "type": "module", @@ -29,11 +29,11 @@ ], "license": "AGPL", "dependencies": { - "@certd/acme-client": "^1.42.4", - "@certd/basic": "^1.42.4", - "@certd/pipeline": "^1.42.4", - "@certd/plugin-lib": "^1.42.4", - "@certd/plus-core": "^1.42.4", + "@certd/acme-client": "^1.42.5", + "@certd/basic": "^1.42.5", + "@certd/pipeline": "^1.42.5", + "@certd/plugin-lib": "^1.42.5", + "@certd/plus-core": "^1.42.5", "@midwayjs/cache": "3.14.0", "@midwayjs/core": "3.20.11", "@midwayjs/i18n": "3.20.13", diff --git a/packages/libs/midway-flyway-js/CHANGELOG.md b/packages/libs/midway-flyway-js/CHANGELOG.md index 4387d3279..2a2505c57 100644 --- a/packages/libs/midway-flyway-js/CHANGELOG.md +++ b/packages/libs/midway-flyway-js/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +### Performance Improvements + +* 给SQLITE_IOERR_WRITE增加友好报错提示,将certd:latest镜像改为certd:slim ([b91c9e4](https://github.com/certd/certd/commit/b91c9e4ea671cb359ef164e27864de1d66cba9d3)) + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/midway-flyway-js diff --git a/packages/libs/midway-flyway-js/package.json b/packages/libs/midway-flyway-js/package.json index 625be1329..037f5e394 100644 --- a/packages/libs/midway-flyway-js/package.json +++ b/packages/libs/midway-flyway-js/package.json @@ -1,6 +1,6 @@ { "name": "@certd/midway-flyway-js", - "version": "1.42.4", + "version": "1.42.5", "description": "midway with flyway, sql upgrade way ", "private": false, "type": "module", diff --git a/packages/plugins/plugin-cert/CHANGELOG.md b/packages/plugins/plugin-cert/CHANGELOG.md index 883cd55a5..035afaaa3 100644 --- a/packages/plugins/plugin-cert/CHANGELOG.md +++ b/packages/plugins/plugin-cert/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +**Note:** Version bump only for package @certd/plugin-cert + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/plugin-cert diff --git a/packages/plugins/plugin-cert/package.json b/packages/plugins/plugin-cert/package.json index d8e14ad92..38a62f883 100644 --- a/packages/plugins/plugin-cert/package.json +++ b/packages/plugins/plugin-cert/package.json @@ -1,7 +1,7 @@ { "name": "@certd/plugin-cert", "private": false, - "version": "1.42.4", + "version": "1.42.5", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,7 +20,7 @@ "lint": "eslint --fix" }, "dependencies": { - "@certd/plugin-lib": "^1.42.4" + "@certd/plugin-lib": "^1.42.5" }, "devDependencies": { "@types/chai": "^4.3.12", diff --git a/packages/plugins/plugin-lib/CHANGELOG.md b/packages/plugins/plugin-lib/CHANGELOG.md index cc90f9688..6f5bbe21e 100644 --- a/packages/plugins/plugin-lib/CHANGELOG.md +++ b/packages/plugins/plugin-lib/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +### Bug Fixes + +* 修复上传到cos报runtimeDepsService未初始化的问题 ([167b303](https://github.com/certd/certd/commit/167b303faeca02cc11cf97e4be2a3df914852167)) + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) ### Bug Fixes diff --git a/packages/plugins/plugin-lib/package.json b/packages/plugins/plugin-lib/package.json index a01889b31..b20de51c6 100644 --- a/packages/plugins/plugin-lib/package.json +++ b/packages/plugins/plugin-lib/package.json @@ -1,7 +1,7 @@ { "name": "@certd/plugin-lib", "private": false, - "version": "1.42.4", + "version": "1.42.5", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -17,9 +17,9 @@ "lint": "eslint --fix" }, "dependencies": { - "@certd/acme-client": "^1.42.4", - "@certd/basic": "^1.42.4", - "@certd/pipeline": "^1.42.4", + "@certd/acme-client": "^1.42.5", + "@certd/basic": "^1.42.5", + "@certd/pipeline": "^1.42.5", "dayjs": "^1.11.7", "jszip": "^3.10.1", "lodash-es": "^4.17.21", diff --git a/packages/ui/certd-client/CHANGELOG.md b/packages/ui/certd-client/CHANGELOG.md index 55cf22b64..97aa3dfc1 100644 --- a/packages/ui/certd-client/CHANGELOG.md +++ b/packages/ui/certd-client/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +### Bug Fixes + +* 修复上传到cos报runtimeDepsService未初始化的问题 ([167b303](https://github.com/certd/certd/commit/167b303faeca02cc11cf97e4be2a3df914852167)) + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) **Note:** Version bump only for package @certd/ui-client diff --git a/packages/ui/certd-client/package.json b/packages/ui/certd-client/package.json index 717ebf026..05d2cd75f 100644 --- a/packages/ui/certd-client/package.json +++ b/packages/ui/certd-client/package.json @@ -1,6 +1,6 @@ { "name": "@certd/ui-client", - "version": "1.42.4", + "version": "1.42.5", "private": true, "scripts": { "dev": "vite --open", @@ -104,8 +104,8 @@ "zod-defaults": "^0.1.3" }, "devDependencies": { - "@certd/lib-iframe": "^1.42.4", - "@certd/pipeline": "^1.42.4", + "@certd/lib-iframe": "^1.42.5", + "@certd/pipeline": "^1.42.5", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@types/chai": "^4.3.12", diff --git a/packages/ui/certd-server/CHANGELOG.md b/packages/ui/certd-server/CHANGELOG.md index a3c9b79a1..3feb25809 100644 --- a/packages/ui/certd-server/CHANGELOG.md +++ b/packages/ui/certd-server/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +### Bug Fixes + +* 修复上传到cos报runtimeDepsService未初始化的问题 ([167b303](https://github.com/certd/certd/commit/167b303faeca02cc11cf97e4be2a3df914852167)) +* 修复dingtalk通知格式没有换行的bug ([7ed1be9](https://github.com/certd/certd/commit/7ed1be994f8b4b74cdeb38743060c912c027248b)) + +### Performance Improvements + +* 优化vke keubconfig获取方式,改成先查询,如果没有再创建临时config ([604fa5b](https://github.com/certd/certd/commit/604fa5be634d099d797bfee5c2b0f26ce0ac8461)) + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) ### Bug Fixes diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index fbceace15..0389ecdce 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -1,6 +1,6 @@ { "name": "@certd/ui-server", - "version": "1.42.4", + "version": "1.42.5", "description": "fast-server base midway", "private": true, "type": "module", @@ -42,20 +42,20 @@ "lint1": "eslint --fix" }, "dependencies": { - "@certd/acme-client": "^1.42.4", - "@certd/basic": "^1.42.4", - "@certd/commercial-core": "^1.42.4", + "@certd/acme-client": "^1.42.5", + "@certd/basic": "^1.42.5", + "@certd/commercial-core": "^1.42.5", "@certd/cv4pve-api-javascript": "^8.4.2", - "@certd/jdcloud": "^1.42.4", - "@certd/lib-huawei": "^1.42.4", - "@certd/lib-k8s": "^1.42.4", - "@certd/lib-server": "^1.42.4", - "@certd/midway-flyway-js": "^1.42.4", - "@certd/pipeline": "^1.42.4", - "@certd/plugin-cert": "^1.42.4", - "@certd/plugin-lib": "^1.42.4", - "@certd/plugin-plus": "^1.42.4", - "@certd/plus-core": "^1.42.4", + "@certd/jdcloud": "^1.42.5", + "@certd/lib-huawei": "^1.42.5", + "@certd/lib-k8s": "^1.42.5", + "@certd/lib-server": "^1.42.5", + "@certd/midway-flyway-js": "^1.42.5", + "@certd/pipeline": "^1.42.5", + "@certd/plugin-cert": "^1.42.5", + "@certd/plugin-lib": "^1.42.5", + "@certd/plugin-plus": "^1.42.5", + "@certd/plus-core": "^1.42.5", "@koa/cors": "^5.0.0", "@midwayjs/bootstrap": "3.20.11", "@midwayjs/cache": "3.14.0", @@ -126,6 +126,7 @@ "@types/nodemailer": "^6.4.8", "c8": "^10.1.2", "cross-env": "^7.0.3", + "eslint": "^7.32.0", "esmock": "^2.7.5", "mocha": "^10.6.0", "mwts": "^1.3.0", @@ -134,7 +135,6 @@ "rimraf": "^5.0.5", "ts-node": "^10.9.2", "tslib": "^2.8.1", - "eslint":"^7.32.0", "typescript": "^5.4.2", "why-is-node-running": "^3.2.2" }, From 2e530bfdb0b49a14d73a69e332259a35f0cac704 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:35:05 +0800 Subject: [PATCH 11/13] build: trigger build image --- trigger/build.trigger | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trigger/build.trigger b/trigger/build.trigger index f8e0bc578..f1d0cff04 100644 --- a/trigger/build.trigger +++ b/trigger/build.trigger @@ -1 +1 @@ -02:48 +23:35 From 89806b828a80ec023e601f24d9421cb9d5091154 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:35:15 +0800 Subject: [PATCH 12/13] build: publish --- docs/guide/changelogs/CHANGELOG.md | 12 ++++++++++++ packages/core/acme-client/package.json | 2 +- packages/core/basic/package.json | 2 +- packages/core/pipeline/package.json | 2 +- packages/libs/lib-huawei/package.json | 2 +- packages/libs/lib-iframe/package.json | 2 +- packages/libs/lib-jdcloud/package.json | 2 +- packages/libs/lib-k8s/package.json | 2 +- packages/libs/lib-server/package.json | 2 +- packages/libs/midway-flyway-js/package.json | 2 +- packages/plugins/plugin-cert/package.json | 2 +- packages/plugins/plugin-lib/package.json | 2 +- 12 files changed, 23 insertions(+), 11 deletions(-) diff --git a/docs/guide/changelogs/CHANGELOG.md b/docs/guide/changelogs/CHANGELOG.md index eb638d909..e9a5b57a3 100644 --- a/docs/guide/changelogs/CHANGELOG.md +++ b/docs/guide/changelogs/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15) + +### Bug Fixes + +* 修复上传到cos报runtimeDepsService未初始化的问题 ([167b303](https://github.com/certd/certd/commit/167b303faeca02cc11cf97e4be2a3df914852167)) +* 修复dingtalk通知格式没有换行的bug ([7ed1be9](https://github.com/certd/certd/commit/7ed1be994f8b4b74cdeb38743060c912c027248b)) + +### Performance Improvements + +* 给SQLITE_IOERR_WRITE增加友好报错提示,将certd:latest镜像改为certd:slim ([b91c9e4](https://github.com/certd/certd/commit/b91c9e4ea671cb359ef164e27864de1d66cba9d3)) +* 优化vke keubconfig获取方式,改成先查询,如果没有再创建临时config ([604fa5b](https://github.com/certd/certd/commit/604fa5be634d099d797bfee5c2b0f26ce0ac8461)) + ## [1.42.4](https://github.com/certd/certd/compare/v1.42.3...v1.42.4) (2026-07-11) ### Bug Fixes diff --git a/packages/core/acme-client/package.json b/packages/core/acme-client/package.json index 8f2b6592e..a974def44 100644 --- a/packages/core/acme-client/package.json +++ b/packages/core/acme-client/package.json @@ -75,5 +75,5 @@ "bugs": { "url": "https://github.com/publishlab/node-acme-client/issues" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/core/basic/package.json b/packages/core/basic/package.json index 0bc93b32b..8a0adb355 100644 --- a/packages/core/basic/package.json +++ b/packages/core/basic/package.json @@ -54,5 +54,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json index f37b4c6fb..6eaf3f489 100644 --- a/packages/core/pipeline/package.json +++ b/packages/core/pipeline/package.json @@ -51,5 +51,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/libs/lib-huawei/package.json b/packages/libs/lib-huawei/package.json index fe0b3ffbd..e29042ee6 100644 --- a/packages/libs/lib-huawei/package.json +++ b/packages/libs/lib-huawei/package.json @@ -31,5 +31,5 @@ "prettier": "3.3.3", "tslib": "^2.8.1" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/libs/lib-iframe/package.json b/packages/libs/lib-iframe/package.json index 2e3154cd3..c40afe542 100644 --- a/packages/libs/lib-iframe/package.json +++ b/packages/libs/lib-iframe/package.json @@ -37,5 +37,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/libs/lib-jdcloud/package.json b/packages/libs/lib-jdcloud/package.json index c47948afd..2f5d6ba3f 100644 --- a/packages/libs/lib-jdcloud/package.json +++ b/packages/libs/lib-jdcloud/package.json @@ -63,5 +63,5 @@ "fetch" ] }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/libs/lib-k8s/package.json b/packages/libs/lib-k8s/package.json index 3fb18e7d5..d003e926a 100644 --- a/packages/libs/lib-k8s/package.json +++ b/packages/libs/lib-k8s/package.json @@ -38,5 +38,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/libs/lib-server/package.json b/packages/libs/lib-server/package.json index 249d5ef53..74cd4cfcf 100644 --- a/packages/libs/lib-server/package.json +++ b/packages/libs/lib-server/package.json @@ -69,5 +69,5 @@ "typeorm": "^0.3.20", "typescript": "^5.4.2" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/libs/midway-flyway-js/package.json b/packages/libs/midway-flyway-js/package.json index 037f5e394..a44ceab29 100644 --- a/packages/libs/midway-flyway-js/package.json +++ b/packages/libs/midway-flyway-js/package.json @@ -52,5 +52,5 @@ "typeorm": "^0.3.20", "typescript": "^5.4.2" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/plugins/plugin-cert/package.json b/packages/plugins/plugin-cert/package.json index 38a62f883..e93247fab 100644 --- a/packages/plugins/plugin-cert/package.json +++ b/packages/plugins/plugin-cert/package.json @@ -38,5 +38,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } diff --git a/packages/plugins/plugin-lib/package.json b/packages/plugins/plugin-lib/package.json index b20de51c6..76b662484 100644 --- a/packages/plugins/plugin-lib/package.json +++ b/packages/plugins/plugin-lib/package.json @@ -45,5 +45,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7" + "gitHead": "83495b32138cf831b4ea02054d12981d9787ebf0" } From 743617dbda4d272be9bd3277f0ac2b44c7110607 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 15 Jul 2026 23:54:59 +0800 Subject: [PATCH 13/13] build: release --- trigger/release.trigger | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trigger/release.trigger b/trigger/release.trigger index 4c82fa5e8..6a639182c 100644 --- a/trigger/release.trigger +++ b/trigger/release.trigger @@ -1 +1 @@ -03:41 +23:54