diff --git a/packages/ui/certd-server/src/plugins/index.ts b/packages/ui/certd-server/src/plugins/index.ts index ea1e0dd5..b8da818a 100644 --- a/packages/ui/certd-server/src/plugins/index.ts +++ b/packages/ui/certd-server/src/plugins/index.ts @@ -10,3 +10,5 @@ export * from './plugin-west/index.js'; export * from './plugin-doge/index.js'; export * from './plugin-qiniu/index.js'; export * from './plugin-woai/index.js'; +export * from './plugin-cachefly/index.js'; +export * from './plugin-gcore/index.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-cachefly/access.ts b/packages/ui/certd-server/src/plugins/plugin-cachefly/access.ts new file mode 100644 index 00000000..fa4cb844 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-cachefly/access.ts @@ -0,0 +1,36 @@ +import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline'; + +@IsAccess({ + name: 'CacheFly', + title: 'CacheFly', + desc: 'CacheFly', +}) +export class CacheflyAccess extends BaseAccess { + @AccessInput({ + title: 'username', + component: { + placeholder: 'username', + }, + required: true, + }) + username = ''; + @AccessInput({ + title: 'password', + component: { + placeholder: 'password', + }, + required: true, + encrypt: true, + }) + password = ''; + @AccessInput({ + title: 'totp key', + component: { + placeholder: 'totp key', + }, + encrypt: true, + }) + otpkey = ''; +} + +new CacheflyAccess(); diff --git a/packages/ui/certd-server/src/plugins/plugin-cachefly/index.ts b/packages/ui/certd-server/src/plugins/plugin-cachefly/index.ts new file mode 100644 index 00000000..fdad254f --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-cachefly/index.ts @@ -0,0 +1,2 @@ +export * from './plugins/index.js'; +export * from './access.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-cachefly/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-cachefly/plugins/index.ts new file mode 100644 index 00000000..cd1308a9 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-cachefly/plugins/index.ts @@ -0,0 +1 @@ +export * from './plugin-deploy-to-cdn.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-cachefly/plugins/plugin-deploy-to-cdn.ts b/packages/ui/certd-server/src/plugins/plugin-cachefly/plugins/plugin-deploy-to-cdn.ts new file mode 100644 index 00000000..47632d32 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-cachefly/plugins/plugin-deploy-to-cdn.ts @@ -0,0 +1,93 @@ +import { AbstractTaskPlugin, HttpClient, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { CertInfo } from '@certd/plugin-cert'; +import { CacheflyAccess } from '../access.js'; + +@IsTaskPlugin({ + name: 'CacheFly', + title: '部署证书到 CacheFly', + desc: '部署证书到 CacheFly', + icon: 'clarity:plugin-line', + group: pluginGroups.cdn.key, + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class CacheFlyPlugin extends AbstractTaskPlugin { + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: ['CertApply', 'CertApplyLego'], + }, + required: true, + }) + cert!: CertInfo; + @TaskInput({ + title: 'Access授权', + helper: 'CacheFly 的授权', + component: { + name: 'access-selector', + type: 'CacheFly', + }, + required: true, + }) + accessId!: string; + http!: HttpClient; + private readonly baseApi = 'https://api.cachefly.com'; + + async onInstance() { + this.http = this.ctx.http; + } + + private async doRequestApi(url: string, data: any = null, method = 'post', token: string | null = null) { + const headers = { + 'Content-Type': 'application/json', + ...(token ? { 'x-cf-authorization': `Bearer ${token}` } : {}), + }; + const res = await this.http.request({ + url, + method, + data, + headers, + }); + + return res; + } + + async execute(): Promise { + const { cert, accessId } = this; + const access = (await this.accessService.getById(accessId)) as CacheflyAccess; + let otp = null; + if (access.otpkey) { + const response = await this.http.request({ + url: `https://cn-api.my-api.cn/api/totp/?key=${access.otpkey}`, + method: 'get', + }); + otp = response; + this.logger.info('获取到otp:', otp); + } + const loginResponse = await this.doRequestApi(`${this.baseApi}/api/2.6/auth/login`, { + username: access.username, + password: access.password, + ...(otp && { otp }), + }); + const token = loginResponse.token; + this.logger.info('Token 获取成功'); + // 更新证书 + await this.doRequestApi( + `${this.baseApi}/api/2.6/certificates`, + { + certificate: cert.crt, + certificateKey: cert.key, + }, + 'post', + token + ); + this.logger.info('证书更新成功'); + } +} + +new CacheFlyPlugin(); diff --git a/packages/ui/certd-server/src/plugins/plugin-gcore/access.ts b/packages/ui/certd-server/src/plugins/plugin-gcore/access.ts new file mode 100644 index 00000000..6ff07f44 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-gcore/access.ts @@ -0,0 +1,36 @@ +import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline'; + +@IsAccess({ + name: 'Gcore', + title: 'Gcore', + desc: 'Gcore', +}) +export class GcoreAccess extends BaseAccess { + @AccessInput({ + title: 'username', + component: { + placeholder: 'username', + }, + required: true, + }) + username = ''; + @AccessInput({ + title: 'password', + component: { + placeholder: 'password', + }, + required: true, + encrypt: true, + }) + password = ''; + @AccessInput({ + title: 'totp key', + component: { + placeholder: 'totp key', + }, + encrypt: true, + }) + otpkey = ''; +} + +new GcoreAccess(); diff --git a/packages/ui/certd-server/src/plugins/plugin-gcore/index.ts b/packages/ui/certd-server/src/plugins/plugin-gcore/index.ts new file mode 100644 index 00000000..fdad254f --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-gcore/index.ts @@ -0,0 +1,2 @@ +export * from './plugins/index.js'; +export * from './access.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/index.ts new file mode 100644 index 00000000..aae7f9dd --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/index.ts @@ -0,0 +1,2 @@ +export * from './plugin-upload.js'; +export * from './plugin-flush.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/plugin-flush.ts b/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/plugin-flush.ts new file mode 100644 index 00000000..9c196a43 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/plugin-flush.ts @@ -0,0 +1,105 @@ +import { AbstractTaskPlugin, HttpClient, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { CertInfo } from '@certd/plugin-cert'; +import { GcoreAccess } from '../access.js'; + +@IsTaskPlugin({ + name: 'Gcoreflush', + title: '刷新证书 Gcore', + desc: '刷新现有的证书', + icon: 'clarity:plugin-line', + group: pluginGroups.cdn.key, + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class GcoreflushPlugin extends AbstractTaskPlugin { + @TaskInput({ + title: '证书名称', + helper: '可以修改也可以和现在的保留一致', + }) + certName!: string; + @TaskInput({ + title: '证书ID', + }) + ssl_id!: string; + + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: ['CertApply', 'CertApplyLego'], + }, + required: true, + }) + cert!: CertInfo; + @TaskInput({ + title: 'Access授权', + helper: 'Gcore', + component: { + name: 'access-selector', + type: 'Gcore', + }, + required: true, + }) + accessId!: string; + http!: HttpClient; + private readonly baseApi = 'https://api.gcore.com'; + + async onInstance() { + this.http = this.ctx.http; + } + + private async doRequestApi(url: string, data: any = null, method = 'post', token: string | null = null) { + const headers = { + 'Content-Type': 'application/json', + ...(token ? { 'authorization': `Bearer ${token}` } : {}), + }; + const res = await this.http.request({ + url, + method, + data, + headers, + }); + + return res; + } + + async execute(): Promise { + const { cert,accessId } = this; + const access = (await this.accessService.getById(accessId)) as GcoreAccess; + let otp = null; + if (access.otpkey) { + const response = await this.http.request({ + url: `https://cn-api.my-api.cn/api/totp/?key=${access.otpkey}`, + method: 'get', + }); + otp = response; + this.logger.info('获取到otp:', otp); + } + const loginResponse = await this.doRequestApi(`${this.baseApi}/iam/auth/jwt/login`, { + username: access.username, + password: access.password, + ...(otp && { otp }), + }); + const token = loginResponse.access; + this.logger.info('Token 获取成功'); + this.logger.info('开始上传证书'); + await this.doRequestApi( + `${this.baseApi}/cdn/sslData/${this.ssl_id}`, + { + name: this.certName, + sslCertificate: cert.crt, + sslPrivateKey: cert.key, + validate_root_ca: true, + }, + 'put', + token + ); + this.logger.info('证书部署成功'); + } +} + +new GcoreflushPlugin(); diff --git a/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/plugin-upload.ts b/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/plugin-upload.ts new file mode 100644 index 00000000..c3fdf540 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-gcore/plugins/plugin-upload.ts @@ -0,0 +1,101 @@ +import { AbstractTaskPlugin, HttpClient, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { CertInfo } from '@certd/plugin-cert'; +import { GcoreAccess } from '../access.js'; + +@IsTaskPlugin({ + name: 'Gcoreupload', + title: '部署证书到 Gcore', + desc: '仅上传 并不会部署到cdn', + icon: 'clarity:plugin-line', + group: pluginGroups.cdn.key, + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class GcoreuploadPlugin extends AbstractTaskPlugin { + @TaskInput({ + title: '证书名称', + helper: '作为备注', + }) + certName!: string; + + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: ['CertApply', 'CertApplyLego'], + }, + required: true, + }) + cert!: CertInfo; + @TaskInput({ + title: 'Access授权', + helper: 'Gcore', + component: { + name: 'access-selector', + type: 'Gcore', + }, + required: true, + }) + accessId!: string; + http!: HttpClient; + private readonly baseApi = 'https://api.gcore.com'; + + async onInstance() { + this.http = this.ctx.http; + } + + private async doRequestApi(url: string, data: any = null, method = 'post', token: string | null = null) { + const headers = { + 'Content-Type': 'application/json', + ...(token ? { 'authorization': `Bearer ${token}` } : {}), + }; + const res = await this.http.request({ + url, + method, + data, + headers, + }); + + return res; + } + + async execute(): Promise { + const { cert,accessId } = this; + const access = (await this.accessService.getById(accessId)) as GcoreAccess; + let otp = null; + if (access.otpkey) { + const response = await this.http.request({ + url: `https://cn-api.my-api.cn/api/totp/?key=${access.otpkey}`, + method: 'get', + }); + otp = response; + this.logger.info('获取到otp:', otp); + } + const loginResponse = await this.doRequestApi(`${this.baseApi}/iam/auth/jwt/login`, { + username: access.username, + password: access.password, + ...(otp && { otp }), + }); + const token = loginResponse.access; + this.logger.info('Token 获取成功'); + this.logger.info('开始上传证书'); + await this.doRequestApi( + `${this.baseApi}/cdn/sslData`, + { + name: this.certName, + sslCertificate: cert.crt, + sslPrivateKey: cert.key, + validate_root_ca: true, + }, + 'post', + token + ); + this.logger.info('证书上传成功'); + } +} + +new GcoreuploadPlugin();