diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index 1131c69a..531f8c39 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 00000000..02f596fc --- /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 3043939b..fdf6ded9 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 e607af73..3c778db0 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