ds2api / cf-worker /src /index.js
204848
Initial commit: ds2api with HF Spaces support
feccc69
Raw
History Blame Contribute Delete
2.97 kB
/**
* Cloudflare Worker — 定时刷新 ds2api 代理健康状态
*
* 部署步骤:
* 1. cd cf-worker && npm install
* 2. 设置 secrets:
* wrangler secret put DS2API_BASE_URL
* wrangler secret put ADMIN_KEY
* wrangler secret put VERCEL_BYPASS_TOKEN (如果开了 Vercel Deployment Protection)
* 3. wrangler deploy
*
* Cron 每 5 小时触发一次 (与 wrangler.toml 中的 crons 对应)。
* 也可以手动 GET/POST https://<worker>/check 触发。
*/
const LOGIN_PATH = "/admin/login"
const CHECK_ALL_PATH = "/admin/proxies/check-all"
function vercelHeaders(env) {
const headers = {}
const bypass = env.VERCEL_BYPASS_TOKEN
if (bypass) {
headers["x-vercel-protection-bypass"] = bypass
}
return headers
}
async function login(baseURL, adminKey, extraHeaders) {
const res = await fetch(`${baseURL}${LOGIN_PATH}`, {
method: "POST",
headers: { "Content-Type": "application/json", ...extraHeaders },
body: JSON.stringify({ admin_key: adminKey, expire_hours: 1 }),
})
if (!res.ok) {
const text = await res.text()
throw new Error(`login failed (${res.status}): ${text}`)
}
const data = await res.json()
return data.token
}
async function checkAll(baseURL, token, extraHeaders) {
const res = await fetch(`${baseURL}${CHECK_ALL_PATH}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
...extraHeaders,
},
})
if (!res.ok) {
const text = await res.text()
throw new Error(`check-all failed (${res.status}): ${text}`)
}
return res.json()
}
async function runCheck(env) {
const baseURL = (env.DS2API_BASE_URL || "").replace(/\/+$/, "")
const adminKey = env.ADMIN_KEY || ""
if (!baseURL || !adminKey) {
throw new Error("DS2API_BASE_URL and ADMIN_KEY secrets must be set")
}
const extraHeaders = vercelHeaders(env)
const token = await login(baseURL, adminKey, extraHeaders)
const result = await checkAll(baseURL, token, extraHeaders)
return result
}
export default {
async scheduled(event, env, ctx) {
ctx.waitUntil(
runCheck(env)
.then((result) => {
const items = result.items || []
const healthy = items.filter((i) => i.healthy && !i.disabled).length
const banned = items.filter((i) => i.disabled).length
console.log(
`proxy health check done: ${healthy}/${items.length} healthy, ${banned} banned`
)
})
.catch((err) => {
console.error(`proxy health check failed: ${err.message}`)
})
)
},
async fetch(request, env) {
const url = new URL(request.url)
if (url.pathname !== "/check") {
return new Response("not found", { status: 404 })
}
try {
const result = await runCheck(env)
return Response.json(result)
} catch (err) {
return Response.json({ error: err.message }, { status: 500 })
}
},
}