diff --git a/packages/core/basic/src/utils/util.request.ts b/packages/core/basic/src/utils/util.request.ts
index 8a363e84..983b1e39 100644
--- a/packages/core/basic/src/utils/util.request.ts
+++ b/packages/core/basic/src/utils/util.request.ts
@@ -1,13 +1,13 @@
-import axios, { AxiosHeaders, AxiosRequestConfig } from 'axios';
-import { ILogger, logger } from './util.log.js';
-import { Logger } from 'log4js';
-import { HttpProxyAgent } from 'http-proxy-agent';
-import { HttpsProxyAgent } from 'https-proxy-agent';
-import nodeHttp from 'http';
-import * as https from 'node:https';
-import { merge } from 'lodash-es';
-import { safePromise } from './util.promise.js';
-import fs from 'fs';
+import axios, { AxiosHeaders, AxiosRequestConfig } from "axios";
+import { ILogger, logger } from "./util.log.js";
+import { Logger } from "log4js";
+import { HttpProxyAgent } from "http-proxy-agent";
+import { HttpsProxyAgent } from "https-proxy-agent";
+import nodeHttp from "http";
+import * as https from "node:https";
+import { merge } from "lodash-es";
+import { safePromise } from "./util.promise.js";
+import fs from "fs";
export class HttpError extends Error {
status?: number;
statusText?: string;
@@ -22,10 +22,10 @@ export class HttpError extends Error {
super(error.message || error.response?.statusText);
const message = error?.message;
- if (message && typeof message === 'string') {
- if (message.indexOf && message.indexOf('ssl3_get_record:wrong version number') >= 0) {
+ if (message && typeof message === "string") {
+ if (message.indexOf && message.indexOf("ssl3_get_record:wrong version number") >= 0) {
this.message = `${message}(http协议错误,服务端要求http协议,请检查是否使用了https请求)`;
- } else if (message.indexOf('getaddrinfo EAI_AGAIN') >= 0) {
+ } else if (message.indexOf("getaddrinfo EAI_AGAIN") >= 0) {
this.message = `${message}(无法解析域名,请检查网络连接或dns配置,更换docker-compose.yaml中dns配置)`;
}
}
@@ -47,7 +47,7 @@ export class HttpError extends Error {
};
let url = error.config?.url;
if (error.config?.baseURL) {
- url = (error.config?.baseURL || '') + url;
+ url = (error.config?.baseURL || "") + url;
}
if (url) {
this.message = `${this.message} 【${url}】`;
@@ -73,7 +73,7 @@ export const HttpCommonError = HttpError;
let defaultAgents = createAgent();
export function setGlobalProxy(opts: { httpProxy?: string; httpsProxy?: string }) {
- logger.info('setGlobalProxy:', opts);
+ logger.info("setGlobalProxy:", opts);
defaultAgents = createAgent(opts);
}
@@ -102,12 +102,12 @@ export function createAxiosService({ logger }: { logger: Logger }) {
if (config.skipSslVerify || config.httpProxy) {
let rejectUnauthorized = true;
if (config.skipSslVerify) {
- logger.info('跳过SSL验证');
+ logger.info("跳过SSL验证");
rejectUnauthorized = false;
}
const proxy: any = {};
if (config.httpProxy) {
- logger.info('使用自定义http代理:', config.httpProxy);
+ logger.info("使用自定义http代理:", config.httpProxy);
proxy.httpProxy = config.httpProxy;
proxy.httpsProxy = config.httpProxy;
}
@@ -128,7 +128,7 @@ export function createAxiosService({ logger }: { logger: Logger }) {
},
(error: Error) => {
// 发送失败
- logger.error('接口请求失败:', error);
+ logger.error("接口请求失败:", error);
return Promise.reject(error);
}
);
@@ -143,7 +143,7 @@ export function createAxiosService({ logger }: { logger: Logger }) {
logger.info(`http response : status=${response?.status},data=${resData}`);
} else {
- logger.info('http response status:', response?.status);
+ logger.info("http response status:", response?.status);
}
if (response?.config?.returnResponse) {
return response;
@@ -154,53 +154,51 @@ export function createAxiosService({ logger }: { logger: Logger }) {
const status = error.response?.status;
switch (status) {
case 400:
- error.message = '请求错误';
+ error.message = "请求错误";
break;
case 401:
- error.message = '认证/登录失败';
+ error.message = "认证/登录失败";
break;
case 403:
- error.message = '拒绝访问';
+ error.message = "拒绝访问";
break;
case 404:
error.message = `请求地址出错`;
break;
case 408:
- error.message = '请求超时';
+ error.message = "请求超时";
break;
case 500:
- error.message = '服务器内部错误';
+ error.message = "服务器内部错误";
break;
case 501:
- error.message = '服务未实现';
+ error.message = "服务未实现";
break;
case 502:
- error.message = '网关错误';
+ error.message = "网关错误";
break;
case 503:
- error.message = '服务不可用';
+ error.message = "服务不可用";
break;
case 504:
- error.message = '网关超时';
+ error.message = "网关超时";
break;
case 505:
- error.message = 'HTTP版本不受支持';
+ error.message = "HTTP版本不受支持";
break;
default:
break;
}
- logger.error(
- `请求出错:status:${error.response?.status},statusText:${error.response?.statusText},url:${error.config?.url},method:${error.config?.method}。`
- );
- logger.error('返回数据:', JSON.stringify(error.response?.data));
+ logger.error(`请求出错:status:${error.response?.status},statusText:${error.response?.statusText},url:${error.config?.url},method:${error.config?.method}。`);
+ logger.error("返回数据:", JSON.stringify(error.response?.data));
if (error.response?.data) {
const message = error.response.data.message || error.response.data.msg || error.response.data.error;
- if (typeof message === 'string') {
+ if (typeof message === "string") {
error.message = message;
}
}
if (error instanceof AggregateError) {
- logger.error('AggregateError', error);
+ logger.error("AggregateError", error);
}
const err = new HttpError(error);
return Promise.reject(err);
@@ -244,24 +242,24 @@ export function createAgent(opts: CreateAgentOptions = {}) {
if (httpProxy) {
process.env.HTTP_PROXY = httpProxy;
process.env.http_proxy = httpProxy;
- logger.info('use httpProxy:', httpProxy);
+ logger.info("use httpProxy:", httpProxy);
httpAgent = new HttpProxyAgent(httpProxy, opts as any);
merge(httpAgent.options, opts);
} else {
- process.env.HTTP_PROXY = '';
- process.env.http_proxy = '';
+ process.env.HTTP_PROXY = "";
+ process.env.http_proxy = "";
httpAgent = new nodeHttp.Agent(opts);
}
const httpsProxy = opts.httpsProxy;
if (httpsProxy) {
process.env.HTTPS_PROXY = httpsProxy;
process.env.https_proxy = httpsProxy;
- logger.info('use httpsProxy:', httpsProxy);
+ logger.info("use httpsProxy:", httpsProxy);
httpsAgent = new HttpsProxyAgent(httpsProxy, opts as any);
merge(httpsAgent.options, opts);
} else {
- process.env.HTTPS_PROXY = '';
- process.env.https_proxy = '';
+ process.env.HTTPS_PROXY = "";
+ process.env.https_proxy = "";
httpsAgent = new https.Agent(opts);
}
return {
@@ -276,27 +274,27 @@ export async function download(req: { http: HttpClient; config: HttpRequestConfi
http
.request({
logRes: false,
- responseType: 'stream',
+ responseType: "stream",
...config,
})
.then(res => {
const writer = fs.createWriteStream(savePath);
res.pipe(writer);
- writer.on('close', () => {
- logger.info('文件下载成功');
+ writer.on("close", () => {
+ logger.info("文件下载成功");
resolve(true);
});
//error
- writer.on('error', err => {
- logger.error('下载失败', err);
+ writer.on("error", err => {
+ logger.error("下载失败", err);
reject(err);
});
//进度条打印
- const totalLength = res.headers['content-length'];
+ const totalLength = res.headers["content-length"];
let currentLength = 0;
// 每5%打印一次
const step = (totalLength / 100) * 5;
- res.on('data', (chunk: any) => {
+ res.on("data", (chunk: any) => {
currentLength += chunk.length;
if (currentLength % step < chunk.length) {
const percent = ((currentLength / totalLength) * 100).toFixed(2);
@@ -305,19 +303,19 @@ export async function download(req: { http: HttpClient; config: HttpRequestConfi
});
})
.catch(err => {
- logger.info('下载失败', err);
+ logger.info("下载失败", err);
reject(err);
});
});
}
export function getCookie(response: any, name: string) {
- const cookies = response.headers['set-cookie'];
+ const cookies = response.headers["set-cookie"];
//根据name 返回对应的cookie
const found = cookies.find((cookie: any) => cookie.includes(name));
if (!found) {
return null;
}
- const cookie = found.split(';')[0];
- return cookie.substring(cookie.indexOf('=') + 1);
+ const cookie = found.split(";")[0];
+ return cookie.substring(cookie.indexOf("=") + 1);
}
diff --git a/packages/plugins/plugin-lib/src/ssh/ssh-access.ts b/packages/plugins/plugin-lib/src/ssh/ssh-access.ts
index ecf75519..2247b9bf 100644
--- a/packages/plugins/plugin-lib/src/ssh/ssh-access.ts
+++ b/packages/plugins/plugin-lib/src/ssh/ssh-access.ts
@@ -63,15 +63,15 @@ export class SshAccess extends BaseAccess {
})
passphrase!: string;
- // @AccessInput({
- // title: "伪终端",
- // helper: "如果登录报错:all authentication methods failed,可以尝试开启伪终端模式进行keyboard-interactive方式登录",
- // component: {
- // name: "a-switch",
- // vModel: "checked",
- // },
- // })
- // pty!: boolean;
+ @AccessInput({
+ title: "伪终端",
+ helper: "如果登录报错:all authentication methods failed,可以尝试开启伪终端模式进行keyboard-interactive方式登录\n开启后对日志输出有一定的影响",
+ component: {
+ name: "a-switch",
+ vModel: "checked",
+ },
+ })
+ pty!: boolean;
@AccessInput({
title: "socks代理",
diff --git a/packages/plugins/plugin-lib/src/ssh/ssh.ts b/packages/plugins/plugin-lib/src/ssh/ssh.ts
index b6322c47..1648c1e4 100644
--- a/packages/plugins/plugin-lib/src/ssh/ssh.ts
+++ b/packages/plugins/plugin-lib/src/ssh/ssh.ts
@@ -172,7 +172,7 @@ export class AsyncSsh2Client {
this.logger.info(`执行命令:[${this.connConf.host}][exec]: \n` + script);
// pty 伪终端,window下的输出会带上conhost.exe之类的多余的字符串,影响返回结果判断
// linux下 当使用keyboard-interactive 登录时,需要pty
- const pty = !this.connConf.windows //linux下开启伪终端,windows下不开启
+ const pty = this.connConf.pty; //linux下开启伪终端,windows下不开启
this.conn.exec(script, { pty, env: opts.env }, (err: Error, stream: any) => {
if (err) {
reject(err);
diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json
index 3cbcb858..b53d43ca 100644
--- a/packages/ui/certd-server/package.json
+++ b/packages/ui/certd-server/package.json
@@ -76,6 +76,7 @@
"cos-nodejs-sdk-v5": "^2.14.6",
"cron-parser": "^4.9.0",
"cross-env": "^7.0.3",
+ "crypto-js": "^4.2.0",
"dayjs": "^1.11.7",
"form-data": "^4.0.0",
"glob": "^11.0.0",
diff --git a/packages/ui/certd-server/src/plugins/index.ts b/packages/ui/certd-server/src/plugins/index.ts
index 587c33cd..594dd56e 100644
--- a/packages/ui/certd-server/src/plugins/index.ts
+++ b/packages/ui/certd-server/src/plugins/index.ts
@@ -18,3 +18,4 @@ export * from './plugin-dnsla/index.js';
export * from './plugin-upyun/index.js';
export * from './plugin-volcengine/index.js'
export * from './plugin-jdcloud/index.js'
+export * from './plugin-51dns/index.js'
diff --git a/packages/ui/certd-server/src/plugins/plugin-51dns/access.ts b/packages/ui/certd-server/src/plugins/plugin-51dns/access.ts
new file mode 100644
index 00000000..b665483e
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-51dns/access.ts
@@ -0,0 +1,40 @@
+import { IsAccess, AccessInput, BaseAccess } from '@certd/pipeline';
+
+/**
+ * 这个注解将注册一个授权配置
+ * 在certd的后台管理系统中,用户可以选择添加此类型的授权
+ */
+@IsAccess({
+ name: '51dns',
+ title: '51dns授权',
+ icon: 'arcticons:dns-changer-3',
+ desc: '',
+})
+export class Dns51Access extends BaseAccess {
+ /**
+ * 授权属性配置
+ */
+ @AccessInput({
+ title: '用户名',
+ component: {
+ placeholder: '用户名或手机号',
+ },
+ required: true,
+ encrypt: false,
+ })
+ username = '';
+
+ @AccessInput({
+ title: '登录密码',
+ component: {
+ name:"a-input-password",
+ vModel:"value",
+ placeholder: '密码',
+ },
+ required: true,
+ encrypt: true,
+ })
+ password = '';
+}
+
+new Dns51Access();
diff --git a/packages/ui/certd-server/src/plugins/plugin-51dns/client.test.mjs b/packages/ui/certd-server/src/plugins/plugin-51dns/client.test.mjs
new file mode 100644
index 00000000..e068ff6e
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-51dns/client.test.mjs
@@ -0,0 +1,136 @@
+import CryptoJS from 'crypto-js'
+
+function aes(val) {
+ var k = CryptoJS.enc.Utf8.parse('1234567890abcDEF');
+ var iv = CryptoJS.enc.Utf8.parse('1234567890abcDEF');
+ const enc = CryptoJS.AES.encrypt(val, k, {
+ iv: iv,
+ mode: CryptoJS.mode.CBC,
+ padding: CryptoJS.pad.ZeroPadding
+ }).toString();
+ return enc;
+}
+
+
+import axios from 'axios'
+
+const instance = axios.create({
+ baseURL: 'https://www.51dns.com',
+ timeout: 5000,
+ withCredentials: true,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ }
+})
+
+async function login() {
+
+ const res = await instance.request({
+ url: 'https://www.51dns.com/login.html',
+ method: 'get',
+ headers: {
+ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36',
+ 'Origin': 'https://www.51dns.com',
+ 'Referer': 'https://www.51dns.com',
+ }
+ })
+
+ //提取 var csrfToken = "ieOfM21eDd9nWJv3OZtMJF6ogDsnPKQHJ17dlMck";
+ const _token = res.data.match(/var csrfToken = "(.*?)"/)[1]
+ console.log(_token)
+ console.log(res.headers)
+
+
+ const setCookie = res.headers['set-cookie']
+ const cookie = setCookie.map(item => {
+ return item.split(';')[0]
+ }).join(';')
+
+
+ var obj = {
+ 'email_or_phone': aes(""),
+ 'password': aes(""),
+ 'type': aes('account'),
+ 'redirectTo': 'https://www.51dns.com/domain',
+ '_token': _token
+ }
+ console.log(JSON.stringify(obj, null, 2))
+ const res2 = await instance.request({
+ url: 'https://www.51dns.com/login',
+ method: 'post',
+ data: {
+ ...obj
+ },
+ headers: {
+ /**
+ * Origin:
+ * https://www.51dns.com
+ * Referer:
+ * https://www.51dns.com/login.html
+ * User-Agent:
+ * Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36
+ // __root_domain_v=.51dns.com;
+ */
+
+ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36',
+ 'Origin': 'https://www.51dns.com',
+ 'Referer': 'https://www.51dns.com/login.html',
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Cookie': cookie,
+ //X-Requested-With:
+ // XMLHttpRequest
+ 'X-Requested-With': 'XMLHttpRequest'
+ }
+ })
+
+ console.log(res2.headers)
+ if (res2.data.code == 0) {
+ console.log("登录成功")
+ }
+
+ const setCookie2 = res2.headers['set-cookie']
+ const cookie2 = setCookie2.map(item => {
+ return item.split(';')[0]
+ }).join(';')
+
+ //
+ // // console.log(res2.data)
+ // // 提取 182****43522
+ // console.log(res2.data.match(/(.*?)<\/span>/)[1])
+ // const success1 = res2.data.includes('DNS解析')
+ // console.log("success", success1)
+
+
+ const res3 = await instance.request({
+ url: 'https://www.51dns.com/domain',
+ method: 'get',
+ withCredentials: true,
+ headers: {
+ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36',
+ 'Origin': 'https://www.51dns.com',
+ 'Referer': 'https://www.51dns.com/login.html',
+ 'Cookie': cookie2,
+ }
+ })
+
+ console.log(res3.statusText)
+ console.log(res3.headers)
+ const success2 = res3.data.includes('DNS解析')
+ console.log("success", success2)
+
+
+ /**
+ * certd.top
+
+ */
+ //上面文本中间有换行,需要提取 193341603 部分,必须有certd.top,使用 new Regexp, .号要能匹配换行符,非贪婪模式
+ const regExp = new RegExp(']*>certd\\.top<\\/a>',"i");
+
+ const domainId = res3.data.match(regExp)[1]
+
+
+ console.log("domainId", domainId)
+}
+
+login()
diff --git a/packages/ui/certd-server/src/plugins/plugin-51dns/client.ts b/packages/ui/certd-server/src/plugins/plugin-51dns/client.ts
new file mode 100644
index 00000000..39cd86a5
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-51dns/client.ts
@@ -0,0 +1,237 @@
+import {createAxiosService, HttpClient, ILogger} from "@certd/basic";
+import {Dns51Access} from "./access.js";
+
+export class Dns51Client {
+ logger: ILogger;
+ access: Dns51Access;
+ http: HttpClient;
+ cryptoJs: any;
+ isLogined = false;
+ _token = "";
+ _cookie = "";
+
+ constructor(options: {
+ logger: ILogger;
+ access: Dns51Access;
+ }) {
+ this.logger = options.logger;
+ this.access = options.access;
+
+ this.http = createAxiosService({
+ logger: this.logger
+ });
+
+ }
+
+
+ aes(val: string) {
+ if (!this.cryptoJs) {
+ throw new Error("crypto-js not init");
+ }
+ const CryptoJS = this.cryptoJs;
+ var k = CryptoJS.enc.Utf8.parse("1234567890abcDEF");
+ var iv = CryptoJS.enc.Utf8.parse("1234567890abcDEF");
+ return CryptoJS.AES.encrypt(val, k, {
+ iv: iv,
+ mode: CryptoJS.mode.CBC,
+ padding: CryptoJS.pad.ZeroPadding
+ }).toString();
+ }
+
+
+ async init() {
+ if (this.cryptoJs) {
+ return;
+ }
+ const CryptoJSModule = await import("crypto-js");
+ this.cryptoJs = CryptoJSModule.default;
+
+ }
+
+ async login() {
+ if (this.isLogined) {
+ return;
+ }
+ await this.init();
+ const res = await this.http.request({
+ url: "https://www.51dns.com/login.html",
+ method: "get",
+ withCredentials: true,
+ logRes: false,
+ returnResponse: true,
+ headers: {
+ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36',
+ 'Origin': 'https://www.51dns.com',
+ 'Referer': 'https://www.51dns.com',
+ },
+ });
+ let setCookie = res.headers['set-cookie']
+ let cookie = setCookie.map((item: any) => {
+ return item.split(';')[0]
+ }).join(';')
+
+
+ //提取 var csrfToken = "ieOfM21eDd9nWJv3OZtMJF6ogDsnPKQHJ17dlMck";
+ const _token = res.data.match(/var csrfToken = "(.*?)"/)[1];
+ this.logger.info("_token:", _token);
+ this._token = _token;
+ var obj = {
+ "email_or_phone": this.aes(this.access.username),
+ "password": this.aes(this.access.password),
+ "type": this.aes("account"),
+ "redirectTo": "https://www.51dns.com/domain",
+ "_token": _token
+ };
+ const res2 = await this.http.request({
+ url: "https://www.51dns.com/login",
+ method: "post",
+ data: {
+ ...obj
+ },
+ withCredentials: true,
+ logRes: false,
+ returnResponse: true,
+ headers: {
+ 'Origin': 'https://www.51dns.com',
+ 'Referer': 'https://www.51dns.com',
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Cookie': cookie,
+ 'X-Requested-With': 'XMLHttpRequest'
+ }
+ });
+ this.logger.info("return headers:", JSON.stringify(res2.headers))
+ if (res2.data.code == 0) {
+ setCookie = res2.headers['set-cookie']
+ this._cookie = setCookie.map((item: any) => {
+ return item.split(';')[0]
+ }).join(';')
+ this.logger.info("cookie:", this._cookie)
+ this.logger.info("登录成功")
+ } else {
+ throw new Error("登录失败:", res2.data)
+ }
+
+
+ const res3 = await this.http.request({
+ url: 'https://www.51dns.com/domain',
+ method: 'get',
+ withCredentials: true,
+ logRes: false,
+ returnResponse: true,
+ headers: {
+ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36',
+ 'Origin': 'https://www.51dns.com',
+ 'Referer': 'https://www.51dns.com/login.html',
+ 'Cookie': this._cookie,
+ }
+ })
+
+ const success2 = res3.data.includes('DNS解析')
+
+ if (!success2) {
+ throw new Error("检查登录失败")
+ }
+ this.logger.info("检查登录成功")
+
+ this.isLogined = true;
+ }
+
+ async getDomainId(domain: string) {
+ await this.login();
+
+ const res = await this.http.request({
+ url: `https://www.51dns.com/domain?domain=${domain}&status=`,
+ method: "get",
+ withCredentials: true,
+ logRes: false,
+ returnResponse: true,
+ headers: this.getRequestHeaders()
+ });
+
+ // 提取 certd.top
+ const regExp = new RegExp(`]*>${domain}<\\/a>`, "i");
+ const matched = res.data.match(regExp);
+ if (!matched || matched.length < 1) {
+ throw new Error(`域名${domain}不存在`);
+ }
+ const domainId = matched[1];
+ this.logger.info(`域名${domain}的id为${domainId}`)
+ return parseInt(domainId);
+ }
+
+ private getRequestHeaders() {
+ return {
+ 'Origin': 'https://www.51dns.com',
+ 'Referer': 'https://www.51dns.com',
+ 'Cookie': this._cookie
+ };
+ }
+
+ async createRecord(param: { domain: string, data: any; domainId: number; host: string; ttl: number; type: string }) {
+ const {domain, data, host, type} = param;
+ const domainId = await this.getDomainId(domain);
+ const url = "https://www.51dns.com/domain/storenNewRecord";
+ const req = {
+ _token: this._token,
+ domain_id: domainId,
+ record: host,
+ type: type,
+ value: data,
+ ttl: 300,
+ mx:"",
+ view_id: 0
+ };
+ this.logger.info("req:", JSON.stringify(req))
+ const res = await this.http.request({
+ url,
+ method: "post",
+ data: req,
+ withCredentials: true,
+ headers: {
+ ...this.getRequestHeaders(),
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ }
+ });
+
+ if (res.status !== 200) {
+ throw new Error(`创建域名解析失败:${res.msg}`);
+ }
+ const id = res.data.id;
+ return {
+ id,
+ domainId
+ };
+
+ }
+
+ async deleteRecord(param: { domainId: number; id: number }) {
+ const url = "https://www.51dns.com/domain/operateRecord"
+ /*
+ type: delete
+ids[0]: 601019779
+domain_id: 193341603
+_token: ieOfM21eDd9nWJv3OZtMJF6ogDsnPKQHJ17dlMck
+ */
+ const body = {
+ type: "delete",
+ ids: [param.id],
+ domain_id: param.domainId,
+ _token: this._token
+ }
+ const res = await this.http.request({
+ url,
+ method: "post",
+ data: body,
+ withCredentials: true,
+ headers: {
+ ...this.getRequestHeaders(),
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ }
+ });
+ if (res.status !== 200) {
+ throw new Error(`删除域名解析失败:${res.msg}`);
+ }
+
+ }
+}
diff --git a/packages/ui/certd-server/src/plugins/plugin-51dns/dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-51dns/dns-provider.ts
new file mode 100644
index 00000000..32d57231
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-51dns/dns-provider.ts
@@ -0,0 +1,98 @@
+import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
+
+import { Dns51Access } from "./access.js";
+import { Dns51Client } from "./client.js";
+
+export type Dns51Record = {
+ id: number;
+ domainId: number,
+};
+
+// 这里通过IsDnsProvider注册一个dnsProvider
+@IsDnsProvider({
+ name: '51dns',
+ title: '51dns',
+ desc: '51DNS',
+ icon: 'arcticons:dns-changer-3',
+ // 这里是对应的 cloudflare的access类型名称
+ accessType: '51dns',
+})
+export class Dns51DnsProvider extends AbstractDnsProvider {
+ // 通过Autowire传递context
+ access!: Dns51Access;
+
+ client!:Dns51Client;
+ async onInstance() {
+ //一些初始化的操作
+ // 也可以通过ctx成员变量传递context, 与Autowire效果一样
+ this.access = this.ctx.access as Dns51Access;
+ this.client = new Dns51Client({
+ logger: this.logger,
+ access: this.access,
+ });
+ }
+
+ /**
+ * 创建dns解析记录,用于验证域名所有权
+ */
+ async createRecord(options: CreateRecordOptions): Promise {
+ /**
+ * fullRecord: '_acme-challenge.test.example.com',
+ * value: 一串uuid
+ * type: 'TXT',
+ * domain: 'example.com'
+ */
+ const { fullRecord,hostRecord, value, type, domain } = options;
+ this.logger.info('添加域名解析:', fullRecord, value, type, domain);
+
+
+
+
+ const domainId = await this.client.getDomainId(domain);
+ this.logger.info('获取domainId成功:', domainId);
+
+ const res = await this.client.createRecord({
+ domain: domain,
+ domainId: domainId,
+ type: 'TXT',
+ host: hostRecord,
+ data: value,
+ ttl: 300,
+ })
+ return {
+ id: res.id,
+ domainId: domainId,
+ };
+ }
+
+
+ /**
+ * 删除dns解析记录,清理申请痕迹
+ * @param options
+ */
+ async removeRecord(options: RemoveRecordOptions): Promise {
+ const { fullRecord, value } = options.recordReq;
+ const record = options.recordRes;
+ this.logger.info('删除域名解析:', fullRecord, value);
+ if (!record) {
+ this.logger.info('record为空,不执行删除');
+ return;
+ }
+ //这里调用删除txt dns解析记录接口
+ /**
+ * 请求示例
+ * DELETE /api/record?id=85371689655342080 HTTP/1.1
+ * Authorization: Basic {token}
+ * 请求参数
+ */
+ const {id,domainId} = record
+ await this.client.deleteRecord({
+ id,
+ domainId
+ })
+ this.logger.info(`删除域名解析成功:fullRecord=${fullRecord},id=${id}`);
+ }
+}
+
+//实例化这个provider,将其自动注册到系统中
+new Dns51DnsProvider();
diff --git a/packages/ui/certd-server/src/plugins/plugin-51dns/index.ts b/packages/ui/certd-server/src/plugins/plugin-51dns/index.ts
new file mode 100644
index 00000000..3a9e1743
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-51dns/index.ts
@@ -0,0 +1,3 @@
+export * from './dns-provider.js';
+export * from './access.js';
+export * from './client.js';
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b8bc7dae..4286cb03 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1566,6 +1566,9 @@ importers:
cross-env:
specifier: ^7.0.3
version: 7.0.3
+ crypto-js:
+ specifier: ^4.2.0
+ version: 4.2.0
dayjs:
specifier: ^1.11.7
version: 1.11.13
@@ -20673,13 +20676,13 @@ snapshots:
resolve: 1.22.10
semver: 6.3.1
- eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8):
+ eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8):
dependencies:
eslint: 7.32.0
prettier: 2.8.8
prettier-linter-helpers: 1.0.0
optionalDependencies:
- eslint-config-prettier: 8.10.0(eslint@8.57.0)
+ eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8):
dependencies:
@@ -23393,7 +23396,7 @@ snapshots:
eslint: 7.32.0
eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-node: 11.1.0(eslint@7.32.0)
- eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8)
+ eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8)
execa: 5.1.1
inquirer: 7.3.3
json5: 2.2.3