From 6f3fd785e77a33c72bdf4115dc5d498e677d1863 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Mon, 16 Feb 2026 00:17:55 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81next-terminal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../public/static/logos/next-terminal.png | Bin 0 -> 5525 bytes .../plugin-demo/plugins/plugin-test.ts | 3 +- .../plugins/plugin-next-terminal/access.ts | 149 ++++++++++++++++++ .../src/plugins/plugin-next-terminal/index.ts | 2 + .../plugin-next-terminal/plugins/index.ts | 1 + .../plugins/plugin-refresh-cert.ts | 117 ++++++++++++++ .../src/plugins/plugin-xinnetconnet/access.ts | 2 +- 7 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 packages/ui/certd-client/public/static/logos/next-terminal.png create mode 100644 packages/ui/certd-server/src/plugins/plugin-next-terminal/access.ts create mode 100644 packages/ui/certd-server/src/plugins/plugin-next-terminal/index.ts create mode 100644 packages/ui/certd-server/src/plugins/plugin-next-terminal/plugins/index.ts create mode 100644 packages/ui/certd-server/src/plugins/plugin-next-terminal/plugins/plugin-refresh-cert.ts diff --git a/packages/ui/certd-client/public/static/logos/next-terminal.png b/packages/ui/certd-client/public/static/logos/next-terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b939ab2e4b19cbe61fbecbc5f5cacd260ac534 GIT binary patch literal 5525 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 literal 0 HcmV?d00001 diff --git a/packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.ts b/packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.ts index b18183af5..d379ab5f0 100644 --- a/packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.ts +++ b/packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.ts @@ -128,9 +128,10 @@ export class DemoTest extends AbstractTaskPlugin { //当以下参数变化时,触发获取选项 watches: ['certDomains', 'accessId'], required: true, + multi: true, }) ) - siteName!: string | string[]; + siteName!: string[]; //插件实例化时执行的方法 async onInstance() {} diff --git a/packages/ui/certd-server/src/plugins/plugin-next-terminal/access.ts b/packages/ui/certd-server/src/plugins/plugin-next-terminal/access.ts new file mode 100644 index 000000000..db129e135 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-next-terminal/access.ts @@ -0,0 +1,149 @@ +import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline'; + +/** + * Next Terminal 授权配置 + */ +@IsAccess({ + name: 'nextTerminal', + title: 'Next Terminal 授权', + icon: 'clarity:plugin-line', + desc: '用于访问 Next Terminal API 的授权配置', +}) +export class NextTerminalAccess extends BaseAccess { + + /** + * Next Terminal 系统地址 + */ + @AccessInput({ + title: '系统地址', + component: { + name: "a-input", + allowClear: true, + placeholder: 'https://nt.example.com:8088', + }, + required: true, + }) + baseUrl = ''; + + /** + * API 令牌 + */ + @AccessInput({ + title: 'API 令牌', + helper: '个人中心->授权令牌->创建令牌', + component: { + name: "a-input", + allowClear: true, + placeholder: 'NT_xxxxx', + }, + required: true, + encrypt: true, + }) + apiToken = ''; + + /** + * 测试按钮 + */ + @AccessInput({ + title: "测试", + component: { + name: "api-test", + action: "TestRequest" + }, + helper: "点击测试接口是否正常" + }) + testRequest = true; + + /** + * 测试接口连接 + */ + async onTestRequest() { + await this.GetCertificateList({}); + return "ok"; + } + + /** + * 获取证书列表 + */ + async GetCertificateList(req: PageSearch): Promise> { + this.ctx.logger.info(`获取 Next Terminal 证书列表,req:${JSON.stringify(req)}`); + const pager = new Pager(req); + const resp = await this.doRequest({ + url: '/api/admin/certificates/paging', + method: 'GET', + params: { + pageIndex: pager.pageNo, + pageSize: pager.pageSize, + sortOrder: 'ascend', + sortField: 'notAfter', + } + }); + + const total = resp?.total || 0; + const list = resp?.items || []; + + return { + total, + list + }; + } + + /** + * 更新证书 + */ + async UpdateCertificate(req: { + certId: string; + commonName: string; + crt: string; + key: string; + }) { + this.ctx.logger.info(`更新 Next Terminal 证书,certId:${req.certId}, commonName:${req.commonName}`); + await this.doRequest({ + url: `/api/admin/certificates/${req.certId}`, + method: 'PUT', + data: { + commonName: req.commonName, + type: 'imported', + id: req.certId, + certificate: req.crt, + privateKey: req.key, + renewBefore: 30, + } + }); + } + + /** + * 通用 API 调用方法 + */ + async doRequest(req: { + url: string; + method: 'GET' | 'POST' | 'PUT' | 'DELETE'; + params?: any; + data?: any; + }) { + const headers = { + 'X-Auth-Token': `${this.apiToken}`, + 'Content-Type': 'application/json', + }; + + + this.ctx.logger.debug(`Next Terminal API 请求: ${req.method} ${this.baseUrl}${req.url}`); + + const resp = await this.ctx.http.request({ + url: req.url, + baseURL: this.baseUrl, + method: req.method, + headers, + params: req.params, + data: req.data, + validateStatus: () => true, // 不自动抛出异常,让我们自己处理 + }); + + if (resp.code >0) { + throw new Error(resp.message); + } + return resp + } +} + +new NextTerminalAccess() \ No newline at end of file diff --git a/packages/ui/certd-server/src/plugins/plugin-next-terminal/index.ts b/packages/ui/certd-server/src/plugins/plugin-next-terminal/index.ts new file mode 100644 index 000000000..c49d8bf88 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-next-terminal/index.ts @@ -0,0 +1,2 @@ +export * from './access.js'; +export * from './plugins/index.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-next-terminal/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-next-terminal/plugins/index.ts new file mode 100644 index 000000000..55ab7cf74 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-next-terminal/plugins/index.ts @@ -0,0 +1 @@ +export * from './plugin-refresh-cert.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-next-terminal/plugins/plugin-refresh-cert.ts b/packages/ui/certd-server/src/plugins/plugin-next-terminal/plugins/plugin-refresh-cert.ts new file mode 100644 index 000000000..6c8be644f --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-next-terminal/plugins/plugin-refresh-cert.ts @@ -0,0 +1,117 @@ +import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { CertInfo } from '@certd/plugin-cert'; +import { CertReader, createRemoteSelectInputDefine } from '@certd/plugin-lib'; + +@IsTaskPlugin({ + name: 'NextTerminalRefreshCert', + title: 'NextTerminal-更新证书', + icon: 'clarity:plugin-line', + desc: '更新 Next Terminal 证书', + group: pluginGroups.panel.key, + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class NextTerminalRefreshCert extends AbstractTaskPlugin { + /** + * 证书选择 + */ + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: ['CertApply'], + }, + required: true, + }) + cert!: CertInfo; + + /** + * Next Terminal 授权 + */ + @TaskInput({ + title: 'Next Terminal 授权', + helper: '选择 Next Terminal 授权配置', + component: { + name: 'access-selector', + type: 'nextTerminal', + }, + required: true, + }) + accessId!: string; + + /** + * 选择要更新的证书 + */ + @TaskInput( + createRemoteSelectInputDefine({ + title: '选择证书', + helper: '选择要更新的 Next Terminal 证书(支持多选),如果这里没有列出,需要先前往控制台上传证书,之后就可以自动更新', + action: NextTerminalRefreshCert.prototype.onGetCertList.name, + watches: ['accessId'], + required: true, + multi: true, + }) + ) + certIds!: string[]; + + /** + * 获取证书列表 + */ + async onGetCertList(req: PageSearch) { + if (!this.accessId) { + throw new Error('请选择 Next Terminal 授权'); + } + + const access = await this.getAccess(this.accessId) as any; + const certList = await access.GetCertificateList(req); + + const options = certList.list.map((item: any) => { + return { + value: item.id, + label: `${item.commonName} <${item.id}>`, + domain: item.commonName, + }; + }); + + return options; + } + + /** + * 执行证书更新 + */ + async execute(): Promise { + const { cert, accessId, certIds } = this; + + try { + const access = await this.getAccess(accessId) as any; + + // 确保 certIds 是数组 + const ids = Array.isArray(certIds) ? certIds : [certIds]; + + const certReader = new CertReader(cert); + const mainDomain = certReader.getMainDomain(); + + for (const certId of ids) { + this.logger.info(`更新 Next Terminal 证书: ${certId}`); + + await access.UpdateCertificate({ + certId, + commonName: mainDomain, + crt: cert.crt, + key: cert.key, + }); + + this.logger.info(`证书 ${certId} 更新成功`); + } + + this.logger.info(`成功更新 ${ids.length} 个 Next Terminal 证书`); + } catch (e) { + this.logger.error('更新 Next Terminal 证书失败', e); + throw e; + } + } +} \ No newline at end of file diff --git a/packages/ui/certd-server/src/plugins/plugin-xinnetconnet/access.ts b/packages/ui/certd-server/src/plugins/plugin-xinnetconnet/access.ts index 384845ada..89700788d 100644 --- a/packages/ui/certd-server/src/plugins/plugin-xinnetconnet/access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-xinnetconnet/access.ts @@ -50,7 +50,7 @@ export class XinnetConnectAccess extends BaseAccess { async onTestRequest() { await this.getDomainList({ pageNo: 1, - pageSize: 1, + pageSize: 10, }); return "ok"; }