@certd/plugin-plus 1.25.5 → 1.25.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.25.6](https://github.com/certd/certd/compare/v1.25.5...v1.25.6) (2024-09-29)
7
+
8
+ **Note:** Version bump only for package @certd/plugin-plus
9
+
6
10
  ## [1.25.5](https://github.com/certd/certd/compare/v1.25.4...v1.25.5) (2024-09-26)
7
11
 
8
12
  **Note:** Version bump only for package @certd/plugin-plus
package/dist/bundle.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("@certd/pipeline"),t=require("@certd/plugin-cert"),s=require("node:crypto"),o=require("node:path"),n=require("https-proxy-agent"),r=require("querystring"),i=require("fs"),a=require("form-data"),c=require("dayjs");function p(){if(!e.isPlus())throw new Error("此插件仅供专业版中使用")}class l extends e.AbstractTaskPlugin{setCtx(e){super.setCtx(e),p()}}function u(e,t,s,o){var n,r=arguments.length,i=r<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,s):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(e,t,s,o);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(i=(r<3?n(i):r>3?n(t,s,i):n(t,s))||i);return r>3&&i&&Object.defineProperty(t,s,i),i}function d(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}"function"==typeof SuppressedError&&SuppressedError;class g{access;http;constructor(e,t){this.access=e,this.http=t}getRequestToken(){const e=(new Date).getTime(),t=e+this.getMd5(this.access.apiSecret);return{request_token:this.getMd5(t),request_time:e}}getMd5(e){return s.createHash("md5").update(e).digest("hex")}async doRequest(e,t,s,o){const n=this.getRequestToken();console.log("token",n);const r={...n,...s};let i=`${this.access.panelUrl}${e}`;t&&(i=`${i}?action=${t}`);const a=await this.http.request({url:i,method:"post",headers:{"Content-Type":"application/x-www-form-urlencoded"},data:r,...o});if(!a.status)throw new Error(a.msg);return a}}exports.BaotaDeployPanelCertPlugin=class extends l{cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:s}=this,o=new t.CertReader(e),n=await this.accessService.getById(s),r=this.ctx.http,i=new g(n,r),a=await i.doRequest("/config","SavePanelSSL",{privateKey:o.key,certPem:o.crt},{skipSslVerify:!0});this.logger.info(a?.msg)}},u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.BaotaDeployPanelCertPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"宝塔授权",helper:"baota的接口密钥,目前测试宝塔面板本身开启ssl之后,API接口就不能用了",component:{name:"pi-access-selector",type:"baota"},required:!0}),d("design:type",String)],exports.BaotaDeployPanelCertPlugin.prototype,"accessId",void 0),exports.BaotaDeployPanelCertPlugin=u([e.IsTaskPlugin({name:"BaotaDeployPanelCert",title:"宝塔面板证书部署",icon:"svg:icon-bt",group:e.pluginGroups.other.key,desc:"部署宝塔面板本身的ssl证书",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.BaotaDeployPanelCertPlugin),new exports.BaotaDeployPanelCertPlugin,exports.BaotaDeployWebSiteCertPlugin=class extends l{siteName;isReverseProxy=!1;cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:s}=this,o=new t.CertReader(e),n=await this.accessService.getById(s),r=this.ctx.http,i=new g(n,r);if(this.logger.info(`siteName:${this.siteName}`),this.isReverseProxy){const e=await i.doRequest("/mod/proxy/com/set_ssl",null,{site_name:this.siteName,key:o.key,csr:o.crt});this.logger.info(e?.msg)}else{const e=await i.doRequest("/site","SetSSL",{type:0,siteName:this.siteName,key:o.key,csr:o.crt});this.logger.info(e?.msg)}}},u([e.TaskInput({title:"网站域名",component:{name:"a-input"},helper:"登录面板->网站->网站名/域名/项目名称",required:!0}),d("design:type",String)],exports.BaotaDeployWebSiteCertPlugin.prototype,"siteName",void 0),u([e.TaskInput({title:"是否反向代理",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"该项目类型是反向代理还是其他项目",required:!0}),d("design:type",Object)],exports.BaotaDeployWebSiteCertPlugin.prototype,"isReverseProxy",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.BaotaDeployWebSiteCertPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"宝塔授权",helper:"baota的接口密钥",component:{name:"pi-access-selector",type:"baota"},required:!0}),d("design:type",String)],exports.BaotaDeployWebSiteCertPlugin.prototype,"accessId",void 0),exports.BaotaDeployWebSiteCertPlugin=u([e.IsTaskPlugin({name:"BaotaDeployWebSiteCert",title:"宝塔网站证书部署",icon:"svg:icon-bt",group:e.pluginGroups.other.key,desc:"部署宝塔管理的站点的ssl证书",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.BaotaDeployWebSiteCertPlugin),new exports.BaotaDeployWebSiteCertPlugin,exports.BaotaAccess=class{panelUrl="";apiSecret=""},u([e.AccessInput({title:"宝塔URL地址",component:{placeholder:"http://192.168.42.237:41896"},helper:"宝塔面板的url地址,例如:http://192.168.42.237:41896",required:!0}),d("design:type",Object)],exports.BaotaAccess.prototype,"panelUrl",void 0),u([e.AccessInput({title:"接口密钥",component:{placeholder:"接口密钥"},helper:"宝塔面板设置->面板设置->API接口->接口配置->接口密钥。\n必须要加IP白名单,你可以先运行一次,报错之后会打印IP,将IP加入白名单之后再次运行即可",required:!0,encrypt:!0}),d("design:type",Object)],exports.BaotaAccess.prototype,"apiSecret",void 0),exports.BaotaAccess=u([e.IsAccess({name:"baota",title:"baota授权",desc:""})],exports.BaotaAccess),new exports.BaotaAccess,exports.YidunDeployToCDNPlugin=class extends l{certId;domain;cert;accessId;async onInstance(){}async execute(){const{domain:e,certId:s,cert:o}=this;if(!e&&!s)throw new Error("证书ID和网站域名必须填写一个");const n=new t.CertReader(o);s>0?await this.updateByCertId(n,s):await this.updateByDomain(n)}async updateByCertId(e,t){this.logger.info(`更新证书,证书ID:${t}`);const s=`http://user.yiduncdn.com/v1/certs/${t}`;await this.doRequest(s,"PUT",{cert:e.crt,key:e.key})}async doRequest(e,t,s){const o=await this.accessService.getById(this.accessId),{apiKey:n,apiSecret:r}=o,i=this.ctx.http,a=await i.request({url:e,method:t,headers:{"api-key":n,"api-secret":r},data:s});if(0!=a.code)throw new Error(a.msg);return a}async updateByDomain(e){const t=await this.doRequest("http://user.yiduncdn.com/v1/sites","GET",{domain:this.domain});if(0===t.data.length)throw new Error(`未找到域名相关站点:${this.domain}`);let s=null;for(const e of t.data)e.domain===this.domain&&(s=e);if(!s)throw new Error(`未找到域名匹配的站点:${this.domain}`);if(s.https_listen?.cert){const t=s.https_listen.cert;await this.updateByCertId(e,t)}else{this.logger.info(`创建证书,域名:${this.domain}`);const t="http://user.yiduncdn.com/v1/certs",o=this.domain+"_"+(new Date).getTime();await this.doRequest(t,"POST",{name:o,type:"custom",cert:e.crt,key:e.key});const n=(await this.doRequest(t,"GET",{name:o})).data[0].id,r="http://user.yiduncdn.com/v1/sites";await this.doRequest(r,"PUT",{id:s.id,https_listen:{cert:n}})}}},u([e.TaskInput({title:"证书ID",component:{name:"a-input-number",vModel:"value"},helper:"证书ID,在证书管理页面查看,每条记录都有证书id"}),d("design:type",Number)],exports.YidunDeployToCDNPlugin.prototype,"certId",void 0),u([e.TaskInput({title:"网站域名",component:{name:"a-input",vModel:"value"},helper:"网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名"}),d("design:type",Number)],exports.YidunDeployToCDNPlugin.prototype,"domain",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.YidunDeployToCDNPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"易盾授权",helper:"易盾CDN授权",component:{name:"pi-access-selector",type:"yidun"},required:!0}),d("design:type",String)],exports.YidunDeployToCDNPlugin.prototype,"accessId",void 0),exports.YidunDeployToCDNPlugin=u([e.IsTaskPlugin({name:"YidunDeployToCDN",title:"部署证书到易盾CDN",icon:"material-symbols:shield-outline",group:e.pluginGroups.cdn.key,desc:"http://user.yiduncdn.com/",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.YidunDeployToCDNPlugin),new exports.YidunDeployToCDNPlugin,exports.YidunAccess=class{apiKey="";apiSecret=""},u([e.AccessInput({title:"api_key",component:{placeholder:"api_key"},helper:"http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",required:!0,encrypt:!0}),d("design:type",Object)],exports.YidunAccess.prototype,"apiKey",void 0),u([e.AccessInput({title:"api_secret",component:{placeholder:"api_secret"},helper:"http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",required:!0,encrypt:!0}),d("design:type",Object)],exports.YidunAccess.prototype,"apiSecret",void 0),exports.YidunAccess=u([e.IsAccess({name:"yidun",title:"易盾云授权",desc:"user.yiduncdn.com"})],exports.YidunAccess),new exports.YidunAccess,exports.FtpAccess=class{host;port;user;password;secure=!1},u([e.AccessInput({title:"host",component:{placeholder:"ip / 域名",name:"a-input",vModel:"value"},helper:"FTP地址",required:!0}),d("design:type",String)],exports.FtpAccess.prototype,"host",void 0),u([e.AccessInput({title:"host",value:21,component:{placeholder:"21",name:"a-input-number",vModel:"value"},helper:"FTP端口",required:!0}),d("design:type",String)],exports.FtpAccess.prototype,"port",void 0),u([e.AccessInput({title:"user",component:{placeholder:"用户名"},helper:"FTP用户名",required:!0}),d("design:type",String)],exports.FtpAccess.prototype,"user",void 0),u([e.AccessInput({title:"password",component:{placeholder:"密码",component:{name:"a-input-password",vModel:"value"}},encrypt:!0,helper:"FTP密码",required:!0}),d("design:type",String)],exports.FtpAccess.prototype,"password",void 0),u([e.AccessInput({title:"secure",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"是否使用SSL",required:!0}),d("design:type",Boolean)],exports.FtpAccess.prototype,"secure",void 0),exports.FtpAccess=u([e.IsAccess({name:"ftp",title:"FTP授权",desc:""})],exports.FtpAccess),new exports.FtpAccess,exports.UploadCertToFTPPlugin=class extends l{crtPath;keyPath;pfxPath;derPath;cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:s}=this,o=new t.CertReader(e);let n=null;const r=async({reader:e,tmpCrtPath:t,tmpKeyPath:o,tmpDerPath:r,tmpPfxPath:i})=>{const a=(await import("basic-ftp")).Client;n=new a,n.ftp.verbose=!0,this.logger.info("开始连接FTP");const c=await this.accessService.getById(s);await n.access(c),this.logger.info("FTP连接成功"),await this.doUpload(n,t,this.crtPath),await this.doUpload(n,o,this.keyPath),await this.doUpload(n,i,this.pfxPath),await this.doUpload(n,r,this.derPath)};try{await o.readCertFile({logger:this.logger,handle:r})}finally{n&&n.close()}this.logger.info("执行完成")}async doUpload(e,t,s){if(!s)return;const n=o.dirname(s);this.logger.info(`确保目录存在:${n}`),await e.ensureDir(n),this.logger.info(`开始上传文件${t} -> ${s}`),await e.uploadFrom(t,s)}},u([e.TaskInput({title:"PEM证书保存路径",helper:"需要有写入权限,路径要包含证书文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.pem"}}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"crtPath",void 0),u([e.TaskInput({title:"私钥保存路径",helper:"需要有写入权限,路径要包含私钥文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.key"}}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"keyPath",void 0),u([e.TaskInput({title:"PFX证书保存路径",helper:"需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.pfx"}}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"pfxPath",void 0),u([e.TaskInput({title:"DER证书保存路径",helper:"需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号\n.der和.cer是相同的东西,改个后缀名即可",component:{placeholder:"/test/cert.der 或 /test/cert.cer"}}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"derPath",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.UploadCertToFTPPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"FTP授权",component:{name:"pi-access-selector",type:"ftp"},required:!0}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"accessId",void 0),exports.UploadCertToFTPPlugin=u([e.IsTaskPlugin({name:"UploadCertToFTP",title:"上传证书到FTP",icon:"mdi:folder-upload-outline",group:e.pluginGroups.host.key,desc:"将证书上传到FTP服务器",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.UploadCertToFTPPlugin),new exports.UploadCertToFTPPlugin,exports.CdnflyDeployToCDNPlugin=class extends l{url;certId;domain;cert;accessId;async onInstance(){}async execute(){const{domain:e,certId:s,cert:o}=this;if(!e&&!s)throw new Error("证书ID和网站域名必须填写一个");const n=new t.CertReader(o);s>0?await this.updateByCertId(n,s):await this.updateByDomain(n)}async updateByCertId(e,t){this.logger.info(`更新证书,证书ID:${t}`);const s=`${this.url}/v1/certs/${t}`;await this.doRequest(s,"PUT",{cert:e.crt,key:e.key})}async doRequest(e,t,s){const o=await this.accessService.getById(this.accessId),{apiKey:n,apiSecret:r}=o,i=this.ctx.http,a=await i.request({url:e,method:t,headers:{"api-key":n,"api-secret":r},data:s});if(0!=a.code)throw new Error(a.msg);return a}async updateByDomain(e){const t=`${this.url}/v1/sites`,s=await this.doRequest(t,"GET",{domain:this.domain});if(0===s.data.length)throw new Error(`未找到域名相关站点:${this.domain}`);let o=null;for(const e of s.data)e.domain===this.domain&&(o=e);if(!o)throw new Error(`未找到域名匹配的站点:${this.domain}`);if(o.https_listen?.cert){const t=o.https_listen.cert;await this.updateByCertId(e,t)}else{this.logger.info(`创建证书,域名:${this.domain}`);const t=`${this.url}/v1/certs`,s=this.domain+"_"+(new Date).getTime();await this.doRequest(t,"POST",{name:s,type:"custom",cert:e.crt,key:e.key});const n=(await this.doRequest(t,"GET",{name:s})).data[0].id,r=`${this.url}/v1/sites`;await this.doRequest(r,"PUT",{id:o.id,https_listen:{cert:n}})}}},u([e.TaskInput({title:"cdnfly系统网址",component:{name:"a-input",vModel:"value"},helper:"例如:http://demo.cdnfly.cn"}),d("design:type",String)],exports.CdnflyDeployToCDNPlugin.prototype,"url",void 0),u([e.TaskInput({title:"证书ID",component:{name:"a-input-number",vModel:"value"},helper:"证书ID,在证书管理页面查看,每条记录都有证书id"}),d("design:type",Number)],exports.CdnflyDeployToCDNPlugin.prototype,"certId",void 0),u([e.TaskInput({title:"网站域名",component:{name:"a-input",vModel:"value"},helper:"网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名"}),d("design:type",Number)],exports.CdnflyDeployToCDNPlugin.prototype,"domain",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.CdnflyDeployToCDNPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"cdnfly授权",helper:"cdnfly授权",component:{name:"pi-access-selector",type:"cdnfly"},required:!0}),d("design:type",String)],exports.CdnflyDeployToCDNPlugin.prototype,"accessId",void 0),exports.CdnflyDeployToCDNPlugin=u([e.IsTaskPlugin({name:"CdnflyDeployToCDN",title:"部署证书到cdnfly",icon:"majesticons:cloud-line",group:e.pluginGroups.cdn.key,desc:"cdnfly",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.CdnflyDeployToCDNPlugin),new exports.CdnflyDeployToCDNPlugin,exports.CdnflyAccess=class{apiKey="";apiSecret=""},u([e.AccessInput({title:"api_key",component:{placeholder:"api_key"},helper:"登录cdnfly控制台->账户中心->Api密钥,点击开启后获取",required:!0,encrypt:!0}),d("design:type",Object)],exports.CdnflyAccess.prototype,"apiKey",void 0),u([e.AccessInput({title:"api_secret",component:{placeholder:"api_secret"},helper:"登录cdnfly控制台->账户中心->Api密钥,点击开启后获取",required:!0,encrypt:!0}),d("design:type",Object)],exports.CdnflyAccess.prototype,"apiSecret",void 0),exports.CdnflyAccess=u([e.IsAccess({name:"cdnfly",title:"cdnfly授权",desc:""})],exports.CdnflyAccess),new exports.CdnflyAccess,exports.DeployCertToAliyunOSS=class extends l{region;bucket;domainName;certName;cert;accessId;async onInstance(){}async execute(){this.logger.info("开始部署证书到阿里云OSS");const e=await this.accessService.getById(this.accessId);this.logger.info(`bucket: ${this.bucket}, region: ${this.region}, domainName: ${this.domainName}`);const t=await this.getClient(e);await this.doRequest(t,{}),this.logger.info("部署完成")}async getClient(e){return new((await import("ali-oss")).default)({accessKeyId:e.accessKeyId,accessKeySecret:e.accessKeySecret,region:this.region,authorizationV4:!0,bucket:this.bucket})}async doRequest(e,t){t=e._bucketRequestParams("POST",this.bucket,{cname:"",comp:"add"});const s=`\n <BucketCnameConfiguration>\n <Cname>\n <Domain>${this.domainName}</Domain>\n <CertificateConfiguration>\n <PrivateKey>${this.cert.key}</PrivateKey>\n <Certificate>${this.cert.crt}</Certificate>\n </CertificateConfiguration>\n </Cname>\n</BucketCnameConfiguration>`;t.content=s,t.mime="xml",t.successStatuses=[200];const o=await e.request(t);return this.checkRet(o),o}checkRet(e){if(null!=e.code)throw new Error("执行失败:"+e.Message)}},u([e.TaskInput({title:"大区",component:{name:"a-auto-complete",vModel:"value",options:[{value:"oss-cn-hangzhou",label:"华东1(杭州)"},{value:"oss-cn-shanghai",label:"华东2(上海)"},{value:"oss-cn-nanjing",label:"华东5(南京-本地地域)"},{value:"oss-cn-fuzhou",label:"华东6(福州-本地地域)"},{value:"oss-cn-wuhan",label:"华中1(武汉-本地地域)"},{value:"oss-cn-qingdao",label:"华北1(青岛)"},{value:"oss-cn-beijing",label:"华北2(北京)"},{value:"oss-cn-zhangjiakou",label:"华北 3(张家口)"},{value:"oss-cn-huhehaote",label:"华北5(呼和浩特)"},{value:"oss-cn-wulanchabu",label:"华北6(乌兰察布)"},{value:"oss-cn-shenzhen",label:"华南1(深圳)"},{value:"oss-cn-heyuan",label:"华南2(河源)"},{value:"oss-cn-guangzhou",label:"华南3(广州)"},{value:"oss-cn-chengdu",label:"西南1(成都)"},{value:"oss-cn-hongkong",label:"中国香港"},{value:"oss-us-west-1",label:"美国(硅谷)①"},{value:"oss-us-east-1",label:"美国(弗吉尼亚)①"},{value:"oss-ap-northeast-1",label:"日本(东京)①"},{value:"oss-ap-northeast-2",label:"韩国(首尔)"},{value:"oss-ap-southeast-1",label:"新加坡①"},{value:"oss-ap-southeast-2",label:"澳大利亚(悉尼)①"},{value:"oss-ap-southeast-3",label:"马来西亚(吉隆坡)①"},{value:"oss-ap-southeast-5",label:"印度尼西亚(雅加达)①"},{value:"oss-ap-southeast-6",label:"菲律宾(马尼拉)"},{value:"oss-ap-southeast-7",label:"泰国(曼谷)"},{value:"oss-eu-central-1",label:"德国(法兰克福)①"},{value:"oss-eu-west-1",label:"英国(伦敦)"},{value:"oss-me-east-1",label:"阿联酋(迪拜)①"},{value:"oss-rg-china-mainland",label:"无地域属性(中国内地)"}]},required:!0}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"region",void 0),u([e.TaskInput({title:"Bucket",helper:"存储桶名称",required:!0}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"bucket",void 0),u([e.TaskInput({title:"绑定的域名",helper:"你在阿里云OSS上绑定的域名,比如:certd.docmirror.cn",required:!0}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"domainName",void 0),u([e.TaskInput({title:"证书名称",helper:"上传后将以此名称作为前缀备注"}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"certName",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.DeployCertToAliyunOSS.prototype,"cert",void 0),u([e.TaskInput({title:"Access授权",helper:"阿里云授权AccessKeyId、AccessKeySecret",component:{name:"pi-access-selector",type:"aliyun"},required:!0}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"accessId",void 0),exports.DeployCertToAliyunOSS=u([e.IsTaskPlugin({name:"DeployCertToAliyunOSS",title:"部署证书至阿里云OSS",icon:"ant-design:aliyun-outlined",group:e.pluginGroups.aliyun.key,desc:"自动部署域名证书至阿里云OSS",needPlus:!0,default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.DeployCertToAliyunOSS),new exports.DeployCertToAliyunOSS;class y{client;logger;agent;useROAClient;constructor(e){this.logger=e.logger,this.useROAClient=e.useROAClient||!1;const t=process.env.HTTPS_PROXY;t&&(this.logger.info(`use proxy:${t}`),this.agent=new n.HttpsProxyAgent(t))}async getSdk(){if(this.useROAClient)return await this.getROAClient();return(await import("@alicloud/pop-core")).default}async getROAClient(){const e=await import("@alicloud/pop-core");return console.log("aliyun sdk",e),e.ROAClient}async init(e){const t=await this.getSdk();return this.client=new t(e),this.client}async request(e,t,s={}){return this.useROAClient||(s.agent=this.agent),await this.client.request(e,t,s)}}exports.DeployCertToAliyunAckPlugin=class extends l{clusterId;secretName;regionId;namespace="default";isPrivateIpAddress;cert;accessId;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){this.logger.info("开始部署证书到阿里云Ack");const{regionId:t,clusterId:s,isPrivateIpAddress:o,cert:n}=this,r=await this.accessService.getById(this.accessId),i=await this.getClient(r,t),a=await this.getKubeConfig(i,s,o);this.logger.info("kubeconfig已成功获取");const c=new this.K8sClient({kubeConfigStr:a,logger:this.logger});await this.patchCertSecret({cert:n,k8sClient:c}),await e.utils.sleep(5e3)}async restartIngress(e){const{k8sClient:t}=e,{namespace:s}=this,o={metadata:{labels:{certd:this.appendTimeSuffix("certd")}}},n=await t.getIngressList({namespace:s});if(this.logger.info("ingressList:",n),!n||!n.items)return;const r=n.items.filter((e=>{if(!e.spec.tls)return!1;for(const t of e.spec.tls)if(t.secretName===this.secretName)return!0;return!1})).map((e=>e.metadata.name));for(const e of r)await t.patchIngress({namespace:s,ingressName:e,body:o}),this.logger.info(`ingress已重启:${e}`)}async patchCertSecret(e){const{cert:t,k8sClient:s}=e,o=t.crt,n=t.key,r=Buffer.from(o).toString("base64"),i=Buffer.from(n).toString("base64"),{namespace:a,secretName:c}=this,p={data:{"tls.crt":r,"tls.key":i},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let l=c;"string"==typeof c&&(l=[c]);for(const e of l)await s.patchSecret({namespace:a,secretName:e,body:p}),this.logger.info(`cert secret已更新: ${e}`)}async getClient(e,t){const s=new y({logger:this.logger,useROAClient:!0});return await s.init({accessKeyId:e.accessKeyId,accessKeySecret:e.accessKeySecret,endpoint:`https://cs.${t}.aliyuncs.com`,apiVersion:"2015-12-15"}),s}async getKubeConfig(e,t,s=!1){const o=`/k8s/${t}/user_config`,n={PrivateIpAddress:s,TemporaryDurationMinutes:15},r={},i={"Content-Type":"application/json"},a={};try{return(await e.request("GET",o,n,r,i,a)).config}catch(e){throw console.error("请求出错:",e),e}}},u([e.TaskInput({title:"集群id",component:{placeholder:"集群id"},required:!0}),d("design:type",String)],exports.DeployCertToAliyunAckPlugin.prototype,"clusterId",void 0),u([e.TaskInput({title:"保密字典Id",component:{placeholder:"保密字典Id"},required:!0}),d("design:type",Object)],exports.DeployCertToAliyunAckPlugin.prototype,"secretName",void 0),u([e.TaskInput({title:"大区",component:{name:"a-auto-complete",vModel:"value",options:[{value:"cn-qingdao",label:"华北1(青岛)"},{value:"cn-beijing",label:"华北2(北京)"},{value:"cn-zhangjiakou",label:"华北3(张家口)"},{value:"cn-huhehaote",label:"华北5(呼和浩特)"},{value:"cn-wulanchabu",label:"华北6(乌兰察布)"},{value:"cn-hangzhou",label:"华东1(杭州)"},{value:"cn-shanghai",label:"华东2(上海)"},{value:"cn-shenzhen",label:"华南1(深圳)"},{value:"cn-guangzhou",label:"华南3(广州)"},{value:"ap-southeast-2",label:"澳大利亚(悉尼)"},{value:"ap-southeast-3",label:"马来西亚(吉隆坡)"},{value:"ap-northeast-1",label:"日本(东京)"},{value:"cn-chengdu",label:"西南1(成都)"},{value:"ap-southeast-1",label:"新加坡"},{value:"ap-southeast-5",label:"印度尼西亚(雅加达)"},{value:"cn-hongkong",label:"中国香港"},{value:"eu-central-1",label:"德国(法兰克福)"},{value:"us-east-1",label:"美国(弗吉尼亚)"},{value:"us-west-1",label:"美国(硅谷)"},{value:"eu-west-1",label:"英国(伦敦)"},{value:"me-east-1",label:"阿联酋(迪拜)"},{value:"cn-beijing-finance-1",label:"华北2 金融云(邀测)"},{value:"cn-hangzhou-finance",label:"华东1 金融云"},{value:"cn-shanghai-finance-1",label:"华东2 金融云"},{value:"cn-shenzhen-finance-1",label:"华南1 金融云"}],placeholder:"集群所属大区"},required:!0}),d("design:type",String)],exports.DeployCertToAliyunAckPlugin.prototype,"regionId",void 0),u([e.TaskInput({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),d("design:type",Object)],exports.DeployCertToAliyunAckPlugin.prototype,"namespace",void 0),u([e.TaskInput({title:"是否私网ip",value:!1,component:{name:"a-switch",vModel:"checked",placeholder:"集群连接端点是否是私网ip"},helper:"如果您当前certd运行在同一个私网下,可以选择是。",required:!0}),d("design:type",Boolean)],exports.DeployCertToAliyunAckPlugin.prototype,"isPrivateIpAddress",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.DeployCertToAliyunAckPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"Access授权",helper:"阿里云授权AccessKeyId、AccessKeySecret",component:{name:"pi-access-selector",type:"aliyun"},required:!0}),d("design:type",String)],exports.DeployCertToAliyunAckPlugin.prototype,"accessId",void 0),exports.DeployCertToAliyunAckPlugin=u([e.IsTaskPlugin({name:"DeployCertToAliyunAck",title:"部署到阿里云Ack",icon:"ant-design:aliyun-outlined",desc:"部署到阿里云Ack集群Ingress等通过Secret管理证书的应用",group:e.pluginGroups.aliyun.key,needPlus:!0,input:{},output:{},default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.DeployCertToAliyunAckPlugin),new exports.DeployCertToAliyunAckPlugin,exports.AliyunAccess=class{accessKeyId="";accessKeySecret=""},u([e.AccessInput({title:"accessKeyId",component:{placeholder:"accessKeyId"},helper:"登录阿里云控制台->AccessKey管理页面获取。",required:!0}),d("design:type",Object)],exports.AliyunAccess.prototype,"accessKeyId",void 0),u([e.AccessInput({title:"accessKeySecret",component:{placeholder:"accessKeySecret"},required:!0,encrypt:!0,helper:"注意:证书申请需要dns解析权限;其他阿里云插件,需要对应的权限,比如证书上传需要证书管理权限;嫌麻烦就用主账号的全量权限的accessKey"}),d("design:type",Object)],exports.AliyunAccess.prototype,"accessKeySecret",void 0),exports.AliyunAccess=u([e.IsAccess({name:"aliyun",title:"阿里云授权",desc:""})],exports.AliyunAccess),new exports.AliyunAccess;const h="certd";class m{access;http;logger;token;constructor(e,t,s){this.access=e,this.http=t,this.logger=s}async doLogin(){const e=this.access;if(e.otp&&null!=e.deviceId)return this.logger.info("OTP登录"),await this.doLoginWithDeviceId(e.deviceId);this.logger.info("使用普通登录");const t=await this.http.request({url:`${e.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:e.username,passwd:e.password,session:"Certd",format:"sid",enable_syno_token:"yes"}});if(!t.success)throw new Error("登录失败: ",t.error);return this.logger.info("登录成功"),this.token=t.data,this.token}async doLoginWithOTPCode(e){const t=this.access,s=await this.http.request({url:`${t.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:t.username,passwd:t.password,otp_code:e,enable_device_token:"yes",device_name:h}});if(!s.success)throw new Error("登录失败: ",s.error);return this.logger.info("登录成功"),this.token=s.data,this.token}async doLoginWithDeviceId(e){const t=this.access,s=await this.http.request({url:`${t.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:t.username,passwd:t.password,device_name:h,device_id:e,session:"Certd",format:"sid",enable_syno_token:"yes"}});if(!s.success)throw new Error("登录失败: ",s.error);return this.logger.info("登录成功"),this.token=s.data,this.token}async doRequest(e){const t=this.token.sid,s=e.method||"POST",o={...e.apiParams,_sid:t,...e.params,SynoToken:this.token.synotoken},n=await this.http.request({url:`${this.access.baseUrl}/webapi/entry.cgi?${r.stringify(o)}`,method:s,data:e.data,headers:e.headers,skipSslVerify:!0});if(!n.success)throw new Error(`API 调用失败: ${JSON.stringify(n.error)}`);return n.data}async getCertList(){return this.logger.info("获取证书列表"),await this.doRequest({method:"GET",apiParams:{api:"SYNO.Core.Certificate.CRT",version:1,method:"list"}})}async getInfo(){return this.logger.info("获取信息"),await this.doRequest({method:"GET",apiParams:{api:"SYNO.API.Info",version:1,method:"query"}})}}exports.SynologyDeployToPanel=class extends l{certName;cert;accessId;async onInstance(){}async execute(){const e=await this.accessService.getById(this.accessId),t=new m(e,this.ctx.http,this.ctx.logger);await t.doLogin();const s=await t.getCertList();if(this.certName){const e=s.certificates.find((e=>e.desc===this.certName||e.subject.common_name===this.certName));if(!e)throw new Error(`未找到证书: ${this.certName}`);this.logger.info(`找到证书: ${e.id}`),await this.updateCertToPanel(t,e)}else{this.logger.info("开始更新全部证书");for(const e of s.certificates)this.logger.info(`更新证书: ${e.id}`),await this.updateCertToPanel(t,e)}}async updateCertToPanel(e,s){this.logger.info(`更新证书:${s.id}`);const o=new t.CertReader(this.cert);return o.readCertFile({logger:this.logger,handle:async t=>{const n=new a,{tmpCrtPath:r,tmpKeyPath:c,tmpIcPath:p}=t;return this.logger.info(`上传证书:${r},${c}`),n.append("key",i.createReadStream(c)),n.append("cert",i.createReadStream(r)),o.ic&&(this.logger.info(`包含中间证书:${p}`),n.append("inter_cert",i.createReadStream(p))),n.append("id",s.id),n.append("desc",s.desc),console.log(JSON.stringify(n.getHeaders())),await e.doRequest({method:"POST",apiParams:{api:"SYNO.Core.Certificate",version:1,method:"import"},data:n,headers:{...n.getHeaders()}})}})}},u([e.TaskInput({title:"群晖证书描述",component:{name:"a-input",vModel:"value",placeholder:"群晖证书描述"},required:!1,helper:"在群晖证书管理页面里面,选择证书,点击操作,给证书设置描述,然后填写到这里\n如果不填,则覆盖更新全部证书"}),d("design:type",String)],exports.SynologyDeployToPanel.prototype,"certName",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.SynologyDeployToPanel.prototype,"cert",void 0),u([e.TaskInput({title:"群晖授权",helper:"群晖登录授权,请确保账户是管理员用户组,并且关闭双重认证",component:{name:"pi-access-selector",type:"synology"},required:!0}),d("design:type",String)],exports.SynologyDeployToPanel.prototype,"accessId",void 0),exports.SynologyDeployToPanel=u([e.IsTaskPlugin({name:"SynologyDeployToPanel",title:"部署证书到群晖面板",icon:"simple-icons:synology",group:e.pluginGroups.other.key,desc:"Synology,仅支持7.x以上版本",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.SynologyDeployToPanel),new exports.SynologyDeployToPanel,exports.SynologyAccess=class extends e.RequestHandler{baseUrl="";username="";password="";otp=!1;deviceId="";onLoginWithOPTCode(e,t){console.log("onLoginWithOPTCode",this);return new m(this,t.http,t.logger).doLoginWithOTPCode(e.otpCode)}},u([e.AccessInput({title:"群晖面板的url",component:{placeholder:"https://yourdomain:5006"},helper:"群晖面板的访问地址,例如:https://yourdomain:5006",required:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"baseUrl",void 0),u([e.AccessInput({title:"账号",component:{placeholder:"账号"},helper:"群晖面板登录账号,必须是处于管理员用户组",required:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"username",void 0),u([e.AccessInput({title:"密码",component:{placeholder:"密码"},helper:"群晖面板登录密码",required:!0,encrypt:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"password",void 0),u([e.AccessInput({title:"双重认证",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"是否启用了双重认证",required:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"otp",void 0),u([e.AccessInput({title:"设备ID",component:{placeholder:"设备ID",name:"pi-synology-device-id-getter",type:"access",typeName:"synology"},mergeScript:"\n return {\n component:{\n form: ctx.compute(({form})=>{\n return form\n })\n },\n show: ctx.compute(({form})=>{\n return form.access.otp\n })\n }\n ",helper:"如果开启了双重认证,需要获取设备ID\n填好上面的必填项,然后点击获取设备ID,输入双重认证APP上的码,确认即可获得设备ID,此操作只需要做一次",required:!1,encrypt:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"deviceId",void 0),exports.SynologyAccess=u([e.IsAccess({name:"synology",title:"群晖登录授权",desc:""})],exports.SynologyAccess),new exports.SynologyAccess,exports.K8sAccess=class{kubeconfig=""},u([e.AccessInput({title:"kubeconfig",component:{name:"a-textarea",vModel:"value",placeholder:"kubeconfig"},required:!0,encrypt:!0}),d("design:type",Object)],exports.K8sAccess.prototype,"kubeconfig",void 0),exports.K8sAccess=u([e.IsAccess({name:"k8s",title:"k8s授权",desc:""})],exports.K8sAccess),new exports.K8sAccess,exports.K8sDeployToSecretPlugin=class extends e.AbstractTaskPlugin{namespace;secretName;accessId;cert;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){const t=await this.accessService.getById(this.accessId),s=new this.K8sClient({kubeConfigStr:t.kubeconfig,logger:this.logger});await this.patchCertSecret({cert:this.cert,k8sClient:s}),await e.utils.sleep(5e3)}async patchCertSecret(e){const{cert:t,k8sClient:s}=e,o=t.crt,n=t.key,r=Buffer.from(o).toString("base64"),i=Buffer.from(n).toString("base64"),{namespace:a,secretName:c}=this,p={data:{"tls.crt":r,"tls.key":i},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let l=c;"string"==typeof c&&(l=[c]);for(const e of l)await s.patchSecret({namespace:a,secretName:e,body:p}),this.logger.info(`ingress cert Secret已更新:${e}`)}},u([e.TaskInput({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),d("design:type",String)],exports.K8sDeployToSecretPlugin.prototype,"namespace",void 0),u([e.TaskInput({title:"保密字典Id",component:{name:"a-select",vModel:"value",mode:"tags",open:!1},required:!0}),d("design:type",Object)],exports.K8sDeployToSecretPlugin.prototype,"secretName",void 0),u([e.TaskInput({title:"k8s授权",helper:"kubeconfig",component:{name:"pi-access-selector",type:"k8s"},required:!0}),d("design:type",String)],exports.K8sDeployToSecretPlugin.prototype,"accessId",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.K8sDeployToSecretPlugin.prototype,"cert",void 0),exports.K8sDeployToSecretPlugin=u([e.IsTaskPlugin({name:"K8sDeployToSecret",title:"K8S证书部署",icon:"mdi:kubernetes",desc:"部署证书到k8s的secret",needPlus:!0,group:e.pluginGroups.other.key,default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.K8sDeployToSecretPlugin),new exports.K8sDeployToSecretPlugin,exports.K8sDeployToIngressPlugin=class extends e.AbstractTaskPlugin{namespace;ingressName;accessId;cert;async execute(){const t=await this.accessService.getById(this.accessId),s=new(0,(await import("@certd/lib-k8s")).K8sClient)({kubeConfigStr:t.kubeconfig,logger:this.logger}),o=(await s.getIngressList({namespace:this.namespace})).items.find((e=>e.metadata.name===this.ingressName));if(!o)throw new Error(`Ingress不存在:${this.ingressName}`);const n=o.spec.tls.map((e=>e.secretName));if(!n||0===n.length)throw new Error(`Ingress:${this.ingressName} 未找到证书Secret`);await this.patchNginxCertSecret({cert:this.cert,k8sClient:s,secretNames:n}),await e.utils.sleep(5e3)}async patchNginxCertSecret(e){const{cert:t,k8sClient:s}=e,o=t.crt,n=t.key,r=Buffer.from(o).toString("base64"),i=Buffer.from(n).toString("base64"),{namespace:a}=this,c={data:{"tls.crt":r,"tls.key":i},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};for(const t of e.secretNames)this.logger.info(`更新ingress cert Secret:${t}`),await s.patchSecret({namespace:a,secretName:t,body:c}),this.logger.info(`ingress cert Secret已更新:${t}`)}},u([e.TaskInput({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),d("design:type",String)],exports.K8sDeployToIngressPlugin.prototype,"namespace",void 0),u([e.TaskInput({title:"IngressName",required:!0,helper:"Ingress名称,根据名称查找证书Secret,然后更新"}),d("design:type",String)],exports.K8sDeployToIngressPlugin.prototype,"ingressName",void 0),u([e.TaskInput({title:"k8s授权",helper:"kubeconfig",component:{name:"pi-access-selector",type:"k8s"},required:!0}),d("design:type",String)],exports.K8sDeployToIngressPlugin.prototype,"accessId",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.K8sDeployToIngressPlugin.prototype,"cert",void 0),exports.K8sDeployToIngressPlugin=u([e.IsTaskPlugin({name:"K8sDeployToIngress",title:"K8S Ingress 证书部署",icon:"mdi:kubernetes",desc:"部署证书到k8s的Ingress",needPlus:!0,group:e.pluginGroups.other.key,default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.K8sDeployToIngressPlugin),new exports.K8sDeployToIngressPlugin,exports.TencentAccess=class{secretId="";secretKey=""},u([e.AccessInput({title:"secretId",helper:"使用对应的插件需要有对应的权限,比如上传证书,需要证书管理权限;部署到clb需要clb相关权限\n前往[密钥管理](https://console.cloud.tencent.com/cam/capi)进行创建",component:{placeholder:"secretId"},rules:[{required:!0,message:"该项必填"}]}),d("design:type",Object)],exports.TencentAccess.prototype,"secretId",void 0),u([e.AccessInput({title:"secretKey",component:{placeholder:"secretKey"},encrypt:!0,rules:[{required:!0,message:"该项必填"}]}),d("design:type",Object)],exports.TencentAccess.prototype,"secretKey",void 0),exports.TencentAccess=u([e.IsAccess({name:"tencent",title:"腾讯云"})],exports.TencentAccess),exports.DeployCertToTencentTKEIngressPlugin=class extends l{region;clusterId;namespace;secretName;ingressName;ingressClass;clusterDomain;accessId;tencentCertId;cert;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){const t=await this.accessService.getById(this.accessId),s=await this.getTkeClient(t,this.region),o=await this.getTkeKubeConfig(s,this.clusterId);this.logger.info("kubeconfig已成功获取");const n=new this.K8sClient({kubeConfigStr:o,logger:this.logger});"qcloud"===(this.ingressClass||"qcloud")?await this.patchQcloudCertSecret({k8sClient:n}):await this.patchNginxCertSecret({k8sClient:n}),await e.utils.sleep(5e3),await this.restartIngress({k8sClient:n})}async getTkeClient(e,t="ap-guangzhou"){return new(0,(await import("tencentcloud-sdk-nodejs/tencentcloud/services/tke/v20180525/index.js")).v20180525.Client)({credential:{secretId:e.secretId,secretKey:e.secretKey},region:t,profile:{httpProfile:{endpoint:"tke.tencentcloudapi.com"}}})}async getTkeKubeConfig(e,t){const s={ClusterId:t},o=await e.DescribeClusterKubeconfig(s);return this.checkRet(o),this.logger.info("注意:后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster"),o.Kubeconfig}appendTimeSuffix(e){return null==e&&(e="certd"),e+"-"+c().format("YYYYMMDD-HHmmss")}async patchQcloudCertSecret(e){if(null==this.tencentCertId)throw new Error("请先将【上传证书到腾讯云】作为前置任务");this.logger.info("腾讯云证书ID:",this.tencentCertId);const t=Buffer.from(this.tencentCertId).toString("base64"),{namespace:s,secretName:o}=this,n={data:{qcloud_cert_id:t},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let r=o;"string"==typeof o&&(r=[o]);for(const t of r)await e.k8sClient.patchSecret({namespace:s,secretName:t,body:n}),this.logger.info(`CertSecret已更新:${t}`)}async patchNginxCertSecret(e){const{k8sClient:t}=e,{cert:s}=this,o=s.crt,n=s.key,r=Buffer.from(o).toString("base64"),i=Buffer.from(n).toString("base64"),{namespace:a,secretName:c}=this,p={data:{"tls.crt":r,"tls.key":i},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let l=c;"string"==typeof c&&(l=[c]);for(const e of l)await t.patchSecret({namespace:a,secretName:e,body:p}),this.logger.info(`CertSecret已更新:${e}`)}async restartIngress(e){const{k8sClient:t}=e,{namespace:s,ingressName:o}=this,n={metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let r=this.ingressName;"string"==typeof o&&(r=[o]);for(const e of r)await t.patchIngress({namespace:s,ingressName:e,body:n}),this.logger.info(`ingress已重启:${e}`)}checkRet(e){if(!e||e.Error)throw new Error("执行失败:"+e.Error.Code+","+e.Error.Message)}},u([e.TaskInput({title:"大区",value:"ap-guangzhou",required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"region",void 0),u([e.TaskInput({title:"集群ID",required:!0,desc:"例如:cls-6lbj1vee",request:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"clusterId",void 0),u([e.TaskInput({title:"集群namespace",value:"default",required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"namespace",void 0),u([e.TaskInput({title:"证书的secret名称",required:!0}),d("design:type",Object)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"secretName",void 0),u([e.TaskInput({title:"ingress名称",required:!0}),d("design:type",Object)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"ingressName",void 0),u([e.TaskInput({title:"ingress类型",component:{name:"a-auto-complete",vModel:"value",options:[{value:"qcloud"},{value:"nginx"}]},helper:"可选 qcloud / nginx"}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"ingressClass",void 0),u([e.TaskInput({title:"集群域名",helper:"可不填,默认为:[clusterId].ccs.tencent-cloud.com"}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"clusterDomain",void 0),u([e.TaskInput({title:"Access授权",helper:"access授权",component:{name:"pi-access-selector",type:"tencent"},required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"accessId",void 0),u([e.TaskInput({title:"腾讯云证书id",helper:"请选择“上传证书到腾讯云”前置任务的输出",component:{name:"pi-output-selector",from:"UploadCertToTencent"},mergeScript:'\n return {\n show: ctx.compute(({form})=>{\n return form.ingressClass === "qcloud"\n })\n }\n ',required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"tencentCertId",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},mergeScript:'\n return {\n show: ctx.compute(({form})=>{\n return form.ingressClass === "nginx"\n })\n }\n ',required:!0}),d("design:type",Object)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"cert",void 0),exports.DeployCertToTencentTKEIngressPlugin=u([e.IsTaskPlugin({name:"DeployCertToTencentTKEIngress",title:"部署到腾讯云TKE-ingress",needPlus:!0,icon:"svg:icon-tencentcloud",group:e.pluginGroups.tencent.key,desc:"Qcloud类型需要【上传到腾讯云】作为前置任务;ApiServer未开启外网访问则需要做域名的内网IP映射",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.DeployCertToTencentTKEIngressPlugin),exports.AbstractPlusTaskPlugin=l,exports.AliyunClient=y,exports.SynologyClient=m,exports.mustPlus=p;
1
+ "use strict";var e=require("@certd/pipeline"),t=require("@certd/plugin-cert"),s=require("node:crypto"),n=require("node:path"),o=require("https-proxy-agent"),r=require("querystring"),i=require("fs"),a=require("form-data"),c=require("dayjs");function p(){if(!e.isPlus())throw new Error("此插件仅供专业版中使用")}class l extends e.AbstractTaskPlugin{setCtx(e){super.setCtx(e),p()}}function u(e,t,s,n){var o,r=arguments.length,i=r<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,s):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(e,t,s,n);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(i=(r<3?o(i):r>3?o(t,s,i):o(t,s))||i);return r>3&&i&&Object.defineProperty(t,s,i),i}function d(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}"function"==typeof SuppressedError&&SuppressedError;class g{access;http;constructor(e,t){this.access=e,this.http=t}getRequestToken(){const e=(new Date).getTime(),t=e+this.getMd5(this.access.apiSecret);return{request_token:this.getMd5(t),request_time:e}}getMd5(e){return s.createHash("md5").update(e).digest("hex")}async doRequest(e,t,s,n){const o=this.getRequestToken();console.log("token",o);const r={...o,...s};let i=`${this.access.panelUrl}${e}`;t&&(i=`${i}?action=${t}`);const a=await this.http.request({url:i,method:"post",headers:{"Content-Type":"application/x-www-form-urlencoded"},data:r,...n});if(!a.status)throw new Error(a.msg);return a}}exports.BaotaDeployPanelCertPlugin=class extends l{cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:s}=this,n=new t.CertReader(e),o=await this.accessService.getById(s),r=this.ctx.http,i=new g(o,r),a=await i.doRequest("/config","SavePanelSSL",{privateKey:n.key,certPem:n.crt},{skipSslVerify:!0});this.logger.info(a?.msg)}},u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.BaotaDeployPanelCertPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"宝塔授权",helper:"baota的接口密钥,目前测试宝塔面板本身开启ssl之后,API接口就不能用了",component:{name:"pi-access-selector",type:"baota"},required:!0}),d("design:type",String)],exports.BaotaDeployPanelCertPlugin.prototype,"accessId",void 0),exports.BaotaDeployPanelCertPlugin=u([e.IsTaskPlugin({name:"BaotaDeployPanelCert",title:"宝塔面板证书部署",icon:"svg:icon-bt",group:e.pluginGroups.panel.key,desc:"部署宝塔面板本身的ssl证书",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.BaotaDeployPanelCertPlugin),new exports.BaotaDeployPanelCertPlugin,exports.BaotaDeployWebSiteCertPlugin=class extends l{siteName;isReverseProxy=!1;cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:s}=this,n=new t.CertReader(e),o=await this.accessService.getById(s),r=this.ctx.http,i=new g(o,r);if(this.logger.info(`siteName:${this.siteName}`),this.isReverseProxy){const e=await i.doRequest("/mod/proxy/com/set_ssl",null,{site_name:this.siteName,key:n.key,csr:n.crt});this.logger.info(e?.msg)}else{const e=await i.doRequest("/site","SetSSL",{type:0,siteName:this.siteName,key:n.key,csr:n.crt});this.logger.info(e?.msg)}}},u([e.TaskInput({title:"网站域名",component:{name:"a-input"},helper:"登录面板->网站->网站名/域名/项目名称",required:!0}),d("design:type",String)],exports.BaotaDeployWebSiteCertPlugin.prototype,"siteName",void 0),u([e.TaskInput({title:"是否反向代理",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"该项目类型是反向代理还是其他项目",required:!0}),d("design:type",Object)],exports.BaotaDeployWebSiteCertPlugin.prototype,"isReverseProxy",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.BaotaDeployWebSiteCertPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"宝塔授权",helper:"baota的接口密钥",component:{name:"pi-access-selector",type:"baota"},required:!0}),d("design:type",String)],exports.BaotaDeployWebSiteCertPlugin.prototype,"accessId",void 0),exports.BaotaDeployWebSiteCertPlugin=u([e.IsTaskPlugin({name:"BaotaDeployWebSiteCert",title:"宝塔网站证书部署",icon:"svg:icon-bt",group:e.pluginGroups.panel.key,desc:"部署宝塔管理的站点的ssl证书",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.BaotaDeployWebSiteCertPlugin),new exports.BaotaDeployWebSiteCertPlugin,exports.BaotaAccess=class{panelUrl="";apiSecret=""},u([e.AccessInput({title:"宝塔URL地址",component:{placeholder:"http://192.168.42.237:41896"},helper:"宝塔面板的url地址,例如:http://192.168.42.237:41896",required:!0}),d("design:type",Object)],exports.BaotaAccess.prototype,"panelUrl",void 0),u([e.AccessInput({title:"接口密钥",component:{placeholder:"接口密钥"},helper:"宝塔面板设置->面板设置->API接口->接口配置->接口密钥。\n必须要加IP白名单,你可以先运行一次,报错之后会打印IP,将IP加入白名单之后再次运行即可",required:!0,encrypt:!0}),d("design:type",Object)],exports.BaotaAccess.prototype,"apiSecret",void 0),exports.BaotaAccess=u([e.IsAccess({name:"baota",title:"baota授权",desc:""})],exports.BaotaAccess),new exports.BaotaAccess,exports.YidunDeployToCDNPlugin=class extends l{certId;domain;cert;accessId;async onInstance(){}async execute(){const{domain:e,certId:s,cert:n}=this;if(!e&&!s)throw new Error("证书ID和网站域名必须填写一个");const o=new t.CertReader(n);s>0?await this.updateByCertId(o,s):await this.updateByDomain(o)}async updateByCertId(e,t){this.logger.info(`更新证书,证书ID:${t}`);const s=`http://user.yiduncdn.com/v1/certs/${t}`;await this.doRequest(s,"PUT",{cert:e.crt,key:e.key})}async doRequest(e,t,s){const n=await this.accessService.getById(this.accessId),{apiKey:o,apiSecret:r}=n,i=this.ctx.http,a=await i.request({url:e,method:t,headers:{"api-key":o,"api-secret":r},data:s});if(0!=a.code)throw new Error(a.msg);return a}async updateByDomain(e){const t=await this.doRequest("http://user.yiduncdn.com/v1/sites","GET",{domain:this.domain});if(0===t.data.length)throw new Error(`未找到域名相关站点:${this.domain}`);let s=null;for(const e of t.data)e.domain===this.domain&&(s=e);if(!s)throw new Error(`未找到域名匹配的站点:${this.domain}`);if(s.https_listen?.cert){const t=s.https_listen.cert;await this.updateByCertId(e,t)}else{this.logger.info(`创建证书,域名:${this.domain}`);const t="http://user.yiduncdn.com/v1/certs",n=this.domain+"_"+(new Date).getTime();await this.doRequest(t,"POST",{name:n,type:"custom",cert:e.crt,key:e.key});const o=(await this.doRequest(t,"GET",{name:n})).data[0].id,r="http://user.yiduncdn.com/v1/sites";await this.doRequest(r,"PUT",{id:s.id,https_listen:{cert:o}})}}},u([e.TaskInput({title:"证书ID",component:{name:"a-input-number",vModel:"value"},helper:"证书ID,在证书管理页面查看,每条记录都有证书id"}),d("design:type",Number)],exports.YidunDeployToCDNPlugin.prototype,"certId",void 0),u([e.TaskInput({title:"网站域名",component:{name:"a-input",vModel:"value"},helper:"网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名"}),d("design:type",Number)],exports.YidunDeployToCDNPlugin.prototype,"domain",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.YidunDeployToCDNPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"易盾授权",helper:"易盾CDN授权",component:{name:"pi-access-selector",type:"yidun"},required:!0}),d("design:type",String)],exports.YidunDeployToCDNPlugin.prototype,"accessId",void 0),exports.YidunDeployToCDNPlugin=u([e.IsTaskPlugin({name:"YidunDeployToCDN",title:"部署证书到易盾CDN",icon:"material-symbols:shield-outline",group:e.pluginGroups.cdn.key,desc:"http://user.yiduncdn.com/",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.YidunDeployToCDNPlugin),new exports.YidunDeployToCDNPlugin,exports.YidunAccess=class{apiKey="";apiSecret=""},u([e.AccessInput({title:"api_key",component:{placeholder:"api_key"},helper:"http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",required:!0,encrypt:!0}),d("design:type",Object)],exports.YidunAccess.prototype,"apiKey",void 0),u([e.AccessInput({title:"api_secret",component:{placeholder:"api_secret"},helper:"http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",required:!0,encrypt:!0}),d("design:type",Object)],exports.YidunAccess.prototype,"apiSecret",void 0),exports.YidunAccess=u([e.IsAccess({name:"yidun",title:"易盾云授权",desc:"user.yiduncdn.com"})],exports.YidunAccess),new exports.YidunAccess,exports.FtpAccess=class{host;port;user;password;secure=!1},u([e.AccessInput({title:"host",component:{placeholder:"ip / 域名",name:"a-input",vModel:"value"},helper:"FTP地址",required:!0}),d("design:type",String)],exports.FtpAccess.prototype,"host",void 0),u([e.AccessInput({title:"host",value:21,component:{placeholder:"21",name:"a-input-number",vModel:"value"},helper:"FTP端口",required:!0}),d("design:type",String)],exports.FtpAccess.prototype,"port",void 0),u([e.AccessInput({title:"user",component:{placeholder:"用户名"},helper:"FTP用户名",required:!0}),d("design:type",String)],exports.FtpAccess.prototype,"user",void 0),u([e.AccessInput({title:"password",component:{placeholder:"密码",component:{name:"a-input-password",vModel:"value"}},encrypt:!0,helper:"FTP密码",required:!0}),d("design:type",String)],exports.FtpAccess.prototype,"password",void 0),u([e.AccessInput({title:"secure",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"是否使用SSL",required:!0}),d("design:type",Boolean)],exports.FtpAccess.prototype,"secure",void 0),exports.FtpAccess=u([e.IsAccess({name:"ftp",title:"FTP授权",desc:""})],exports.FtpAccess),new exports.FtpAccess,exports.UploadCertToFTPPlugin=class extends l{crtPath;keyPath;pfxPath;derPath;cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:s}=this,n=new t.CertReader(e);let o=null;const r=async({reader:e,tmpCrtPath:t,tmpKeyPath:n,tmpDerPath:r,tmpPfxPath:i})=>{const a=(await import("basic-ftp")).Client;o=new a,o.ftp.verbose=!0,this.logger.info("开始连接FTP");const c=await this.accessService.getById(s);await o.access(c),this.logger.info("FTP连接成功"),await this.doUpload(o,t,this.crtPath),await this.doUpload(o,n,this.keyPath),await this.doUpload(o,i,this.pfxPath),await this.doUpload(o,r,this.derPath)};try{await n.readCertFile({logger:this.logger,handle:r})}finally{o&&o.close()}this.logger.info("执行完成")}async doUpload(e,t,s){if(!s)return;const o=n.dirname(s);this.logger.info(`确保目录存在:${o}`),await e.ensureDir(o),this.logger.info(`开始上传文件${t} -> ${s}`),await e.uploadFrom(t,s)}},u([e.TaskInput({title:"PEM证书保存路径",helper:"需要有写入权限,路径要包含证书文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.pem"}}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"crtPath",void 0),u([e.TaskInput({title:"私钥保存路径",helper:"需要有写入权限,路径要包含私钥文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.key"}}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"keyPath",void 0),u([e.TaskInput({title:"PFX证书保存路径",helper:"需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.pfx"}}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"pfxPath",void 0),u([e.TaskInput({title:"DER证书保存路径",helper:"需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号\n.der和.cer是相同的东西,改个后缀名即可",component:{placeholder:"/test/cert.der 或 /test/cert.cer"}}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"derPath",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.UploadCertToFTPPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"FTP授权",component:{name:"pi-access-selector",type:"ftp"},required:!0}),d("design:type",String)],exports.UploadCertToFTPPlugin.prototype,"accessId",void 0),exports.UploadCertToFTPPlugin=u([e.IsTaskPlugin({name:"UploadCertToFTP",title:"上传证书到FTP",icon:"mdi:folder-upload-outline",group:e.pluginGroups.host.key,desc:"将证书上传到FTP服务器",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.UploadCertToFTPPlugin),new exports.UploadCertToFTPPlugin,exports.CdnflyDeployToCDNPlugin=class extends l{url;certId;domain;cert;accessId;async onInstance(){}async execute(){const{domain:e,certId:s,cert:n}=this;if(!e&&!s)throw new Error("证书ID和网站域名必须填写一个");const o=new t.CertReader(n);s>0?await this.updateByCertId(o,s):await this.updateByDomain(o)}async updateByCertId(e,t){this.logger.info(`更新证书,证书ID:${t}`);const s=`${this.url}/v1/certs/${t}`;await this.doRequest(s,"PUT",{cert:e.crt,key:e.key})}async doRequest(e,t,s){const n=await this.accessService.getById(this.accessId),{apiKey:o,apiSecret:r}=n,i=this.ctx.http,a=await i.request({url:e,method:t,headers:{"api-key":o,"api-secret":r},data:s});if(0!=a.code)throw new Error(a.msg);return a}async updateByDomain(e){const t=`${this.url}/v1/sites`,s=await this.doRequest(t,"GET",{domain:this.domain});if(0===s.data.length)throw new Error(`未找到域名相关站点:${this.domain}`);let n=null;for(const e of s.data)e.domain===this.domain&&(n=e);if(!n)throw new Error(`未找到域名匹配的站点:${this.domain}`);if(n.https_listen?.cert){const t=n.https_listen.cert;await this.updateByCertId(e,t)}else{this.logger.info(`创建证书,域名:${this.domain}`);const t=`${this.url}/v1/certs`,s=this.domain+"_"+(new Date).getTime();await this.doRequest(t,"POST",{name:s,type:"custom",cert:e.crt,key:e.key});const o=(await this.doRequest(t,"GET",{name:s})).data[0].id,r=`${this.url}/v1/sites`;await this.doRequest(r,"PUT",{id:n.id,https_listen:{cert:o}})}}},u([e.TaskInput({title:"cdnfly系统网址",component:{name:"a-input",vModel:"value"},helper:"例如:http://demo.cdnfly.cn"}),d("design:type",String)],exports.CdnflyDeployToCDNPlugin.prototype,"url",void 0),u([e.TaskInput({title:"证书ID",component:{name:"a-input-number",vModel:"value"},helper:"证书ID,在证书管理页面查看,每条记录都有证书id"}),d("design:type",Number)],exports.CdnflyDeployToCDNPlugin.prototype,"certId",void 0),u([e.TaskInput({title:"网站域名",component:{name:"a-input",vModel:"value"},helper:"网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名"}),d("design:type",Number)],exports.CdnflyDeployToCDNPlugin.prototype,"domain",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.CdnflyDeployToCDNPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"cdnfly授权",helper:"cdnfly授权",component:{name:"pi-access-selector",type:"cdnfly"},required:!0}),d("design:type",String)],exports.CdnflyDeployToCDNPlugin.prototype,"accessId",void 0),exports.CdnflyDeployToCDNPlugin=u([e.IsTaskPlugin({name:"CdnflyDeployToCDN",title:"部署证书到cdnfly",icon:"majesticons:cloud-line",group:e.pluginGroups.cdn.key,desc:"cdnfly",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.CdnflyDeployToCDNPlugin),new exports.CdnflyDeployToCDNPlugin,exports.CdnflyAccess=class{apiKey="";apiSecret=""},u([e.AccessInput({title:"api_key",component:{placeholder:"api_key"},helper:"登录cdnfly控制台->账户中心->Api密钥,点击开启后获取",required:!0,encrypt:!0}),d("design:type",Object)],exports.CdnflyAccess.prototype,"apiKey",void 0),u([e.AccessInput({title:"api_secret",component:{placeholder:"api_secret"},helper:"登录cdnfly控制台->账户中心->Api密钥,点击开启后获取",required:!0,encrypt:!0}),d("design:type",Object)],exports.CdnflyAccess.prototype,"apiSecret",void 0),exports.CdnflyAccess=u([e.IsAccess({name:"cdnfly",title:"cdnfly授权",desc:""})],exports.CdnflyAccess),new exports.CdnflyAccess,exports.DeployCertToAliyunOSS=class extends l{region;bucket;domainName;certName;cert;accessId;async onInstance(){}async execute(){this.logger.info("开始部署证书到阿里云OSS");const e=await this.accessService.getById(this.accessId);this.logger.info(`bucket: ${this.bucket}, region: ${this.region}, domainName: ${this.domainName}`);const t=await this.getClient(e);await this.doRequest(t,{}),this.logger.info("部署完成")}async getClient(e){return new((await import("ali-oss")).default)({accessKeyId:e.accessKeyId,accessKeySecret:e.accessKeySecret,region:this.region,authorizationV4:!0,bucket:this.bucket})}async doRequest(e,t){t=e._bucketRequestParams("POST",this.bucket,{cname:"",comp:"add"});const s=`\n <BucketCnameConfiguration>\n <Cname>\n <Domain>${this.domainName}</Domain>\n <CertificateConfiguration>\n <PrivateKey>${this.cert.key}</PrivateKey>\n <Certificate>${this.cert.crt}</Certificate>\n </CertificateConfiguration>\n </Cname>\n</BucketCnameConfiguration>`;t.content=s,t.mime="xml",t.successStatuses=[200];const n=await e.request(t);return this.checkRet(n),n}checkRet(e){if(null!=e.code)throw new Error("执行失败:"+e.Message)}},u([e.TaskInput({title:"大区",component:{name:"a-auto-complete",vModel:"value",options:[{value:"oss-cn-hangzhou",label:"华东1(杭州)"},{value:"oss-cn-shanghai",label:"华东2(上海)"},{value:"oss-cn-nanjing",label:"华东5(南京-本地地域)"},{value:"oss-cn-fuzhou",label:"华东6(福州-本地地域)"},{value:"oss-cn-wuhan",label:"华中1(武汉-本地地域)"},{value:"oss-cn-qingdao",label:"华北1(青岛)"},{value:"oss-cn-beijing",label:"华北2(北京)"},{value:"oss-cn-zhangjiakou",label:"华北 3(张家口)"},{value:"oss-cn-huhehaote",label:"华北5(呼和浩特)"},{value:"oss-cn-wulanchabu",label:"华北6(乌兰察布)"},{value:"oss-cn-shenzhen",label:"华南1(深圳)"},{value:"oss-cn-heyuan",label:"华南2(河源)"},{value:"oss-cn-guangzhou",label:"华南3(广州)"},{value:"oss-cn-chengdu",label:"西南1(成都)"},{value:"oss-cn-hongkong",label:"中国香港"},{value:"oss-us-west-1",label:"美国(硅谷)①"},{value:"oss-us-east-1",label:"美国(弗吉尼亚)①"},{value:"oss-ap-northeast-1",label:"日本(东京)①"},{value:"oss-ap-northeast-2",label:"韩国(首尔)"},{value:"oss-ap-southeast-1",label:"新加坡①"},{value:"oss-ap-southeast-2",label:"澳大利亚(悉尼)①"},{value:"oss-ap-southeast-3",label:"马来西亚(吉隆坡)①"},{value:"oss-ap-southeast-5",label:"印度尼西亚(雅加达)①"},{value:"oss-ap-southeast-6",label:"菲律宾(马尼拉)"},{value:"oss-ap-southeast-7",label:"泰国(曼谷)"},{value:"oss-eu-central-1",label:"德国(法兰克福)①"},{value:"oss-eu-west-1",label:"英国(伦敦)"},{value:"oss-me-east-1",label:"阿联酋(迪拜)①"},{value:"oss-rg-china-mainland",label:"无地域属性(中国内地)"}]},required:!0}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"region",void 0),u([e.TaskInput({title:"Bucket",helper:"存储桶名称",required:!0}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"bucket",void 0),u([e.TaskInput({title:"绑定的域名",helper:"你在阿里云OSS上绑定的域名,比如:certd.docmirror.cn",required:!0}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"domainName",void 0),u([e.TaskInput({title:"证书名称",helper:"上传后将以此名称作为前缀备注"}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"certName",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.DeployCertToAliyunOSS.prototype,"cert",void 0),u([e.TaskInput({title:"Access授权",helper:"阿里云授权AccessKeyId、AccessKeySecret",component:{name:"pi-access-selector",type:"aliyun"},required:!0}),d("design:type",String)],exports.DeployCertToAliyunOSS.prototype,"accessId",void 0),exports.DeployCertToAliyunOSS=u([e.IsTaskPlugin({name:"DeployCertToAliyunOSS",title:"部署证书至阿里云OSS",icon:"ant-design:aliyun-outlined",group:e.pluginGroups.aliyun.key,desc:"自动部署域名证书至阿里云OSS",needPlus:!0,default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.DeployCertToAliyunOSS),new exports.DeployCertToAliyunOSS;class y{client;logger;agent;useROAClient;constructor(e){this.logger=e.logger,this.useROAClient=e.useROAClient||!1;const t=process.env.HTTPS_PROXY;t&&(this.logger.info(`use proxy:${t}`),this.agent=new o.HttpsProxyAgent(t))}async getSdk(){if(this.useROAClient)return await this.getROAClient();return(await import("@alicloud/pop-core")).default}async getROAClient(){const e=await import("@alicloud/pop-core");return console.log("aliyun sdk",e),e.ROAClient}async init(e){const t=await this.getSdk();return this.client=new t(e),this.client}async request(e,t,s={}){return this.useROAClient||(s.agent=this.agent),await this.client.request(e,t,s)}}exports.DeployCertToAliyunAckPlugin=class extends l{clusterId;secretName;regionId;namespace="default";isPrivateIpAddress;cert;accessId;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){this.logger.info("开始部署证书到阿里云Ack");const{regionId:t,clusterId:s,isPrivateIpAddress:n,cert:o}=this,r=await this.accessService.getById(this.accessId),i=await this.getClient(r,t),a=await this.getKubeConfig(i,s,n);this.logger.info("kubeconfig已成功获取");const c=new this.K8sClient({kubeConfigStr:a,logger:this.logger});await this.patchCertSecret({cert:o,k8sClient:c}),await e.utils.sleep(5e3)}async restartIngress(e){const{k8sClient:t}=e,{namespace:s}=this,n={metadata:{labels:{certd:this.appendTimeSuffix("certd")}}},o=await t.getIngressList({namespace:s});if(this.logger.info("ingressList:",o),!o||!o.items)return;const r=o.items.filter((e=>{if(!e.spec.tls)return!1;for(const t of e.spec.tls)if(t.secretName===this.secretName)return!0;return!1})).map((e=>e.metadata.name));for(const e of r)await t.patchIngress({namespace:s,ingressName:e,body:n}),this.logger.info(`ingress已重启:${e}`)}async patchCertSecret(e){const{cert:t,k8sClient:s}=e,n=t.crt,o=t.key,r=Buffer.from(n).toString("base64"),i=Buffer.from(o).toString("base64"),{namespace:a,secretName:c}=this,p={data:{"tls.crt":r,"tls.key":i},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let l=c;"string"==typeof c&&(l=[c]);for(const e of l)await s.patchSecret({namespace:a,secretName:e,body:p}),this.logger.info(`cert secret已更新: ${e}`)}async getClient(e,t){const s=new y({logger:this.logger,useROAClient:!0});return await s.init({accessKeyId:e.accessKeyId,accessKeySecret:e.accessKeySecret,endpoint:`https://cs.${t}.aliyuncs.com`,apiVersion:"2015-12-15"}),s}async getKubeConfig(e,t,s=!1){const n=`/k8s/${t}/user_config`,o={PrivateIpAddress:s,TemporaryDurationMinutes:15},r={},i={"Content-Type":"application/json"},a={};try{return(await e.request("GET",n,o,r,i,a)).config}catch(e){throw console.error("请求出错:",e),e}}},u([e.TaskInput({title:"集群id",component:{placeholder:"集群id"},required:!0}),d("design:type",String)],exports.DeployCertToAliyunAckPlugin.prototype,"clusterId",void 0),u([e.TaskInput({title:"保密字典Id",component:{placeholder:"保密字典Id"},required:!0}),d("design:type",Object)],exports.DeployCertToAliyunAckPlugin.prototype,"secretName",void 0),u([e.TaskInput({title:"大区",component:{name:"a-auto-complete",vModel:"value",options:[{value:"cn-qingdao",label:"华北1(青岛)"},{value:"cn-beijing",label:"华北2(北京)"},{value:"cn-zhangjiakou",label:"华北3(张家口)"},{value:"cn-huhehaote",label:"华北5(呼和浩特)"},{value:"cn-wulanchabu",label:"华北6(乌兰察布)"},{value:"cn-hangzhou",label:"华东1(杭州)"},{value:"cn-shanghai",label:"华东2(上海)"},{value:"cn-shenzhen",label:"华南1(深圳)"},{value:"cn-guangzhou",label:"华南3(广州)"},{value:"ap-southeast-2",label:"澳大利亚(悉尼)"},{value:"ap-southeast-3",label:"马来西亚(吉隆坡)"},{value:"ap-northeast-1",label:"日本(东京)"},{value:"cn-chengdu",label:"西南1(成都)"},{value:"ap-southeast-1",label:"新加坡"},{value:"ap-southeast-5",label:"印度尼西亚(雅加达)"},{value:"cn-hongkong",label:"中国香港"},{value:"eu-central-1",label:"德国(法兰克福)"},{value:"us-east-1",label:"美国(弗吉尼亚)"},{value:"us-west-1",label:"美国(硅谷)"},{value:"eu-west-1",label:"英国(伦敦)"},{value:"me-east-1",label:"阿联酋(迪拜)"},{value:"cn-beijing-finance-1",label:"华北2 金融云(邀测)"},{value:"cn-hangzhou-finance",label:"华东1 金融云"},{value:"cn-shanghai-finance-1",label:"华东2 金融云"},{value:"cn-shenzhen-finance-1",label:"华南1 金融云"}],placeholder:"集群所属大区"},required:!0}),d("design:type",String)],exports.DeployCertToAliyunAckPlugin.prototype,"regionId",void 0),u([e.TaskInput({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),d("design:type",Object)],exports.DeployCertToAliyunAckPlugin.prototype,"namespace",void 0),u([e.TaskInput({title:"是否私网ip",value:!1,component:{name:"a-switch",vModel:"checked",placeholder:"集群连接端点是否是私网ip"},helper:"如果您当前certd运行在同一个私网下,可以选择是。",required:!0}),d("design:type",Boolean)],exports.DeployCertToAliyunAckPlugin.prototype,"isPrivateIpAddress",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.DeployCertToAliyunAckPlugin.prototype,"cert",void 0),u([e.TaskInput({title:"Access授权",helper:"阿里云授权AccessKeyId、AccessKeySecret",component:{name:"pi-access-selector",type:"aliyun"},required:!0}),d("design:type",String)],exports.DeployCertToAliyunAckPlugin.prototype,"accessId",void 0),exports.DeployCertToAliyunAckPlugin=u([e.IsTaskPlugin({name:"DeployCertToAliyunAck",title:"部署到阿里云Ack",icon:"ant-design:aliyun-outlined",desc:"部署到阿里云Ack集群Ingress等通过Secret管理证书的应用",group:e.pluginGroups.aliyun.key,needPlus:!0,input:{},output:{},default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.DeployCertToAliyunAckPlugin),new exports.DeployCertToAliyunAckPlugin,exports.AliyunAccess=class{accessKeyId="";accessKeySecret=""},u([e.AccessInput({title:"accessKeyId",component:{placeholder:"accessKeyId"},helper:"登录阿里云控制台->AccessKey管理页面获取。",required:!0}),d("design:type",Object)],exports.AliyunAccess.prototype,"accessKeyId",void 0),u([e.AccessInput({title:"accessKeySecret",component:{placeholder:"accessKeySecret"},required:!0,encrypt:!0,helper:"注意:证书申请需要dns解析权限;其他阿里云插件,需要对应的权限,比如证书上传需要证书管理权限;嫌麻烦就用主账号的全量权限的accessKey"}),d("design:type",Object)],exports.AliyunAccess.prototype,"accessKeySecret",void 0),exports.AliyunAccess=u([e.IsAccess({name:"aliyun",title:"阿里云授权",desc:""})],exports.AliyunAccess),new exports.AliyunAccess;const h="certd";class m{access;http;logger;token;constructor(e,t,s){this.access=e,this.http=t,this.logger=s}async doLogin(){const e=this.access;if(e.otp&&null!=e.deviceId)return this.logger.info("OTP登录"),await this.doLoginWithDeviceId(e.deviceId);this.logger.info("使用普通登录");const t=await this.http.request({url:`${e.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:e.username,passwd:e.password,session:"Certd",format:"sid",enable_syno_token:"yes"}});if(!t.success)throw new Error("登录失败: ",t.error);return this.logger.info("登录成功"),this.token=t.data,this.token}async doLoginWithOTPCode(e){const t=this.access,s=await this.http.request({url:`${t.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:t.username,passwd:t.password,otp_code:e,enable_device_token:"yes",device_name:h},skipSslVerify:!0});if(!s.success)throw new Error("登录失败: ",s.error);return this.logger.info("登录成功"),this.token=s.data,this.token}async doLoginWithDeviceId(e){const t=this.access,s=await this.http.request({url:`${t.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:t.username,passwd:t.password,device_name:h,device_id:e,session:"Certd",format:"sid",enable_syno_token:"yes"},skipSslVerify:!0});if(!s.success)throw new Error("登录失败: ",s.error);return this.logger.info("登录成功"),this.token=s.data,this.token}async doRequest(e){const t=this.token.sid,s=e.method||"POST",n={...e.apiParams,_sid:t,...e.params,SynoToken:this.token.synotoken},o=await this.http.request({url:`${this.access.baseUrl}/webapi/entry.cgi?${r.stringify(n)}`,method:s,data:e.data,headers:e.headers,skipSslVerify:!0});if(!o.success)throw new Error(`API 调用失败: ${JSON.stringify(o.error)}`);return o.data}async getCertList(){return this.logger.info("获取证书列表"),await this.doRequest({method:"GET",apiParams:{api:"SYNO.Core.Certificate.CRT",version:1,method:"list"}})}async getInfo(){return this.logger.info("获取信息"),await this.doRequest({method:"GET",apiParams:{api:"SYNO.API.Info",version:1,method:"query"}})}}exports.SynologyDeployToPanel=class extends l{certName;cert;accessId;async onInstance(){}async execute(){const e=await this.accessService.getById(this.accessId),t=new m(e,this.ctx.http,this.ctx.logger);await t.doLogin();const s=await t.getCertList();if(this.certName){const e=s.certificates.find((e=>e.desc===this.certName||e.subject.common_name===this.certName));if(!e)throw new Error(`未找到证书: ${this.certName}`);this.logger.info(`找到证书: ${e.id}`),await this.updateCertToPanel(t,e)}else{this.logger.info("开始更新全部证书");for(const e of s.certificates)this.logger.info(`更新证书: ${e.id}`),await this.updateCertToPanel(t,e)}}async updateCertToPanel(e,s){this.logger.info(`更新证书:${s.id}`);const n=new t.CertReader(this.cert);return n.readCertFile({logger:this.logger,handle:async t=>{const o=new a,{tmpCrtPath:r,tmpKeyPath:c,tmpIcPath:p}=t;return this.logger.info(`上传证书:${r},${c}`),o.append("key",i.createReadStream(c)),o.append("cert",i.createReadStream(r)),n.ic&&(this.logger.info(`包含中间证书:${p}`),o.append("inter_cert",i.createReadStream(p))),o.append("id",s.id),o.append("desc",s.desc),console.log(JSON.stringify(o.getHeaders())),await e.doRequest({method:"POST",apiParams:{api:"SYNO.Core.Certificate",version:1,method:"import"},data:o,headers:{...o.getHeaders()}})}})}},u([e.TaskInput({title:"群晖证书描述",component:{name:"a-input",vModel:"value",placeholder:"群晖证书描述"},required:!1,helper:"在群晖证书管理页面里面,选择证书,点击操作,给证书设置描述,然后填写到这里\n如果不填,则覆盖更新全部证书"}),d("design:type",String)],exports.SynologyDeployToPanel.prototype,"certName",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.SynologyDeployToPanel.prototype,"cert",void 0),u([e.TaskInput({title:"群晖授权",helper:"群晖登录授权,请确保账户是管理员用户组",component:{name:"pi-access-selector",type:"synology"},required:!0}),d("design:type",String)],exports.SynologyDeployToPanel.prototype,"accessId",void 0),exports.SynologyDeployToPanel=u([e.IsTaskPlugin({name:"SynologyDeployToPanel",title:"部署证书到群晖面板",icon:"simple-icons:synology",group:e.pluginGroups.other.key,desc:"Synology,仅支持7.x以上版本",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.SynologyDeployToPanel),new exports.SynologyDeployToPanel,exports.SynologyAccess=class extends e.AccessRequestHandler{baseUrl="";username="";password="";otp=!1;deviceId="";onLoginWithOPTCode(e,t){console.log("onLoginWithOPTCode",this);return new m(this,t.http,t.logger).doLoginWithOTPCode(e.otpCode)}},u([e.AccessInput({title:"群晖面板的url",component:{placeholder:"https://yourdomain:5006"},helper:"群晖面板的访问地址,例如:https://yourdomain:5006",required:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"baseUrl",void 0),u([e.AccessInput({title:"账号",component:{placeholder:"账号"},helper:"群晖面板登录账号,必须是处于管理员用户组",required:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"username",void 0),u([e.AccessInput({title:"密码",component:{placeholder:"密码"},helper:"群晖面板登录密码",required:!0,encrypt:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"password",void 0),u([e.AccessInput({title:"双重认证",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"是否启用了双重认证",required:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"otp",void 0),u([e.AccessInput({title:"设备ID",component:{placeholder:"设备ID",name:"synology-device-id-getter",type:"access",typeName:"synology"},mergeScript:"\n return {\n component:{\n form: ctx.compute(({form})=>{\n return form\n })\n },\n show: ctx.compute(({form})=>{\n return form.access.otp\n })\n }\n ",helper:"如果开启了双重认证,需要获取设备ID\n填好上面的必填项,然后点击获取设备ID,输入双重认证APP上的码,确认即可获得设备ID,此操作只需要做一次\n如果是第二次获取设备id,密码要重新填,不要有星号",required:!1,encrypt:!0}),d("design:type",Object)],exports.SynologyAccess.prototype,"deviceId",void 0),exports.SynologyAccess=u([e.IsAccess({name:"synology",title:"群晖登录授权",desc:""})],exports.SynologyAccess),new exports.SynologyAccess,exports.K8sAccess=class{kubeconfig=""},u([e.AccessInput({title:"kubeconfig",component:{name:"a-textarea",vModel:"value",placeholder:"kubeconfig"},required:!0,encrypt:!0}),d("design:type",Object)],exports.K8sAccess.prototype,"kubeconfig",void 0),exports.K8sAccess=u([e.IsAccess({name:"k8s",title:"k8s授权",desc:""})],exports.K8sAccess),new exports.K8sAccess,exports.K8sDeployToSecretPlugin=class extends e.AbstractTaskPlugin{namespace;secretName;accessId;cert;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){const t=await this.accessService.getById(this.accessId),s=new this.K8sClient({kubeConfigStr:t.kubeconfig,logger:this.logger});await this.patchCertSecret({cert:this.cert,k8sClient:s}),await e.utils.sleep(5e3)}async patchCertSecret(e){const{cert:t,k8sClient:s}=e,n=t.crt,o=t.key,r=Buffer.from(n).toString("base64"),i=Buffer.from(o).toString("base64"),{namespace:a,secretName:c}=this,p={data:{"tls.crt":r,"tls.key":i},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let l=c;"string"==typeof c&&(l=[c]);for(const e of l)await s.patchSecret({namespace:a,secretName:e,body:p}),this.logger.info(`ingress cert Secret已更新:${e}`)}},u([e.TaskInput({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),d("design:type",String)],exports.K8sDeployToSecretPlugin.prototype,"namespace",void 0),u([e.TaskInput({title:"保密字典Id",component:{name:"a-select",vModel:"value",mode:"tags",open:!1},required:!0}),d("design:type",Object)],exports.K8sDeployToSecretPlugin.prototype,"secretName",void 0),u([e.TaskInput({title:"k8s授权",helper:"kubeconfig",component:{name:"pi-access-selector",type:"k8s"},required:!0}),d("design:type",String)],exports.K8sDeployToSecretPlugin.prototype,"accessId",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.K8sDeployToSecretPlugin.prototype,"cert",void 0),exports.K8sDeployToSecretPlugin=u([e.IsTaskPlugin({name:"K8sDeployToSecret",title:"K8S证书部署",icon:"mdi:kubernetes",desc:"部署证书到k8s的secret",needPlus:!0,group:e.pluginGroups.other.key,default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.K8sDeployToSecretPlugin),new exports.K8sDeployToSecretPlugin,exports.K8sDeployToIngressPlugin=class extends e.AbstractTaskPlugin{namespace;ingressName;accessId;cert;async execute(){const t=await this.accessService.getById(this.accessId),s=new(0,(await import("@certd/lib-k8s")).K8sClient)({kubeConfigStr:t.kubeconfig,logger:this.logger}),n=(await s.getIngressList({namespace:this.namespace})).items.find((e=>e.metadata.name===this.ingressName));if(!n)throw new Error(`Ingress不存在:${this.ingressName}`);const o=n.spec.tls.map((e=>e.secretName));if(!o||0===o.length)throw new Error(`Ingress:${this.ingressName} 未找到证书Secret`);await this.patchNginxCertSecret({cert:this.cert,k8sClient:s,secretNames:o}),await e.utils.sleep(5e3)}async patchNginxCertSecret(e){const{cert:t,k8sClient:s}=e,n=t.crt,o=t.key,r=Buffer.from(n).toString("base64"),i=Buffer.from(o).toString("base64"),{namespace:a}=this,c={data:{"tls.crt":r,"tls.key":i},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};for(const t of e.secretNames)this.logger.info(`更新ingress cert Secret:${t}`),await s.patchSecret({namespace:a,secretName:t,body:c}),this.logger.info(`ingress cert Secret已更新:${t}`)}},u([e.TaskInput({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),d("design:type",String)],exports.K8sDeployToIngressPlugin.prototype,"namespace",void 0),u([e.TaskInput({title:"IngressName",required:!0,helper:"Ingress名称,根据名称查找证书Secret,然后更新"}),d("design:type",String)],exports.K8sDeployToIngressPlugin.prototype,"ingressName",void 0),u([e.TaskInput({title:"k8s授权",helper:"kubeconfig",component:{name:"pi-access-selector",type:"k8s"},required:!0}),d("design:type",String)],exports.K8sDeployToIngressPlugin.prototype,"accessId",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.K8sDeployToIngressPlugin.prototype,"cert",void 0),exports.K8sDeployToIngressPlugin=u([e.IsTaskPlugin({name:"K8sDeployToIngress",title:"K8S Ingress 证书部署",icon:"mdi:kubernetes",desc:"部署证书到k8s的Ingress",needPlus:!0,group:e.pluginGroups.other.key,default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.K8sDeployToIngressPlugin),new exports.K8sDeployToIngressPlugin,exports.TencentAccess=class{secretId="";secretKey=""},u([e.AccessInput({title:"secretId",helper:"使用对应的插件需要有对应的权限,比如上传证书,需要证书管理权限;部署到clb需要clb相关权限\n前往[密钥管理](https://console.cloud.tencent.com/cam/capi)进行创建",component:{placeholder:"secretId"},rules:[{required:!0,message:"该项必填"}]}),d("design:type",Object)],exports.TencentAccess.prototype,"secretId",void 0),u([e.AccessInput({title:"secretKey",component:{placeholder:"secretKey"},encrypt:!0,rules:[{required:!0,message:"该项必填"}]}),d("design:type",Object)],exports.TencentAccess.prototype,"secretKey",void 0),exports.TencentAccess=u([e.IsAccess({name:"tencent",title:"腾讯云"})],exports.TencentAccess),exports.DeployCertToTencentTKEIngressPlugin=class extends l{region;clusterId;namespace;secretName;ingressName;ingressClass;clusterDomain;accessId;tencentCertId;cert;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){const t=await this.accessService.getById(this.accessId),s=await this.getTkeClient(t,this.region),n=await this.getTkeKubeConfig(s,this.clusterId);this.logger.info("kubeconfig已成功获取");const o=new this.K8sClient({kubeConfigStr:n,logger:this.logger});"qcloud"===(this.ingressClass||"qcloud")?await this.patchQcloudCertSecret({k8sClient:o}):await this.patchNginxCertSecret({k8sClient:o}),await e.utils.sleep(5e3),await this.restartIngress({k8sClient:o})}async getTkeClient(e,t="ap-guangzhou"){return new(0,(await import("tencentcloud-sdk-nodejs/tencentcloud/services/tke/v20180525/index.js")).v20180525.Client)({credential:{secretId:e.secretId,secretKey:e.secretKey},region:t,profile:{httpProfile:{endpoint:"tke.tencentcloudapi.com"}}})}async getTkeKubeConfig(e,t){const s={ClusterId:t},n=await e.DescribeClusterKubeconfig(s);return this.checkRet(n),this.logger.info("注意:后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster"),n.Kubeconfig}appendTimeSuffix(e){return null==e&&(e="certd"),e+"-"+c().format("YYYYMMDD-HHmmss")}async patchQcloudCertSecret(e){if(null==this.tencentCertId)throw new Error("请先将【上传证书到腾讯云】作为前置任务");this.logger.info("腾讯云证书ID:",this.tencentCertId);const t=Buffer.from(this.tencentCertId).toString("base64"),{namespace:s,secretName:n}=this,o={data:{qcloud_cert_id:t},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let r=n;"string"==typeof n&&(r=[n]);for(const t of r)await e.k8sClient.patchSecret({namespace:s,secretName:t,body:o}),this.logger.info(`CertSecret已更新:${t}`)}async patchNginxCertSecret(e){const{k8sClient:t}=e,{cert:s}=this,n=s.crt,o=s.key,r=Buffer.from(n).toString("base64"),i=Buffer.from(o).toString("base64"),{namespace:a,secretName:c}=this,p={data:{"tls.crt":r,"tls.key":i},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let l=c;"string"==typeof c&&(l=[c]);for(const e of l)await t.patchSecret({namespace:a,secretName:e,body:p}),this.logger.info(`CertSecret已更新:${e}`)}async restartIngress(e){const{k8sClient:t}=e,{namespace:s,ingressName:n}=this,o={metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let r=this.ingressName;"string"==typeof n&&(r=[n]);for(const e of r)await t.patchIngress({namespace:s,ingressName:e,body:o}),this.logger.info(`ingress已重启:${e}`)}checkRet(e){if(!e||e.Error)throw new Error("执行失败:"+e.Error.Code+","+e.Error.Message)}},u([e.TaskInput({title:"大区",value:"ap-guangzhou",required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"region",void 0),u([e.TaskInput({title:"集群ID",required:!0,desc:"例如:cls-6lbj1vee",request:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"clusterId",void 0),u([e.TaskInput({title:"集群namespace",value:"default",required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"namespace",void 0),u([e.TaskInput({title:"证书的secret名称",required:!0}),d("design:type",Object)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"secretName",void 0),u([e.TaskInput({title:"ingress名称",required:!0}),d("design:type",Object)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"ingressName",void 0),u([e.TaskInput({title:"ingress类型",component:{name:"a-auto-complete",vModel:"value",options:[{value:"qcloud"},{value:"nginx"}]},helper:"可选 qcloud / nginx"}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"ingressClass",void 0),u([e.TaskInput({title:"集群域名",helper:"可不填,默认为:[clusterId].ccs.tencent-cloud.com"}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"clusterDomain",void 0),u([e.TaskInput({title:"Access授权",helper:"access授权",component:{name:"pi-access-selector",type:"tencent"},required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"accessId",void 0),u([e.TaskInput({title:"腾讯云证书id",helper:"请选择“上传证书到腾讯云”前置任务的输出",component:{name:"pi-output-selector",from:"UploadCertToTencent"},mergeScript:'\n return {\n show: ctx.compute(({form})=>{\n return form.ingressClass === "qcloud"\n })\n }\n ',required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"tencentCertId",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},mergeScript:'\n return {\n show: ctx.compute(({form})=>{\n return form.ingressClass === "nginx"\n })\n }\n ',required:!0}),d("design:type",Object)],exports.DeployCertToTencentTKEIngressPlugin.prototype,"cert",void 0),exports.DeployCertToTencentTKEIngressPlugin=u([e.IsTaskPlugin({name:"DeployCertToTencentTKEIngress",title:"部署到腾讯云TKE-ingress",needPlus:!0,icon:"svg:icon-tencentcloud",group:e.pluginGroups.tencent.key,desc:"Qcloud类型需要【上传到腾讯云】作为前置任务;ApiServer未开启外网访问则需要做域名的内网IP映射",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.DeployCertToTencentTKEIngressPlugin),exports.DeployCertToTencentTKEAllPlugin=class extends l{instanceIdList;accessId;tencentCertId;async onInstance(){}async execute(){const e=await this.accessService.getById(this.accessId),t=new(0,(await import("tencentcloud-sdk-nodejs/tencentcloud/services/ssl/v20191205/index.js")).v20191205.Client)({credential:{secretId:e.secretId,secretKey:e.secretKey},region:"",profile:{httpProfile:{endpoint:"ssl.tencentcloudapi.com"}}}),s={CertificateId:this.tencentCertId,InstanceIdList:this.instanceIdList},n=await t.DeployCertificateInstance(s);this.checkRet(n),this.logger.info("部署成功")}checkRet(e){if(!e||e.Error)throw new Error("执行失败:"+e.Error.Code+","+e.Error.Message)}},u([e.TaskInput({title:"云资源实例Id列表",component:{name:"a-select",vModel:"value",open:!1,mode:"tags"},helper:""}),d("design:type",Array)],exports.DeployCertToTencentTKEAllPlugin.prototype,"instanceIdList",void 0),u([e.TaskInput({title:"Access授权",helper:"access授权",component:{name:"pi-access-selector",type:"tencent"},required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEAllPlugin.prototype,"accessId",void 0),u([e.TaskInput({title:"腾讯云证书id",helper:"请选择“上传证书到腾讯云”前置任务的输出",component:{name:"pi-output-selector",from:"UploadCertToTencent"},required:!0}),d("design:type",String)],exports.DeployCertToTencentTKEAllPlugin.prototype,"tencentCertId",void 0),exports.DeployCertToTencentTKEAllPlugin=u([e.IsTaskPlugin({name:"DeployCertToTencentAllIngress",title:"部署证书到腾讯云任意云资源",needPlus:!0,icon:"svg:icon-tencentcloud",group:e.pluginGroups.tencent.key,desc:"需要【上传到腾讯云】作为前置任务",default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}}})],exports.DeployCertToTencentTKEAllPlugin),exports.OnePanelDeployToWebsitePlugin=class extends l{accessId;sslIds;cert;access;async onInstance(){this.access=await this.getAccess(this.accessId)}async execute(){const e=this.sslIds;for(const t of e){const s=await this.doRequest({url:"/api/v1/websites/ssl/upload",method:"post",data:{sslIds:e,certificate:this.cert.crt,certificatePath:"",description:"",privateKey:this.cert.key,privateKeyPath:"",sslID:t,type:"paste"}});console.log("uploadRes",JSON.stringify(s))}}async doRequest(e){const t=await this.getAccessToken();return e.headers={PanelAuthorization:t},await this.doRequestWithoutAuth(e)}async doRequestWithoutAuth(e){e.baseURL=this.access.baseUrl;const t=await this.ctx.http.request(e);if(200===t.code)return t.data;throw new Error(t.message)}async getAccessToken(){console.log("getAccessToken",this);const e=`1panel-token-${this.accessId}`;let t=this.ctx.utils.cache.get(e);if(t)return t;return t=(await this.doRequestWithoutAuth({url:"/api/v1/auth/login",method:"post",headers:{EntranceCode:Buffer.from(this.access.safeEnter).toString("base64")},data:{name:this.access.username,password:this.access.password,ignoreCaptcha:!0,captcha:"",captchaID:"",authMethod:"jwt",language:"zh"}})).token,this.ctx.utils.cache.set(e,t),t}async onGetSSLIds(){return(await this.doRequest({url:"api/v1/websites/ssl/search",method:"post",data:{page:1,pageSize:99999}})).items.map((e=>({label:`${e.primaryDomain}<${e.id}>`,value:e.id})))}},u([e.TaskInput({title:"1Panel授权",helper:"1Panel授权",component:{name:"pi-access-selector",type:"1panel"},required:!0}),d("design:type",String)],exports.OnePanelDeployToWebsitePlugin.prototype,"accessId",void 0),u([e.TaskInput({title:"1Panel证书ID",component:{name:"remote-select",vModel:"value",mode:"tags",type:"plugin",typeName:"1PanelDeployToWebsitePlugin",action:"GetSSLIds",watches:["accessId"]},mergeScript:"\n return {\n component:{\n form: ctx.compute(({form})=>{\n return form\n })\n }\n }\n ",helper:"要更新的1Panel证书id,选择授权之后,从下拉框中选择"}),d("design:type",Array)],exports.OnePanelDeployToWebsitePlugin.prototype,"sslIds",void 0),u([e.TaskInput({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),d("design:type",Object)],exports.OnePanelDeployToWebsitePlugin.prototype,"cert",void 0),exports.OnePanelDeployToWebsitePlugin=u([e.IsTaskPlugin({name:"1PanelDeployToWebsitePlugin",title:"部署证书到1Panel",icon:"material-symbols:shield-outline",group:e.pluginGroups.panel.key,default:{strategy:{runStrategy:e.RunStrategy.SkipWhenSucceed}},needPlus:!0})],exports.OnePanelDeployToWebsitePlugin),new exports.OnePanelDeployToWebsitePlugin,exports.OnePanelAccess=class{baseUrl="";safeEnter="";username="";password=""},u([e.AccessInput({title:"1Panel面板的url",component:{placeholder:"http://xxxx.com:1231"},required:!0}),d("design:type",Object)],exports.OnePanelAccess.prototype,"baseUrl",void 0),u([e.AccessInput({title:"安全入口",component:{placeholder:"登录的安全入口"},encrypt:!0,required:!0}),d("design:type",Object)],exports.OnePanelAccess.prototype,"safeEnter",void 0),u([e.AccessInput({title:"用户名",component:{placeholder:"username"},required:!0}),d("design:type",Object)],exports.OnePanelAccess.prototype,"username",void 0),u([e.AccessInput({title:"密码",component:{placeholder:"password"},helper:"",required:!0,encrypt:!0}),d("design:type",Object)],exports.OnePanelAccess.prototype,"password",void 0),exports.OnePanelAccess=u([e.IsAccess({name:"1panel",title:"1panel授权",desc:"账号和密码"})],exports.OnePanelAccess),new exports.OnePanelAccess,exports.AbstractPlusTaskPlugin=l,exports.AliyunClient=y,exports.SynologyClient=m,exports.mustPlus=p;
package/dist/bundle.mjs CHANGED
@@ -1 +1 @@
1
- import{isPlus as e,AbstractTaskPlugin as t,TaskInput as s,IsTaskPlugin as r,pluginGroups as n,RunStrategy as i,AccessInput as o,IsAccess as a,utils as c,RequestHandler as l}from"@certd/pipeline";import{CertReader as p}from"@certd/plugin-cert";import d from"node:crypto";import u from"node:path";import{HttpsProxyAgent as h}from"https-proxy-agent";import g from"querystring";import y from"fs";import m from"form-data";import f from"dayjs";function v(){if(!e())throw new Error("此插件仅供专业版中使用")}class w extends t{setCtx(e){super.setCtx(e),v()}}function S(e,t,s,r){var n,i=arguments.length,o=i<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,s):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,s,r);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(o=(i<3?n(o):i>3?n(t,s,o):n(t,s))||o);return i>3&&o&&Object.defineProperty(t,s,o),o}function b(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}"function"==typeof SuppressedError&&SuppressedError;class I{access;http;constructor(e,t){this.access=e,this.http=t}getRequestToken(){const e=(new Date).getTime(),t=e+this.getMd5(this.access.apiSecret);return{request_token:this.getMd5(t),request_time:e}}getMd5(e){return d.createHash("md5").update(e).digest("hex")}async doRequest(e,t,s,r){const n=this.getRequestToken();console.log("token",n);const i={...n,...s};let o=`${this.access.panelUrl}${e}`;t&&(o=`${o}?action=${t}`);const a=await this.http.request({url:o,method:"post",headers:{"Content-Type":"application/x-www-form-urlencoded"},data:i,...r});if(!a.status)throw new Error(a.msg);return a}}let k=class extends w{cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:t}=this,s=new p(e),r=await this.accessService.getById(t),n=this.ctx.http,i=new I(r,n),o=await i.doRequest("/config","SavePanelSSL",{privateKey:s.key,certPem:s.crt},{skipSslVerify:!0});this.logger.info(o?.msg)}};S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],k.prototype,"cert",void 0),S([s({title:"宝塔授权",helper:"baota的接口密钥,目前测试宝塔面板本身开启ssl之后,API接口就不能用了",component:{name:"pi-access-selector",type:"baota"},required:!0}),b("design:type",String)],k.prototype,"accessId",void 0),k=S([r({name:"BaotaDeployPanelCert",title:"宝塔面板证书部署",icon:"svg:icon-bt",group:n.other.key,desc:"部署宝塔面板本身的ssl证书",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],k),new k;let C=class extends w{siteName;isReverseProxy=!1;cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:t}=this,s=new p(e),r=await this.accessService.getById(t),n=this.ctx.http,i=new I(r,n);if(this.logger.info(`siteName:${this.siteName}`),this.isReverseProxy){const e=await i.doRequest("/mod/proxy/com/set_ssl",null,{site_name:this.siteName,key:s.key,csr:s.crt});this.logger.info(e?.msg)}else{const e=await i.doRequest("/site","SetSSL",{type:0,siteName:this.siteName,key:s.key,csr:s.crt});this.logger.info(e?.msg)}}};S([s({title:"网站域名",component:{name:"a-input"},helper:"登录面板->网站->网站名/域名/项目名称",required:!0}),b("design:type",String)],C.prototype,"siteName",void 0),S([s({title:"是否反向代理",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"该项目类型是反向代理还是其他项目",required:!0}),b("design:type",Object)],C.prototype,"isReverseProxy",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],C.prototype,"cert",void 0),S([s({title:"宝塔授权",helper:"baota的接口密钥",component:{name:"pi-access-selector",type:"baota"},required:!0}),b("design:type",String)],C.prototype,"accessId",void 0),C=S([r({name:"BaotaDeployWebSiteCert",title:"宝塔网站证书部署",icon:"svg:icon-bt",group:n.other.key,desc:"部署宝塔管理的站点的ssl证书",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],C),new C;let q=class{panelUrl="";apiSecret=""};S([o({title:"宝塔URL地址",component:{placeholder:"http://192.168.42.237:41896"},helper:"宝塔面板的url地址,例如:http://192.168.42.237:41896",required:!0}),b("design:type",Object)],q.prototype,"panelUrl",void 0),S([o({title:"接口密钥",component:{placeholder:"接口密钥"},helper:"宝塔面板设置->面板设置->API接口->接口配置->接口密钥。\n必须要加IP白名单,你可以先运行一次,报错之后会打印IP,将IP加入白名单之后再次运行即可",required:!0,encrypt:!0}),b("design:type",Object)],q.prototype,"apiSecret",void 0),q=S([a({name:"baota",title:"baota授权",desc:""})],q),new q;let P=class extends w{certId;domain;cert;accessId;async onInstance(){}async execute(){const{domain:e,certId:t,cert:s}=this;if(!e&&!t)throw new Error("证书ID和网站域名必须填写一个");const r=new p(s);t>0?await this.updateByCertId(r,t):await this.updateByDomain(r)}async updateByCertId(e,t){this.logger.info(`更新证书,证书ID:${t}`);const s=`http://user.yiduncdn.com/v1/certs/${t}`;await this.doRequest(s,"PUT",{cert:e.crt,key:e.key})}async doRequest(e,t,s){const r=await this.accessService.getById(this.accessId),{apiKey:n,apiSecret:i}=r,o=this.ctx.http,a=await o.request({url:e,method:t,headers:{"api-key":n,"api-secret":i},data:s});if(0!=a.code)throw new Error(a.msg);return a}async updateByDomain(e){const t=await this.doRequest("http://user.yiduncdn.com/v1/sites","GET",{domain:this.domain});if(0===t.data.length)throw new Error(`未找到域名相关站点:${this.domain}`);let s=null;for(const e of t.data)e.domain===this.domain&&(s=e);if(!s)throw new Error(`未找到域名匹配的站点:${this.domain}`);if(s.https_listen?.cert){const t=s.https_listen.cert;await this.updateByCertId(e,t)}else{this.logger.info(`创建证书,域名:${this.domain}`);const t="http://user.yiduncdn.com/v1/certs",r=this.domain+"_"+(new Date).getTime();await this.doRequest(t,"POST",{name:r,type:"custom",cert:e.crt,key:e.key});const n=(await this.doRequest(t,"GET",{name:r})).data[0].id,i="http://user.yiduncdn.com/v1/sites";await this.doRequest(i,"PUT",{id:s.id,https_listen:{cert:n}})}}};S([s({title:"证书ID",component:{name:"a-input-number",vModel:"value"},helper:"证书ID,在证书管理页面查看,每条记录都有证书id"}),b("design:type",Number)],P.prototype,"certId",void 0),S([s({title:"网站域名",component:{name:"a-input",vModel:"value"},helper:"网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名"}),b("design:type",Number)],P.prototype,"domain",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],P.prototype,"cert",void 0),S([s({title:"易盾授权",helper:"易盾CDN授权",component:{name:"pi-access-selector",type:"yidun"},required:!0}),b("design:type",String)],P.prototype,"accessId",void 0),P=S([r({name:"YidunDeployToCDN",title:"部署证书到易盾CDN",icon:"material-symbols:shield-outline",group:n.cdn.key,desc:"http://user.yiduncdn.com/",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],P),new P;let T=class{apiKey="";apiSecret=""};S([o({title:"api_key",component:{placeholder:"api_key"},helper:"http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",required:!0,encrypt:!0}),b("design:type",Object)],T.prototype,"apiKey",void 0),S([o({title:"api_secret",component:{placeholder:"api_secret"},helper:"http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",required:!0,encrypt:!0}),b("design:type",Object)],T.prototype,"apiSecret",void 0),T=S([a({name:"yidun",title:"易盾云授权",desc:"user.yiduncdn.com"})],T),new T;let N=class{host;port;user;password;secure=!1};S([o({title:"host",component:{placeholder:"ip / 域名",name:"a-input",vModel:"value"},helper:"FTP地址",required:!0}),b("design:type",String)],N.prototype,"host",void 0),S([o({title:"host",value:21,component:{placeholder:"21",name:"a-input-number",vModel:"value"},helper:"FTP端口",required:!0}),b("design:type",String)],N.prototype,"port",void 0),S([o({title:"user",component:{placeholder:"用户名"},helper:"FTP用户名",required:!0}),b("design:type",String)],N.prototype,"user",void 0),S([o({title:"password",component:{placeholder:"密码",component:{name:"a-input-password",vModel:"value"}},encrypt:!0,helper:"FTP密码",required:!0}),b("design:type",String)],N.prototype,"password",void 0),S([o({title:"secure",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"是否使用SSL",required:!0}),b("design:type",Boolean)],N.prototype,"secure",void 0),N=S([a({name:"ftp",title:"FTP授权",desc:""})],N),new N;let x=class extends w{crtPath;keyPath;pfxPath;derPath;cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:t}=this,s=new p(e);let r=null;const n=async({reader:e,tmpCrtPath:s,tmpKeyPath:n,tmpDerPath:i,tmpPfxPath:o})=>{const a=(await import("basic-ftp")).Client;r=new a,r.ftp.verbose=!0,this.logger.info("开始连接FTP");const c=await this.accessService.getById(t);await r.access(c),this.logger.info("FTP连接成功"),await this.doUpload(r,s,this.crtPath),await this.doUpload(r,n,this.keyPath),await this.doUpload(r,o,this.pfxPath),await this.doUpload(r,i,this.derPath)};try{await s.readCertFile({logger:this.logger,handle:n})}finally{r&&r.close()}this.logger.info("执行完成")}async doUpload(e,t,s){if(!s)return;const r=u.dirname(s);this.logger.info(`确保目录存在:${r}`),await e.ensureDir(r),this.logger.info(`开始上传文件${t} -> ${s}`),await e.uploadFrom(t,s)}};S([s({title:"PEM证书保存路径",helper:"需要有写入权限,路径要包含证书文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.pem"}}),b("design:type",String)],x.prototype,"crtPath",void 0),S([s({title:"私钥保存路径",helper:"需要有写入权限,路径要包含私钥文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.key"}}),b("design:type",String)],x.prototype,"keyPath",void 0),S([s({title:"PFX证书保存路径",helper:"需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.pfx"}}),b("design:type",String)],x.prototype,"pfxPath",void 0),S([s({title:"DER证书保存路径",helper:"需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号\n.der和.cer是相同的东西,改个后缀名即可",component:{placeholder:"/test/cert.der 或 /test/cert.cer"}}),b("design:type",String)],x.prototype,"derPath",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],x.prototype,"cert",void 0),S([s({title:"FTP授权",component:{name:"pi-access-selector",type:"ftp"},required:!0}),b("design:type",String)],x.prototype,"accessId",void 0),x=S([r({name:"UploadCertToFTP",title:"上传证书到FTP",icon:"mdi:folder-upload-outline",group:n.host.key,desc:"将证书上传到FTP服务器",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],x),new x;let O=class extends w{url;certId;domain;cert;accessId;async onInstance(){}async execute(){const{domain:e,certId:t,cert:s}=this;if(!e&&!t)throw new Error("证书ID和网站域名必须填写一个");const r=new p(s);t>0?await this.updateByCertId(r,t):await this.updateByDomain(r)}async updateByCertId(e,t){this.logger.info(`更新证书,证书ID:${t}`);const s=`${this.url}/v1/certs/${t}`;await this.doRequest(s,"PUT",{cert:e.crt,key:e.key})}async doRequest(e,t,s){const r=await this.accessService.getById(this.accessId),{apiKey:n,apiSecret:i}=r,o=this.ctx.http,a=await o.request({url:e,method:t,headers:{"api-key":n,"api-secret":i},data:s});if(0!=a.code)throw new Error(a.msg);return a}async updateByDomain(e){const t=`${this.url}/v1/sites`,s=await this.doRequest(t,"GET",{domain:this.domain});if(0===s.data.length)throw new Error(`未找到域名相关站点:${this.domain}`);let r=null;for(const e of s.data)e.domain===this.domain&&(r=e);if(!r)throw new Error(`未找到域名匹配的站点:${this.domain}`);if(r.https_listen?.cert){const t=r.https_listen.cert;await this.updateByCertId(e,t)}else{this.logger.info(`创建证书,域名:${this.domain}`);const t=`${this.url}/v1/certs`,s=this.domain+"_"+(new Date).getTime();await this.doRequest(t,"POST",{name:s,type:"custom",cert:e.crt,key:e.key});const n=(await this.doRequest(t,"GET",{name:s})).data[0].id,i=`${this.url}/v1/sites`;await this.doRequest(i,"PUT",{id:r.id,https_listen:{cert:n}})}}};S([s({title:"cdnfly系统网址",component:{name:"a-input",vModel:"value"},helper:"例如:http://demo.cdnfly.cn"}),b("design:type",String)],O.prototype,"url",void 0),S([s({title:"证书ID",component:{name:"a-input-number",vModel:"value"},helper:"证书ID,在证书管理页面查看,每条记录都有证书id"}),b("design:type",Number)],O.prototype,"certId",void 0),S([s({title:"网站域名",component:{name:"a-input",vModel:"value"},helper:"网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名"}),b("design:type",Number)],O.prototype,"domain",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],O.prototype,"cert",void 0),S([s({title:"cdnfly授权",helper:"cdnfly授权",component:{name:"pi-access-selector",type:"cdnfly"},required:!0}),b("design:type",String)],O.prototype,"accessId",void 0),O=S([r({name:"CdnflyDeployToCDN",title:"部署证书到cdnfly",icon:"majesticons:cloud-line",group:n.cdn.key,desc:"cdnfly",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],O),new O;let K=class{apiKey="";apiSecret=""};S([o({title:"api_key",component:{placeholder:"api_key"},helper:"登录cdnfly控制台->账户中心->Api密钥,点击开启后获取",required:!0,encrypt:!0}),b("design:type",Object)],K.prototype,"apiKey",void 0),S([o({title:"api_secret",component:{placeholder:"api_secret"},helper:"登录cdnfly控制台->账户中心->Api密钥,点击开启后获取",required:!0,encrypt:!0}),b("design:type",Object)],K.prototype,"apiSecret",void 0),K=S([a({name:"cdnfly",title:"cdnfly授权",desc:""})],K),new K;let A=class extends w{region;bucket;domainName;certName;cert;accessId;async onInstance(){}async execute(){this.logger.info("开始部署证书到阿里云OSS");const e=await this.accessService.getById(this.accessId);this.logger.info(`bucket: ${this.bucket}, region: ${this.region}, domainName: ${this.domainName}`);const t=await this.getClient(e);await this.doRequest(t,{}),this.logger.info("部署完成")}async getClient(e){return new((await import("ali-oss")).default)({accessKeyId:e.accessKeyId,accessKeySecret:e.accessKeySecret,region:this.region,authorizationV4:!0,bucket:this.bucket})}async doRequest(e,t){t=e._bucketRequestParams("POST",this.bucket,{cname:"",comp:"add"});const s=`\n <BucketCnameConfiguration>\n <Cname>\n <Domain>${this.domainName}</Domain>\n <CertificateConfiguration>\n <PrivateKey>${this.cert.key}</PrivateKey>\n <Certificate>${this.cert.crt}</Certificate>\n </CertificateConfiguration>\n </Cname>\n</BucketCnameConfiguration>`;t.content=s,t.mime="xml",t.successStatuses=[200];const r=await e.request(t);return this.checkRet(r),r}checkRet(e){if(null!=e.code)throw new Error("执行失败:"+e.Message)}};S([s({title:"大区",component:{name:"a-auto-complete",vModel:"value",options:[{value:"oss-cn-hangzhou",label:"华东1(杭州)"},{value:"oss-cn-shanghai",label:"华东2(上海)"},{value:"oss-cn-nanjing",label:"华东5(南京-本地地域)"},{value:"oss-cn-fuzhou",label:"华东6(福州-本地地域)"},{value:"oss-cn-wuhan",label:"华中1(武汉-本地地域)"},{value:"oss-cn-qingdao",label:"华北1(青岛)"},{value:"oss-cn-beijing",label:"华北2(北京)"},{value:"oss-cn-zhangjiakou",label:"华北 3(张家口)"},{value:"oss-cn-huhehaote",label:"华北5(呼和浩特)"},{value:"oss-cn-wulanchabu",label:"华北6(乌兰察布)"},{value:"oss-cn-shenzhen",label:"华南1(深圳)"},{value:"oss-cn-heyuan",label:"华南2(河源)"},{value:"oss-cn-guangzhou",label:"华南3(广州)"},{value:"oss-cn-chengdu",label:"西南1(成都)"},{value:"oss-cn-hongkong",label:"中国香港"},{value:"oss-us-west-1",label:"美国(硅谷)①"},{value:"oss-us-east-1",label:"美国(弗吉尼亚)①"},{value:"oss-ap-northeast-1",label:"日本(东京)①"},{value:"oss-ap-northeast-2",label:"韩国(首尔)"},{value:"oss-ap-southeast-1",label:"新加坡①"},{value:"oss-ap-southeast-2",label:"澳大利亚(悉尼)①"},{value:"oss-ap-southeast-3",label:"马来西亚(吉隆坡)①"},{value:"oss-ap-southeast-5",label:"印度尼西亚(雅加达)①"},{value:"oss-ap-southeast-6",label:"菲律宾(马尼拉)"},{value:"oss-ap-southeast-7",label:"泰国(曼谷)"},{value:"oss-eu-central-1",label:"德国(法兰克福)①"},{value:"oss-eu-west-1",label:"英国(伦敦)"},{value:"oss-me-east-1",label:"阿联酋(迪拜)①"},{value:"oss-rg-china-mainland",label:"无地域属性(中国内地)"}]},required:!0}),b("design:type",String)],A.prototype,"region",void 0),S([s({title:"Bucket",helper:"存储桶名称",required:!0}),b("design:type",String)],A.prototype,"bucket",void 0),S([s({title:"绑定的域名",helper:"你在阿里云OSS上绑定的域名,比如:certd.docmirror.cn",required:!0}),b("design:type",String)],A.prototype,"domainName",void 0),S([s({title:"证书名称",helper:"上传后将以此名称作为前缀备注"}),b("design:type",String)],A.prototype,"certName",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],A.prototype,"cert",void 0),S([s({title:"Access授权",helper:"阿里云授权AccessKeyId、AccessKeySecret",component:{name:"pi-access-selector",type:"aliyun"},required:!0}),b("design:type",String)],A.prototype,"accessId",void 0),A=S([r({name:"DeployCertToAliyunOSS",title:"部署证书至阿里云OSS",icon:"ant-design:aliyun-outlined",group:n.aliyun.key,desc:"自动部署域名证书至阿里云OSS",needPlus:!0,default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],A),new A;class D{client;logger;agent;useROAClient;constructor(e){this.logger=e.logger,this.useROAClient=e.useROAClient||!1;const t=process.env.HTTPS_PROXY;t&&(this.logger.info(`use proxy:${t}`),this.agent=new h(t))}async getSdk(){if(this.useROAClient)return await this.getROAClient();return(await import("@alicloud/pop-core")).default}async getROAClient(){const e=await import("@alicloud/pop-core");return console.log("aliyun sdk",e),e.ROAClient}async init(e){const t=await this.getSdk();return this.client=new t(e),this.client}async request(e,t,s={}){return this.useROAClient||(s.agent=this.agent),await this.client.request(e,t,s)}}let R=class extends w{clusterId;secretName;regionId;namespace="default";isPrivateIpAddress;cert;accessId;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){this.logger.info("开始部署证书到阿里云Ack");const{regionId:e,clusterId:t,isPrivateIpAddress:s,cert:r}=this,n=await this.accessService.getById(this.accessId),i=await this.getClient(n,e),o=await this.getKubeConfig(i,t,s);this.logger.info("kubeconfig已成功获取");const a=new this.K8sClient({kubeConfigStr:o,logger:this.logger});await this.patchCertSecret({cert:r,k8sClient:a}),await c.sleep(5e3)}async restartIngress(e){const{k8sClient:t}=e,{namespace:s}=this,r={metadata:{labels:{certd:this.appendTimeSuffix("certd")}}},n=await t.getIngressList({namespace:s});if(this.logger.info("ingressList:",n),!n||!n.items)return;const i=n.items.filter((e=>{if(!e.spec.tls)return!1;for(const t of e.spec.tls)if(t.secretName===this.secretName)return!0;return!1})).map((e=>e.metadata.name));for(const e of i)await t.patchIngress({namespace:s,ingressName:e,body:r}),this.logger.info(`ingress已重启:${e}`)}async patchCertSecret(e){const{cert:t,k8sClient:s}=e,r=t.crt,n=t.key,i=Buffer.from(r).toString("base64"),o=Buffer.from(n).toString("base64"),{namespace:a,secretName:c}=this,l={data:{"tls.crt":i,"tls.key":o},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let p=c;"string"==typeof c&&(p=[c]);for(const e of p)await s.patchSecret({namespace:a,secretName:e,body:l}),this.logger.info(`cert secret已更新: ${e}`)}async getClient(e,t){const s=new D({logger:this.logger,useROAClient:!0});return await s.init({accessKeyId:e.accessKeyId,accessKeySecret:e.accessKeySecret,endpoint:`https://cs.${t}.aliyuncs.com`,apiVersion:"2015-12-15"}),s}async getKubeConfig(e,t,s=!1){const r=`/k8s/${t}/user_config`,n={PrivateIpAddress:s,TemporaryDurationMinutes:15},i={},o={"Content-Type":"application/json"},a={};try{return(await e.request("GET",r,n,i,o,a)).config}catch(e){throw console.error("请求出错:",e),e}}};S([s({title:"集群id",component:{placeholder:"集群id"},required:!0}),b("design:type",String)],R.prototype,"clusterId",void 0),S([s({title:"保密字典Id",component:{placeholder:"保密字典Id"},required:!0}),b("design:type",Object)],R.prototype,"secretName",void 0),S([s({title:"大区",component:{name:"a-auto-complete",vModel:"value",options:[{value:"cn-qingdao",label:"华北1(青岛)"},{value:"cn-beijing",label:"华北2(北京)"},{value:"cn-zhangjiakou",label:"华北3(张家口)"},{value:"cn-huhehaote",label:"华北5(呼和浩特)"},{value:"cn-wulanchabu",label:"华北6(乌兰察布)"},{value:"cn-hangzhou",label:"华东1(杭州)"},{value:"cn-shanghai",label:"华东2(上海)"},{value:"cn-shenzhen",label:"华南1(深圳)"},{value:"cn-guangzhou",label:"华南3(广州)"},{value:"ap-southeast-2",label:"澳大利亚(悉尼)"},{value:"ap-southeast-3",label:"马来西亚(吉隆坡)"},{value:"ap-northeast-1",label:"日本(东京)"},{value:"cn-chengdu",label:"西南1(成都)"},{value:"ap-southeast-1",label:"新加坡"},{value:"ap-southeast-5",label:"印度尼西亚(雅加达)"},{value:"cn-hongkong",label:"中国香港"},{value:"eu-central-1",label:"德国(法兰克福)"},{value:"us-east-1",label:"美国(弗吉尼亚)"},{value:"us-west-1",label:"美国(硅谷)"},{value:"eu-west-1",label:"英国(伦敦)"},{value:"me-east-1",label:"阿联酋(迪拜)"},{value:"cn-beijing-finance-1",label:"华北2 金融云(邀测)"},{value:"cn-hangzhou-finance",label:"华东1 金融云"},{value:"cn-shanghai-finance-1",label:"华东2 金融云"},{value:"cn-shenzhen-finance-1",label:"华南1 金融云"}],placeholder:"集群所属大区"},required:!0}),b("design:type",String)],R.prototype,"regionId",void 0),S([s({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),b("design:type",Object)],R.prototype,"namespace",void 0),S([s({title:"是否私网ip",value:!1,component:{name:"a-switch",vModel:"checked",placeholder:"集群连接端点是否是私网ip"},helper:"如果您当前certd运行在同一个私网下,可以选择是。",required:!0}),b("design:type",Boolean)],R.prototype,"isPrivateIpAddress",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],R.prototype,"cert",void 0),S([s({title:"Access授权",helper:"阿里云授权AccessKeyId、AccessKeySecret",component:{name:"pi-access-selector",type:"aliyun"},required:!0}),b("design:type",String)],R.prototype,"accessId",void 0),R=S([r({name:"DeployCertToAliyunAck",title:"部署到阿里云Ack",icon:"ant-design:aliyun-outlined",desc:"部署到阿里云Ack集群Ingress等通过Secret管理证书的应用",group:n.aliyun.key,needPlus:!0,input:{},output:{},default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],R),new R;let $=class{accessKeyId="";accessKeySecret=""};S([o({title:"accessKeyId",component:{placeholder:"accessKeyId"},helper:"登录阿里云控制台->AccessKey管理页面获取。",required:!0}),b("design:type",Object)],$.prototype,"accessKeyId",void 0),S([o({title:"accessKeySecret",component:{placeholder:"accessKeySecret"},required:!0,encrypt:!0,helper:"注意:证书申请需要dns解析权限;其他阿里云插件,需要对应的权限,比如证书上传需要证书管理权限;嫌麻烦就用主账号的全量权限的accessKey"}),b("design:type",Object)],$.prototype,"accessKeySecret",void 0),$=S([a({name:"aliyun",title:"阿里云授权",desc:""})],$),new $;const j="certd";class E{access;http;logger;token;constructor(e,t,s){this.access=e,this.http=t,this.logger=s}async doLogin(){const e=this.access;if(e.otp&&null!=e.deviceId)return this.logger.info("OTP登录"),await this.doLoginWithDeviceId(e.deviceId);this.logger.info("使用普通登录");const t=await this.http.request({url:`${e.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:e.username,passwd:e.password,session:"Certd",format:"sid",enable_syno_token:"yes"}});if(!t.success)throw new Error("登录失败: ",t.error);return this.logger.info("登录成功"),this.token=t.data,this.token}async doLoginWithOTPCode(e){const t=this.access,s=await this.http.request({url:`${t.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:t.username,passwd:t.password,otp_code:e,enable_device_token:"yes",device_name:j}});if(!s.success)throw new Error("登录失败: ",s.error);return this.logger.info("登录成功"),this.token=s.data,this.token}async doLoginWithDeviceId(e){const t=this.access,s=await this.http.request({url:`${t.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:t.username,passwd:t.password,device_name:j,device_id:e,session:"Certd",format:"sid",enable_syno_token:"yes"}});if(!s.success)throw new Error("登录失败: ",s.error);return this.logger.info("登录成功"),this.token=s.data,this.token}async doRequest(e){const t=this.token.sid,s=e.method||"POST",r={...e.apiParams,_sid:t,...e.params,SynoToken:this.token.synotoken},n=await this.http.request({url:`${this.access.baseUrl}/webapi/entry.cgi?${g.stringify(r)}`,method:s,data:e.data,headers:e.headers,skipSslVerify:!0});if(!n.success)throw new Error(`API 调用失败: ${JSON.stringify(n.error)}`);return n.data}async getCertList(){return this.logger.info("获取证书列表"),await this.doRequest({method:"GET",apiParams:{api:"SYNO.Core.Certificate.CRT",version:1,method:"list"}})}async getInfo(){return this.logger.info("获取信息"),await this.doRequest({method:"GET",apiParams:{api:"SYNO.API.Info",version:1,method:"query"}})}}let _=class extends w{certName;cert;accessId;async onInstance(){}async execute(){const e=await this.accessService.getById(this.accessId),t=new E(e,this.ctx.http,this.ctx.logger);await t.doLogin();const s=await t.getCertList();if(this.certName){const e=s.certificates.find((e=>e.desc===this.certName||e.subject.common_name===this.certName));if(!e)throw new Error(`未找到证书: ${this.certName}`);this.logger.info(`找到证书: ${e.id}`),await this.updateCertToPanel(t,e)}else{this.logger.info("开始更新全部证书");for(const e of s.certificates)this.logger.info(`更新证书: ${e.id}`),await this.updateCertToPanel(t,e)}}async updateCertToPanel(e,t){this.logger.info(`更新证书:${t.id}`);const s=new p(this.cert);return s.readCertFile({logger:this.logger,handle:async r=>{const n=new m,{tmpCrtPath:i,tmpKeyPath:o,tmpIcPath:a}=r;return this.logger.info(`上传证书:${i},${o}`),n.append("key",y.createReadStream(o)),n.append("cert",y.createReadStream(i)),s.ic&&(this.logger.info(`包含中间证书:${a}`),n.append("inter_cert",y.createReadStream(a))),n.append("id",t.id),n.append("desc",t.desc),console.log(JSON.stringify(n.getHeaders())),await e.doRequest({method:"POST",apiParams:{api:"SYNO.Core.Certificate",version:1,method:"import"},data:n,headers:{...n.getHeaders()}})}})}};S([s({title:"群晖证书描述",component:{name:"a-input",vModel:"value",placeholder:"群晖证书描述"},required:!1,helper:"在群晖证书管理页面里面,选择证书,点击操作,给证书设置描述,然后填写到这里\n如果不填,则覆盖更新全部证书"}),b("design:type",String)],_.prototype,"certName",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],_.prototype,"cert",void 0),S([s({title:"群晖授权",helper:"群晖登录授权,请确保账户是管理员用户组,并且关闭双重认证",component:{name:"pi-access-selector",type:"synology"},required:!0}),b("design:type",String)],_.prototype,"accessId",void 0),_=S([r({name:"SynologyDeployToPanel",title:"部署证书到群晖面板",icon:"simple-icons:synology",group:n.other.key,desc:"Synology,仅支持7.x以上版本",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],_),new _;let B=class extends l{baseUrl="";username="";password="";otp=!1;deviceId="";onLoginWithOPTCode(e,t){console.log("onLoginWithOPTCode",this);return new E(this,t.http,t.logger).doLoginWithOTPCode(e.otpCode)}};S([o({title:"群晖面板的url",component:{placeholder:"https://yourdomain:5006"},helper:"群晖面板的访问地址,例如:https://yourdomain:5006",required:!0}),b("design:type",Object)],B.prototype,"baseUrl",void 0),S([o({title:"账号",component:{placeholder:"账号"},helper:"群晖面板登录账号,必须是处于管理员用户组",required:!0}),b("design:type",Object)],B.prototype,"username",void 0),S([o({title:"密码",component:{placeholder:"密码"},helper:"群晖面板登录密码",required:!0,encrypt:!0}),b("design:type",Object)],B.prototype,"password",void 0),S([o({title:"双重认证",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"是否启用了双重认证",required:!0}),b("design:type",Object)],B.prototype,"otp",void 0),S([o({title:"设备ID",component:{placeholder:"设备ID",name:"pi-synology-device-id-getter",type:"access",typeName:"synology"},mergeScript:"\n return {\n component:{\n form: ctx.compute(({form})=>{\n return form\n })\n },\n show: ctx.compute(({form})=>{\n return form.access.otp\n })\n }\n ",helper:"如果开启了双重认证,需要获取设备ID\n填好上面的必填项,然后点击获取设备ID,输入双重认证APP上的码,确认即可获得设备ID,此操作只需要做一次",required:!1,encrypt:!0}),b("design:type",Object)],B.prototype,"deviceId",void 0),B=S([a({name:"synology",title:"群晖登录授权",desc:""})],B),new B;let L=class{kubeconfig=""};S([o({title:"kubeconfig",component:{name:"a-textarea",vModel:"value",placeholder:"kubeconfig"},required:!0,encrypt:!0}),b("design:type",Object)],L.prototype,"kubeconfig",void 0),L=S([a({name:"k8s",title:"k8s授权",desc:""})],L),new L;let M=class extends t{namespace;secretName;accessId;cert;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){const e=await this.accessService.getById(this.accessId),t=new this.K8sClient({kubeConfigStr:e.kubeconfig,logger:this.logger});await this.patchCertSecret({cert:this.cert,k8sClient:t}),await c.sleep(5e3)}async patchCertSecret(e){const{cert:t,k8sClient:s}=e,r=t.crt,n=t.key,i=Buffer.from(r).toString("base64"),o=Buffer.from(n).toString("base64"),{namespace:a,secretName:c}=this,l={data:{"tls.crt":i,"tls.key":o},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let p=c;"string"==typeof c&&(p=[c]);for(const e of p)await s.patchSecret({namespace:a,secretName:e,body:l}),this.logger.info(`ingress cert Secret已更新:${e}`)}};S([s({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),b("design:type",String)],M.prototype,"namespace",void 0),S([s({title:"保密字典Id",component:{name:"a-select",vModel:"value",mode:"tags",open:!1},required:!0}),b("design:type",Object)],M.prototype,"secretName",void 0),S([s({title:"k8s授权",helper:"kubeconfig",component:{name:"pi-access-selector",type:"k8s"},required:!0}),b("design:type",String)],M.prototype,"accessId",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],M.prototype,"cert",void 0),M=S([r({name:"K8sDeployToSecret",title:"K8S证书部署",icon:"mdi:kubernetes",desc:"部署证书到k8s的secret",needPlus:!0,group:n.other.key,default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],M),new M;let U=class extends t{namespace;ingressName;accessId;cert;async execute(){const e=await this.accessService.getById(this.accessId),t=new(0,(await import("@certd/lib-k8s")).K8sClient)({kubeConfigStr:e.kubeconfig,logger:this.logger}),s=(await t.getIngressList({namespace:this.namespace})).items.find((e=>e.metadata.name===this.ingressName));if(!s)throw new Error(`Ingress不存在:${this.ingressName}`);const r=s.spec.tls.map((e=>e.secretName));if(!r||0===r.length)throw new Error(`Ingress:${this.ingressName} 未找到证书Secret`);await this.patchNginxCertSecret({cert:this.cert,k8sClient:t,secretNames:r}),await c.sleep(5e3)}async patchNginxCertSecret(e){const{cert:t,k8sClient:s}=e,r=t.crt,n=t.key,i=Buffer.from(r).toString("base64"),o=Buffer.from(n).toString("base64"),{namespace:a}=this,c={data:{"tls.crt":i,"tls.key":o},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};for(const t of e.secretNames)this.logger.info(`更新ingress cert Secret:${t}`),await s.patchSecret({namespace:a,secretName:t,body:c}),this.logger.info(`ingress cert Secret已更新:${t}`)}};S([s({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),b("design:type",String)],U.prototype,"namespace",void 0),S([s({title:"IngressName",required:!0,helper:"Ingress名称,根据名称查找证书Secret,然后更新"}),b("design:type",String)],U.prototype,"ingressName",void 0),S([s({title:"k8s授权",helper:"kubeconfig",component:{name:"pi-access-selector",type:"k8s"},required:!0}),b("design:type",String)],U.prototype,"accessId",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],U.prototype,"cert",void 0),U=S([r({name:"K8sDeployToIngress",title:"K8S Ingress 证书部署",icon:"mdi:kubernetes",desc:"部署证书到k8s的Ingress",needPlus:!0,group:n.other.key,default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],U),new U;let W=class{secretId="";secretKey=""};S([o({title:"secretId",helper:"使用对应的插件需要有对应的权限,比如上传证书,需要证书管理权限;部署到clb需要clb相关权限\n前往[密钥管理](https://console.cloud.tencent.com/cam/capi)进行创建",component:{placeholder:"secretId"},rules:[{required:!0,message:"该项必填"}]}),b("design:type",Object)],W.prototype,"secretId",void 0),S([o({title:"secretKey",component:{placeholder:"secretKey"},encrypt:!0,rules:[{required:!0,message:"该项必填"}]}),b("design:type",Object)],W.prototype,"secretKey",void 0),W=S([a({name:"tencent",title:"腾讯云"})],W);let F=class extends w{region;clusterId;namespace;secretName;ingressName;ingressClass;clusterDomain;accessId;tencentCertId;cert;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){const e=await this.accessService.getById(this.accessId),t=await this.getTkeClient(e,this.region),s=await this.getTkeKubeConfig(t,this.clusterId);this.logger.info("kubeconfig已成功获取");const r=new this.K8sClient({kubeConfigStr:s,logger:this.logger});"qcloud"===(this.ingressClass||"qcloud")?await this.patchQcloudCertSecret({k8sClient:r}):await this.patchNginxCertSecret({k8sClient:r}),await c.sleep(5e3),await this.restartIngress({k8sClient:r})}async getTkeClient(e,t="ap-guangzhou"){return new(0,(await import("tencentcloud-sdk-nodejs/tencentcloud/services/tke/v20180525/index.js")).v20180525.Client)({credential:{secretId:e.secretId,secretKey:e.secretKey},region:t,profile:{httpProfile:{endpoint:"tke.tencentcloudapi.com"}}})}async getTkeKubeConfig(e,t){const s={ClusterId:t},r=await e.DescribeClusterKubeconfig(s);return this.checkRet(r),this.logger.info("注意:后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster"),r.Kubeconfig}appendTimeSuffix(e){return null==e&&(e="certd"),e+"-"+f().format("YYYYMMDD-HHmmss")}async patchQcloudCertSecret(e){if(null==this.tencentCertId)throw new Error("请先将【上传证书到腾讯云】作为前置任务");this.logger.info("腾讯云证书ID:",this.tencentCertId);const t=Buffer.from(this.tencentCertId).toString("base64"),{namespace:s,secretName:r}=this,n={data:{qcloud_cert_id:t},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let i=r;"string"==typeof r&&(i=[r]);for(const t of i)await e.k8sClient.patchSecret({namespace:s,secretName:t,body:n}),this.logger.info(`CertSecret已更新:${t}`)}async patchNginxCertSecret(e){const{k8sClient:t}=e,{cert:s}=this,r=s.crt,n=s.key,i=Buffer.from(r).toString("base64"),o=Buffer.from(n).toString("base64"),{namespace:a,secretName:c}=this,l={data:{"tls.crt":i,"tls.key":o},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let p=c;"string"==typeof c&&(p=[c]);for(const e of p)await t.patchSecret({namespace:a,secretName:e,body:l}),this.logger.info(`CertSecret已更新:${e}`)}async restartIngress(e){const{k8sClient:t}=e,{namespace:s,ingressName:r}=this,n={metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let i=this.ingressName;"string"==typeof r&&(i=[r]);for(const e of i)await t.patchIngress({namespace:s,ingressName:e,body:n}),this.logger.info(`ingress已重启:${e}`)}checkRet(e){if(!e||e.Error)throw new Error("执行失败:"+e.Error.Code+","+e.Error.Message)}};S([s({title:"大区",value:"ap-guangzhou",required:!0}),b("design:type",String)],F.prototype,"region",void 0),S([s({title:"集群ID",required:!0,desc:"例如:cls-6lbj1vee",request:!0}),b("design:type",String)],F.prototype,"clusterId",void 0),S([s({title:"集群namespace",value:"default",required:!0}),b("design:type",String)],F.prototype,"namespace",void 0),S([s({title:"证书的secret名称",required:!0}),b("design:type",Object)],F.prototype,"secretName",void 0),S([s({title:"ingress名称",required:!0}),b("design:type",Object)],F.prototype,"ingressName",void 0),S([s({title:"ingress类型",component:{name:"a-auto-complete",vModel:"value",options:[{value:"qcloud"},{value:"nginx"}]},helper:"可选 qcloud / nginx"}),b("design:type",String)],F.prototype,"ingressClass",void 0),S([s({title:"集群域名",helper:"可不填,默认为:[clusterId].ccs.tencent-cloud.com"}),b("design:type",String)],F.prototype,"clusterDomain",void 0),S([s({title:"Access授权",helper:"access授权",component:{name:"pi-access-selector",type:"tencent"},required:!0}),b("design:type",String)],F.prototype,"accessId",void 0),S([s({title:"腾讯云证书id",helper:"请选择“上传证书到腾讯云”前置任务的输出",component:{name:"pi-output-selector",from:"UploadCertToTencent"},mergeScript:'\n return {\n show: ctx.compute(({form})=>{\n return form.ingressClass === "qcloud"\n })\n }\n ',required:!0}),b("design:type",String)],F.prototype,"tencentCertId",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},mergeScript:'\n return {\n show: ctx.compute(({form})=>{\n return form.ingressClass === "nginx"\n })\n }\n ',required:!0}),b("design:type",Object)],F.prototype,"cert",void 0),F=S([r({name:"DeployCertToTencentTKEIngress",title:"部署到腾讯云TKE-ingress",needPlus:!0,icon:"svg:icon-tencentcloud",group:n.tencent.key,desc:"Qcloud类型需要【上传到腾讯云】作为前置任务;ApiServer未开启外网访问则需要做域名的内网IP映射",default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],F);export{w as AbstractPlusTaskPlugin,$ as AliyunAccess,D as AliyunClient,q as BaotaAccess,k as BaotaDeployPanelCertPlugin,C as BaotaDeployWebSiteCertPlugin,K as CdnflyAccess,O as CdnflyDeployToCDNPlugin,R as DeployCertToAliyunAckPlugin,A as DeployCertToAliyunOSS,F as DeployCertToTencentTKEIngressPlugin,N as FtpAccess,L as K8sAccess,U as K8sDeployToIngressPlugin,M as K8sDeployToSecretPlugin,B as SynologyAccess,E as SynologyClient,_ as SynologyDeployToPanel,W as TencentAccess,x as UploadCertToFTPPlugin,T as YidunAccess,P as YidunDeployToCDNPlugin,v as mustPlus};
1
+ import{isPlus as e,AbstractTaskPlugin as t,TaskInput as s,IsTaskPlugin as n,pluginGroups as r,RunStrategy as i,AccessInput as o,IsAccess as a,utils as c,AccessRequestHandler as l}from"@certd/pipeline";import{CertReader as p}from"@certd/plugin-cert";import d from"node:crypto";import u from"node:path";import{HttpsProxyAgent as h}from"https-proxy-agent";import g from"querystring";import y from"fs";import m from"form-data";import f from"dayjs";function v(){if(!e())throw new Error("此插件仅供专业版中使用")}class w extends t{setCtx(e){super.setCtx(e),v()}}function S(e,t,s,n){var r,i=arguments.length,o=i<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,s):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,s,n);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(i<3?r(o):i>3?r(t,s,o):r(t,s))||o);return i>3&&o&&Object.defineProperty(t,s,o),o}function b(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}"function"==typeof SuppressedError&&SuppressedError;class I{access;http;constructor(e,t){this.access=e,this.http=t}getRequestToken(){const e=(new Date).getTime(),t=e+this.getMd5(this.access.apiSecret);return{request_token:this.getMd5(t),request_time:e}}getMd5(e){return d.createHash("md5").update(e).digest("hex")}async doRequest(e,t,s,n){const r=this.getRequestToken();console.log("token",r);const i={...r,...s};let o=`${this.access.panelUrl}${e}`;t&&(o=`${o}?action=${t}`);const a=await this.http.request({url:o,method:"post",headers:{"Content-Type":"application/x-www-form-urlencoded"},data:i,...n});if(!a.status)throw new Error(a.msg);return a}}let k=class extends w{cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:t}=this,s=new p(e),n=await this.accessService.getById(t),r=this.ctx.http,i=new I(n,r),o=await i.doRequest("/config","SavePanelSSL",{privateKey:s.key,certPem:s.crt},{skipSslVerify:!0});this.logger.info(o?.msg)}};S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],k.prototype,"cert",void 0),S([s({title:"宝塔授权",helper:"baota的接口密钥,目前测试宝塔面板本身开启ssl之后,API接口就不能用了",component:{name:"pi-access-selector",type:"baota"},required:!0}),b("design:type",String)],k.prototype,"accessId",void 0),k=S([n({name:"BaotaDeployPanelCert",title:"宝塔面板证书部署",icon:"svg:icon-bt",group:r.panel.key,desc:"部署宝塔面板本身的ssl证书",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],k),new k;let C=class extends w{siteName;isReverseProxy=!1;cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:t}=this,s=new p(e),n=await this.accessService.getById(t),r=this.ctx.http,i=new I(n,r);if(this.logger.info(`siteName:${this.siteName}`),this.isReverseProxy){const e=await i.doRequest("/mod/proxy/com/set_ssl",null,{site_name:this.siteName,key:s.key,csr:s.crt});this.logger.info(e?.msg)}else{const e=await i.doRequest("/site","SetSSL",{type:0,siteName:this.siteName,key:s.key,csr:s.crt});this.logger.info(e?.msg)}}};S([s({title:"网站域名",component:{name:"a-input"},helper:"登录面板->网站->网站名/域名/项目名称",required:!0}),b("design:type",String)],C.prototype,"siteName",void 0),S([s({title:"是否反向代理",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"该项目类型是反向代理还是其他项目",required:!0}),b("design:type",Object)],C.prototype,"isReverseProxy",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],C.prototype,"cert",void 0),S([s({title:"宝塔授权",helper:"baota的接口密钥",component:{name:"pi-access-selector",type:"baota"},required:!0}),b("design:type",String)],C.prototype,"accessId",void 0),C=S([n({name:"BaotaDeployWebSiteCert",title:"宝塔网站证书部署",icon:"svg:icon-bt",group:r.panel.key,desc:"部署宝塔管理的站点的ssl证书",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],C),new C;let q=class{panelUrl="";apiSecret=""};S([o({title:"宝塔URL地址",component:{placeholder:"http://192.168.42.237:41896"},helper:"宝塔面板的url地址,例如:http://192.168.42.237:41896",required:!0}),b("design:type",Object)],q.prototype,"panelUrl",void 0),S([o({title:"接口密钥",component:{placeholder:"接口密钥"},helper:"宝塔面板设置->面板设置->API接口->接口配置->接口密钥。\n必须要加IP白名单,你可以先运行一次,报错之后会打印IP,将IP加入白名单之后再次运行即可",required:!0,encrypt:!0}),b("design:type",Object)],q.prototype,"apiSecret",void 0),q=S([a({name:"baota",title:"baota授权",desc:""})],q),new q;let P=class extends w{certId;domain;cert;accessId;async onInstance(){}async execute(){const{domain:e,certId:t,cert:s}=this;if(!e&&!t)throw new Error("证书ID和网站域名必须填写一个");const n=new p(s);t>0?await this.updateByCertId(n,t):await this.updateByDomain(n)}async updateByCertId(e,t){this.logger.info(`更新证书,证书ID:${t}`);const s=`http://user.yiduncdn.com/v1/certs/${t}`;await this.doRequest(s,"PUT",{cert:e.crt,key:e.key})}async doRequest(e,t,s){const n=await this.accessService.getById(this.accessId),{apiKey:r,apiSecret:i}=n,o=this.ctx.http,a=await o.request({url:e,method:t,headers:{"api-key":r,"api-secret":i},data:s});if(0!=a.code)throw new Error(a.msg);return a}async updateByDomain(e){const t=await this.doRequest("http://user.yiduncdn.com/v1/sites","GET",{domain:this.domain});if(0===t.data.length)throw new Error(`未找到域名相关站点:${this.domain}`);let s=null;for(const e of t.data)e.domain===this.domain&&(s=e);if(!s)throw new Error(`未找到域名匹配的站点:${this.domain}`);if(s.https_listen?.cert){const t=s.https_listen.cert;await this.updateByCertId(e,t)}else{this.logger.info(`创建证书,域名:${this.domain}`);const t="http://user.yiduncdn.com/v1/certs",n=this.domain+"_"+(new Date).getTime();await this.doRequest(t,"POST",{name:n,type:"custom",cert:e.crt,key:e.key});const r=(await this.doRequest(t,"GET",{name:n})).data[0].id,i="http://user.yiduncdn.com/v1/sites";await this.doRequest(i,"PUT",{id:s.id,https_listen:{cert:r}})}}};S([s({title:"证书ID",component:{name:"a-input-number",vModel:"value"},helper:"证书ID,在证书管理页面查看,每条记录都有证书id"}),b("design:type",Number)],P.prototype,"certId",void 0),S([s({title:"网站域名",component:{name:"a-input",vModel:"value"},helper:"网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名"}),b("design:type",Number)],P.prototype,"domain",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],P.prototype,"cert",void 0),S([s({title:"易盾授权",helper:"易盾CDN授权",component:{name:"pi-access-selector",type:"yidun"},required:!0}),b("design:type",String)],P.prototype,"accessId",void 0),P=S([n({name:"YidunDeployToCDN",title:"部署证书到易盾CDN",icon:"material-symbols:shield-outline",group:r.cdn.key,desc:"http://user.yiduncdn.com/",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],P),new P;let T=class{apiKey="";apiSecret=""};S([o({title:"api_key",component:{placeholder:"api_key"},helper:"http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",required:!0,encrypt:!0}),b("design:type",Object)],T.prototype,"apiKey",void 0),S([o({title:"api_secret",component:{placeholder:"api_secret"},helper:"http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",required:!0,encrypt:!0}),b("design:type",Object)],T.prototype,"apiSecret",void 0),T=S([a({name:"yidun",title:"易盾云授权",desc:"user.yiduncdn.com"})],T),new T;let x=class{host;port;user;password;secure=!1};S([o({title:"host",component:{placeholder:"ip / 域名",name:"a-input",vModel:"value"},helper:"FTP地址",required:!0}),b("design:type",String)],x.prototype,"host",void 0),S([o({title:"host",value:21,component:{placeholder:"21",name:"a-input-number",vModel:"value"},helper:"FTP端口",required:!0}),b("design:type",String)],x.prototype,"port",void 0),S([o({title:"user",component:{placeholder:"用户名"},helper:"FTP用户名",required:!0}),b("design:type",String)],x.prototype,"user",void 0),S([o({title:"password",component:{placeholder:"密码",component:{name:"a-input-password",vModel:"value"}},encrypt:!0,helper:"FTP密码",required:!0}),b("design:type",String)],x.prototype,"password",void 0),S([o({title:"secure",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"是否使用SSL",required:!0}),b("design:type",Boolean)],x.prototype,"secure",void 0),x=S([a({name:"ftp",title:"FTP授权",desc:""})],x),new x;let A=class extends w{crtPath;keyPath;pfxPath;derPath;cert;accessId;async onInstance(){}async execute(){const{cert:e,accessId:t}=this,s=new p(e);let n=null;const r=async({reader:e,tmpCrtPath:s,tmpKeyPath:r,tmpDerPath:i,tmpPfxPath:o})=>{const a=(await import("basic-ftp")).Client;n=new a,n.ftp.verbose=!0,this.logger.info("开始连接FTP");const c=await this.accessService.getById(t);await n.access(c),this.logger.info("FTP连接成功"),await this.doUpload(n,s,this.crtPath),await this.doUpload(n,r,this.keyPath),await this.doUpload(n,o,this.pfxPath),await this.doUpload(n,i,this.derPath)};try{await s.readCertFile({logger:this.logger,handle:r})}finally{n&&n.close()}this.logger.info("执行完成")}async doUpload(e,t,s){if(!s)return;const n=u.dirname(s);this.logger.info(`确保目录存在:${n}`),await e.ensureDir(n),this.logger.info(`开始上传文件${t} -> ${s}`),await e.uploadFrom(t,s)}};S([s({title:"PEM证书保存路径",helper:"需要有写入权限,路径要包含证书文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.pem"}}),b("design:type",String)],A.prototype,"crtPath",void 0),S([s({title:"私钥保存路径",helper:"需要有写入权限,路径要包含私钥文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.key"}}),b("design:type",String)],A.prototype,"keyPath",void 0),S([s({title:"PFX证书保存路径",helper:"需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号",component:{placeholder:"/test/cert.pfx"}}),b("design:type",String)],A.prototype,"pfxPath",void 0),S([s({title:"DER证书保存路径",helper:"需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号\n.der和.cer是相同的东西,改个后缀名即可",component:{placeholder:"/test/cert.der 或 /test/cert.cer"}}),b("design:type",String)],A.prototype,"derPath",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],A.prototype,"cert",void 0),S([s({title:"FTP授权",component:{name:"pi-access-selector",type:"ftp"},required:!0}),b("design:type",String)],A.prototype,"accessId",void 0),A=S([n({name:"UploadCertToFTP",title:"上传证书到FTP",icon:"mdi:folder-upload-outline",group:r.host.key,desc:"将证书上传到FTP服务器",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],A),new A;let N=class extends w{url;certId;domain;cert;accessId;async onInstance(){}async execute(){const{domain:e,certId:t,cert:s}=this;if(!e&&!t)throw new Error("证书ID和网站域名必须填写一个");const n=new p(s);t>0?await this.updateByCertId(n,t):await this.updateByDomain(n)}async updateByCertId(e,t){this.logger.info(`更新证书,证书ID:${t}`);const s=`${this.url}/v1/certs/${t}`;await this.doRequest(s,"PUT",{cert:e.crt,key:e.key})}async doRequest(e,t,s){const n=await this.accessService.getById(this.accessId),{apiKey:r,apiSecret:i}=n,o=this.ctx.http,a=await o.request({url:e,method:t,headers:{"api-key":r,"api-secret":i},data:s});if(0!=a.code)throw new Error(a.msg);return a}async updateByDomain(e){const t=`${this.url}/v1/sites`,s=await this.doRequest(t,"GET",{domain:this.domain});if(0===s.data.length)throw new Error(`未找到域名相关站点:${this.domain}`);let n=null;for(const e of s.data)e.domain===this.domain&&(n=e);if(!n)throw new Error(`未找到域名匹配的站点:${this.domain}`);if(n.https_listen?.cert){const t=n.https_listen.cert;await this.updateByCertId(e,t)}else{this.logger.info(`创建证书,域名:${this.domain}`);const t=`${this.url}/v1/certs`,s=this.domain+"_"+(new Date).getTime();await this.doRequest(t,"POST",{name:s,type:"custom",cert:e.crt,key:e.key});const r=(await this.doRequest(t,"GET",{name:s})).data[0].id,i=`${this.url}/v1/sites`;await this.doRequest(i,"PUT",{id:n.id,https_listen:{cert:r}})}}};S([s({title:"cdnfly系统网址",component:{name:"a-input",vModel:"value"},helper:"例如:http://demo.cdnfly.cn"}),b("design:type",String)],N.prototype,"url",void 0),S([s({title:"证书ID",component:{name:"a-input-number",vModel:"value"},helper:"证书ID,在证书管理页面查看,每条记录都有证书id"}),b("design:type",Number)],N.prototype,"certId",void 0),S([s({title:"网站域名",component:{name:"a-input",vModel:"value"},helper:"网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名"}),b("design:type",Number)],N.prototype,"domain",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],N.prototype,"cert",void 0),S([s({title:"cdnfly授权",helper:"cdnfly授权",component:{name:"pi-access-selector",type:"cdnfly"},required:!0}),b("design:type",String)],N.prototype,"accessId",void 0),N=S([n({name:"CdnflyDeployToCDN",title:"部署证书到cdnfly",icon:"majesticons:cloud-line",group:r.cdn.key,desc:"cdnfly",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],N),new N;let O=class{apiKey="";apiSecret=""};S([o({title:"api_key",component:{placeholder:"api_key"},helper:"登录cdnfly控制台->账户中心->Api密钥,点击开启后获取",required:!0,encrypt:!0}),b("design:type",Object)],O.prototype,"apiKey",void 0),S([o({title:"api_secret",component:{placeholder:"api_secret"},helper:"登录cdnfly控制台->账户中心->Api密钥,点击开启后获取",required:!0,encrypt:!0}),b("design:type",Object)],O.prototype,"apiSecret",void 0),O=S([a({name:"cdnfly",title:"cdnfly授权",desc:""})],O),new O;let K=class extends w{region;bucket;domainName;certName;cert;accessId;async onInstance(){}async execute(){this.logger.info("开始部署证书到阿里云OSS");const e=await this.accessService.getById(this.accessId);this.logger.info(`bucket: ${this.bucket}, region: ${this.region}, domainName: ${this.domainName}`);const t=await this.getClient(e);await this.doRequest(t,{}),this.logger.info("部署完成")}async getClient(e){return new((await import("ali-oss")).default)({accessKeyId:e.accessKeyId,accessKeySecret:e.accessKeySecret,region:this.region,authorizationV4:!0,bucket:this.bucket})}async doRequest(e,t){t=e._bucketRequestParams("POST",this.bucket,{cname:"",comp:"add"});const s=`\n <BucketCnameConfiguration>\n <Cname>\n <Domain>${this.domainName}</Domain>\n <CertificateConfiguration>\n <PrivateKey>${this.cert.key}</PrivateKey>\n <Certificate>${this.cert.crt}</Certificate>\n </CertificateConfiguration>\n </Cname>\n</BucketCnameConfiguration>`;t.content=s,t.mime="xml",t.successStatuses=[200];const n=await e.request(t);return this.checkRet(n),n}checkRet(e){if(null!=e.code)throw new Error("执行失败:"+e.Message)}};S([s({title:"大区",component:{name:"a-auto-complete",vModel:"value",options:[{value:"oss-cn-hangzhou",label:"华东1(杭州)"},{value:"oss-cn-shanghai",label:"华东2(上海)"},{value:"oss-cn-nanjing",label:"华东5(南京-本地地域)"},{value:"oss-cn-fuzhou",label:"华东6(福州-本地地域)"},{value:"oss-cn-wuhan",label:"华中1(武汉-本地地域)"},{value:"oss-cn-qingdao",label:"华北1(青岛)"},{value:"oss-cn-beijing",label:"华北2(北京)"},{value:"oss-cn-zhangjiakou",label:"华北 3(张家口)"},{value:"oss-cn-huhehaote",label:"华北5(呼和浩特)"},{value:"oss-cn-wulanchabu",label:"华北6(乌兰察布)"},{value:"oss-cn-shenzhen",label:"华南1(深圳)"},{value:"oss-cn-heyuan",label:"华南2(河源)"},{value:"oss-cn-guangzhou",label:"华南3(广州)"},{value:"oss-cn-chengdu",label:"西南1(成都)"},{value:"oss-cn-hongkong",label:"中国香港"},{value:"oss-us-west-1",label:"美国(硅谷)①"},{value:"oss-us-east-1",label:"美国(弗吉尼亚)①"},{value:"oss-ap-northeast-1",label:"日本(东京)①"},{value:"oss-ap-northeast-2",label:"韩国(首尔)"},{value:"oss-ap-southeast-1",label:"新加坡①"},{value:"oss-ap-southeast-2",label:"澳大利亚(悉尼)①"},{value:"oss-ap-southeast-3",label:"马来西亚(吉隆坡)①"},{value:"oss-ap-southeast-5",label:"印度尼西亚(雅加达)①"},{value:"oss-ap-southeast-6",label:"菲律宾(马尼拉)"},{value:"oss-ap-southeast-7",label:"泰国(曼谷)"},{value:"oss-eu-central-1",label:"德国(法兰克福)①"},{value:"oss-eu-west-1",label:"英国(伦敦)"},{value:"oss-me-east-1",label:"阿联酋(迪拜)①"},{value:"oss-rg-china-mainland",label:"无地域属性(中国内地)"}]},required:!0}),b("design:type",String)],K.prototype,"region",void 0),S([s({title:"Bucket",helper:"存储桶名称",required:!0}),b("design:type",String)],K.prototype,"bucket",void 0),S([s({title:"绑定的域名",helper:"你在阿里云OSS上绑定的域名,比如:certd.docmirror.cn",required:!0}),b("design:type",String)],K.prototype,"domainName",void 0),S([s({title:"证书名称",helper:"上传后将以此名称作为前缀备注"}),b("design:type",String)],K.prototype,"certName",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],K.prototype,"cert",void 0),S([s({title:"Access授权",helper:"阿里云授权AccessKeyId、AccessKeySecret",component:{name:"pi-access-selector",type:"aliyun"},required:!0}),b("design:type",String)],K.prototype,"accessId",void 0),K=S([n({name:"DeployCertToAliyunOSS",title:"部署证书至阿里云OSS",icon:"ant-design:aliyun-outlined",group:r.aliyun.key,desc:"自动部署域名证书至阿里云OSS",needPlus:!0,default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],K),new K;class R{client;logger;agent;useROAClient;constructor(e){this.logger=e.logger,this.useROAClient=e.useROAClient||!1;const t=process.env.HTTPS_PROXY;t&&(this.logger.info(`use proxy:${t}`),this.agent=new h(t))}async getSdk(){if(this.useROAClient)return await this.getROAClient();return(await import("@alicloud/pop-core")).default}async getROAClient(){const e=await import("@alicloud/pop-core");return console.log("aliyun sdk",e),e.ROAClient}async init(e){const t=await this.getSdk();return this.client=new t(e),this.client}async request(e,t,s={}){return this.useROAClient||(s.agent=this.agent),await this.client.request(e,t,s)}}let D=class extends w{clusterId;secretName;regionId;namespace="default";isPrivateIpAddress;cert;accessId;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){this.logger.info("开始部署证书到阿里云Ack");const{regionId:e,clusterId:t,isPrivateIpAddress:s,cert:n}=this,r=await this.accessService.getById(this.accessId),i=await this.getClient(r,e),o=await this.getKubeConfig(i,t,s);this.logger.info("kubeconfig已成功获取");const a=new this.K8sClient({kubeConfigStr:o,logger:this.logger});await this.patchCertSecret({cert:n,k8sClient:a}),await c.sleep(5e3)}async restartIngress(e){const{k8sClient:t}=e,{namespace:s}=this,n={metadata:{labels:{certd:this.appendTimeSuffix("certd")}}},r=await t.getIngressList({namespace:s});if(this.logger.info("ingressList:",r),!r||!r.items)return;const i=r.items.filter((e=>{if(!e.spec.tls)return!1;for(const t of e.spec.tls)if(t.secretName===this.secretName)return!0;return!1})).map((e=>e.metadata.name));for(const e of i)await t.patchIngress({namespace:s,ingressName:e,body:n}),this.logger.info(`ingress已重启:${e}`)}async patchCertSecret(e){const{cert:t,k8sClient:s}=e,n=t.crt,r=t.key,i=Buffer.from(n).toString("base64"),o=Buffer.from(r).toString("base64"),{namespace:a,secretName:c}=this,l={data:{"tls.crt":i,"tls.key":o},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let p=c;"string"==typeof c&&(p=[c]);for(const e of p)await s.patchSecret({namespace:a,secretName:e,body:l}),this.logger.info(`cert secret已更新: ${e}`)}async getClient(e,t){const s=new R({logger:this.logger,useROAClient:!0});return await s.init({accessKeyId:e.accessKeyId,accessKeySecret:e.accessKeySecret,endpoint:`https://cs.${t}.aliyuncs.com`,apiVersion:"2015-12-15"}),s}async getKubeConfig(e,t,s=!1){const n=`/k8s/${t}/user_config`,r={PrivateIpAddress:s,TemporaryDurationMinutes:15},i={},o={"Content-Type":"application/json"},a={};try{return(await e.request("GET",n,r,i,o,a)).config}catch(e){throw console.error("请求出错:",e),e}}};S([s({title:"集群id",component:{placeholder:"集群id"},required:!0}),b("design:type",String)],D.prototype,"clusterId",void 0),S([s({title:"保密字典Id",component:{placeholder:"保密字典Id"},required:!0}),b("design:type",Object)],D.prototype,"secretName",void 0),S([s({title:"大区",component:{name:"a-auto-complete",vModel:"value",options:[{value:"cn-qingdao",label:"华北1(青岛)"},{value:"cn-beijing",label:"华北2(北京)"},{value:"cn-zhangjiakou",label:"华北3(张家口)"},{value:"cn-huhehaote",label:"华北5(呼和浩特)"},{value:"cn-wulanchabu",label:"华北6(乌兰察布)"},{value:"cn-hangzhou",label:"华东1(杭州)"},{value:"cn-shanghai",label:"华东2(上海)"},{value:"cn-shenzhen",label:"华南1(深圳)"},{value:"cn-guangzhou",label:"华南3(广州)"},{value:"ap-southeast-2",label:"澳大利亚(悉尼)"},{value:"ap-southeast-3",label:"马来西亚(吉隆坡)"},{value:"ap-northeast-1",label:"日本(东京)"},{value:"cn-chengdu",label:"西南1(成都)"},{value:"ap-southeast-1",label:"新加坡"},{value:"ap-southeast-5",label:"印度尼西亚(雅加达)"},{value:"cn-hongkong",label:"中国香港"},{value:"eu-central-1",label:"德国(法兰克福)"},{value:"us-east-1",label:"美国(弗吉尼亚)"},{value:"us-west-1",label:"美国(硅谷)"},{value:"eu-west-1",label:"英国(伦敦)"},{value:"me-east-1",label:"阿联酋(迪拜)"},{value:"cn-beijing-finance-1",label:"华北2 金融云(邀测)"},{value:"cn-hangzhou-finance",label:"华东1 金融云"},{value:"cn-shanghai-finance-1",label:"华东2 金融云"},{value:"cn-shenzhen-finance-1",label:"华南1 金融云"}],placeholder:"集群所属大区"},required:!0}),b("design:type",String)],D.prototype,"regionId",void 0),S([s({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),b("design:type",Object)],D.prototype,"namespace",void 0),S([s({title:"是否私网ip",value:!1,component:{name:"a-switch",vModel:"checked",placeholder:"集群连接端点是否是私网ip"},helper:"如果您当前certd运行在同一个私网下,可以选择是。",required:!0}),b("design:type",Boolean)],D.prototype,"isPrivateIpAddress",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],D.prototype,"cert",void 0),S([s({title:"Access授权",helper:"阿里云授权AccessKeyId、AccessKeySecret",component:{name:"pi-access-selector",type:"aliyun"},required:!0}),b("design:type",String)],D.prototype,"accessId",void 0),D=S([n({name:"DeployCertToAliyunAck",title:"部署到阿里云Ack",icon:"ant-design:aliyun-outlined",desc:"部署到阿里云Ack集群Ingress等通过Secret管理证书的应用",group:r.aliyun.key,needPlus:!0,input:{},output:{},default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],D),new D;let j=class{accessKeyId="";accessKeySecret=""};S([o({title:"accessKeyId",component:{placeholder:"accessKeyId"},helper:"登录阿里云控制台->AccessKey管理页面获取。",required:!0}),b("design:type",Object)],j.prototype,"accessKeyId",void 0),S([o({title:"accessKeySecret",component:{placeholder:"accessKeySecret"},required:!0,encrypt:!0,helper:"注意:证书申请需要dns解析权限;其他阿里云插件,需要对应的权限,比如证书上传需要证书管理权限;嫌麻烦就用主账号的全量权限的accessKey"}),b("design:type",Object)],j.prototype,"accessKeySecret",void 0),j=S([a({name:"aliyun",title:"阿里云授权",desc:""})],j),new j;const $="certd";class E{access;http;logger;token;constructor(e,t,s){this.access=e,this.http=t,this.logger=s}async doLogin(){const e=this.access;if(e.otp&&null!=e.deviceId)return this.logger.info("OTP登录"),await this.doLoginWithDeviceId(e.deviceId);this.logger.info("使用普通登录");const t=await this.http.request({url:`${e.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:e.username,passwd:e.password,session:"Certd",format:"sid",enable_syno_token:"yes"}});if(!t.success)throw new Error("登录失败: ",t.error);return this.logger.info("登录成功"),this.token=t.data,this.token}async doLoginWithOTPCode(e){const t=this.access,s=await this.http.request({url:`${t.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:t.username,passwd:t.password,otp_code:e,enable_device_token:"yes",device_name:$},skipSslVerify:!0});if(!s.success)throw new Error("登录失败: ",s.error);return this.logger.info("登录成功"),this.token=s.data,this.token}async doLoginWithDeviceId(e){const t=this.access,s=await this.http.request({url:`${t.baseUrl}/webapi/entry.cgi`,method:"GET",params:{api:"SYNO.API.Auth",version:6,method:"login",account:t.username,passwd:t.password,device_name:$,device_id:e,session:"Certd",format:"sid",enable_syno_token:"yes"},skipSslVerify:!0});if(!s.success)throw new Error("登录失败: ",s.error);return this.logger.info("登录成功"),this.token=s.data,this.token}async doRequest(e){const t=this.token.sid,s=e.method||"POST",n={...e.apiParams,_sid:t,...e.params,SynoToken:this.token.synotoken},r=await this.http.request({url:`${this.access.baseUrl}/webapi/entry.cgi?${g.stringify(n)}`,method:s,data:e.data,headers:e.headers,skipSslVerify:!0});if(!r.success)throw new Error(`API 调用失败: ${JSON.stringify(r.error)}`);return r.data}async getCertList(){return this.logger.info("获取证书列表"),await this.doRequest({method:"GET",apiParams:{api:"SYNO.Core.Certificate.CRT",version:1,method:"list"}})}async getInfo(){return this.logger.info("获取信息"),await this.doRequest({method:"GET",apiParams:{api:"SYNO.API.Info",version:1,method:"query"}})}}let B=class extends w{certName;cert;accessId;async onInstance(){}async execute(){const e=await this.accessService.getById(this.accessId),t=new E(e,this.ctx.http,this.ctx.logger);await t.doLogin();const s=await t.getCertList();if(this.certName){const e=s.certificates.find((e=>e.desc===this.certName||e.subject.common_name===this.certName));if(!e)throw new Error(`未找到证书: ${this.certName}`);this.logger.info(`找到证书: ${e.id}`),await this.updateCertToPanel(t,e)}else{this.logger.info("开始更新全部证书");for(const e of s.certificates)this.logger.info(`更新证书: ${e.id}`),await this.updateCertToPanel(t,e)}}async updateCertToPanel(e,t){this.logger.info(`更新证书:${t.id}`);const s=new p(this.cert);return s.readCertFile({logger:this.logger,handle:async n=>{const r=new m,{tmpCrtPath:i,tmpKeyPath:o,tmpIcPath:a}=n;return this.logger.info(`上传证书:${i},${o}`),r.append("key",y.createReadStream(o)),r.append("cert",y.createReadStream(i)),s.ic&&(this.logger.info(`包含中间证书:${a}`),r.append("inter_cert",y.createReadStream(a))),r.append("id",t.id),r.append("desc",t.desc),console.log(JSON.stringify(r.getHeaders())),await e.doRequest({method:"POST",apiParams:{api:"SYNO.Core.Certificate",version:1,method:"import"},data:r,headers:{...r.getHeaders()}})}})}};S([s({title:"群晖证书描述",component:{name:"a-input",vModel:"value",placeholder:"群晖证书描述"},required:!1,helper:"在群晖证书管理页面里面,选择证书,点击操作,给证书设置描述,然后填写到这里\n如果不填,则覆盖更新全部证书"}),b("design:type",String)],B.prototype,"certName",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],B.prototype,"cert",void 0),S([s({title:"群晖授权",helper:"群晖登录授权,请确保账户是管理员用户组",component:{name:"pi-access-selector",type:"synology"},required:!0}),b("design:type",String)],B.prototype,"accessId",void 0),B=S([n({name:"SynologyDeployToPanel",title:"部署证书到群晖面板",icon:"simple-icons:synology",group:r.other.key,desc:"Synology,仅支持7.x以上版本",default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],B),new B;let _=class extends l{baseUrl="";username="";password="";otp=!1;deviceId="";onLoginWithOPTCode(e,t){console.log("onLoginWithOPTCode",this);return new E(this,t.http,t.logger).doLoginWithOTPCode(e.otpCode)}};S([o({title:"群晖面板的url",component:{placeholder:"https://yourdomain:5006"},helper:"群晖面板的访问地址,例如:https://yourdomain:5006",required:!0}),b("design:type",Object)],_.prototype,"baseUrl",void 0),S([o({title:"账号",component:{placeholder:"账号"},helper:"群晖面板登录账号,必须是处于管理员用户组",required:!0}),b("design:type",Object)],_.prototype,"username",void 0),S([o({title:"密码",component:{placeholder:"密码"},helper:"群晖面板登录密码",required:!0,encrypt:!0}),b("design:type",Object)],_.prototype,"password",void 0),S([o({title:"双重认证",value:!1,component:{name:"a-switch",vModel:"checked"},helper:"是否启用了双重认证",required:!0}),b("design:type",Object)],_.prototype,"otp",void 0),S([o({title:"设备ID",component:{placeholder:"设备ID",name:"synology-device-id-getter",type:"access",typeName:"synology"},mergeScript:"\n return {\n component:{\n form: ctx.compute(({form})=>{\n return form\n })\n },\n show: ctx.compute(({form})=>{\n return form.access.otp\n })\n }\n ",helper:"如果开启了双重认证,需要获取设备ID\n填好上面的必填项,然后点击获取设备ID,输入双重认证APP上的码,确认即可获得设备ID,此操作只需要做一次\n如果是第二次获取设备id,密码要重新填,不要有星号",required:!1,encrypt:!0}),b("design:type",Object)],_.prototype,"deviceId",void 0),_=S([a({name:"synology",title:"群晖登录授权",desc:""})],_),new _;let L=class{kubeconfig=""};S([o({title:"kubeconfig",component:{name:"a-textarea",vModel:"value",placeholder:"kubeconfig"},required:!0,encrypt:!0}),b("design:type",Object)],L.prototype,"kubeconfig",void 0),L=S([a({name:"k8s",title:"k8s授权",desc:""})],L),new L;let M=class extends t{namespace;secretName;accessId;cert;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){const e=await this.accessService.getById(this.accessId),t=new this.K8sClient({kubeConfigStr:e.kubeconfig,logger:this.logger});await this.patchCertSecret({cert:this.cert,k8sClient:t}),await c.sleep(5e3)}async patchCertSecret(e){const{cert:t,k8sClient:s}=e,n=t.crt,r=t.key,i=Buffer.from(n).toString("base64"),o=Buffer.from(r).toString("base64"),{namespace:a,secretName:c}=this,l={data:{"tls.crt":i,"tls.key":o},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let p=c;"string"==typeof c&&(p=[c]);for(const e of p)await s.patchSecret({namespace:a,secretName:e,body:l}),this.logger.info(`ingress cert Secret已更新:${e}`)}};S([s({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),b("design:type",String)],M.prototype,"namespace",void 0),S([s({title:"保密字典Id",component:{name:"a-select",vModel:"value",mode:"tags",open:!1},required:!0}),b("design:type",Object)],M.prototype,"secretName",void 0),S([s({title:"k8s授权",helper:"kubeconfig",component:{name:"pi-access-selector",type:"k8s"},required:!0}),b("design:type",String)],M.prototype,"accessId",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],M.prototype,"cert",void 0),M=S([n({name:"K8sDeployToSecret",title:"K8S证书部署",icon:"mdi:kubernetes",desc:"部署证书到k8s的secret",needPlus:!0,group:r.other.key,default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],M),new M;let U=class extends t{namespace;ingressName;accessId;cert;async execute(){const e=await this.accessService.getById(this.accessId),t=new(0,(await import("@certd/lib-k8s")).K8sClient)({kubeConfigStr:e.kubeconfig,logger:this.logger}),s=(await t.getIngressList({namespace:this.namespace})).items.find((e=>e.metadata.name===this.ingressName));if(!s)throw new Error(`Ingress不存在:${this.ingressName}`);const n=s.spec.tls.map((e=>e.secretName));if(!n||0===n.length)throw new Error(`Ingress:${this.ingressName} 未找到证书Secret`);await this.patchNginxCertSecret({cert:this.cert,k8sClient:t,secretNames:n}),await c.sleep(5e3)}async patchNginxCertSecret(e){const{cert:t,k8sClient:s}=e,n=t.crt,r=t.key,i=Buffer.from(n).toString("base64"),o=Buffer.from(r).toString("base64"),{namespace:a}=this,c={data:{"tls.crt":i,"tls.key":o},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};for(const t of e.secretNames)this.logger.info(`更新ingress cert Secret:${t}`),await s.patchSecret({namespace:a,secretName:t,body:c}),this.logger.info(`ingress cert Secret已更新:${t}`)}};S([s({title:"命名空间",value:"default",component:{placeholder:"命名空间"},required:!0}),b("design:type",String)],U.prototype,"namespace",void 0),S([s({title:"IngressName",required:!0,helper:"Ingress名称,根据名称查找证书Secret,然后更新"}),b("design:type",String)],U.prototype,"ingressName",void 0),S([s({title:"k8s授权",helper:"kubeconfig",component:{name:"pi-access-selector",type:"k8s"},required:!0}),b("design:type",String)],U.prototype,"accessId",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],U.prototype,"cert",void 0),U=S([n({name:"K8sDeployToIngress",title:"K8S Ingress 证书部署",icon:"mdi:kubernetes",desc:"部署证书到k8s的Ingress",needPlus:!0,group:r.other.key,default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],U),new U;let W=class{secretId="";secretKey=""};S([o({title:"secretId",helper:"使用对应的插件需要有对应的权限,比如上传证书,需要证书管理权限;部署到clb需要clb相关权限\n前往[密钥管理](https://console.cloud.tencent.com/cam/capi)进行创建",component:{placeholder:"secretId"},rules:[{required:!0,message:"该项必填"}]}),b("design:type",Object)],W.prototype,"secretId",void 0),S([o({title:"secretKey",component:{placeholder:"secretKey"},encrypt:!0,rules:[{required:!0,message:"该项必填"}]}),b("design:type",Object)],W.prototype,"secretKey",void 0),W=S([a({name:"tencent",title:"腾讯云"})],W);let z=class extends w{region;clusterId;namespace;secretName;ingressName;ingressClass;clusterDomain;accessId;tencentCertId;cert;K8sClient;async onInstance(){const e=await import("@certd/lib-k8s");this.K8sClient=e.K8sClient}async execute(){const e=await this.accessService.getById(this.accessId),t=await this.getTkeClient(e,this.region),s=await this.getTkeKubeConfig(t,this.clusterId);this.logger.info("kubeconfig已成功获取");const n=new this.K8sClient({kubeConfigStr:s,logger:this.logger});"qcloud"===(this.ingressClass||"qcloud")?await this.patchQcloudCertSecret({k8sClient:n}):await this.patchNginxCertSecret({k8sClient:n}),await c.sleep(5e3),await this.restartIngress({k8sClient:n})}async getTkeClient(e,t="ap-guangzhou"){return new(0,(await import("tencentcloud-sdk-nodejs/tencentcloud/services/tke/v20180525/index.js")).v20180525.Client)({credential:{secretId:e.secretId,secretKey:e.secretKey},region:t,profile:{httpProfile:{endpoint:"tke.tencentcloudapi.com"}}})}async getTkeKubeConfig(e,t){const s={ClusterId:t},n=await e.DescribeClusterKubeconfig(s);return this.checkRet(n),this.logger.info("注意:后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster"),n.Kubeconfig}appendTimeSuffix(e){return null==e&&(e="certd"),e+"-"+f().format("YYYYMMDD-HHmmss")}async patchQcloudCertSecret(e){if(null==this.tencentCertId)throw new Error("请先将【上传证书到腾讯云】作为前置任务");this.logger.info("腾讯云证书ID:",this.tencentCertId);const t=Buffer.from(this.tencentCertId).toString("base64"),{namespace:s,secretName:n}=this,r={data:{qcloud_cert_id:t},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let i=n;"string"==typeof n&&(i=[n]);for(const t of i)await e.k8sClient.patchSecret({namespace:s,secretName:t,body:r}),this.logger.info(`CertSecret已更新:${t}`)}async patchNginxCertSecret(e){const{k8sClient:t}=e,{cert:s}=this,n=s.crt,r=s.key,i=Buffer.from(n).toString("base64"),o=Buffer.from(r).toString("base64"),{namespace:a,secretName:c}=this,l={data:{"tls.crt":i,"tls.key":o},metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let p=c;"string"==typeof c&&(p=[c]);for(const e of p)await t.patchSecret({namespace:a,secretName:e,body:l}),this.logger.info(`CertSecret已更新:${e}`)}async restartIngress(e){const{k8sClient:t}=e,{namespace:s,ingressName:n}=this,r={metadata:{labels:{certd:this.appendTimeSuffix("certd")}}};let i=this.ingressName;"string"==typeof n&&(i=[n]);for(const e of i)await t.patchIngress({namespace:s,ingressName:e,body:r}),this.logger.info(`ingress已重启:${e}`)}checkRet(e){if(!e||e.Error)throw new Error("执行失败:"+e.Error.Code+","+e.Error.Message)}};S([s({title:"大区",value:"ap-guangzhou",required:!0}),b("design:type",String)],z.prototype,"region",void 0),S([s({title:"集群ID",required:!0,desc:"例如:cls-6lbj1vee",request:!0}),b("design:type",String)],z.prototype,"clusterId",void 0),S([s({title:"集群namespace",value:"default",required:!0}),b("design:type",String)],z.prototype,"namespace",void 0),S([s({title:"证书的secret名称",required:!0}),b("design:type",Object)],z.prototype,"secretName",void 0),S([s({title:"ingress名称",required:!0}),b("design:type",Object)],z.prototype,"ingressName",void 0),S([s({title:"ingress类型",component:{name:"a-auto-complete",vModel:"value",options:[{value:"qcloud"},{value:"nginx"}]},helper:"可选 qcloud / nginx"}),b("design:type",String)],z.prototype,"ingressClass",void 0),S([s({title:"集群域名",helper:"可不填,默认为:[clusterId].ccs.tencent-cloud.com"}),b("design:type",String)],z.prototype,"clusterDomain",void 0),S([s({title:"Access授权",helper:"access授权",component:{name:"pi-access-selector",type:"tencent"},required:!0}),b("design:type",String)],z.prototype,"accessId",void 0),S([s({title:"腾讯云证书id",helper:"请选择“上传证书到腾讯云”前置任务的输出",component:{name:"pi-output-selector",from:"UploadCertToTencent"},mergeScript:'\n return {\n show: ctx.compute(({form})=>{\n return form.ingressClass === "qcloud"\n })\n }\n ',required:!0}),b("design:type",String)],z.prototype,"tencentCertId",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},mergeScript:'\n return {\n show: ctx.compute(({form})=>{\n return form.ingressClass === "nginx"\n })\n }\n ',required:!0}),b("design:type",Object)],z.prototype,"cert",void 0),z=S([n({name:"DeployCertToTencentTKEIngress",title:"部署到腾讯云TKE-ingress",needPlus:!0,icon:"svg:icon-tencentcloud",group:r.tencent.key,desc:"Qcloud类型需要【上传到腾讯云】作为前置任务;ApiServer未开启外网访问则需要做域名的内网IP映射",default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],z);let F=class extends w{instanceIdList;accessId;tencentCertId;async onInstance(){}async execute(){const e=await this.accessService.getById(this.accessId),t=new(0,(await import("tencentcloud-sdk-nodejs/tencentcloud/services/ssl/v20191205/index.js")).v20191205.Client)({credential:{secretId:e.secretId,secretKey:e.secretKey},region:"",profile:{httpProfile:{endpoint:"ssl.tencentcloudapi.com"}}}),s={CertificateId:this.tencentCertId,InstanceIdList:this.instanceIdList},n=await t.DeployCertificateInstance(s);this.checkRet(n),this.logger.info("部署成功")}checkRet(e){if(!e||e.Error)throw new Error("执行失败:"+e.Error.Code+","+e.Error.Message)}};S([s({title:"云资源实例Id列表",component:{name:"a-select",vModel:"value",open:!1,mode:"tags"},helper:""}),b("design:type",Array)],F.prototype,"instanceIdList",void 0),S([s({title:"Access授权",helper:"access授权",component:{name:"pi-access-selector",type:"tencent"},required:!0}),b("design:type",String)],F.prototype,"accessId",void 0),S([s({title:"腾讯云证书id",helper:"请选择“上传证书到腾讯云”前置任务的输出",component:{name:"pi-output-selector",from:"UploadCertToTencent"},required:!0}),b("design:type",String)],F.prototype,"tencentCertId",void 0),F=S([n({name:"DeployCertToTencentAllIngress",title:"部署证书到腾讯云任意云资源",needPlus:!0,icon:"svg:icon-tencentcloud",group:r.tencent.key,desc:"需要【上传到腾讯云】作为前置任务",default:{strategy:{runStrategy:i.SkipWhenSucceed}}})],F);let G=class extends w{accessId;sslIds;cert;access;async onInstance(){this.access=await this.getAccess(this.accessId)}async execute(){const e=this.sslIds;for(const t of e){const s=await this.doRequest({url:"/api/v1/websites/ssl/upload",method:"post",data:{sslIds:e,certificate:this.cert.crt,certificatePath:"",description:"",privateKey:this.cert.key,privateKeyPath:"",sslID:t,type:"paste"}});console.log("uploadRes",JSON.stringify(s))}}async doRequest(e){const t=await this.getAccessToken();return e.headers={PanelAuthorization:t},await this.doRequestWithoutAuth(e)}async doRequestWithoutAuth(e){e.baseURL=this.access.baseUrl;const t=await this.ctx.http.request(e);if(200===t.code)return t.data;throw new Error(t.message)}async getAccessToken(){console.log("getAccessToken",this);const e=`1panel-token-${this.accessId}`;let t=this.ctx.utils.cache.get(e);if(t)return t;return t=(await this.doRequestWithoutAuth({url:"/api/v1/auth/login",method:"post",headers:{EntranceCode:Buffer.from(this.access.safeEnter).toString("base64")},data:{name:this.access.username,password:this.access.password,ignoreCaptcha:!0,captcha:"",captchaID:"",authMethod:"jwt",language:"zh"}})).token,this.ctx.utils.cache.set(e,t),t}async onGetSSLIds(){return(await this.doRequest({url:"api/v1/websites/ssl/search",method:"post",data:{page:1,pageSize:99999}})).items.map((e=>({label:`${e.primaryDomain}<${e.id}>`,value:e.id})))}};S([s({title:"1Panel授权",helper:"1Panel授权",component:{name:"pi-access-selector",type:"1panel"},required:!0}),b("design:type",String)],G.prototype,"accessId",void 0),S([s({title:"1Panel证书ID",component:{name:"remote-select",vModel:"value",mode:"tags",type:"plugin",typeName:"1PanelDeployToWebsitePlugin",action:"GetSSLIds",watches:["accessId"]},mergeScript:"\n return {\n component:{\n form: ctx.compute(({form})=>{\n return form\n })\n }\n }\n ",helper:"要更新的1Panel证书id,选择授权之后,从下拉框中选择"}),b("design:type",Array)],G.prototype,"sslIds",void 0),S([s({title:"域名证书",helper:"请选择前置任务输出的域名证书",component:{name:"pi-output-selector",from:["CertApply","CertApplyLego"]},required:!0}),b("design:type",Object)],G.prototype,"cert",void 0),G=S([n({name:"1PanelDeployToWebsitePlugin",title:"部署证书到1Panel",icon:"material-symbols:shield-outline",group:r.panel.key,default:{strategy:{runStrategy:i.SkipWhenSucceed}},needPlus:!0})],G),new G;let Y=class{baseUrl="";safeEnter="";username="";password=""};S([o({title:"1Panel面板的url",component:{placeholder:"http://xxxx.com:1231"},required:!0}),b("design:type",Object)],Y.prototype,"baseUrl",void 0),S([o({title:"安全入口",component:{placeholder:"登录的安全入口"},encrypt:!0,required:!0}),b("design:type",Object)],Y.prototype,"safeEnter",void 0),S([o({title:"用户名",component:{placeholder:"username"},required:!0}),b("design:type",Object)],Y.prototype,"username",void 0),S([o({title:"密码",component:{placeholder:"password"},helper:"",required:!0,encrypt:!0}),b("design:type",Object)],Y.prototype,"password",void 0),Y=S([a({name:"1panel",title:"1panel授权",desc:"账号和密码"})],Y),new Y;export{w as AbstractPlusTaskPlugin,j as AliyunAccess,R as AliyunClient,q as BaotaAccess,k as BaotaDeployPanelCertPlugin,C as BaotaDeployWebSiteCertPlugin,O as CdnflyAccess,N as CdnflyDeployToCDNPlugin,D as DeployCertToAliyunAckPlugin,K as DeployCertToAliyunOSS,F as DeployCertToTencentTKEAllPlugin,z as DeployCertToTencentTKEIngressPlugin,x as FtpAccess,L as K8sAccess,U as K8sDeployToIngressPlugin,M as K8sDeployToSecretPlugin,Y as OnePanelAccess,G as OnePanelDeployToWebsitePlugin,_ as SynologyAccess,E as SynologyClient,B as SynologyDeployToPanel,W as TencentAccess,A as UploadCertToFTPPlugin,T as YidunAccess,P as YidunDeployToCDNPlugin,v as mustPlus};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 这个注解将注册一个授权配置
3
+ * 在certd的后台管理系统中,用户可以选择添加此类型的授权
4
+ */
5
+ export declare class OnePanelAccess {
6
+ baseUrl: string;
7
+ safeEnter: string;
8
+ username: string;
9
+ password: string;
10
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./plugins/index.js";
2
+ export * from "./access.js";
@@ -0,0 +1,16 @@
1
+ import { HttpRequestConfig } from "@certd/pipeline";
2
+ import { CertInfo } from "@certd/plugin-cert";
3
+ import { AbstractPlusTaskPlugin } from "../../lib/index.js";
4
+ import { OnePanelAccess } from "../access";
5
+ export declare class OnePanelDeployToWebsitePlugin extends AbstractPlusTaskPlugin {
6
+ accessId: string;
7
+ sslIds: string[];
8
+ cert: CertInfo;
9
+ access: OnePanelAccess;
10
+ onInstance(): Promise<void>;
11
+ execute(): Promise<void>;
12
+ doRequest(config: HttpRequestConfig<any>): Promise<any>;
13
+ doRequestWithoutAuth(config: HttpRequestConfig<any>): Promise<any>;
14
+ getAccessToken(): Promise<any>;
15
+ onGetSSLIds(): Promise<any>;
16
+ }
@@ -0,0 +1 @@
1
+ export * from "./deploy-to-website.js";
package/dist/d/index.d.ts CHANGED
@@ -7,3 +7,4 @@ export * from "./aliyun/index.js";
7
7
  export * from "./synology/index.js";
8
8
  export * from "./k8s/index.js";
9
9
  export * from "./tencent/index.js";
10
+ export * from "./1panel/index.js";
@@ -1,10 +1,9 @@
1
- import { RequestHandleContext } from "@certd/pipeline";
2
- import { RequestHandler } from "@certd/pipeline";
1
+ import { AccessRequestHandleContext, AccessRequestHandler } from "@certd/pipeline";
3
2
  /**
4
3
  * 这个注解将注册一个授权配置
5
4
  * 在certd的后台管理系统中,用户可以选择添加此类型的授权
6
5
  */
7
- export declare class SynologyAccess extends RequestHandler {
6
+ export declare class SynologyAccess extends AccessRequestHandler<SynologyAccess> {
8
7
  baseUrl: string;
9
8
  username: string;
10
9
  password: string;
@@ -12,5 +11,5 @@ export declare class SynologyAccess extends RequestHandler {
12
11
  deviceId: string;
13
12
  onLoginWithOPTCode(data: {
14
13
  otpCode: string;
15
- }, ctx: RequestHandleContext): Promise<import("./client").SynologyAccessToken>;
14
+ }, ctx: AccessRequestHandleContext): Promise<import("./client").SynologyAccessToken>;
16
15
  }
@@ -0,0 +1,12 @@
1
+ import { AbstractPlusTaskPlugin } from "../../../lib/index.js";
2
+ export declare class DeployCertToTencentTKEAllPlugin extends AbstractPlusTaskPlugin {
3
+ instanceIdList: string[];
4
+ /**
5
+ * AccessProvider的key,或者一个包含access的具体的对象
6
+ */
7
+ accessId: string;
8
+ tencentCertId: string;
9
+ onInstance(): Promise<void>;
10
+ execute(): Promise<void>;
11
+ checkRet(ret: any): void;
12
+ }
@@ -1 +1,2 @@
1
1
  export * from "./deploy-to-tke-ingress/index.js";
2
+ export * from "./deploy-to-all/index.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@certd/plugin-plus",
3
3
  "private": false,
4
- "version": "1.25.5",
4
+ "version": "1.25.6",
5
5
  "main": "./dist/bundle.mjs",
6
6
  "module": "./dist/bundle.mjs",
7
7
  "types": "./dist/d/index.d.ts",
@@ -15,9 +15,9 @@
15
15
  },
16
16
  "devDependencies": {
17
17
  "@alicloud/pop-core": "^1.7.10",
18
- "@certd/lib-k8s": "^1.25.5",
19
- "@certd/pipeline": "^1.25.5",
20
- "@certd/plugin-cert": "^1.25.5",
18
+ "@certd/lib-k8s": "^1.25.6",
19
+ "@certd/pipeline": "^1.25.6",
20
+ "@certd/plugin-cert": "^1.25.6",
21
21
  "@rollup/plugin-json": "^6.0.0",
22
22
  "@rollup/plugin-terser": "^0.4.3",
23
23
  "@rollup/plugin-typescript": "^11.0.0",
@@ -40,9 +40,9 @@
40
40
  "rollup": "^3.7.4",
41
41
  "ts-node": "^10.9.1",
42
42
  "tslib": "^2.5.2",
43
- "typescript": "^5.0.4"
43
+ "typescript": "^5.4.2"
44
44
  },
45
- "gitHead": "be13390b3a9177c9d99f1efabfc285d0c377b013",
45
+ "gitHead": "5668a3e2225059d9d2d236e698b020e272bba076",
46
46
  "dependencies": {
47
47
  "https-proxy-agent": "^7.0.5",
48
48
  "syno": "^2.2.0",