Compare commits

..

18 Commits

Author SHA1 Message Date
xiaojunnuo f1b67049d1 perf: 证书成功通知增加有效期显示 2026-07-28 19:22:12 +08:00
xiaojunnuo 8cbca5761e chore(pipeline): adjust npm install log truncation logic
change log output to take last 2000 chars instead of first 2000 for both stdout/stderr, and add consistent empty log placeholder text
2026-07-27 00:35:00 +08:00
xiaojunnuo 967846bef5 perf(tencent-clb): 为腾讯云CLB部署插件支持远程选择输入
- 重构插件输入配置,使用createRemoteSelectInputDefine统一封装远程选择组件
- 新增onGetCLBList、onGetListenerList、onGetDomainList方法实现数据拉取和格式转换
- 补全完整的单元测试用例,覆盖输入配置校验和数据映射逻辑
2026-07-26 19:01:18 +08:00
xiaojunnuo 18b2d3ac20 perf: 优化第三方依赖安装失败日志输出 2026-07-23 18:35:04 +08:00
xiaojunnuo 7d22fe3d7d perf: 优化SqliteError: disk I/O error报错修复提示 2026-07-23 10:49:00 +08:00
xiaojunnuo ee67b6c042 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-07-21 14:13:16 +08:00
xiaojunnuo 1cfa76683b perf: 优化宝塔报错提示 2026-07-21 14:13:07 +08:00
xiaojunnuo 4662e45e58 build: release 2026-07-19 01:28:05 +08:00
xiaojunnuo 1fefbdc9ab build: publish 2026-07-19 01:14:35 +08:00
xiaojunnuo 1cb2a57c55 build: trigger build image 2026-07-19 01:14:24 +08:00
xiaojunnuo 246ee83015 v1.42.6 2026-07-19 01:13:42 +08:00
xiaojunnuo 335ddfc7a5 build: prepare to build 2026-07-19 01:12:01 +08:00
xiaojunnuo 5b500830a1 fix: 修复正常批量删除流水线报权限不足的bug 2026-07-19 01:05:03 +08:00
xiaojunnuo 5f53b81c75 perf: 优化动态加载依赖镜像地址,多次重试 2026-07-18 21:07:56 +08:00
xiaojunnuo c8c269f612 chore: 1 2026-07-17 15:44:23 +08:00
xiaojunnuo 743617dbda build: release 2026-07-15 23:54:59 +08:00
xiaojunnuo 89806b828a build: publish 2026-07-15 23:35:15 +08:00
xiaojunnuo 2e530bfdb0 build: trigger build image 2026-07-15 23:35:05 +08:00
50 changed files with 624 additions and 162 deletions
+3 -1
View File
@@ -70,6 +70,8 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web
- 先读本文,再按任务读取具体代码或技能文件。
- PowerShell 读取中文、Markdown、locale、文档类文件时使用 `Get-Content -Raw -Encoding UTF8`;仍乱码时先执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()`
- PowerShell 中用 `rg` 搜索含引号、括号、反斜杠的 pattern 时,优先用单引号包裹整个 pattern,例如 `rg 'await import\("tencentcloud-sdk-nodejs' packages/ui/certd-server/src -g '*.ts'`
- 手工编辑或创建文件时优先使用 `apply_patch`。单个文件内有多处不连续改动时,拆成多个独立的 `*** Update File` 块,每块只改一处附近上下文;不要在同一个 update hunk 里强塞多个 `@@`
- 只有真正机械化的大批量替换、格式化或生成任务才考虑脚本/工具。若必须使用临时脚本,应放在临时目录并在同一个受控步骤内完成创建、执行、删除;不要把临时脚本落在仓库里跨多步工具调用执行。
- 不要主动运行 `pnpm install`;缺依赖、TTY、网络导致安装或测试失败时,停止尝试并说明环境问题。
- 优先沿用现有模块、插件、service、页面模式;不要为形式上的复用制造过度抽象。
- 代码可读性优先于短写法。复杂条件、三元表达式、链式调用、内联对象和多层 helper 调用要拆成命名清晰的中间变量或小方法。
@@ -210,7 +212,7 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web
- 后端业务数据、接口、实体、权限、迁移:改 `packages/ui/certd-server/src/modules``src/controller`
- 表单、列表、插件配置 UI:改 `packages/ui/certd-client/src/views/certd` 及对应 `src/api`
## 注意事项
## 其他注意事项
### 旧版数据兼容
-1
View File
@@ -1 +0,0 @@

+10
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
### Bug Fixes
* 修复正常批量删除流水线报权限不足的bug ([5b50083](https://github.com/certd/certd/commit/5b500830a122c6c42dab054e57fed509050f94da))
### Performance Improvements
* 优化动态加载依赖镜像地址,多次重试 ([5f53b81](https://github.com/certd/certd/commit/5f53b81c75dd242b4260ac08cae14c6d1a08a883))
## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15)
### Bug Fixes
+6 -4
View File
@@ -107,11 +107,13 @@ https://certd.handfree.work/
**镜像版本:**
| 版本标签 | 基础系统 | 说明 |
| 标签 | 指定版本 | 基础系统 | 说明 |
| --- | --- | --- | --- |
| `latest` / `[version]` | Alpine Linux | 默认版本,镜像体积小 |
| `slim` / `[version]-slim` | Debian slim | 基于glibcdns解析兼容性好(可能需要配置security_opt -seccomp=unconfined |
| `armv7` / `[version]-armv7` | Alpine Linux | ARMv7 架构专用版本 |
| `latest` | `[version]` | Alpine Linux | 默认版本,镜像体积小 |
| `slim` | `[version]-slim` | Debian slim | 基于glibcdns解析兼容性好(可能需要配置security_opt -seccomp=unconfined |
| `armv7` | `[version]-armv7` | Alpine Linux | ARMv7 架构专用版本 |
**镜像地址:**
+22
View File
@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.42.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
### Bug Fixes
* 修复正常批量删除流水线报权限不足的bug ([5b50083](https://github.com/certd/certd/commit/5b500830a122c6c42dab054e57fed509050f94da))
### Performance Improvements
* 优化动态加载依赖镜像地址,多次重试 ([5f53b81](https://github.com/certd/certd/commit/5f53b81c75dd242b4260ac08cae14c6d1a08a883))
## [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
+1 -1
View File
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.42.5"
"version": "1.42.6"
}
+4
View File
@@ -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.6](https://github.com/publishlab/node-acme-client/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/acme-client
## [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
+3 -3
View File
@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.42.5",
"version": "1.42.6",
"type": "module",
"module": "./dist/index.js",
"main": "./dist/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.42.5",
"@certd/basic": "^1.42.6",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.9.0",
@@ -75,5 +75,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+4
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/basic
## [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 -1
View File
@@ -1 +1 @@
23:31
01:12
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.42.5",
"version": "1.42.6",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -54,5 +54,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+6
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
### Performance Improvements
* 优化动态加载依赖镜像地址,多次重试 ([5f53b81](https://github.com/certd/certd/commit/5f53b81c75dd242b4260ac08cae14c6d1a08a883))
## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15)
### Bug Fixes
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.42.5",
"version": "1.42.6",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -21,8 +21,8 @@
"lint": "eslint --fix"
},
"dependencies": {
"@certd/basic": "^1.42.5",
"@certd/plus-core": "^1.42.5",
"@certd/basic": "^1.42.6",
"@certd/plus-core": "^1.42.6",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.2.2"
@@ -51,5 +51,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
@@ -1,4 +1,4 @@
import assert from "assert";
import assert from "assert";
import fs from "fs";
import path from "path";
import os from "os";
@@ -34,6 +34,9 @@ describe("RuntimeDepsService", () => {
async resolve() {
return "https://registry.npmmirror.com";
},
async resolveOrdered() {
return ["https://registry.npmmirror.com"];
},
} as any;
service.commandRunner = {
async run(command: string, args: string[]) {
@@ -60,6 +63,9 @@ describe("RuntimeDepsService", () => {
async resolve() {
return "";
},
async resolveOrdered() {
return [""];
},
} as any;
service.commandRunner = {
async run(command: string, args: string[]) {
@@ -100,6 +106,9 @@ describe("RuntimeDepsService", () => {
async resolve() {
return "";
},
async resolveOrdered() {
return [""];
},
} as any;
service.commandRunner = {
async run(command: string, args: string[]) {
@@ -127,6 +136,9 @@ describe("RuntimeDepsService", () => {
async resolve() {
return "";
},
async resolveOrdered() {
return [""];
},
} as any;
service.commandRunner = {
async run(command: string, args: string[]) {
@@ -167,6 +179,9 @@ describe("RuntimeDepsService", () => {
async resolve() {
return "";
},
async resolveOrdered() {
return [""];
},
} as any;
service.commandRunner = {
async run(command: string, args: string[]) {
@@ -211,6 +226,9 @@ describe("RuntimeDepsService", () => {
async resolve() {
return "";
},
async resolveOrdered() {
return [""];
},
} as any;
service.commandRunner = {
async run(command: string, args: string[]) {
@@ -279,6 +297,9 @@ describe("RuntimeDepsService", () => {
async resolve() {
return "";
},
async resolveOrdered() {
return [""];
},
} as any;
service.commandRunner = {
async run(command: string, args: string[], options: { env?: NodeJS.ProcessEnv }) {
@@ -315,7 +336,7 @@ describe("RuntimeDepsService", () => {
});
describe("NpmRegistryResolver", () => {
it("chooses the fastest successful registry in auto mode", async () => {
it("returns the fastest successful registry via resolve()", async () => {
const resolver = new NpmRegistryResolver({
mode: "auto",
candidates: ["https://slow.example.com", "https://fast.example.com"],
@@ -341,4 +362,60 @@ describe("NpmRegistryResolver", () => {
const result = await resolver.resolve();
assert.equal(result, "https://registry.example.com");
});
it("returns ordered list via resolveOrdered (fastest first)", 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.resolveOrdered();
assert.deepEqual(result, ["https://fast.example.com", "https://slow.example.com"]);
});
it("includes failed registries at the end of resolveOrdered", async () => {
const resolver = new NpmRegistryResolver({
mode: "auto",
candidates: ["https://good.example.com", "https://bad.example.com"],
probeTimeoutMs: 100,
cacheTtlMs: 1000,
});
resolver.probe = async (registryUrl: string) => {
if (registryUrl.includes("bad")) {
return { registryUrl, ok: false, elapsedMs: 200 };
}
return { registryUrl, ok: true, elapsedMs: 30 };
};
const result = await resolver.resolveOrdered();
assert.deepEqual(result, ["https://good.example.com", "https://bad.example.com"]);
});
it("returns empty ordered list when no candidates", async () => {
const resolver = new NpmRegistryResolver({ mode: "auto", candidates: [] });
const result = await resolver.resolveOrdered();
assert.deepEqual(result, []);
const single = await resolver.resolve();
assert.equal(single, "");
});
it("re-validates cached URL on resolveOrdered call", async () => {
let probeCount = 0;
const resolver = new NpmRegistryResolver({
mode: "auto",
candidates: ["https://mirror.example.com"],
cacheTtlMs: 60000,
});
resolver.probe = async (registryUrl: string) => {
probeCount++;
return { registryUrl, ok: true, elapsedMs: 10 };
};
const first = await resolver.resolveOrdered();
assert.deepEqual(first, ["https://mirror.example.com"]);
assert.equal(probeCount, 1);
const second = await resolver.resolveOrdered();
assert.deepEqual(second, ["https://mirror.example.com"]);
assert.equal(probeCount, 2);
});
});
+85 -24
View File
@@ -1,4 +1,4 @@
import fs from "fs";
import fs from "fs";
import path from "path";
import { spawn } from "child_process";
import crypto from "crypto";
@@ -89,7 +89,7 @@ export type RegistryProbeResult = {
export class NpmRegistryResolver {
config: NpmRegistryResolverConfig;
private cache?: { registryUrl: string; expiresAt: number };
private cache?: { orderedUrls: string[]; expiresAt: number };
constructor(config?: NpmRegistryResolverConfig) {
this.config = config || {};
@@ -105,21 +105,67 @@ export class NpmRegistryResolver {
}
const cached = this.cache;
if (cached && cached.expiresAt > Date.now()) {
return cached.registryUrl;
const fastUrl = cached.orderedUrls[0];
if (fastUrl) {
const probeResult = await this.probe(fastUrl);
if (probeResult.ok) {
return cached.orderedUrls[0] || "";
}
}
this.cache = undefined;
}
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;
const orderedUrls = await this.internalProbeAll(candidates);
this.cache = { orderedUrls, expiresAt: Date.now() + (config?.cacheTtlMs ?? 300_000) };
return orderedUrls[0] || "";
}
return "";
async resolveOrdered(): Promise<string[]> {
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()) {
const fastUrl = cached.orderedUrls[0];
if (fastUrl) {
const probeResult = await this.probe(fastUrl);
if (probeResult.ok) {
return cached.orderedUrls;
}
}
this.cache = undefined;
}
const candidates = (config?.candidates || []).filter(Boolean);
if (candidates.length === 0) {
return [];
}
const orderedUrls = await this.internalProbeAll(candidates);
this.cache = { orderedUrls, expiresAt: Date.now() + (config?.cacheTtlMs ?? 300_000) };
return orderedUrls;
}
private async internalProbeAll(candidates: string[]): Promise<string[]> {
const probes = await Promise.allSettled(candidates.map(registryUrl => this.probe(registryUrl)));
const okList: RegistryProbeResult[] = [];
const failList: RegistryProbeResult[] = [];
for (const item of probes) {
const result = item.status === "fulfilled" ? item.value : null;
if (result && result.ok) {
okList.push(result);
} else if (result) {
failList.push(result);
}
}
okList.sort((a, b) => a.elapsedMs - b.elapsedMs);
failList.sort((a, b) => a.elapsedMs - b.elapsedMs);
return [...okList.map(r => r.registryUrl), ...failList.map(r => r.registryUrl)];
}
async probe(registryUrl: string): Promise<RegistryProbeResult> {
@@ -368,6 +414,7 @@ export class RuntimeDepsService {
await this.ensureLazyDependency(packageName, logger);
return this.resolveRuntimeSpecifier(specifier).resolved;
} catch (lazyError: any) {
logger?.error?.(`动态依赖安装失败: ${lazyError.message}`);
return this.resolveProjectSpecifier(specifier, lazyError).resolved;
}
}
@@ -508,30 +555,44 @@ export class RuntimeDepsService {
const env = this.buildChildEnv(registryUrl);
const command = this.getPnpmCommand();
const pnpmVersion = await this.getPnpmVersion(command, env);
const allRegistryUrls = await this.registryResolver.resolveOrdered();
const urlsToTry = allRegistryUrls.length > 0 ? allRegistryUrls : [""];
let lastError: string | undefined;
for (const tryUrl of urlsToTry) {
const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--no-frozen-lockfile", "--reporter=append-only"];
if (registryUrl) {
args.push(`--registry=${registryUrl}`);
if (tryUrl) {
args.push(`--registry=${tryUrl}`);
}
const tryEnv = tryUrl ? this.buildChildEnv(tryUrl) : env;
log.info(`开始安装第三方依赖: ${Object.keys(dependencies).join(", ")}${tryUrl ? `,镜像: ${tryUrl}` : ""}`);
const result = await this.commandRunner.run(command, args, { cwd: rootDir, timeoutMs: this.installTimeoutMs, env: tryEnv });
if (result.code === 0) {
this.writeInstallState(statePath, { installedAt: new Date().toISOString(), registryUrl: tryUrl, dependenciesHash, nodeVersion: process.version, pnpmVersion, lockFileExists: fs.existsSync(lockPath) });
log.info(`${result.stdout?.slice(-2000) || "无npm安装日志输出"}`);
log.info("第三方依赖安装完成");
return { registryUrl: tryUrl, packageJsonPath };
}
const errOutput = (result.stderr || "").trim();
const outOutput = (result.stdout || "").trim();
lastError = errOutput || outOutput || "unknown error";
log.info(`镜像 ${tryUrl || "默认"} 安装失败,退出码: ${result.code}${urlsToTry.length > 1 ? ",尝试下一个镜像..." : ""}`);
log.info(` pnpm stderr: ${(errOutput || "无npm安装日志输出").slice(-2000)}`);
if (outOutput) {
log.info(` pnpm stdout: ${outOutput.slice(-2000)}`);
}
}
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,
registryUrl: urlsToTry[0],
dependenciesHash,
nodeVersion: process.version,
pnpmVersion,
lockFileExists: fs.existsSync(lockPath),
lastError: message,
lastError,
});
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 };
throw new Error(`动态依赖安装失败: ${lastError}`);
});
}
+4
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/lib-huawei
## [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
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.42.5",
"version": "1.42.6",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -31,5 +31,5 @@
"prettier": "3.3.3",
"tslib": "^2.8.1"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+4
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/lib-iframe
## [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
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.42.5",
"version": "1.42.6",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -37,5 +37,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+4
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/jdcloud
## [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
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.42.5",
"version": "1.42.6",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -63,5 +63,5 @@
"fetch"
]
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+4
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/lib-k8s
## [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
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.42.5",
"version": "1.42.6",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -21,7 +21,7 @@
"lint": "eslint --fix"
},
"dependencies": {
"@certd/basic": "^1.42.5",
"@certd/basic": "^1.42.6",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+6
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
### Bug Fixes
* 修复正常批量删除流水线报权限不足的bug ([5b50083](https://github.com/certd/certd/commit/5b500830a122c6c42dab054e57fed509050f94da))
## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15)
### Bug Fixes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.42.5",
"version": "1.42.6",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -29,11 +29,11 @@
],
"license": "AGPL",
"dependencies": {
"@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",
"@certd/acme-client": "^1.42.6",
"@certd/basic": "^1.42.6",
"@certd/pipeline": "^1.42.6",
"@certd/plugin-lib": "^1.42.6",
"@certd/plus-core": "^1.42.6",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
@@ -69,5 +69,5 @@
"typeorm": "^0.3.20",
"typescript": "^5.4.2"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
@@ -253,7 +253,6 @@ export abstract class BaseService<T> {
if (!Array.isArray(ids)) {
ids = [ids];
}
ids = this.filterIds(ids);
const res = await this.getRepository().find({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@@ -266,7 +265,7 @@ export abstract class BaseService<T> {
},
});
if (!res || res.length === ids.length) {
return;
return ids;
}
throw new PermissionException("权限不足");
}
@@ -280,6 +279,12 @@ export abstract class BaseService<T> {
});
}
async batchDelete(ids: number[], userId: number, projectId?: number): Promise<number> {
if (!ids || ids.length === 0) {
throw new ValidateException("ids不能为空");
}
if (!Array.isArray(ids)) {
ids = [ids];
}
ids = this.filterIds(ids);
if (userId != null) {
const userProjectQuery = this.buildUserProjectQuery(userId, projectId);
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15)
### Performance Improvements
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.42.5",
"version": "1.42.6",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -52,5 +52,5 @@
"typeorm": "^0.3.20",
"typescript": "^5.4.2"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+8 -5
View File
@@ -93,19 +93,21 @@ export class Flyway {
await queryRunner.commitTransaction();
} catch (err) {
this.logger.error(err);
this.errorTip(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-------------");
}
private errorTip(err: any) {
if (err.code === "SQLITE_IOERR_WRITE") {
this.logger.warn("SQLite数据库写入失败,可能您的操作系统版本太低,请将「certd:latest」镜像改为「certd:slim」即可。(如需指定版本可以修改成「certd:[version]-slim」)");
}
}
private async storeSqlExecLog(filename: string, filepath: string, success: boolean, queryRunner: QueryRunner) {
const hash = await this.getFileHash(filepath);
//先删除
@@ -265,6 +267,7 @@ export class Flyway {
await queryRunner.query(sql);
} catch (err: any) {
this.logger.error("exec sql error ", err.message, err);
this.errorTip(err);
throw err;
}
}
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/plugin-cert
## [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
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.42.5",
"version": "1.42.6",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -20,7 +20,7 @@
"lint": "eslint --fix"
},
"dependencies": {
"@certd/plugin-lib": "^1.42.5"
"@certd/plugin-lib": "^1.42.6"
},
"devDependencies": {
"@types/chai": "^4.3.12",
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+4
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/plugin-lib
## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15)
### Bug Fixes
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.42.5",
"version": "1.42.6",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -17,9 +17,9 @@
"lint": "eslint --fix"
},
"dependencies": {
"@certd/acme-client": "^1.42.5",
"@certd/basic": "^1.42.5",
"@certd/pipeline": "^1.42.5",
"@certd/acme-client": "^1.42.6",
"@certd/basic": "^1.42.6",
"@certd/pipeline": "^1.42.6",
"dayjs": "^1.11.7",
"jszip": "^3.10.1",
"lodash-es": "^4.17.21",
@@ -45,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "268cd6cc9cb4f1f3d5d5d77859a82f18f0cb6db7"
"gitHead": "246ee83015bf5589adc2a5fa3d1388c8d9a2a252"
}
+4
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
**Note:** Version bump only for package @certd/ui-client
## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15)
### Bug Fixes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.42.5",
"version": "1.42.6",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -104,8 +104,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.42.5",
"@certd/pipeline": "^1.42.5",
"@certd/lib-iframe": "^1.42.6",
"@certd/pipeline": "^1.42.6",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",
@@ -64,6 +64,7 @@ export default {
projectUserManager: "Project User Management",
myProjectManager: "My Projects",
myProjectDetail: "Project Detail",
projectDetail: "Project Detail",
projectJoin: "Join Project",
currentProject: "Current Project",
projectMemberManager: "Project Member",
@@ -64,6 +64,7 @@ export default {
enterpriseSetting: "企业设置",
myProjectManager: "我的项目",
myProjectDetail: "项目详情",
projectDetail: "项目详情",
projectJoin: "加入项目",
currentProject: "当前项目",
projectMemberManager: "项目成员管理",
@@ -17,7 +17,7 @@
</template>
<script lang="ts">
import { defineComponent, reactive, ref, watch, inject, onMounted } from "vue";
import { defineComponent, reactive, ref, watch, inject, onMounted, Ref } from "vue";
import CertAccessModal from "./access/index.vue";
import { createAccessApi } from "../api";
import { message } from "ant-design-vue";
@@ -64,9 +64,9 @@ export default defineComponent({
setup(props, ctx) {
const api = createAccessApi(props.from);
const target = ref({});
const target:Ref<any> = ref({});
const selectedId = ref();
async function refreshTarget(value) {
async function refreshTarget(value:any) {
selectedId.value = value;
if (value > 0) {
target.value = await api.GetSimpleInfo(value);
@@ -83,7 +83,7 @@ export default defineComponent({
const userStore = useUserStore();
const projectStore = useProjectStore();
async function emitValue(value) {
async function emitValue(value:any) {
const userId = userStore.userInfo.id;
const isEnterprice = projectStore.isEnterprise;
if (pipeline?.value) {
@@ -132,7 +132,7 @@ export default defineComponent({
const providerDefine = ref({});
async function refreshProviderDefine(type) {
async function refreshProviderDefine(type:any) {
providerDefine.value = await api.GetProviderDefine(type);
}
watch(
+6
View File
@@ -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.6](https://github.com/certd/certd/compare/v1.42.5...v1.42.6) (2026-07-18)
### Bug Fixes
* 修复正常批量删除流水线报权限不足的bug ([5b50083](https://github.com/certd/certd/commit/5b500830a122c6c42dab054e57fed509050f94da))
## [1.42.5](https://github.com/certd/certd/compare/v1.42.4...v1.42.5) (2026-07-15)
### Bug Fixes
+14 -14
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.42.5",
"version": "1.42.6",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -42,20 +42,20 @@
"lint1": "eslint --fix"
},
"dependencies": {
"@certd/acme-client": "^1.42.5",
"@certd/basic": "^1.42.5",
"@certd/commercial-core": "^1.42.5",
"@certd/acme-client": "^1.42.6",
"@certd/basic": "^1.42.6",
"@certd/commercial-core": "^1.42.6",
"@certd/cv4pve-api-javascript": "^8.4.2",
"@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",
"@certd/jdcloud": "^1.42.6",
"@certd/lib-huawei": "^1.42.6",
"@certd/lib-k8s": "^1.42.6",
"@certd/lib-server": "^1.42.6",
"@certd/midway-flyway-js": "^1.42.6",
"@certd/pipeline": "^1.42.6",
"@certd/plugin-cert": "^1.42.6",
"@certd/plugin-lib": "^1.42.6",
"@certd/plugin-plus": "^1.42.6",
"@certd/plus-core": "^1.42.6",
"@koa/cors": "^5.0.0",
"@midwayjs/bootstrap": "3.20.11",
"@midwayjs/cache": "3.14.0",
@@ -806,6 +806,8 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
}
async batchDelete(ids: number[], userId: number, projectId?: number): Promise<number> {
ids = this.filterIds(ids);
const userProjectQuery = this.buildUserProjectQuery(userId, projectId);
await this.repository.delete({
id: In(ids),
@@ -1,7 +1,7 @@
import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from "@midwayjs/core";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { In, MoreThan, Repository } from "typeorm";
import { AccessService, BaseService, isEnterprise, NeedSuiteException, NeedVIPException, PageReq, SysPublicSettings, SysSettingsService, SysSiteInfo } from "@certd/lib-server";
import { AccessService, BaseService, isEnterprise, NeedSuiteException, NeedVIPException, PageReq, SysPublicSettings, SysSettingsService, SysSiteInfo, ValidateException } from "@certd/lib-server";
import { PipelineEntity } from "../entity/pipeline.js";
import { PipelineDetail } from "../entity/vo/pipeline-detail.js";
import { Executor, IAccessService, ICnameProxyService, INotificationService, Notification, Pipeline, pluginRegistry, ResultType, RunHistory, RunnableCollection, SysInfo, UserInfo } from "@certd/pipeline";
@@ -966,15 +966,19 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级Certd专业版");
}
for (const id of ids) {
if (!ids || ids.length === 0) {
throw new ValidateException("ids不能为空");
}
ids = this.filterIds(ids);
if (userId && userId > 0) {
await this.checkUserId(id, userId);
await this.checkUserId(ids, userId);
}
if (projectId) {
await this.checkUserId(id, projectId, "projectId");
await this.checkUserId(ids, projectId, "projectId");
}
for (const id of ids) {
await this.delete(id);
ids.push(id);
}
return ids.length;
}
@@ -88,6 +88,7 @@ export class TemplateService extends BaseService<TemplateEntity> {
}
async batchDelete(ids: number[], userId: number, projectId?: number): Promise<number> {
ids = this.filterIds(ids);
const where: any = {
id: In(ids),
};
@@ -76,7 +76,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
this.clearLastStatus();
if (this.successNotify) {
await this.sendSuccessNotify();
await this.sendSuccessNotify(cert);
}
} else {
throw new Error("申请证书失败");
@@ -165,12 +165,14 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
nextUpdateDays: leftDays - maxDays,
};
}
async sendSuccessNotify() {
async sendSuccessNotify(certReader: CertReader) {
this.logger.info("发送证书申请成功通知");
const url = await this.ctx.urlService.getPipelineDetailUrl(this.pipeline.id, this.ctx.runtime.id);
const body: NotificationBody = {
title: `证书申请成功【${this.pipeline.title}`,
content: `域名:${this.domains.join(",")}`,
content: `域名:${this.domains.join(",")}\n
${dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss")}\n
`,
url: url,
notificationType: "certApplySuccess",
};
@@ -1,15 +1,17 @@
import crypto from "node:crypto";
import { BaotaAccess } from "../access.js";
import { HttpClient, HttpRequestConfig } from "@certd/basic";
import { HttpClient, HttpRequestConfig, ILogger } from "@certd/basic";
import * as querystring from "node:querystring";
export class BaotaClient {
access: BaotaAccess;
http: HttpClient;
logger: ILogger
constructor(access: BaotaAccess, http: HttpClient) {
this.access = access;
this.http = http;
this.logger = access.ctx.logger;
}
//将以上 java代码 翻译成nodejs 代码
@@ -104,12 +104,15 @@ export class BaotaDeployWebSiteCert extends AbstractTaskPlugin {
}
const lockKey = `baota-lock-${accessId}`;
if (this.isDockerSite) {
this.logger.info(`当前已勾选docker站点(如果部署失败,请确认站点:${siteNames} 是否全部为docker站点)`);
}
for (const site of siteNames) {
// 加锁,防止并发部署证书, 宝塔并发部署会导致nginx的conf错乱
await this.ctx.utils.locker.execute(lockKey, async () => {
this.logger.info(`为站点:${site}设置证书,目前支持宝塔网站站点、docker站点`);
try {
if (this.isDockerSite) {
this.logger.info(`为Docker站点:${site} 设置证书`);
const res = await client.doRequest("/mod/docker/com/set_ssl", "", {
site_name: site,
key: cert.key,
@@ -117,6 +120,7 @@ export class BaotaDeployWebSiteCert extends AbstractTaskPlugin {
});
this.logger.info(res?.msg);
} else {
this.logger.info(`为非Docker站点:${site} 设置证书`);
const res = await client.doRequest("/site", "SetSSL", {
type: 0,
siteName: site,
@@ -125,6 +129,13 @@ export class BaotaDeployWebSiteCert extends AbstractTaskPlugin {
});
this.logger.info(res?.msg);
}
} catch (e: any) {
if (e?.message?.includes("没有服务器配置文件")) {
this.logger.error(e.message);
this.logger.warn(`首先请确认站点 ${site} 是否存在,如果存在,请到宝塔上手动保存一下该站点证书试试`);
}
throw e
}
});
}
@@ -0,0 +1,111 @@
/// <reference types="mocha" />
import assert from "node:assert/strict";
import { DeployCertToTencentCLB } from "./index.js";
describe("DeployCertToTencentCLB", () => {
it("uses remote single-select inputs for CLB and HTTPS listener", () => {
const input = (DeployCertToTencentCLB as any).define.input;
assert.equal(input.loadBalancerId.component.name, "remote-select");
assert.equal(input.loadBalancerId.component.single, true);
assert.equal(input.loadBalancerId.component.action, "onGetCLBList");
assert.equal(input.loadBalancerId.required, true);
assert.equal(input.listenerId.component.name, "remote-select");
assert.equal(input.listenerId.component.single, true);
assert.equal(input.listenerId.component.action, "onGetListenerList");
assert.deepEqual(input.listenerId.component.watches, ["certDomains", "accessId", "region", "loadBalancerId"]);
assert.equal(input.listenerId.required, true);
assert.equal(input.domain.component.name, "remote-select");
assert.equal(input.domain.component.single, false);
assert.equal(input.domain.component.action, "onGetDomainList");
assert.deepEqual(input.domain.component.watches, ["certDomains", "accessId", "region", "loadBalancerId", "listenerId"]);
assert.equal(input.domain.required, false);
});
it("maps CLB API results to remote-select options", async () => {
const plugin = new DeployCertToTencentCLB();
plugin.accessId = "access-1";
plugin.logger = { info: () => undefined } as any;
(plugin as any).getClient = async () => ({});
(plugin as any).getCLBList = async () => [
{
LoadBalancerId: "lb-1",
LoadBalancerName: "业务负载均衡",
},
];
const options = await plugin.onGetCLBList({});
assert.deepEqual(options, [
{
value: "lb-1",
label: "业务负载均衡<lb-1>",
},
]);
});
it("maps HTTPS listener API results after selecting a CLB", async () => {
const plugin = new DeployCertToTencentCLB();
plugin.accessId = "access-1";
plugin.loadBalancerId = "lb-1";
plugin.logger = { info: () => undefined } as any;
(plugin as any).getClient = async () => ({});
(plugin as any).getListenerList = async (_client: any, loadBalancerId: string, listenerIds: any) => {
assert.equal(loadBalancerId, "lb-1");
assert.equal(listenerIds, null);
return [
{
ListenerId: "listener-1",
ListenerName: "HTTPS监听器",
Port: 443,
},
];
};
const options = await plugin.onGetListenerList({});
assert.deepEqual(options, [
{
value: "listener-1",
label: "HTTPS监听器:443<listener-1>",
},
]);
});
it("maps SNI domains from the selected listener rules", async () => {
const plugin = new DeployCertToTencentCLB();
plugin.accessId = "access-1";
plugin.loadBalancerId = "lb-1";
plugin.listenerId = "listener-1";
plugin.logger = { info: () => undefined } as any;
(plugin as any).getClient = async () => ({});
(plugin as any).getListenerList = async (_client: any, loadBalancerId: string, listenerIds: string[]) => {
assert.equal(loadBalancerId, "lb-1");
assert.deepEqual(listenerIds, ["listener-1"]);
return [
{
ListenerId: "listener-1",
Rules: [{ Domain: "www.example.com" }, { Domain: "api.example.com" }],
},
];
};
const options = await plugin.onGetDomainList({});
assert.deepEqual(options, [
{
value: "www.example.com",
label: "www.example.com",
domain: "www.example.com",
},
{
value: "api.example.com",
label: "api.example.com",
domain: "api.example.com",
},
]);
});
});
@@ -1,7 +1,8 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import dayjs from "dayjs";
import { TencentAccess } from "../../../plugin-lib/tencent/index.js";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { createRemoteSelectInputDefine } from "@certd/plugin-lib";
@IsTaskPlugin({
name: "DeployCertToTencentCLB",
title: "腾讯云-部署到CLB",
@@ -73,29 +74,44 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
})
region!: string;
@TaskInput({
@TaskInput(
createRemoteSelectInputDefine({
title: "负载均衡ID",
required: true,
helper: "请选择要部署证书的负载均衡",
action: DeployCertToTencentCLB.prototype.onGetCLBList.name,
watches: ["region"],
single: true,
pager: false,
search: false,
})
)
loadBalancerId!: string;
@TaskInput({
@TaskInput(
createRemoteSelectInputDefine({
title: "监听器ID",
required: true,
helper: "请选择要部署证书的HTTPS监听器",
action: DeployCertToTencentCLB.prototype.onGetListenerList.name,
watches: ["region", "loadBalancerId"],
single: true,
pager: false,
search: false,
})
)
listenerId!: string;
@TaskInput({
@TaskInput(
createRemoteSelectInputDefine({
title: "域名",
helper: "如果开启了SNI,请选择要部署证书的域名;未开启SNI时可以留空",
action: DeployCertToTencentCLB.prototype.onGetDomainList.name,
watches: ["region", "loadBalancerId", "listenerId"],
required: false,
component: {
name: "a-select",
vModel: "value",
open: false,
mode: "tags",
},
helper: "如果开启了sni,则此项必须填写,未开启,则不要填写",
single: false,
pager: false,
search: false,
})
)
domain!: string | string[];
@TaskInput({
@@ -272,6 +288,27 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
return ret.LoadBalancerSet;
}
async onGetCLBList(data: PageSearch) {
if (!this.accessId) {
throw new Error("请选择Access提供者");
}
const client = await this.getClient();
const list = await this.getCLBList(client);
if (!list || list.length === 0) {
return [];
}
return list.map((item: any) => {
const loadBalancerId = item.LoadBalancerId;
const loadBalancerName = item.LoadBalancerName || loadBalancerId;
return {
value: loadBalancerId,
label: `${loadBalancerName}<${loadBalancerId}>`,
};
});
}
async getListenerList(client: any, balancerId: any, listenerIds: any) {
// HTTPS
const params = {
@@ -284,6 +321,57 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
return ret.Listeners;
}
async onGetListenerList(data: PageSearch) {
if (!this.accessId) {
throw new Error("请选择Access提供者");
}
if (!this.loadBalancerId) {
throw new Error("请先选择负载均衡");
}
const client = await this.getClient();
const list = await this.getListenerList(client, this.loadBalancerId, null);
if (!list || list.length === 0) {
return [];
}
return list.map((item: any) => {
const listenerId = item.ListenerId;
const listenerName = item.ListenerName || "HTTPS监听器";
const port = item.Port ? `:${item.Port}` : "";
return {
value: listenerId,
label: `${listenerName}${port}<${listenerId}>`,
};
});
}
async onGetDomainList(data: PageSearch) {
if (!this.accessId) {
throw new Error("请选择Access提供者");
}
if (!this.loadBalancerId) {
throw new Error("请先选择负载均衡");
}
if (!this.listenerId) {
throw new Error("请先选择监听器");
}
const client = await this.getClient();
const listeners = await this.getListenerList(client, this.loadBalancerId, [this.listenerId]);
const listener = listeners?.[0];
const domains = listener?.Rules?.map((rule: any) => rule.Domain).filter(Boolean) || [];
const uniqueDomains = [...new Set(domains)];
return uniqueDomains.map(domain => {
return {
value: domain,
label: domain,
domain,
};
});
}
checkRet(ret: any) {
if (!ret || ret.Error) {
throw new Error("执行失败:" + ret.Error.Code + "," + ret.Error.Message);
+1 -1
View File
@@ -1 +1 @@
02:48
01:14
+1 -1
View File
@@ -1 +1 @@
03:41
01:28