From 1c6028abcf8849163462bb2f8441b6838357e09b Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Tue, 15 Oct 2024 12:59:40 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E5=AF=86=E9=92=A5=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/pipeline/src/core/executor.ts | 8 +- packages/core/pipeline/src/plugin/api.ts | 3 +- .../src/system/settings/service/models.ts | 16 +++- .../settings/service/sys-settings-service.ts | 21 ++++- .../src/plugin/cert-plugin/base.ts | 4 + .../src/plugin/cert-plugin/index.ts | 13 ++- .../src/views/certd/pipeline/api.ts | 17 ++++ .../src/views/certd/pipeline/crud.tsx | 85 ++++++++++++++----- .../controller/pipeline/cert-controller.ts | 21 +++++ .../sys/settings/sys-settings-controller.ts | 4 +- .../src/modules/auto/auto-init-site.ts | 2 + .../pipeline/service/storage-service.ts | 16 ++++ 12 files changed, 180 insertions(+), 30 deletions(-) create mode 100644 packages/ui/certd-server/src/controller/pipeline/cert-controller.ts diff --git a/packages/core/pipeline/src/core/executor.ts b/packages/core/pipeline/src/core/executor.ts index 79159f82..adb60bec 100644 --- a/packages/core/pipeline/src/core/executor.ts +++ b/packages/core/pipeline/src/core/executor.ts @@ -307,10 +307,16 @@ export class Executor { //更新pipeline vars if (Object.keys(instance._result.pipelineVars).length > 0) { // 判断 pipelineVars 有值时更新 - const vars = this.pipelineContext.getObj("vars"); + const vars = await this.pipelineContext.getObj("vars"); merge(vars, instance._result.pipelineVars); await this.pipelineContext.setObj("vars", vars); } + if (Object.keys(instance._result.pipelinePrivateVars).length > 0) { + // 判断 pipelineVars 有值时更新 + const vars = await this.pipelineContext.getObj("privateVars"); + merge(vars, instance._result.pipelinePrivateVars); + await this.pipelineContext.setObj("privateVars", vars); + } } async notification(when: NotificationWhen, error?: any) { diff --git a/packages/core/pipeline/src/plugin/api.ts b/packages/core/pipeline/src/plugin/api.ts index ab5e02ac..5fd30609 100644 --- a/packages/core/pipeline/src/plugin/api.ts +++ b/packages/core/pipeline/src/plugin/api.ts @@ -60,6 +60,7 @@ export type TaskResult = { clearLastStatus?: boolean; files?: FileItem[]; pipelineVars: Record; + pipelinePrivateVars?: Record; }; export type TaskInstanceContext = { //流水线定义 @@ -97,7 +98,7 @@ export type TaskInstanceContext = { }; export abstract class AbstractTaskPlugin implements ITaskPlugin { - _result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {} }; + _result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {}, pipelinePrivateVars: {} }; ctx!: TaskInstanceContext; logger!: ILogger; accessService!: IAccessService; diff --git a/packages/libs/lib-server/src/system/settings/service/models.ts b/packages/libs/lib-server/src/system/settings/service/models.ts index cd887fc9..f6549454 100644 --- a/packages/libs/lib-server/src/system/settings/service/models.ts +++ b/packages/libs/lib-server/src/system/settings/service/models.ts @@ -1,3 +1,5 @@ +import { cloneDeep } from 'lodash-es'; + export class BaseSettings { static __key__: string; static __title__: string; @@ -29,8 +31,10 @@ export class SysPrivateSettings extends BaseSettings { httpProxy? = ''; removeSecret() { - delete this.jwtKey; - delete this.encryptSecret; + const clone = cloneDeep(this); + delete clone.jwtKey; + delete clone.encryptSecret; + return clone; } } @@ -83,6 +87,14 @@ export class SysSiteInfo extends BaseSettings { loginLogo?: string; } +export class SysSecretBackup extends BaseSettings { + static __title__ = '密钥信息备份'; + static __key__ = 'sys.secret'; + static __access__ = 'private'; + siteId?: string; + encryptSecret?: string; +} + export class SysSiteEnv { agent?: { enabled?: boolean; diff --git a/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts b/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts index f49cb819..5d3f2a8f 100644 --- a/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts +++ b/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts @@ -3,10 +3,10 @@ import { InjectEntityModel } from '@midwayjs/typeorm'; import { Repository } from 'typeorm'; import { SysSettingsEntity } from '../entity/sys-settings.js'; import { CacheManager } from '@midwayjs/cache'; -import { BaseSettings, SysPrivateSettings, SysPublicSettings } from './models.js'; +import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, SysSecretBackup } from './models.js'; import * as _ from 'lodash-es'; import { BaseService } from '../../../basic/index.js'; -import { setGlobalProxy } from '@certd/basic'; +import { logger, setGlobalProxy } from '@certd/basic'; /** * 设置 @@ -146,4 +146,21 @@ export class SysSettingsService extends BaseService { } await this.cache.del(`settings.${key}`); } + + async backupSecret() { + const settings = await this.getSettingByKey(SysSecretBackup.__key__); + if (settings == null) { + const backup = new SysSecretBackup(); + const privateSettings = await this.getPrivateSettings(); + const installInfo = await this.getSetting(SysInstallInfo); + if (installInfo.siteId == null || privateSettings.encryptSecret == null) { + logger.error('备份密钥失败,siteId或encryptSecret为空'); + return; + } + backup.siteId = installInfo.siteId; + backup.encryptSecret = privateSettings.encryptSecret; + await this.saveSetting(backup); + logger.info('备份密钥成功'); + } + } } diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts index 41a68e2c..bfbcf3cf 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts @@ -144,6 +144,10 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin { this.cert = cert; this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf(); + if (!this._result.pipelinePrivateVars) { + this._result.pipelinePrivateVars = {}; + } + this._result.pipelinePrivateVars.cert = cert; if (cert.pfx == null || cert.der == null) { try { diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts index d75658e7..0681f53d 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts @@ -146,6 +146,13 @@ export class CertApplyPlugin extends CertApplyBasePlugin { }) googleCommonEabAccessId!: number; + @TaskInput({ + title: "ZeroSSL公共EAB授权", + isSys: true, + show: false, + }) + zerosslCommonEabAccessId!: number; + @TaskInput({ title: "EAB授权", component: { @@ -159,7 +166,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin { mergeScript: ` return { show: ctx.compute(({form})=>{ - return form.sslProvider === 'zerossl' || (form.sslProvider === 'google' && !form.googleCommonEabAccessId) + return (form.sslProvider === 'zerossl' || !form.zerosslCommonEabAccessId) || (form.sslProvider === 'google' && !form.googleCommonEabAccessId) }) } `, @@ -266,7 +273,11 @@ export class CertApplyPlugin extends CertApplyBasePlugin { } } else if (this.sslProvider === "zerossl") { if (this.eabAccessId) { + this.logger.info("当前正在使用 zerossl EAB授权"); eab = await this.ctx.accessService.getById(this.eabAccessId); + } else if (this.zerosslCommonEabAccessId) { + this.logger.info("当前正在使用 zerossl 公共EAB授权"); + eab = await this.ctx.accessService.getById(this.zerosslCommonEabAccessId); } else { this.logger.error("zerossl需要配置EAB授权"); return; diff --git a/packages/ui/certd-client/src/views/certd/pipeline/api.ts b/packages/ui/certd-client/src/views/certd/pipeline/api.ts index 22abab61..b01defa6 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/api.ts +++ b/packages/ui/certd-client/src/views/certd/pipeline/api.ts @@ -2,6 +2,7 @@ import { request } from "/src/api/service"; const apiPrefix = "/pi/pipeline"; const historyApiPrefix = "/pi/history"; +const certApiPrefix = "/pi/cert"; export async function GetList(query: any) { return await request({ @@ -82,3 +83,19 @@ export async function GetFiles(pipelineId: number) { params: { pipelineId } }); } + +export type CertInfo = { + crt: string; + key: string; + ic: string; + der: string; + pfx: string; +}; + +export async function GetCert(pipelineId: number): Promise { + return await request({ + url: certApiPrefix + "/get", + method: "post", + params: { id: pipelineId } + }); +} diff --git a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx index 1d65f46b..df99df5a 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx @@ -148,6 +148,69 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } }); }); } + + const viewCert = async (row: any) => { + const cert = await api.GetCert(row.id); + if (!cert) { + notification.error({ message: "还没有产生证书,请先运行流水线" }); + return; + } + + Modal.success({ + title: "查看证书", + maskClosable: true, + okText: "关闭", + content: () => { + return ( +
+
+

fullchain.pem

+
{cert.crt}
+
+
+

private.pem

+
{cert.key}
+
+
+

中间证书.pem

+
{cert.ic}
+
+
+ ); + } + }); + }; + + const downloadCert = async (row: any) => { + const files = await api.GetFiles(row.id); + Modal.success({ + title: "文件下载", + maskClosable: true, + okText: "↑↑↑ 点击上面链接下载", + content: () => { + const children = []; + for (const file of files) { + const downloadUrl = `${env.API}/pi/history/download?pipelineId=${row.id}&fileId=${file.id}`; + children.push( +
+ +
+ + 直接查看证书 + +
+
+ ); + } + + return
{children}
; + } + }); + }; const userStore = useUserStore(); const settingStore = useSettingStore(); return { @@ -243,27 +306,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp type: "link", icon: "ant-design:download-outlined", async click({ row }) { - const files = await api.GetFiles(row.id); - Modal.success({ - title: "文件下载", - maskClosable: true, - okText: "↑↑↑ 点击上面链接下载", - content: () => { - const children = []; - for (const file of files) { - const downloadUrl = `${env.API}/pi/history/download?pipelineId=${row.id}&fileId=${file.id}`; - children.push( -

- - {file.filename} - -

- ); - } - - return
{children}
; - } - }); + downloadCert(row); } }, remove: { diff --git a/packages/ui/certd-server/src/controller/pipeline/cert-controller.ts b/packages/ui/certd-server/src/controller/pipeline/cert-controller.ts new file mode 100644 index 00000000..4f5186ee --- /dev/null +++ b/packages/ui/certd-server/src/controller/pipeline/cert-controller.ts @@ -0,0 +1,21 @@ +import { Controller, Inject, Post, Provide, Query } from '@midwayjs/core'; +import { PipelineService } from '../../modules/pipeline/service/pipeline-service.js'; +import { BaseController, Constants } from '@certd/lib-server'; +import { StorageService } from '../../modules/pipeline/service/storage-service.js'; + +@Provide() +@Controller('/api/pi/cert') +export class CertController extends BaseController { + @Inject() + pipelineService: PipelineService; + @Inject() + storeService: StorageService; + + @Post('/get', { summary: Constants.per.authOnly }) + async getCert(@Query('id') id: number) { + const userId = this.getUserId(); + await this.pipelineService.checkUserId(id, userId); + const privateVars = this.storeService.getPipelinePrivateVars(id); + return this.ok(privateVars); + } +} 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 2acdc87a..129bed89 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 @@ -80,8 +80,8 @@ export class SysSettingsController extends CrudController { @Post('/getSysSettings', { summary: 'sys:settings:edit' }) async getSysSettings() { const publicSettings = await this.service.getPublicSettings(); - const privateSettings = await this.service.getPrivateSettings(); - privateSettings.removeSecret(); + let privateSettings = await this.service.getPrivateSettings(); + privateSettings = privateSettings.removeSecret(); return this.ok({ public: publicSettings, private: privateSettings }); } diff --git a/packages/ui/certd-server/src/modules/auto/auto-init-site.ts b/packages/ui/certd-server/src/modules/auto/auto-init-site.ts index 6f861b65..5632f2fe 100644 --- a/packages/ui/certd-server/src/modules/auto/auto-init-site.ts +++ b/packages/ui/certd-server/src/modules/auto/auto-init-site.ts @@ -43,6 +43,8 @@ export class AutoInitSite { await this.sysSettingsService.saveSetting(privateInfo); } + await this.sysSettingsService.backupSecret(); + await this.sysSettingsService.reloadPrivateSettings(); // 授权许可 diff --git a/packages/ui/certd-server/src/modules/pipeline/service/storage-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/storage-service.ts index 35d464a8..e7788023 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/storage-service.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/storage-service.ts @@ -44,6 +44,9 @@ export class StorageService extends BaseService { } async findPipelineVars(pipelineIds: number[]) { + if (pipelineIds == null || pipelineIds.length === 0) { + throw new Error('pipelineIds 不能为空'); + } return await this.repository.find({ where: { scope: 'pipeline', @@ -52,4 +55,17 @@ export class StorageService extends BaseService { }, }); } + + async getPipelinePrivateVars(pipelineId: number) { + if (pipelineId == null) { + throw new Error('pipelineId 不能为空'); + } + return await this.repository.find({ + where: { + scope: 'pipeline', + namespace: pipelineId + '', + key: 'privateVars', + }, + }); + } }