207 lines
7.6 KiB
JavaScript
207 lines
7.6 KiB
JavaScript
//[create_at: 2026-03-18 20:45:00]
|
|
//[router: /m/myck]
|
|
//[method: get]
|
|
//[method: post]
|
|
//[title: qlck]
|
|
//[author: authook]
|
|
//[class: 工具类]
|
|
//[price: 0]
|
|
//[disable: false]
|
|
//[version: 2.3]
|
|
//[service: authook]
|
|
//[description: 青龙CK同步API:将CK同步到autMan已对接的青龙面板。]
|
|
|
|
//==========================参数配置数据===============================
|
|
//[param: {"required":true,"key":"otto.vhook_qlck_token","bool":false,"placeholder":"鉴权token","name":"接口鉴权Token","desc":"鉴权token,自定义鉴权token(防止恶意调用)"}]
|
|
//[param: {"required":false,"key":"otto.vhook_ql_default","bool":false,"placeholder":"青龙容器全称","name":"默认青龙容器名","desc":"未指定容器时,默认同步到此名称的青龙容器(全称匹配)"}]
|
|
|
|
/**
|
|
* 核心逻辑:
|
|
* 1. 参考 消息推送群1.js 的解析风格。
|
|
* 2. 支持 GET/POST 参数混合读取。
|
|
* 3. 探测回复使用 JSON 对象以防网页无法渲染。
|
|
*/
|
|
|
|
const method = getMethod()
|
|
const params = getRouterParams() || {}
|
|
|
|
var j = {
|
|
"ok": true,
|
|
"status": "ok",
|
|
"retcode": 0,
|
|
"msg": "success"
|
|
}
|
|
|
|
// 1. 数据解析
|
|
let access_token = params.access_token || params.token || ""
|
|
let ck = params.ck || params.cookie || ""
|
|
let env_name = params.env_name || ""
|
|
let ql_name = params.ql_name || ""
|
|
let mode = params.mode || "replace"
|
|
let joiner = params.joiner || "\n"
|
|
|
|
if (method == "post") {
|
|
const data = getRouterData()
|
|
if (data) {
|
|
try {
|
|
const body = JSON.parse(data)
|
|
access_token = body.access_token || body.token || access_token
|
|
ck = body.ck || body.cookie || ck
|
|
env_name = body.env_name || env_name
|
|
ql_name = body.ql_name || ql_name
|
|
mode = body.mode || mode
|
|
joiner = body.joiner || joiner
|
|
match = body.match || "" // 新增:匹配关键字
|
|
} catch (e) {
|
|
console.error("JSON解析失败: " + e.message)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 如果 GET 方式下也有 match
|
|
if (typeof match === "undefined") match = ""
|
|
|
|
// --- 探测逻辑提示 (无参数访问) ---
|
|
if (!access_token && !ck && !env_name) {
|
|
j.msg = "接口已启用"
|
|
j.version = "2.3"
|
|
sender.reply(j)
|
|
// 退出
|
|
} else {
|
|
// 2. 鉴权
|
|
const configured_token = get("vhook_qlck_token")
|
|
if (configured_token && configured_token !== access_token) {
|
|
sender.reply({ "ok": false, "status": "fail", "retcode": -1, "msg": "token错误" })
|
|
} else if (!ck || !env_name) {
|
|
sender.reply({ "ok": false, "status": "fail", "retcode": -2, "msg": "ck 或 env_name 不能为空" })
|
|
} else {
|
|
// 3. 执行同步逻辑
|
|
try {
|
|
main_sync(ck, env_name, ql_name, mode, joiner)
|
|
} catch (e) {
|
|
sender.reply({ "ok": false, "status": "error", "msg": "执行中出现异常: " + e.message })
|
|
}
|
|
}
|
|
}
|
|
|
|
function main_sync(ck, env_name, ql_name, mode, joiner) {
|
|
// 读取容器
|
|
let ql_configs = []
|
|
const ql_ids = bucketKeys("qls")
|
|
if (ql_ids) {
|
|
for (let id of ql_ids) {
|
|
let val = bucketGet("qls", id)
|
|
if (val) {
|
|
try {
|
|
let conf = JSON.parse(val)
|
|
ql_configs.push(conf)
|
|
} catch (e) { }
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ql_configs.length === 0) {
|
|
sender.reply({ "ok": false, "retcode": -3, "msg": "未在面板[青龙管理]中添加容器" })
|
|
return
|
|
}
|
|
|
|
// 确定容器列表
|
|
let target_ql_name = ql_name || get("vhook_ql_default") || ""
|
|
let target_qls = []
|
|
if (target_ql_name) {
|
|
target_qls = ql_configs.filter(ql => ql.name === target_ql_name)
|
|
} else {
|
|
target_qls = [ql_configs[0]]
|
|
}
|
|
|
|
if (target_qls.length === 0) {
|
|
sender.reply({ "ok": false, "retcode": -4, "msg": "未找到名为 [" + target_ql_name + "] 的容器" })
|
|
return
|
|
}
|
|
|
|
let results = []
|
|
for (let target_ql of target_qls) {
|
|
let ql_res = { container: target_ql.name, ok: false, msg: "" }
|
|
let host = target_ql.host.replace(/\/+$/, "")
|
|
|
|
// 分步执行
|
|
let loginUrl = host + "/open/auth/token?client_id=" + target_ql.client_id + "&client_secret=" + target_ql.client_secret
|
|
let loginResp = request({ url: loginUrl, method: "get", dataType: "json" })
|
|
|
|
if (loginResp && loginResp.code == 200 && loginResp.data && loginResp.data.token) {
|
|
let ql_token = loginResp.data.token
|
|
let envsResp = request({
|
|
url: host + "/open/envs?searchValue=" + env_name,
|
|
method: "get",
|
|
headers: { "Authorization": "Bearer " + ql_token },
|
|
dataType: "json"
|
|
})
|
|
|
|
let existing = null
|
|
if (envsResp && envsResp.code == 200 && Array.isArray(envsResp.data)) {
|
|
existing = envsResp.data.find(e => e.name === env_name)
|
|
}
|
|
|
|
if (existing && mode !== "add") {
|
|
let final_ck = ck
|
|
let envId = existing.id || existing._id
|
|
if (mode === "append") {
|
|
// 追加/精准聚合模式
|
|
if (match) {
|
|
// 如果提供了匹配键,执行“局部替换”
|
|
let items = existing.value.split(joiner)
|
|
let found = false
|
|
items = items.map(item => {
|
|
if (item.indexOf(match) > -1) {
|
|
found = true
|
|
return ck // 用新 CK 替换该子项
|
|
}
|
|
return item
|
|
})
|
|
if (!found) items.push(ck) // 如果没找到匹配项,仍执行追加
|
|
final_ck = items.join(joiner)
|
|
} else {
|
|
// 原有的普通去重追加逻辑
|
|
if (existing.value.indexOf(ck) === -1) {
|
|
final_ck = existing.value + joiner + ck
|
|
} else {
|
|
ql_res.ok = true; ql_res.msg = "CK已存在,跳过"; results.push(ql_res); continue
|
|
}
|
|
}
|
|
}
|
|
|
|
let updateResp = request({
|
|
url: host + "/open/envs",
|
|
method: "put",
|
|
headers: { "Authorization": "Bearer " + ql_token, "Content-Type": "application/json" },
|
|
body: { id: envId, name: env_name, value: final_ck, remarks: existing.remarks || "" },
|
|
dataType: "json"
|
|
})
|
|
if (updateResp && updateResp.code == 200) {
|
|
ql_res.ok = true; ql_res.msg = "更新成功"
|
|
} else {
|
|
ql_res.msg = "更新失败: " + JSON.stringify(updateResp)
|
|
}
|
|
} else {
|
|
let createResp = request({
|
|
url: host + "/open/envs",
|
|
method: "post",
|
|
headers: { "Authorization": "Bearer " + ql_token, "Content-Type": "application/json" },
|
|
body: [{ name: env_name, value: ck, remarks: "qlck同步" }],
|
|
dataType: "json"
|
|
})
|
|
if (createResp && createResp.code == 200) {
|
|
ql_res.ok = true; ql_res.msg = "新建成功"
|
|
} else {
|
|
ql_res.msg = "新建失败: " + JSON.stringify(createResp)
|
|
}
|
|
}
|
|
} else {
|
|
ql_res.msg = "登录失败: " + JSON.stringify(loginResp)
|
|
}
|
|
results.push(ql_res)
|
|
}
|
|
|
|
sender.reply({ "ok": results.every(r => r.ok), "results": results })
|
|
}
|