From fe03f9942b5662fb90cad86da10782f5dc3603f5 Mon Sep 17 00:00:00 2001 From: ahe Date: Sat, 9 Aug 2025 16:41:57 +0800 Subject: [PATCH 01/39] =?UTF-8?q?perf:=20=E5=A2=9E=E5=8A=A0=E6=89=BE?= =?UTF-8?q?=E5=9B=9E=E5=AF=86=E7=A0=81=E7=9A=84=E9=AA=8C=E8=AF=81=E7=A0=81?= =?UTF-8?q?=E5=8F=AF=E9=87=8D=E8=AF=95=E6=AC=A1=E6=95=B0=20@nicheng-he=20?= =?UTF-8?q?=20(#496)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2.找回密码邮件方式增加长度到6位 3.开启自主找回密码放置更合适的位置 --- .../src/views/sys/settings/tabs/base.vue | 4 -- .../src/views/sys/settings/tabs/register.vue | 3 ++ .../src/controller/basic/code-controller.ts | 22 ++++++++- .../user/login/forgot-password-controller.ts | 4 ++ .../src/modules/basic/service/code-service.ts | 45 ++++++++++++++----- 5 files changed, 60 insertions(+), 18 deletions(-) diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue index 8d2138b6d..a3024ae4c 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue @@ -47,10 +47,6 @@
- - - - {{ t("certd.saveButton") }} diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue index 45a468e9a..2272ae8fd 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue @@ -11,6 +11,9 @@ + + +
diff --git a/packages/ui/certd-server/src/controller/basic/code-controller.ts b/packages/ui/certd-server/src/controller/basic/code-controller.ts index fb0eedc29..bb7c0f6e9 100644 --- a/packages/ui/certd-server/src/controller/basic/code-controller.ts +++ b/packages/ui/certd-server/src/controller/basic/code-controller.ts @@ -16,6 +16,9 @@ export class SmsCodeReq { @Rule(RuleType.string().required().max(4)) imgCode: string; + + @Rule(RuleType.string()) + verificationType: string; } export class EmailCodeReq { @@ -32,6 +35,9 @@ export class EmailCodeReq { verificationType: string; } +// 找回密码的验证码有效期 +const FORGOT_PASSWORD_CODE_DURATION = 3 + /** */ @Provide() @@ -48,8 +54,18 @@ export class BasicController extends BaseController { @Body(ALL) body: SmsCodeReq ) { + const opts = { + verificationType: body.verificationType, + verificationCodeLength: undefined, + duration: undefined, + }; + if(body?.verificationType === 'forgotPassword') { + opts.duration = FORGOT_PASSWORD_CODE_DURATION; + // opts.verificationCodeLength = 6; //部分厂商这里会设置参数长度这里就不改了 + } + await this.codeService.checkCaptcha(body.randomStr, body.imgCode); - await this.codeService.sendSmsCode(body.phoneCode, body.mobile, body.randomStr); + await this.codeService.sendSmsCode(body.phoneCode, body.mobile, body.randomStr, opts); return this.ok(null); } @@ -60,6 +76,7 @@ export class BasicController extends BaseController { ) { const opts = { verificationType: body.verificationType, + verificationCodeLength: undefined, title: undefined, content: undefined, duration: undefined, @@ -67,7 +84,8 @@ export class BasicController extends BaseController { if(body?.verificationType === 'forgotPassword') { opts.title = '找回密码'; opts.content = '验证码:${code}。您正在找回密码,请输入验证码并完成操作。如非本人操作请忽略'; - opts.duration = 3; + opts.duration = FORGOT_PASSWORD_CODE_DURATION; + opts.verificationCodeLength = 6; } await this.codeService.checkCaptcha(body.randomStr, body.imgCode); diff --git a/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts b/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts index a497504ba..4c60d394c 100644 --- a/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts +++ b/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts @@ -28,6 +28,8 @@ export class LoginController extends BaseController { if(!sysSettings.selfServicePasswordRetrievalEnabled) { throw new CommonException('暂未开启自助找回'); } + // 找回密码的验证码允许错误次数 + const errorNum = 5; if(body.type === 'email') { this.codeService.checkEmailCode({ @@ -35,6 +37,7 @@ export class LoginController extends BaseController { email: body.input, randomStr: body.randomStr, validateCode: body.validateCode, + errorNum, throwError: true, }); } else if(body.type === 'mobile') { @@ -44,6 +47,7 @@ export class LoginController extends BaseController { randomStr: body.randomStr, phoneCode: body.phoneCode, smsCode: body.validateCode, + errorNum, throwError: true, }); } else { 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 62d108cad..c84ab3b1d 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 @@ -63,7 +63,8 @@ export class CodeService { randomStr: string, opts?: { duration?: number, - verificationType?: string + verificationType?: string, + verificationCodeLength?: number, }, ) { if (!mobile) { @@ -73,7 +74,8 @@ export class CodeService { throw new Error('randomStr不能为空'); } - const duration = Math.max(Math.floor(Math.min(opts?.duration || 5, 15)), 1); + const verificationCodeLength = Math.floor(Math.max(Math.min(opts?.verificationCodeLength || 4, 8), 4)); + const duration = Math.floor(Math.max(Math.min(opts?.duration || 5, 15), 1)); const sysSettings = await this.sysSettingsService.getPrivateSettings(); if (!sysSettings.sms?.config?.accessId) { @@ -87,7 +89,7 @@ export class CodeService { accessService: accessGetter, config: smsConfig, }); - const smsCode = randomNumber(4); + const smsCode = randomNumber(verificationCodeLength); await sender.sendSmsCode({ mobile, code: smsCode, @@ -114,7 +116,8 @@ export class CodeService { title?: string, content?: string, duration?: number, - verificationType?: string + verificationType?: string, + verificationCodeLength?: number, }, ) { if (!email) { @@ -132,8 +135,10 @@ export class CodeService { } } - const code = randomNumber(4); - const duration = Math.max(Math.floor(Math.min(opts?.duration || 5, 15)), 1); + const verificationCodeLength = Math.floor(Math.max(Math.min(opts?.verificationCodeLength || 4, 8), 4)); + const duration = Math.floor(Math.max(Math.min(opts?.duration || 5, 15), 1)); + + const code = randomNumber(verificationCodeLength); const title = `【${siteTitle}】${!!opts?.title ? opts.title : '验证码'}`; const content = !!opts.content ? this.compile(opts.content)({code, duration}) : `您的验证码是${code},请勿泄露`; @@ -154,12 +159,12 @@ export class CodeService { /** * checkSms */ - async checkSmsCode(opts: { mobile: string; phoneCode: string; smsCode: string; randomStr: string; verificationType?: string; throwError: boolean }) { + async checkSmsCode(opts: { mobile: string; phoneCode: string; smsCode: string; randomStr: string; verificationType?: string; throwError: boolean; errorNum?: number }) { const key = this.buildSmsCodeKey(opts.phoneCode, opts.mobile, opts.randomStr, opts.verificationType); if (isDev()) { return true; } - return this.checkValidateCode(key, opts.smsCode, opts.throwError); + return this.checkValidateCode(key, opts.smsCode, opts.throwError, opts.errorNum); } buildSmsCodeKey(phoneCode: string, mobile: string, randomStr: string, verificationType?: string) { @@ -169,22 +174,38 @@ export class CodeService { buildEmailCodeKey(email: string, randomStr: string, verificationType?: string) { return ['email', verificationType, email, randomStr].filter(item => !!item).join(':'); } - checkValidateCode(key: string, userCode: string, throwError = true) { + checkValidateCode(key: string, userCode: string, throwError = true, errorNum = 0) { + // 记录异常次数key + const err_num_key = key + ':err_num'; //验证图片验证码 const code = cache.get(key); if (code == null || code !== userCode) { + let maxRetryCount = false; + if (!!code && errorNum > 0) { + const err_num = cache.get(err_num_key) || 0 + if(err_num >= errorNum - 1) { + maxRetryCount = true; + cache.delete(key); + cache.delete(err_num_key); + } else { + cache.set(err_num_key, err_num + 1, { + ttl: 30 * 60 * 1000 + }); + } + } if (throwError) { - throw new CodeErrorException('验证码错误'); + throw new CodeErrorException(!maxRetryCount ? '验证码错误': '验证码错误请获取新的验证码'); } return false; } cache.delete(key); + cache.delete(err_num_key); return true; } - checkEmailCode(opts: { randomStr: string; validateCode: string; email: string; verificationType?: string; throwError: boolean }) { + checkEmailCode(opts: { randomStr: string; validateCode: string; email: string; verificationType?: string; throwError: boolean; errorNum?: number }) { const key = this.buildEmailCodeKey(opts.email, opts.randomStr, opts.verificationType); - return this.checkValidateCode(key, opts.validateCode, opts.throwError); + return this.checkValidateCode(key, opts.validateCode, opts.throwError, opts.errorNum); } compile(templateString: string) { From 1bdceeecf4b5daecdd621a05a2596b6eb45ce8ea Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sat, 9 Aug 2025 16:59:48 +0800 Subject: [PATCH 02/39] =?UTF-8?q?perf:=20=E9=AA=8C=E8=AF=81=E7=A0=81?= =?UTF-8?q?=E5=8F=AF=E9=87=8D=E8=AF=95=E6=AC=A1=E6=95=B0=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=B8=BA3=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/run/docker-compose.yaml | 3 +++ docs/guide/qa/index.md | 12 ++++++++++-- .../src/modules/basic/service/code-service.ts | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docker/run/docker-compose.yaml b/docker/run/docker-compose.yaml index ef05998db..618cd6904 100644 --- a/docker/run/docker-compose.yaml +++ b/docker/run/docker-compose.yaml @@ -11,6 +11,9 @@ services: # ↓↓↓↓↓ -------------------------------------------------------- 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下,【您需要定时备份此目录,以保障数据容灾】 # 只要修改冒号前面的,冒号后面的/app/data不要动 - /data/certd:/app/data + # ↓↓↓↓↓ -------------------------------------------------------- 如果走时不准,考虑挂载localtime文件 + #- /etc/localtime:/etc/localtime + #- /etc/timezone:/etc/timezone ports: # 端口映射 # ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突,可以修改第一个7001为其他不冲突的端口号,第二个7001不要动 - "7001:7001" diff --git a/docs/guide/qa/index.md b/docs/guide/qa/index.md index 7171795b4..88a367146 100644 --- a/docs/guide/qa/index.md +++ b/docs/guide/qa/index.md @@ -65,8 +65,16 @@ networks: docker logs -f --tail 200 certd ``` - - +## 6. 容器内走时不准,或者时区不对 +走时不准确,慢慢偏差越来越大 +或者整个时区都不对 +可以尝试挂载localtime文件 +```yaml + volumes: + # ↓↓↓↓↓ -------------------- 如果走时不准,请尝试挂载localtime文件 + - /etc/localtime:/etc/localtime + - /etc/timezone:/etc/timezone +``` 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 c84ab3b1d..93445b7d4 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 @@ -174,7 +174,7 @@ export class CodeService { buildEmailCodeKey(email: string, randomStr: string, verificationType?: string) { return ['email', verificationType, email, randomStr].filter(item => !!item).join(':'); } - checkValidateCode(key: string, userCode: string, throwError = true, errorNum = 0) { + checkValidateCode(key: string, userCode: string, throwError = true, errorNum = 3) { // 记录异常次数key const err_num_key = key + ':err_num'; //验证图片验证码 From 221e068bac3af6cd5d1794f8cd4c2ec5c0bc3f45 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sat, 9 Aug 2025 18:11:20 +0800 Subject: [PATCH 03/39] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E9=85=8D=E7=BD=AE=E5=A4=8D=E5=88=B6=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E6=97=A0=E6=B3=95=E5=A4=8D=E5=88=B6=E5=B7=B2=E5=8A=A0?= =?UTF-8?q?=E5=AF=86=E5=AD=97=E6=AE=B5=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/user/access/service/access-service.ts | 13 ++++++++++++- .../certd-client/src/views/certd/access/crud.tsx | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/libs/lib-server/src/user/access/service/access-service.ts b/packages/libs/lib-server/src/user/access/service/access-service.ts index f99f6330d..756e72d1d 100644 --- a/packages/libs/lib-server/src/user/access/service/access-service.ts +++ b/packages/libs/lib-server/src/user/access/service/access-service.ts @@ -34,7 +34,18 @@ export class AccessService extends BaseService { } async add(param) { - this.encryptSetting(param, null); + let oldEntity = null; + if (param._copyFrom){ + oldEntity = await this.info(param._copyFrom); + if (oldEntity == null) { + throw new ValidateException('该授权配置不存在,请确认是否已被删除'); + } + if (oldEntity.userId !== param.userId) { + throw new ValidateException('您无权查看该授权配置'); + } + } + delete param._copyFrom + this.encryptSetting(param, oldEntity); return await super.add(param); } diff --git a/packages/ui/certd-client/src/views/certd/access/crud.tsx b/packages/ui/certd-client/src/views/certd/access/crud.tsx index 00323872e..0e8aea819 100644 --- a/packages/ui/certd-client/src/views/certd/access/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/access/crud.tsx @@ -44,6 +44,20 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, rowHandle: { width: 200, + buttons: { + copy: { + async click(ctx: any) { + const { row, index } = ctx; + await crudExpose.openCopy({ + row: { + ...row, + _copyFrom: row.id, + }, + index: index, + }); + }, + }, + }, }, columns: { id: { From 9e1e4eeec2859759ca5b07834c9d24cf88a6ad33 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Thu, 14 Aug 2025 11:00:10 +0800 Subject: [PATCH 04/39] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91API=E7=BD=91=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ui/certd-client/package.json | 2 +- .../public/static/icons/demo_index.html | 117 ++++++++- .../public/static/icons/iconfont.css | 22 +- .../public/static/icons/iconfont.js | 2 +- .../public/static/icons/iconfont.json | 35 +++ .../public/static/icons/iconfont.svg | 10 + packages/ui/certd-server/package.json | 1 + .../plugin/deploy-to-apigateway/index.ts | 228 ++++++++++++++++++ .../plugin/deploy-to-dcdn/index.ts | 4 +- .../src/plugins/plugin-aliyun/plugin/index.ts | 1 + pnpm-lock.yaml | 107 ++++---- 11 files changed, 478 insertions(+), 51 deletions(-) create mode 100644 packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apigateway/index.ts diff --git a/packages/ui/certd-client/package.json b/packages/ui/certd-client/package.json index ea2b3827a..5fcbf3021 100644 --- a/packages/ui/certd-client/package.json +++ b/packages/ui/certd-client/package.json @@ -120,7 +120,7 @@ "@vue/compiler-sfc": "^3.4.21", "@vue/eslint-config-typescript": "^13.0.0", "@vue/test-utils": "^2.4.6", - "autoprefixer": "^10.4.20", + "autoprefixer": "^10.4.21", "caller-path": "^4.0.0", "chai": "^5.1.0", "dependency-cruiser": "^16.2.3", diff --git a/packages/ui/certd-client/public/static/icons/demo_index.html b/packages/ui/certd-client/public/static/icons/demo_index.html index 4f3dde4d0..14e926579 100644 --- a/packages/ui/certd-client/public/static/icons/demo_index.html +++ b/packages/ui/certd-client/public/static/icons/demo_index.html @@ -54,6 +54,36 @@
    +
  • + +
    social-foursquare
    +
    
    +
  • + +
  • + +
    ksyun-logo
    +
    
    +
  • + +
  • + +
    雨-copy
    +
    
    +
  • + +
  • + +
    网宿
    +
    
    +
  • + +
  • + +
    ai客服
    +
    
    +
  • +
  • cdn
    @@ -198,7 +228,7 @@
    @font-face {
       font-family: 'iconfont';
    -  src: url('iconfont.svg?t=1743267254898#iconfont') format('svg');
    +  src: url('iconfont.svg?t=1754884110189#iconfont') format('svg');
     }
     

    第二步:定义使用 iconfont 的样式

    @@ -224,6 +254,51 @@
      +
    • + +
      + social-foursquare +
      +
      .icon-four +
      +
    • + +
    • + +
      + ksyun-logo +
      +
      .icon-ksyun +
      +
    • + +
    • + +
      + 雨-copy +
      +
      .icon-rainyun +
      +
    • + +
    • + +
      + 网宿 +
      +
      .icon-wangsu +
      +
    • + +
    • + +
      + ai客服 +
      +
      .icon-aikefu +
      +
    • +
    • @@ -440,6 +515,46 @@
        +
      • + +
        social-foursquare
        +
        #icon-four
        +
      • + +
      • + +
        ksyun-logo
        +
        #icon-ksyun
        +
      • + +
      • + +
        雨-copy
        +
        #icon-rainyun
        +
      • + +
      • + +
        网宿
        +
        #icon-wangsu
        +
      • + +
      • + +
        ai客服
        +
        #icon-aikefu
        +
      • +
      • @@ -17,7 +17,7 @@ -
        需要开通电脑网站支付, 支付宝配置帮助文档
        +
        需要开通电脑网站支付, 支付宝配置帮助文档
        @@ -25,7 +25,7 @@ -
        需要开通Native支付, 微信配置帮助文档
        +
        需要开通Native支付, 微信配置帮助文档
        diff --git a/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts b/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts index e35bf5dde..8aea00cbd 100644 --- a/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts @@ -39,6 +39,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { { value: 'der', label: 'der,一般用于Apache' }, { value: 'jks', label: 'jks,一般用于JAVA应用' }, { value: 'one', label: '证书私钥一体,crt+key简单合并为一个pem文件' }, + { value: 'p7b', label: 'p7b格式' }, ], }, required: true, @@ -71,7 +72,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { mergeScript: ` return { show: ctx.compute(({form})=>{ - return form.certType === 'pem'; + return form.certType === 'pem' || form.certType === 'p7b' ; }) } `, @@ -169,6 +170,24 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { }) onePath!: string; + @TaskInput({ + title: 'p7b证书保存路径', + helper: '填写应用原本的证书保存路径,路径要包含证书文件名,例如:/tmp/domain_cert.p7b', + component: { + placeholder: '/root/deploy/app/domain_cert.p7b', + }, + mergeScript: ` + return { + show: ctx.compute(({form})=>{ + return form.certType === 'p7b'; + }) + } + `, + required: true, + rules: [{ type: 'filepath' }], + }) + p7bPath!: string; + @TaskInput({ title: '主机登录配置', helper: 'access授权', @@ -277,12 +296,17 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { }) hostOnePath!: string; + @TaskOutput({ + title: 'p7b证书保存路径', + }) + hostP7bPath!: string; + async onInstance() {} async execute(): Promise { const { cert, accessId } = this; - let { crtPath, keyPath, icPath, pfxPath, derPath, jksPath, onePath } = this; + let { crtPath, keyPath, icPath, pfxPath, derPath, jksPath, onePath,p7bPath } = this; const certReader = new CertReader(cert); const executeCmd = async ( script:string)=> { @@ -308,6 +332,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { env['HOST_DER_PATH'] = this.hostDerPath || ''; env['HOST_JKS_PATH'] = this.hostJksPath || ''; env['HOST_ONE_PATH'] = this.hostOnePath || ''; + env['HOST_P7B_PATH'] = this.hostOnePath || ''; } const scripts = script.split('\n'); @@ -320,7 +345,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { } const handle = async (opts: CertReaderHandleContext) => { - const { tmpCrtPath, tmpKeyPath, tmpDerPath, tmpJksPath, tmpPfxPath, tmpIcPath, tmpOnePath } = opts; + const { tmpCrtPath, tmpKeyPath, tmpDerPath, tmpJksPath, tmpPfxPath, tmpIcPath, tmpOnePath ,tmpP7bPath} = opts; if (accessId == null) { this.logger.error('复制到当前主机功能已迁移到 “复制到本机”插件,请换成复制到本机插件'); @@ -392,6 +417,14 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { remotePath: this.onePath, }); } + if (this.p7bPath) { + this.logger.info(`上传p7b证书到主机:${this.p7bPath}`); + p7bPath = this.p7bPath.trim(); + transports.push({ + localPath: tmpP7bPath, + remotePath: this.p7bPath, + }); + } this.logger.info('开始上传文件到服务器'); await sshClient.uploadFiles({ @@ -410,6 +443,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { this.hostDerPath = derPath; this.hostJksPath = jksPath; this.hostOnePath = onePath; + this.hostP7bPath = p7bPath; }; //执行前置命令 From e4489343fee7754be07bcfc3323969dc3a30e90c Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Mon, 25 Aug 2025 18:38:35 +0800 Subject: [PATCH 38/39] =?UTF-8?q?perf:=20lecdnv2=E6=94=AF=E6=8C=81api=20to?= =?UTF-8?q?ken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ui/certd-server/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui/certd-server/README.md b/packages/ui/certd-server/README.md index 97e32f251..0745defec 100755 --- a/packages/ui/certd-server/README.md +++ b/packages/ui/certd-server/README.md @@ -10,6 +10,7 @@ ``` + ```shell npm run heap ``` From 9feb9d04b3c56ec95c06fcf4fd071eb0e88ffc6f Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Mon, 25 Aug 2025 23:22:17 +0800 Subject: [PATCH 39/39] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E5=88=B0=E5=8D=8E=E4=B8=BA=E4=BA=91obs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ui/certd-server/package.json | 1 + .../plugins/deploy-to-obs/index.ts | 185 ++++++++++++++++++ .../plugins/plugin-huawei/plugins/index.ts | 1 + pnpm-lock.yaml | 37 ++-- 4 files changed, 204 insertions(+), 20 deletions(-) create mode 100644 packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-obs/index.ts diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index 1131c69a5..531f8c391 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -82,6 +82,7 @@ "cross-env": "^7.0.3", "crypto-js": "^4.2.0", "dayjs": "^1.11.7", + "esdk-obs-nodejs": "^3.25.6", "form-data": "^4.0.0", "glob": "^11.0.0", "https-proxy-agent": "^7.0.5", diff --git a/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-obs/index.ts b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-obs/index.ts new file mode 100644 index 000000000..02f596fc6 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-obs/index.ts @@ -0,0 +1,185 @@ +import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { HuaweiAccess } from "../../access/index.js"; +import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; +import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; + +@IsTaskPlugin({ + name: 'HauweiDeployCertToOBS', + title: '华为云-部署证书至OBS', + icon: 'svg:icon-huawei', + group: pluginGroups.huawei.key, + desc: '', + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class HauweiDeployCertToOBS extends AbstractTaskPlugin { + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书\n如果你选择使用ccm证书ID,则需要在[域名管理页面右上角开启SCM授权](https://console.huaweicloud.com/cdn/#/cdn/domain)', + component: { + name: 'output-selector', + from: [...CertApplyPluginNames,'HauweiUploadToCCM'], + }, + required: true, + }) + cert!: CertInfo | string; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + @TaskInput({ + title: 'Access授权', + helper: '华为云授权AccessKeyId、AccessKeySecret', + component: { + name: 'access-selector', + type: 'huawei', + }, + required: true, + }) + accessId!: string; + + + @TaskInput( + createRemoteSelectInputDefine({ + title: '存储桶', + helper: '请选择存储桶', + action: HauweiDeployCertToOBS.prototype.onGetBucketList.name, + }) + ) + bucketList!: string[]; + + @TaskInput( + createRemoteSelectInputDefine({ + title: '自定义域名', + helper: '请选择自定义域名', + action: HauweiDeployCertToOBS.prototype.onGetDomainList.name, + watches: ['bucketList'], + }) + ) + domainList!: string[]; + + + + async execute(): Promise { + if (!this.cert) { + throw new Error('域名证书不能为空'); + } + this.logger.info('开始部署证书到华为云obs'); + + for (const domainStr of this.domainList) { + const [location, bucket,domain] = domainStr.split('_'); + + await this.setDomainCert({ + location, + bucket, + domain, + cert: this.cert + }); + } + + this.logger.info('部署证书到华为云cdn完成'); + } + + checkRet(ret: any){ + if (ret?.CommonMsg?.Status>300){ + + throw new Error(`【${ret?.CommonMsg?.Code}】${ret?.CommonMsg?.Message}`); + } + } + + + async getObsClient(opts:{region?:string,bucket?:string} = {}) { + const { region,bucket } = opts; + const regionStr = region? `${region}.`: 'cn-north-4.'; + const bucketStr = bucket? `${bucket}.` : ''; + const access = await this.getAccess(this.accessId); + const sdk = await import('esdk-obs-nodejs'); + const obsClient = new sdk.default({ + // 推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险 + // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html + access_key_id: access.accessKeyId, + secret_access_key: access.accessKeySecret, + // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入 + // security_token: process.env.SECURITY_TOKEN, + // endpoint填写Bucket对应的Endpoint, 这里以华北-北京四为例,其他地区请按实际情况填写 + server: `https://${bucketStr}obs.${regionStr}myhuaweicloud.com`, + }); + return obsClient + } + + async onGetBucketList(data: any) { + const obsClient = await this.getObsClient(); + const res = await obsClient.listBuckets({ + QueryLocation:true + }) + + this.checkRet(res) + + const list = res.InterfaceResult?.Buckets + + if (!list || list.length === 0) { + return [] + } + + return list.map(item => { + return { + value: `${item.Location}_${item.BucketName}`, + label: `${item.BucketName}<${item.Location}>`, + }; + }); + } + + async onGetDomainList(data:any) { + if (!this.bucketList || this.bucketList.length === 0) { + return [] + } + const optionList = [] + for (const item of this.bucketList) { + const [location,bucket] = item.split('_') + + const obsClient = await this.getObsClient({region:location}); + const res = await obsClient.getBucketCustomDomain({ + Bucket: bucket, + }) + this.checkRet(res) + + const list = res.InterfaceResult?.Domains + + if (!list || list.length === 0) { + continue + } + const options= list.map(item => { + return { + value: `${location}_${bucket}_${item.DomainName}`, + label: `${item.DomainName}<${bucket}_${location}>`, + domain: item.DomainName, + }; + }); + optionList.push(...options) + } + + return this.ctx.utils.options.buildGroupOptions( optionList,this.certDomains) + } + + async setDomainCert(opts:{location:string,bucket:string,domain:string,cert:string|CertInfo}){ + const {location,bucket,domain,cert} = opts + const obsClient = await this.getObsClient({region:location}); + const params:any = { + Bucket: bucket, + DomainName: domain, + Name: this.buildCertName( domain) + }; + if (typeof cert === 'string'){ + params.CertificateId= cert + }else{ + params.Certificate= cert.crt + params.PrivateKey = cert.key + } + const res = await obsClient.setBucketCustomDomain(params) + this.checkRet(res) + } +} +new HauweiDeployCertToOBS(); diff --git a/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/index.ts index 3043939b5..fdf6ded9d 100644 --- a/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/index.ts @@ -1,2 +1,3 @@ export * from './deploy-to-cdn/index.js' export * from './upload-to-ccm/index.js' +export * from './deploy-to-obs/index.js' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e607af734..3c778db0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1602,6 +1602,9 @@ importers: dayjs: specifier: ^1.11.7 version: 1.11.13 + esdk-obs-nodejs: + specifier: ^3.25.6 + version: 3.25.6 form-data: specifier: ^4.0.0 version: 4.0.2 @@ -1629,9 +1632,6 @@ importers: koa-send: specifier: ^5.0.1 version: 5.0.1 - ksyun-sdk-node: - specifier: ^1.2.4 - version: 1.2.4(encoding@0.1.13) kubernetes-client: specifier: ^9.0.0 version: 9.0.0 @@ -7390,6 +7390,10 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} + esdk-obs-nodejs@3.25.6: + resolution: {integrity: sha512-bDEznGBoSjqmFNjkL0PvkMzF6o50wa+1PSKQ1tT5CtBP/yw7Egx0c/kIVsu5Raqcip1SjKu7muzslG4xo/skew==} + engines: {node: '>=0.12.7'} + eslint-config-prettier@8.10.0: resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true @@ -9189,9 +9193,6 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - ksyun-sdk-node@1.2.4: - resolution: {integrity: sha512-W/c1nhnZskadPP7ObmizMh+jJeHXWka0HkS8lcZfLWxwEH83B8iMFF0DrtSaDCjQRuBpgzwDLGbbp+U1D1rXlQ==} - kubernetes-client@9.0.0: resolution: {integrity: sha512-Qy8o42dZVHB9P+cIiKdWpQbz/65l/qW1fDYvlzzeSLftmL1Ne3HEiM+0TmKAwNuRW0pTJN2tRWhcccToclxJ8g==} engines: {node: '>=10.13.0'} @@ -21386,6 +21387,13 @@ snapshots: escape-string-regexp@5.0.0: {} + esdk-obs-nodejs@3.25.6: + dependencies: + fast-xml-parser: 4.5.0 + log4js: 6.9.1 + transitivePeerDependencies: + - supports-color + eslint-config-prettier@8.10.0(eslint@7.32.0): dependencies: eslint: 7.32.0 @@ -21527,13 +21535,13 @@ snapshots: resolve: 1.22.10 semver: 6.3.1 - eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8): + eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8): dependencies: eslint: 7.32.0 prettier: 2.8.8 prettier-linter-helpers: 1.0.0 optionalDependencies: - eslint-config-prettier: 8.10.0(eslint@8.57.0) + eslint-config-prettier: 8.10.0(eslint@7.32.0) eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): dependencies: @@ -23518,17 +23526,6 @@ snapshots: kolorist@1.8.0: {} - ksyun-sdk-node@1.2.4(encoding@0.1.13): - dependencies: - abort-controller: 3.0.0 - core-js: 3.42.0 - crypto-js: 4.2.0 - dayjs: 1.11.13 - node-fetch: 2.7.0(encoding@0.1.13) - qs: 6.14.0 - transitivePeerDependencies: - - encoding - kubernetes-client@9.0.0: dependencies: '@kubernetes/client-node': 0.10.2 @@ -24252,7 +24249,7 @@ snapshots: eslint: 7.32.0 eslint-config-prettier: 8.10.0(eslint@7.32.0) eslint-plugin-node: 11.1.0(eslint@7.32.0) - eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8) + eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8) execa: 5.1.1 inquirer: 7.3.3 json5: 2.2.3