diff --git a/packages/ui/certd-server/src/plugins/index.ts b/packages/ui/certd-server/src/plugins/index.ts index c00a82601..3addaea37 100644 --- a/packages/ui/certd-server/src/plugins/index.ts +++ b/packages/ui/certd-server/src/plugins/index.ts @@ -45,4 +45,5 @@ // export * from './plugin-plus/index.js' // export * from './plugin-cert/index.js' // export * from './plugin-zenlayer/index.js' -export * from './plugin-dnsmgr/index.js' \ No newline at end of file +export * from './plugin-dnsmgr/index.js' +export * from './plugin-hipmdnsmgr/index.js' \ No newline at end of file diff --git a/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/access/hipmdnsmgr-access.ts b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/access/hipmdnsmgr-access.ts new file mode 100644 index 000000000..573f570be --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/access/hipmdnsmgr-access.ts @@ -0,0 +1,172 @@ +import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline'; + +/** + * HiPM DNSMgr Access + * 使用 API Token 认证(Bearer Token) + */ +@IsAccess({ + name: 'hipmdnsmgr', + title: 'HiPM DNSMgr', + icon: 'svg:icon-dns', + desc: 'HiPM DNSMgr API Token 授权', +}) +export class HipmDnsmgrAccess extends BaseAccess { + @AccessInput({ + title: '服务器地址', + component: { + name: 'a-input', + allowClear: true, + placeholder: 'http://localhost:3001', + }, + required: true, + helper: 'HiPM DNSMgr 服务器地址,例如: http://localhost:3001', + }) + endpoint = ''; + + @AccessInput({ + title: 'API Token', + required: true, + encrypt: true, + helper: '在 DNSMgr 设置 > API Token 中创建的令牌', + }) + apiToken = ''; + + @AccessInput({ + title: '测试连接', + component: { + name: 'api-test', + action: 'TestRequest', + }, + helper: '点击测试接口是否正常', + }) + testRequest = true; + + async onTestRequest() { + await this.getDomainList(); + return '连接成功'; + } + + /** + * 获取域名列表 + */ + async getDomainList() { + this.ctx.logger.info(`[HiPM DNSMgr] 获取域名列表`); + + const resp = await this.doRequest({ + method: 'GET', + path: '/domains', + params: { + page: 1, + pageSize: 100, + }, + }); + + // DNSMgr 返回数组格式 + return resp?.map((item: any) => ({ + id: String(item.id), + domain: item.name, + ...item, + })) || []; + } + + /** + * 获取域名记录列表 + */ + async getDomainRecords(domainId: string, params?: { type?: string; subdomain?: string; value?: string }) { + this.ctx.logger.info(`[HiPM DNSMgr] 获取域名记录列表:domainId=${domainId}`); + + let path = `/domains/${domainId}/records?page=1&pageSize=100`; + if (params?.type) path += `&type=${encodeURIComponent(params.type)}`; + if (params?.subdomain) path += `&subdomain=${encodeURIComponent(params.subdomain)}`; + if (params?.value) path += `&value=${encodeURIComponent(params.value)}`; + + const resp = await this.doRequest({ + method: 'GET', + path, + }); + + return resp; + } + + /** + * 创建 DNS 记录 + */ + async createDnsRecord(domainId: string, name: string, value: string, type: string) { + this.ctx.logger.info(`[HiPM DNSMgr] 创建DNS记录:${name} ${type} ${value}`); + + const resp = await this.doRequest({ + method: 'POST', + path: `/domains/${domainId}/records`, + data: { + name, + type, + value, + ttl: 600, + line: '0', + }, + }); + + return resp; + } + + /** + * 删除 DNS 记录 + */ + async deleteDnsRecord(domainId: string, recordId: string) { + this.ctx.logger.info(`[HiPM DNSMgr] 删除DNS记录:domainId=${domainId}, recordId=${recordId}`); + + const resp = await this.doRequest({ + method: 'DELETE', + path: `/domains/${domainId}/records/${recordId}`, + }); + + return resp; + } + + /** + * 发送 HTTP 请求 + */ + async doRequest(req: { method: string; path: string; data?: any; params?: any }) { + // 处理 URL + let baseUrl = this.endpoint.trim(); + baseUrl = baseUrl.replace(/\/$/, ''); + baseUrl = baseUrl.replace(/\/api$/, ''); + + let url = `${baseUrl}/api${req.path}`; + + // 添加查询参数 + if (req.params) { + const searchParams = new URLSearchParams(); + for (const [key, value] of Object.entries(req.params)) { + if (value !== undefined && value !== null && value !== '') { + searchParams.append(key, String(value)); + } + } + const queryString = searchParams.toString(); + if (queryString) { + url += `?${queryString}`; + } + } + + this.ctx.logger.debug(`[HiPM DNSMgr] 请求: ${req.method} ${url}`); + + const res = await this.ctx.http.request({ + url, + method: req.method, + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.apiToken}`, + }, + data: req.data, + }); + + this.ctx.logger.debug(`[HiPM DNSMgr] 响应:`, res); + + // DNSMgr API 返回格式: { code: 0, data: ..., msg: ... } + if (res.code !== undefined && res.code !== 0) { + throw new Error(res.msg || '请求失败'); + } + + return res.data; + } +} diff --git a/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/access/index.ts b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/access/index.ts new file mode 100644 index 000000000..2ef8fb7fb --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/access/index.ts @@ -0,0 +1 @@ +export * from './hipmdnsmgr-access.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/dns-provider/hipmdnsmgr-dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/dns-provider/hipmdnsmgr-dns-provider.ts new file mode 100644 index 000000000..6a6c79135 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/dns-provider/hipmdnsmgr-dns-provider.ts @@ -0,0 +1,77 @@ +import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert'; +import { HipmDnsmgrAccess } from '../access/hipmdnsmgr-access.js'; + +/** + * HiPM DNSMgr DNS Provider + * 用于 ACME DNS-01 挑战验证 + */ +@IsDnsProvider({ + name: 'hipmdnsmgr', + title: 'HiPM DNSMgr', + desc: 'HiPM DNSMgr DNS 解析提供商', + accessType: 'hipmdnsmgr', + icon: 'svg:icon-dns', +}) +export class HipmDnsmgrDnsProvider extends AbstractDnsProvider<{ domainId: string; recordId: string; name: string; value: string }> { + access!: HipmDnsmgrAccess; + + async onInstance() { + this.access = this.ctx.access as HipmDnsmgrAccess; + this.logger.debug('[HiPM DNSMgr] 初始化完成'); + } + + /** + * 创建 DNS 记录(用于 ACME DNS-01 验证) + */ + async createRecord(options: CreateRecordOptions): Promise { + const { fullRecord, hostRecord, value, type, domain } = options; + this.logger.info('[HiPM DNSMgr] 添加域名解析:', fullRecord, value, type, domain); + + // 1. 获取域名列表,找到对应的域名 ID + const domainList = await this.access.getDomainList(); + const domainInfo = domainList.find((item: any) => item.domain === domain); + + if (!domainInfo) { + throw new Error(`[HiPM DNSMgr] 未找到域名:${domain}`); + } + + const domainId = String(domainInfo.id); + this.logger.debug('[HiPM DNSMgr] 找到域名:', domain, 'ID:', domainId); + + // 2. 创建 DNS 记录 + const name = hostRecord; // 使用子域名,如 _acme-challenge + const res = await this.access.createDnsRecord(domainId, name, value, type); + + this.logger.info('[HiPM DNSMgr] 添加域名解析成功:', JSON.stringify(options), res?.id); + + // 返回记录信息,用于后续删除 + return { + domainId, + recordId: res?.id, + name, + value, + }; + } + + /** + * 删除 DNS 记录(ACME 验证完成后清理) + */ + async removeRecord(options: RemoveRecordOptions<{ domainId: string; recordId: string; name: string; value: string }>): Promise { + const { fullRecord, value } = options.recordReq; + const record = options.recordRes; + + this.logger.info('[HiPM DNSMgr] 删除域名解析:', fullRecord, value, record); + + if (record && record.domainId && record.recordId) { + try { + await this.access.deleteDnsRecord(record.domainId, record.recordId); + this.logger.info('[HiPM DNSMgr] 删除域名解析成功:', fullRecord, value); + } catch (e: any) { + // 记录可能已经被删除,忽略错误 + this.logger.warn('[HiPM DNSMgr] 删除域名解析失败(可能已不存在):', e.message); + } + } else { + this.logger.warn('[HiPM DNSMgr] 无法删除记录,缺少 domainId 或 recordId'); + } + } +} diff --git a/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/dns-provider/index.ts b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/dns-provider/index.ts new file mode 100644 index 000000000..9282058db --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/dns-provider/index.ts @@ -0,0 +1 @@ +export * from './hipmdnsmgr-dns-provider.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/index.ts b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/index.ts new file mode 100644 index 000000000..e5dd343ce --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-hipmdnsmgr/index.ts @@ -0,0 +1,2 @@ +export * from './access/index.js'; +export * from './dns-provider/index.js';