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 000000000..b6b939ab2 Binary files /dev/null and b/packages/ui/certd-client/public/static/logos/next-terminal.png differ 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"; }