mirror of
https://github.com/certd/certd.git
synced 2026-04-23 11:37:23 +08:00
Merge remote-tracking branch 'origin/v2-dev' into v2-dev
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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();
|
||||
@@ -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)
|
||||
// // 提取 <span class="user_email">182****43522</span><br>
|
||||
// console.log(res2.data.match(/<span class="user_email">(.*?)<\/span>/)[1])
|
||||
// const success1 = res2.data.includes('<span class="nav-title">DNS解析</span>')
|
||||
// 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('<span class="nav-title">DNS解析</span>')
|
||||
console.log("success", success2)
|
||||
|
||||
|
||||
/**
|
||||
* <a target="_blank" href="https://www.51dns.com/domain/record/193341603"
|
||||
* class="color47">certd.top</a>
|
||||
|
||||
*/
|
||||
//上面文本中间有换行,需要提取 193341603 部分,必须有certd.top,使用 new Regexp, .号要能匹配换行符,非贪婪模式
|
||||
const regExp = new RegExp('<a target="_blank" href="https://www.51dns.com/domain/record/(\\d+)"[^>]*>certd\\.top<\\/a>',"i");
|
||||
|
||||
const domainId = res3.data.match(regExp)[1]
|
||||
|
||||
|
||||
console.log("domainId", domainId)
|
||||
}
|
||||
|
||||
login()
|
||||
@@ -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('<span class="nav-title">DNS解析</span>')
|
||||
|
||||
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()
|
||||
});
|
||||
|
||||
// 提取 <a target="_blank" href="https://www.51dns.com/domain/record/193341603"
|
||||
// class="color47">certd.top</a>
|
||||
const regExp = new RegExp(`<a target="_blank" href="https://www.51dns.com/domain/record/(\\d+)"[^>]*>${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}`);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<Dns51Record> {
|
||||
// 通过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<Dns51Record> {
|
||||
/**
|
||||
* 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<Dns51Record>): Promise<void> {
|
||||
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();
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './dns-provider.js';
|
||||
export * from './access.js';
|
||||
export * from './client.js';
|
||||
Reference in New Issue
Block a user