(
c: Context,
opts: ApplyGuardOptions,
)
| 84 | * 执行三重校验。失败时返回具体 code / status / message。 |
| 85 | */ |
| 86 | export async function checkApplyGuard( |
| 87 | c: Context, |
| 88 | opts: ApplyGuardOptions, |
| 89 | ): Promise<ApplyGuardResult> { |
| 90 | const {source, user, captchaToken} = opts; |
| 91 | const env = c.env as any; |
| 92 | |
| 93 | // ---------- 1) 人机验证 ---------- |
| 94 | try { |
| 95 | const captchaEnabled = await readBool(env, "CERT_CAPTCHA_ENABLED", false); |
| 96 | if (captchaEnabled) { |
| 97 | if (source === "api") { |
| 98 | return { |
| 99 | ok: false, |
| 100 | code: "CAPTCHA_REQUIRED", |
| 101 | status: 403, |
| 102 | message: "当前已开启人机验证,请改用网页提交证书申请", |
| 103 | }; |
| 104 | } |
| 105 | const token = (captchaToken ?? "").trim(); |
| 106 | if (!token) { |
| 107 | return { |
| 108 | ok: false, |
| 109 | code: "CAPTCHA_REQUIRED", |
| 110 | status: 400, |
| 111 | message: "请先完成人机验证", |
| 112 | }; |
| 113 | } |
| 114 | const provider = (await readConf(env, "CERT_CAPTCHA_PROVIDER")) ?? "turnstile"; |
| 115 | // 验证码 Secret:优先新键 AUTH_KEYS,回退旧键 CERT_CAPTCHA_SECRET_KEY |
| 116 | const secret = |
| 117 | (await readConf(env, "AUTH_KEYS")) || |
| 118 | (await readConf(env, "CERT_CAPTCHA_SECRET_KEY")) || |
| 119 | ""; |
| 120 | if (!secret) { |
| 121 | return { |
| 122 | ok: false, |
| 123 | code: "INTERNAL", |
| 124 | status: 500, |
| 125 | message: "系统未配置验证码 Secret Key(AUTH_KEYS)", |
| 126 | }; |
| 127 | } |
| 128 | const ip = c.req.header("CF-Connecting-IP") ?? undefined; |
| 129 | const passed = await verifyCaptcha(provider, secret, token, ip); |
| 130 | if (!passed) { |
| 131 | return { |
| 132 | ok: false, |
| 133 | code: "CAPTCHA_INVALID", |
| 134 | status: 400, |
| 135 | message: "人机验证未通过,请重试", |
| 136 | }; |
| 137 | } |
| 138 | } |
| 139 | } catch (e) { |
| 140 | console.error("[applyGuard] captcha step failed:", e); |
| 141 | // 降级:不因 captcha 读配置失败而放行 |
| 142 | return {ok: false, code: "INTERNAL", status: 500, message: "人机验证配置读取失败"}; |
| 143 | } |
no test coverage detected