feat(web): 站点显示 Cloudflare 状态(CF-2)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
16083942c0
commit
f16b97151b
4 files changed
+155
-3
No files matched your search
@@ -11,6 +11,7 @@ import {
|
||||
type SiteListItem,
|
||||
type SiteLoginFailureReason,
|
||||
type SiteRateLimit,
|
||||
siteCloudflareState,
|
||||
} from "@umt/contracts";
|
||||
import type { StatusTone } from "~/components/console/status-badge.tsx";
|
||||
import { relTime } from "~/components/workbench/release-fields.ts";
|
||||
@@ -180,6 +181,64 @@ export function healthTechItems(h: SiteLastHealth | null): { label: string; valu
|
||||
].filter((it) => it.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloudflare 状态一行(CF-2):没遇到过挑战不显示。
|
||||
* 需要你验证 → 浏览器代理中,至 hh:mm → 自动通过(最近一次怎么过的)。
|
||||
*/
|
||||
export function cloudflareLine(
|
||||
config: unknown,
|
||||
now: Date = new Date(),
|
||||
): { text: string; tone: StatusTone; takeover: boolean } | null {
|
||||
const st = siteCloudflareState(config);
|
||||
if (!st?.lastChallengeAt) return null;
|
||||
if (st.pending === "human") return { text: "Cloudflare:需要你验证", tone: "warning", takeover: true };
|
||||
if (st.browserUntil && Date.parse(st.browserUntil) > now.getTime()) {
|
||||
const t = new Date(st.browserUntil);
|
||||
const hm = `${String(t.getHours()).padStart(2, "0")}:${String(t.getMinutes()).padStart(2, "0")}`;
|
||||
return { text: `Cloudflare:浏览器代理中,至 ${hm}`, tone: "neutral", takeover: false };
|
||||
}
|
||||
if (st.lastSolvedBy) return { text: "Cloudflare:自动通过", tone: "success", takeover: false };
|
||||
if (st.lastError) return { text: "Cloudflare:没能通过", tone: "warning", takeover: false };
|
||||
return null;
|
||||
}
|
||||
|
||||
const CF_SOLVED_BY: Record<string, string> = {
|
||||
auto: "等它自动放行",
|
||||
vision: "视觉子流程点了复选框",
|
||||
human: "你在原站视图里通过",
|
||||
};
|
||||
const CF_RESULT: Record<string, string> = {
|
||||
passed: "已通过",
|
||||
reuse_ok: "已通过,HTTP 复用 cf_clearance 有效",
|
||||
reuse_fails: "已通过,HTTP 复用 cf_clearance 无效,改由浏览器代抓",
|
||||
browser: "由浏览器代抓",
|
||||
browser_challenge: "浏览器也被拦下",
|
||||
needs_human: "等你在原站视图里验证",
|
||||
failed: "没能通过",
|
||||
skipped: "没有已登录的账号,未处理",
|
||||
};
|
||||
|
||||
/** Cloudflare 原始记录 → 技术信息条目 */
|
||||
export function cloudflareTechItems(config: unknown): { label: string; value: string; mono?: boolean }[] {
|
||||
const st = siteCloudflareState(config);
|
||||
if (!st?.lastChallengeAt) return [];
|
||||
const t = (v: string | null) => (v ? new Date(v).toLocaleString("zh-CN", { hour12: false }) : "");
|
||||
return [
|
||||
{ label: "最近遇到挑战", value: t(st.lastChallengeAt) },
|
||||
{ label: "挑战规则", value: st.lastRule ?? "", mono: true },
|
||||
{ label: "处理结果", value: st.lastResult ? `${CF_RESULT[st.lastResult] ?? st.lastResult}(${st.lastResult})` : "" },
|
||||
{ label: "最近通过方式", value: st.lastSolvedBy ? (CF_SOLVED_BY[st.lastSolvedBy] ?? st.lastSolvedBy) : "" },
|
||||
{ label: "最近通过时间", value: t(st.lastSolvedAt) },
|
||||
{
|
||||
label: "cf_clearance 复用",
|
||||
value: st.cfReuse === "ok" ? "有效(ok)" : st.cfReuse === "fails" ? "无效(fails)" : "",
|
||||
},
|
||||
{ label: "浏览器代理至", value: t(st.browserUntil) },
|
||||
{ label: "最近尝试", value: t(st.lastAttemptAt) },
|
||||
{ label: "最近错误", value: st.lastError ?? "" },
|
||||
].filter((it) => it.value);
|
||||
}
|
||||
|
||||
/** 限流:"20 次/分";空对象 = 默认 */
|
||||
export function rateLimitText(r: SiteRateLimit | null | undefined): string {
|
||||
if (!r || (!r.requestsPerMinute && !r.minIntervalMs && !r.concurrency)) return "默认";
|
||||
@@ -202,7 +261,10 @@ export function loginFailureText(
|
||||
case "two_factor":
|
||||
return { text: "登录失败:站点要求二次验证。填写 TOTP 密钥后重试,或改用 Cookie", suggestCookie: true };
|
||||
case "challenge":
|
||||
return { text: "站点返回 Cloudflare 挑战页,无法自动登录。改用 Cookie", suggestCookie: true };
|
||||
return {
|
||||
text: "站点要求人机验证(Cloudflare)。会用浏览器自动过,过不去时在待处理里提醒你",
|
||||
suggestCookie: false,
|
||||
};
|
||||
case "bad_credentials":
|
||||
return { text: "登录失败:账号或密码错误", suggestCookie: false };
|
||||
case "account_blocked":
|
||||
|
||||
Reference in new issue
Block a user