generated from template/slidev-template
update
This commit is contained in:
37
agent/ddns/cloudflare/index.ts
Normal file
37
agent/ddns/cloudflare/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
interface DnsUpdate {
|
||||
zone_id: string;
|
||||
record_id: string;
|
||||
domain: string;
|
||||
new_ip: string;
|
||||
api_token: string;
|
||||
type?: string; // 'A' or 'AAAA'
|
||||
}
|
||||
export class CloudflareDDNS {
|
||||
makeHeader(api_token: string) {
|
||||
return {
|
||||
'Authorization': `Bearer ${api_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
}
|
||||
async updateRecord(data: DnsUpdate) {
|
||||
const { zone_id, record_id, domain, type ,new_ip, api_token } = data;
|
||||
const url = `https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${record_id}`;
|
||||
const body = {
|
||||
"type": type || 'A',
|
||||
"name": domain,
|
||||
"content": new_ip,
|
||||
"ttl": 0,
|
||||
"proxied": false,
|
||||
}
|
||||
const response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: this.makeHeader(api_token),
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const result = await response.json();
|
||||
if(!result.success) {
|
||||
throw new Error(`更新失败: ${JSON.stringify(result.errors)}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
12
agent/ddns/volcengine/index.ts
Normal file
12
agent/ddns/volcengine/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// 文档地址 https://www.volcengine.com/docs/6758/155086
|
||||
|
||||
// 记录管理
|
||||
// 1. 获取域名记录列表 ListRecords
|
||||
// 2. 获取单个域名记录 QueryRecord
|
||||
// 3. 添加域名记录 CreateRecord
|
||||
// 4. 修改域名记录 UpdateRecord
|
||||
// 5. 删除域名记录 DeleteRecord
|
||||
export class VolcengineDDNS {
|
||||
baseURL = 'https://dns.volcengineapi.com';
|
||||
version = '2018-08-01'
|
||||
}
|
||||
250
agent/ddns/volcengine/sign.ts
Normal file
250
agent/ddns/volcengine/sign.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
const Service = "DNS";
|
||||
const Version = "2018-08-01";
|
||||
const Region = "cn-north-1";
|
||||
const Host = "dns.volcengineapi.com";
|
||||
|
||||
interface Credential {
|
||||
access_key_id: string;
|
||||
secret_access_key: string;
|
||||
service: string;
|
||||
region: string;
|
||||
}
|
||||
|
||||
interface RequestParam {
|
||||
body: string;
|
||||
host: string;
|
||||
path: string;
|
||||
method: string;
|
||||
content_type: string;
|
||||
date: Date;
|
||||
query: Record<string, any>;
|
||||
}
|
||||
|
||||
interface SignResult {
|
||||
Host: string;
|
||||
'X-Content-Sha256': string;
|
||||
'X-Date': string;
|
||||
'Content-Type': string;
|
||||
Authorization?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转ArrayBuffer
|
||||
*/
|
||||
function stringToArrayBuffer(str: string): ArrayBuffer {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(str).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayBuffer转十六进制字符串
|
||||
*/
|
||||
function arrayBufferToHex(buffer: ArrayBuffer): string {
|
||||
const hashArray = Array.from(new Uint8Array(buffer));
|
||||
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* URL编码函数,类似Python的quote函数
|
||||
*/
|
||||
function encodeURIComponentSafe(str: string): string {
|
||||
return encodeURIComponent(str)
|
||||
.replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase())
|
||||
.replace(/%20/g, '%20');
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化查询参数
|
||||
*/
|
||||
function normQuery(params: Record<string, any>): string {
|
||||
let query = "";
|
||||
const sortedKeys = Object.keys(params).sort();
|
||||
|
||||
for (const key of sortedKeys) {
|
||||
if (Array.isArray(params[key])) {
|
||||
for (const k of params[key]) {
|
||||
query += encodeURIComponentSafe(key) + "=" + encodeURIComponentSafe(k) + "&";
|
||||
}
|
||||
} else {
|
||||
query += encodeURIComponentSafe(key) + "=" + encodeURIComponentSafe(params[key]) + "&";
|
||||
}
|
||||
}
|
||||
|
||||
query = query.slice(0, -1);
|
||||
return query.replace(/\+/g, "%20");
|
||||
}
|
||||
|
||||
/**
|
||||
* HMAC-SHA256 加密 (使用Web Crypto API)
|
||||
*/
|
||||
async function hmacSha256(key: ArrayBuffer, content: string): Promise<ArrayBuffer> {
|
||||
const cryptoKey = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
key,
|
||||
{ name: 'HMAC', hash: 'SHA-256' },
|
||||
false,
|
||||
['sign']
|
||||
);
|
||||
return crypto.subtle.sign('HMAC', cryptoKey, stringToArrayBuffer(content));
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA256 哈希算法 (使用Web Crypto API)
|
||||
*/
|
||||
async function hashSha256(content: string): Promise<string> {
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', stringToArrayBuffer(content));
|
||||
return arrayBufferToHex(hashBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签名
|
||||
*/
|
||||
export async function getSignature(
|
||||
method: string,
|
||||
query: Record<string, any>,
|
||||
header: Record<string, string>,
|
||||
ak: string,
|
||||
sk: string,
|
||||
action: string,
|
||||
body: any
|
||||
): Promise<SignResult> {
|
||||
const credential: Credential = {
|
||||
access_key_id: ak,
|
||||
secret_access_key: sk,
|
||||
service: Service,
|
||||
region: Region,
|
||||
};
|
||||
|
||||
const fullQuery = { Action: action, Version: Version, ...query };
|
||||
const sortedQuery: Record<string, any> = {};
|
||||
Object.keys(fullQuery).sort().forEach(key => {
|
||||
sortedQuery[key] = (fullQuery as any)[key];
|
||||
});
|
||||
|
||||
const requestParam: RequestParam = {
|
||||
body: "",
|
||||
host: Host,
|
||||
path: "/",
|
||||
method: method,
|
||||
content_type: "application/json",
|
||||
date: new Date(),
|
||||
query: sortedQuery,
|
||||
};
|
||||
|
||||
if (method === "POST") {
|
||||
requestParam.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const xDate = requestParam.date.toISOString().replace(/[:\-]|\.\d{3}/g, '').slice(0, -1) + 'Z';
|
||||
const shortXDate = xDate.slice(0, 8);
|
||||
const xContentSha256 = await hashSha256(requestParam.body);
|
||||
|
||||
const signResult: SignResult = {
|
||||
Host: requestParam.host,
|
||||
'X-Content-Sha256': xContentSha256,
|
||||
'X-Date': xDate,
|
||||
'Content-Type': requestParam.content_type,
|
||||
};
|
||||
|
||||
const signedHeadersStr = ["content-type", "host", "x-content-sha256", "x-date"].join(";");
|
||||
|
||||
const canonicalRequestStr = [
|
||||
requestParam.method,
|
||||
requestParam.path,
|
||||
normQuery(requestParam.query),
|
||||
[
|
||||
"content-type:" + requestParam.content_type,
|
||||
"host:" + requestParam.host,
|
||||
"x-content-sha256:" + xContentSha256,
|
||||
"x-date:" + xDate,
|
||||
].join("\n"),
|
||||
"",
|
||||
signedHeadersStr,
|
||||
xContentSha256,
|
||||
].join("\n");
|
||||
|
||||
const hashedCanonicalRequest = await hashSha256(canonicalRequestStr);
|
||||
const credentialScope = [shortXDate, credential.region, credential.service, "request"].join("/");
|
||||
const stringToSign = ["HMAC-SHA256", xDate, credentialScope, hashedCanonicalRequest].join("\n");
|
||||
|
||||
const secretKeyBuffer = stringToArrayBuffer(credential.secret_access_key);
|
||||
const kDate = await hmacSha256(secretKeyBuffer, shortXDate);
|
||||
const kRegion = await hmacSha256(kDate, credential.region);
|
||||
const kService = await hmacSha256(kRegion, credential.service);
|
||||
const kSigning = await hmacSha256(kService, "request");
|
||||
const signatureBuffer = await hmacSha256(kSigning, stringToSign);
|
||||
const signature = arrayBufferToHex(signatureBuffer);
|
||||
|
||||
signResult.Authorization = `HMAC-SHA256 Credential=${credential.access_key_id}/${credentialScope}, SignedHeaders=${signedHeadersStr}, Signature=${signature}`;
|
||||
|
||||
return signResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送API请求
|
||||
*/
|
||||
export async function request(
|
||||
method: string,
|
||||
query: Record<string, any>,
|
||||
header: Record<string, string>,
|
||||
ak: string,
|
||||
sk: string,
|
||||
action: string,
|
||||
body: any
|
||||
): Promise<any> {
|
||||
const signResult = await getSignature(method, query, header, ak, sk, action, body);
|
||||
const finalHeader = { ...header, ...signResult };
|
||||
|
||||
const requestParam: RequestParam = {
|
||||
body: method === "POST" ? JSON.stringify(body) : "",
|
||||
host: Host,
|
||||
path: "/",
|
||||
method: method,
|
||||
content_type: "application/json",
|
||||
date: new Date(),
|
||||
query: { Action: action, Version: Version, ...query },
|
||||
};
|
||||
|
||||
const url = new URL(`https://${requestParam.host}${requestParam.path}`);
|
||||
Object.keys(requestParam.query).forEach(key => {
|
||||
url.searchParams.append(key, requestParam.query[key]);
|
||||
});
|
||||
|
||||
const fetchOptions: RequestInit = {
|
||||
method: method,
|
||||
headers: finalHeader,
|
||||
};
|
||||
|
||||
if (method === "POST") {
|
||||
fetchOptions.body = requestParam.body;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), fetchOptions);
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
throw new Error(`Request failed: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用示例
|
||||
*/
|
||||
export async function example(ak: string, sk: string) {
|
||||
try {
|
||||
// POST 请求示例 - 调用 UpdateZone API
|
||||
const requestBody = {
|
||||
ZID: 100,
|
||||
Remark: "example",
|
||||
};
|
||||
const updateZoneResult = await request("POST", {}, {}, ak, sk, "UpdateZone", requestBody);
|
||||
console.log(updateZoneResult);
|
||||
|
||||
// GET 请求示例 - 调用 CheckZone API
|
||||
const requestQuery = { ZoneName: "example.com" };
|
||||
const checkZoneResult = await request("GET", requestQuery, {}, ak, sk, "CheckZone", {});
|
||||
console.log(checkZoneResult);
|
||||
} catch (error) {
|
||||
console.error('API request failed:', error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user