UNPKG

@ghini/kit

Version:

js practical tools to assist efficient development

548 lines (507 loc) 16.5 kB
// cloudflare使用api的便捷封装 - 兼容Global API Key和API Token import { req } from "./http/req.js"; export { cf }; async function cf(obj) { const key = obj.key; const domain = obj.domain; const email = obj.email; // 根据是否提供email决定认证方式 let auth, headers = {}; if (email) { // Global API Key认证方式 - 使用headers headers = { "X-Auth-Email": email, "X-Auth-Key": key, }; console.dev("使用Global API Key认证"); } else { // API Token认证方式 - 使用auth auth = "Bearer " + key; console.dev("使用API Token认证"); } // 调用时传入正确的参数 const zid = await getZoneId.bind({ domain, auth, headers })(); return { auth, headers, domain, zid, getZoneId, add, madd, set, mset, del, mdel, setSecurity, }; } async function getZoneId() { try { console.dev("获取Zone ID,域名:", this.domain); let res; if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证(通过headers) res = await req( `https://api.cloudflare.com/client/v4/zones?name=${this.domain}`, {}, // 空对象 this.headers // 第三个参数传入headers ); } else { // 使用API Token认证(通过auth) res = await req( `https://api.cloudflare.com/client/v4/zones?name=${this.domain}`, { auth: this.auth } ); } // console.log("API 响应:", res.data); if (res.data.success && res.data.result.length > 0) { return res.data.result[0].id; } else { throw new Error("记录未找到或权限不足"); } } catch (error) { console.error("获取 Zone ID 失败:", error.message); return null; // 返回null而不是抛出错误,以便允许回退到API Token } } async function mset(arr) { return Promise.all(arr.map((item) => this.set(item))); } // 查询ID+修改(没有批量) /** * 强大的set能力 能够处理如'em962 "test test test" txt' * 默认ttl = 1 * @param {*} str */ async function set(str) { let pre, content, type, priority, ttl; if (Array.isArray(str)) { [pre, content, type, priority, ttl] = str; } else { // 省略处理字符串的代码...此处保持不变 // 处理引号内的空格 let processedStr = ""; let inQuotes = false; // 首先替换引号内的空格为{+} for (let i = 0; i < str.length; i++) { const char = str.charAt(i); if (char === '"') { inQuotes = !inQuotes; processedStr += char; } else if (char === " ") { processedStr += inQuotes ? "{+}" : char; } else { processedStr += char; } } // 然后将多个空格标准化为一个空格 processedStr = processedStr.replace(/ +/g, " ").trim(); // 分割并还原{+}为空格 const parts = processedStr.split(" "); pre = parts[0]; // 处理带引号的内容 if (parts[1] && parts[1].startsWith('"')) { // 找到闭合引号的位置 let quoteContent = parts[1]; let contentEndIndex = 1; // 如果第一个部分没有闭合引号,继续查找 if (!parts[1].endsWith('"') || parts[1].length <= 1) { for (let i = 2; i < parts.length; i++) { quoteContent += " " + parts[i]; contentEndIndex = i; if (parts[i].endsWith('"')) break; } } // 提取引号内的内容并还原{+}为空格 content = '"' + quoteContent .substring(1, quoteContent.length - 1) .replace(/\{\+\}/g, " ") + '"'; // 提取剩余部分 type = parts[contentEndIndex + 1] || "A"; priority = parts[contentEndIndex + 2] || 10; ttl = parts[contentEndIndex + 3] || 60; // 添加TTL参数,默认为60秒 } else { // 没有引号的情况 content = parts[1] || ""; type = parts[2] || "A"; priority = parts[3] || 10; ttl = parts[4] || 60; // 添加TTL参数,默认为60秒 } } const host = pre + "." + this.domain; // 处理ttl,如果是auto或其他非数字值,强制设为60 const recordTtl = ttl === "auto" || isNaN(parseInt(ttl)) ? 60 : parseInt(ttl) || 60; try { // 确保zid存在 if (!this.zid) { throw new Error(`无法获取Zone ID,请检查域名: ${this.domain}`); } // 特殊情况:处理A记录的多IP地址(逗号分隔) if (type === "A" && content.includes(",")) { // 如果是A记录且包含逗号,将其视为多个IP地址 const ipList = [ ...new Set( content .split(",") .map((ip) => ip.trim()) .filter((ip) => ip !== "") ), ]; // 1. 查询所有现有记录 let res; if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records?type=${type}&name=${host}`, {}, this.headers ); } else { // 使用API Token认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records?type=${type}&name=${host}`, { auth: this.auth } ); } // 2. 删除所有现有记录 if (res.data.result && res.data.result.length > 0) { const deletePromises = res.data.result.map((record) => { if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证 return req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records/${record.id} delete`, {}, this.headers ); } else { // 使用API Token认证 return req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records/${record.id} delete`, { auth: this.auth } ); } }); await Promise.all(deletePromises); } // 3. 添加新的记录 console.log(`添加: ${host} ${type} ${content} 数量:${ipList.length}`); // 为每个IP创建一条新记录 const addPromises = ipList.map((ip) => { const recordData = { type: type, name: host, content: ip, proxied: false, priority: parseInt(priority) || 10, ttl: recordTtl, }; // 使用add函数添加记录 return add.bind({ auth: this.auth, headers: this.headers, zid: this.zid, })(recordData); }); await Promise.all(addPromises); return { success: true, message: `已为 ${host} 添加 ${ipList.length} 条A记录`, }; } else { // 处理普通记录(单IP的A记录或其他类型记录) // 1. 查询现有记录 let res; if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records?type=${type}&name=${host}`, {}, this.headers ); } else { // 使用API Token认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records?type=${type}&name=${host}`, { auth: this.auth } ); } // 2. 删除所有现有记录 if (res.data.result && res.data.result.length > 0) { const deletePromises = res.data.result.map((record) => { if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证 return req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records/${record.id} delete`, {}, this.headers ); } else { // 使用API Token认证 return req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records/${record.id} delete`, { auth: this.auth } ); } }); await Promise.all(deletePromises); } // 3. 添加新记录 console.log(`添加: ${host} ${type} ${content}`); const result = await add.bind({ auth: this.auth, headers: this.headers, zid: this.zid, })({ type: type || "A", name: host, content, proxied: false, priority: parseInt(priority) || 10, ttl: recordTtl, }); return { success: true, message: `已更新 ${host} 的记录` }; } } catch (error) { console.error(`操作 ${host} 时出错:`, error.message); return { success: false, error: error.message }; } } async function madd(arr) { return Promise.all(arr.map((item) => this.add(item))); } /** * * @param {*} json * { type: "A", name: "starlink-sfo2", content: "146.190.127.168", "proxied": true, * } */ async function add(json) { try { let res; if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records post`, { json }, this.headers ); } else { // 使用API Token认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records post`, { auth: this.auth, json } ); } return res.data; } catch (error) { console.error(`添加记录 ${json.name} 失败:`, error.message); throw error; } } async function mdel(arr) { return Promise.all(arr.map((item) => this.del(item))); } // 删除单个记录(需先查询 ID) async function del(pre) { try { // 1. 查询记录 ID const host = pre + "." + this.domain; let res; if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records?type=A&name=${host}`, {}, this.headers ); } else { // 使用API Token认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records?type=A&name=${host}`, { auth: this.auth } ); } const recordId = res.data.result[0]?.id; if (!recordId) { console.log(`记录 ${host} 不存在,跳过删除`); return; } // 2. 删除记录 if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records/${recordId} delete`, {}, this.headers ); } else { // 使用API Token认证 res = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/dns_records/${recordId} delete`, { auth: this.auth } ); } console.log(`删除${host}: ${res.data.success ? "成功" : "失败"}`); return res.data; } catch (error) { console.error(`删除记录失败:`, error.message); throw error; } } /** * 添加安全规则 * @param {Object} options - 规则配置选项 * @param {string} options.description - 规则描述 * @param {string} options.expression - 规则表达式 * @param {string} options.action - 规则触发的动作,如 "managed_challenge", "block", "js_challenge" 等 * @param {number} options.priority - 规则优先级,数字越大优先级越低 * @returns {Promise<Object>} - 返回 API 响应结果 */ async function setSecurity(options = {}) { try { const { description = "安全规则", expression = "", action = "managed_challenge", priority = 999, } = options; // 首先查找是否存在同名规则 let existingRule = null; let listResponse; // 查询所有规则 if (this.headers && Object.keys(this.headers).length > 0) { listResponse = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/firewall/rules`, {}, this.headers ); } else { listResponse = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/firewall/rules`, { auth: this.auth } ); } // 查找同名规则 if (listResponse.data.success && listResponse.data.result.length > 0) { existingRule = listResponse.data.result.find( (rule) => rule.description === description ); } let response; if (existingRule) { // 更新现有规则 // console.log(`找到现有规则 "${description}",准备更新...`); // 更新过滤器表达式 const filterId = existingRule.filter.id; let filterUpdateResponse; if (this.headers && Object.keys(this.headers).length > 0) { filterUpdateResponse = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/filters/${filterId} put`, { json: { expression: expression, paused: false } }, this.headers ); } else { filterUpdateResponse = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/filters/${filterId} put`, { auth: this.auth, json: { expression: expression, paused: false } } ); } if (!filterUpdateResponse.data.success) { throw new Error( `更新过滤器失败: ${JSON.stringify(filterUpdateResponse.data.errors)}` ); } // 更新规则本身 if (this.headers && Object.keys(this.headers).length > 0) { response = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/firewall/rules/${existingRule.id} put`, { json: { action: action, priority: priority, paused: false, description: description, // 保留原有描述 filter: { id: filterId, // 更新规则时必须包含过滤器ID }, }, }, this.headers ); } else { response = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/firewall/rules/${existingRule.id} put`, { auth: this.auth, json: { action: action, priority: priority, paused: false, description: description, // 保留原有描述 filter: { id: filterId, // 更新规则时必须包含过滤器ID }, }, } ); } if (response.data.success) { console.log(`安全规则 "${description}" 更新成功!`); return response.data.result; } else { console.error("更新安全规则失败:", response.data.errors); throw new Error(JSON.stringify(response.data.errors)); } } else { // 创建新规则 console.log(`未找到安全规则 "${description}",准备创建...`); // 构建正确的请求体结构 const requestBody = [ { filter: { expression: expression, paused: false, }, action: action, priority: priority, paused: false, description: description, }, ]; if (this.headers && Object.keys(this.headers).length > 0) { // 使用Global API Key认证 response = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/firewall/rules post`, { json: requestBody }, this.headers ); } else { // 使用API Token认证 response = await req( `https://api.cloudflare.com/client/v4/zones/${this.zid}/firewall/rules post`, { auth: this.auth, json: requestBody } ); } if (response.data.success) { console.log(`安全规则 "${description}" 创建成功!`); return response.data.result[0]; } else { console.error("创建安全规则失败:", response.data.errors); throw new Error(JSON.stringify(response.data.errors)); } } } catch (error) { console.error("设置安全规则时出错:", error.message); throw error; } }