From 1102952b4703e8c0bbc17b0700c0ed3ef6f866d3 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Tue, 18 Nov 2025 01:04:47 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BF=AE=E5=A4=8D=E8=A5=BF=E6=95=B0?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E8=AE=B0=E5=BD=95=E6=B7=BB=E5=8A=A0=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E7=9A=84bug=EF=BC=8C=E6=94=AF=E6=8C=81=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E8=AF=81=E4=B9=A6=E5=88=B0=E8=A5=BF=E6=95=B0=E8=99=9A?= =?UTF-8?q?=E6=8B=9F=E4=B8=BB=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugins/plugin-west/access.ts | 99 +++++++- .../src/plugins/plugin-west/dns-provider.ts | 65 ++--- .../src/plugins/plugin-west/index.ts | 1 + .../plugin-west/plugins/deploy-to-vhost.ts | 227 ++++++++++++++++++ 4 files changed, 350 insertions(+), 42 deletions(-) create mode 100644 packages/ui/certd-server/src/plugins/plugin-west/plugins/deploy-to-vhost.ts diff --git a/packages/ui/certd-server/src/plugins/plugin-west/access.ts b/packages/ui/certd-server/src/plugins/plugin-west/access.ts index f9abd036..a4e36ea6 100644 --- a/packages/ui/certd-server/src/plugins/plugin-west/access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-west/access.ts @@ -1,5 +1,6 @@ +import { HttpRequestConfig } from '@certd/basic'; import { IsAccess, AccessInput, BaseAccess } from '@certd/pipeline'; - +import qs from 'qs'; /** * 这个注解将注册一个授权配置 * 在certd的后台管理系统中,用户可以选择添加此类型的授权 @@ -55,7 +56,7 @@ export class WestAccess extends BaseAccess { component: { placeholder: '账户级别的key,对整个账户都有管理权限', }, - helper: '账户级别的key,对整个账户都有管理权限\n前往https://www.west.cn/manager/API/APIconfig.asp,手动设置“api连接密码”', + helper: '账户级别的key,对整个账户都有管理权限\n前往[API接口配置](https://www.west.cn/manager/API/APIconfig.asp),手动设置“api连接密码”', encrypt: true, required: false, mergeScript: ` @@ -88,6 +89,100 @@ export class WestAccess extends BaseAccess { `, }) apidomainkey = ''; + + + @AccessInput({ + title: "测试", + component: { + name: "api-test", + action: "TestRequest" + }, + helper: "点击测试接口是否正常" + }) + testRequest = true; + + async onTestRequest() { + await this.getDomainList(); + return "ok"; + } + + + async getDomainList() { + const res = await this.doRequest({ + url: '/v2/domain', + method: 'GET', + data:{ + act:'getdomains', + limit:1, + page:1 + } + }); + return res; + } + + + + public async doRequest(req: HttpRequestConfig) { + let { url, method, data } = req; + if (data == null) { + data = {}; + } + if (!method) { + method = 'POST'; + } + + if (this.scope === 'account') { + /** + * token text 身份验证字符串,取值为:md5(username+api_password+timestamp),其中: +username:您在我司注册的用户名。 +api_password:您设置的API密码。您可登录官网管理中心,在“代理商管理”-""页面查看您的api密码。 +timestamp:当前时间的毫秒时间戳。 +将字符串username与字符串api_password连接,再与timestamp连接,然后将生成的字符串进行md5求值,md5算法要求为: +32位16进制字符串,小写格式。 +身份验证串有效期10分钟。 + +比如,您的用户名为:zhangsan,您的API密码为:5dh232kfg!* ,当前毫秒时间戳为:1554691950854,则: +token=md5(zhangsan + 5dh232kfg!* + 1554691950854)=cfcd208495d565ef66e7dff9f98764da + */ + // data.apikey = this.ctx.utils.hash.md5(this.apikey); + data.username = this.username; + const timestamp = new Date().getTime(); + const token = this.ctx.utils.hash.md5(`${this.username}${this.apikey}${timestamp}`).toLowerCase(); + data.token = token; + data.time = timestamp; + } else { + data.apidomainkey = this.apidomainkey; + } + + + const headers = {} + const body: any = {} + if (method.toUpperCase() === 'POST') { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + body.data = data + } else if (method.toUpperCase() === 'GET') { + let queryString = ''; + if (method.toUpperCase() === 'GET') { + queryString = qs.stringify(data); + } + url = `${url}?${queryString}` + } + + + const res = await this.ctx.http.request({ + baseURL: 'https://api.west.cn/api', + url, + method, + ...body, + headers, + }); + this.ctx.logger.info(`request ${url} ${method} res:${JSON.stringify(res)}`); + if (res.msg !== 'success' && res.result!= 200) { + throw new Error(`${JSON.stringify(res.msg)}`); + } + return res; + } + } new WestAccess(); diff --git a/packages/ui/certd-server/src/plugins/plugin-west/dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-west/dns-provider.ts index 2df31192..9179f121 100644 --- a/packages/ui/certd-server/src/plugins/plugin-west/dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-west/dns-provider.ts @@ -4,10 +4,10 @@ import { WestAccess } from './access.js'; type westRecord = { // 这里定义Record记录的数据结构,跟对应云平台接口返回值一样即可,一般是拿到id就行,用于删除txt解析记录,清理申请痕迹 - code: number; + result: number; msg: string; - body: { - record_id: number; + data: { + id: number; }; }; @@ -31,27 +31,6 @@ export class WestDnsProvider extends AbstractDnsProvider { //... } - private async doRequestApi(url: string, data: any = null, method = 'post') { - if (this.access.scope === 'account') { - data.apikey = this.ctx.utils.hash.md5(this.access.apikey); - data.username = this.access.username; - } else { - data.apidomainkey = this.access.apidomainkey; - } - const res = await this.ctx.http.request({ - url, - method, - data, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }); - if (res.msg !== 'success') { - throw new Error(`${JSON.stringify(res.msg)}`); - } - return res; - } - /** * 创建dns解析记录,用于验证域名所有权 */ @@ -63,22 +42,26 @@ export class WestDnsProvider extends AbstractDnsProvider { * type: 'TXT', * domain: 'example.com' */ - const { fullRecord, value, type, domain } = options; + const { fullRecord, value, type, domain,hostRecord } = options; this.logger.info('添加域名解析:', fullRecord, value, type, domain); // 准备要发送到API的请求体 const requestBody = { - act: 'dnsrec.add', // API动作类型 + act: 'adddnsrecord', // API动作类型 domain: domain, // 域名 - record_type: 'TXT', // DNS记录类型 - hostname: fullRecord, // 完整的记录名 - record_value: value, // 记录的值 - record_line: '', // 记录线路 - record_ttl: 60, // TTL (生存时间),设置为60秒 + type: 'TXT', // DNS记录类型 + host: hostRecord, // 完整的记录名 + value: value, // 记录的值 + line: '', // 记录线路 + ttl: 60, // TTL (生存时间),设置为60秒 }; - const url = 'https://api.west.cn/API/v2/domain/dns/'; - const res = await this.doRequestApi(url, requestBody); + const url = '/v2/domain/'; + const res = await this.access.doRequest({ + url, + method:'POST', + data: requestBody, + }); const record = res as westRecord; this.logger.info(`添加域名解析成功:fullRecord=${fullRecord},value=${value}`); this.logger.info(`dns解析记录:${JSON.stringify(record)}`); @@ -90,6 +73,7 @@ export class WestDnsProvider extends AbstractDnsProvider { /** * 删除dns解析记录,清理申请痕迹 + * https://console-docs.apipost.cn/preview/ab2c3103b22855ba/fac91d1e43fafb69?target_id=c4564349-6687-413d-a3d4-b0e8db5b34b2 * @param options */ async removeRecord(options: RemoveRecordOptions): Promise { @@ -104,16 +88,17 @@ export class WestDnsProvider extends AbstractDnsProvider { // 准备要发送到API的请求体 const requestBody = { - act: 'dnsrec.remove', // API动作类型 + act: 'deldnsrecord', // API动作类型 domain: domain, // 域名 - record_id: record.body.record_id, - hostname: fullRecord, // 完整的记录名 - record_type: 'TXT', // DNS记录类型 - record_line: '', // 记录线路 + id: record.data?.id, }; - const url = 'https://api.west.cn/API/v2/domain/dns/'; - const res = await this.doRequestApi(url, requestBody); + const url = '/v2/domain/'; + const res = await this.access.doRequest({ + url, + method:'POST', + data: requestBody, + }); const result = res.result; this.logger.info('删除域名解析成功:', fullRecord, value, JSON.stringify(result)); } diff --git a/packages/ui/certd-server/src/plugins/plugin-west/index.ts b/packages/ui/certd-server/src/plugins/plugin-west/index.ts index db899c71..8f767a7c 100644 --- a/packages/ui/certd-server/src/plugins/plugin-west/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-west/index.ts @@ -1,2 +1,3 @@ export * from './dns-provider.js'; export * from './access.js'; +export * from './plugins/deploy-to-vhost.js'; \ No newline at end of file diff --git a/packages/ui/certd-server/src/plugins/plugin-west/plugins/deploy-to-vhost.ts b/packages/ui/certd-server/src/plugins/plugin-west/plugins/deploy-to-vhost.ts new file mode 100644 index 00000000..b55c357a --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-west/plugins/deploy-to-vhost.ts @@ -0,0 +1,227 @@ +import { + AbstractTaskPlugin, + IsTaskPlugin, + PageSearch, + pluginGroups, + RunStrategy, + TaskInput +} from "@certd/pipeline"; +import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; +import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; +import { WestAccess } from "../access.js"; + +@IsTaskPlugin({ + //命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名 + name: "WestDeployToVhost", + title: "西数-部署到虚拟主机", + desc: "西部数码部署证书到虚拟主机", + icon: "svg:icon-lucky", + //插件分组 + group: pluginGroups.cdn.key, + needPlus: false, + default: { + //默认值配置照抄即可 + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed + } + } +}) +//类名规范,跟上面插件名称(name)一致 +export class WestDeployToVhost extends AbstractTaskPlugin { + //证书选择,此项必须要有 + @TaskInput({ + title: "域名证书", + helper: "请选择前置任务输出的域名证书", + component: { + name: "output-selector", + from: [...CertApplyPluginNames] + } + // required: true, // 必填 + }) + cert!: CertInfo; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + + //授权选择框 + @TaskInput({ + title: "西数授权", + component: { + name: "access-selector", + type: "west" //固定授权类型 + }, + required: true //必填 + }) + accessId!: string; + // + + + @TaskInput( + createRemoteSelectInputDefine({ + title: "虚拟主机列表", + helper: "虚拟主机列表", + action: WestDeployToVhost.prototype.onGetVhostList.name, + pager: false, + search: false + }) + ) + vhostList!: string[]; + + // @TaskInput( + // createRemoteSelectInputDefine({ + // title: "证书Id", + // helper: "要部署的西数证书id", + // action: WestDeployToVhost.prototype.onGetCertList.name, + // pager: false, + // search: false + // }) + // ) + // certList!: string[]; + + //插件实例化时执行的方法 + async onInstance() { + } + + //插件执行方法 + async execute(): Promise { + const access = await this.getAccess(this.accessId); + + for (const item of this.vhostList) { + this.logger.info(`----------- 开始更新证书到虚拟主机:${item}`); + const arr = item.split("_"); + const sitename = arr[1]; + await this.uploadCert({access,sitename}); + await this.ctx.utils.sleep(2000); + const res = await this.getVhostSslInfo({access,sitename}); + this.logger.info(`----------- 虚拟主机${sitename}证书信息:${JSON.stringify(res)}`); + this.logger.info(`----------- 更新证书${item}成功`); + } + + this.logger.info("部署完成"); + } + + // async onGetCertList(data: PageSearch = {}) { + // const access = await this.getAccess(this.accessId); + + // const list = await access.getCertList({}); + // if (!list || list.length === 0) { + // throw new Error("没有找到证书,请先在控制台上传一次证书且关联域名"); + // } + + // /** + // * certificate-id + // * name + // * dns-names + // */ + // const options = list.map((item: any) => { + // const domains = item["dns-names"] + // const certId = item["certificate-id"]; + // return { + // label: `${item.name}<${certId}-${domains[0]}>`, + // value: certId, + // domain: item["dns-names"] + // }; + // }); + // return { + // list: this.ctx.utils.options.buildGroupOptions(options, this.certDomains), + // total: list.length, + // pageNo: 1, + // pageSize: list.length + // }; + // } + + async uploadCert(req:{access:any,sitename:string}){ + const {access,sitename} = req; + const data = { + /** + * act +vhostssl +是 +String +sitename +westly +是 +String +cmd +import +是 +String +openssl/closessl 部署/关闭 +keycontent +是 +String +私匙 +certcontent + */ + act:"vhostssl", + sitename:sitename, + westly:"1", + cmd:"import", + opensslclosessl:"openssl", + keycontent:this.cert.key, + certcontent:this.cert.crt, + } + + const res = await access.doRequest({ + url: `/v2/vhost/`, + method:"POST", + data:data + }); + return res; + } + + async getVhostSslInfo(req:{access:any,sitename:string}){ + const {access,sitename} = req; + const data = { + act:"vhostssl", + sitename:sitename, + cmd:"info", + } + const res = await access.doRequest({ + url: `/v2/vhost/`, + method:"POST", + data:data + }); + return res; + } + + async onGetVhostList(data: PageSearch = {}) { + const access = await this.getAccess(this.accessId); + + const res = await access.doRequest({ + url: `/v2/vhost/`, + method:"POST", + data:{ + act:"sync", + westid: 1, + } + }); + const list = res.data + if (!list || list.length === 0) { + throw new Error("没有找到虚拟主机"); + } + + /** + * certificate-id + * name + * dns-names + */ + const options = list.map((item: any) => { + return { + label: `${item.sitename}<${item.westid}-${item.bindings}>`, + value: `${item.westid}_${item.sitename}`, + domain: item.bindings.split(",") + }; + }); + return { + list: this.ctx.utils.options.buildGroupOptions(options, this.certDomains), + total: list.length, + pageNo: 1, + pageSize: list.length + }; + } +} + +//实例化一下,注册插件 +new WestDeployToVhost();