Add Gmail+Email Worker hybrid mail path and private runtime config

Prefer Cloudflare Worker KV for verification codes with optional short IMAP
fallback disabled for private deploy; track config.json/.env for private repo.
Also add worker project, protocol mint backoff, and fail-fast Turnstile/mail
timeouts from batch runs.
This commit is contained in:
chaos committed 2026-07-11 23:21:23 +08:00
1 parent 187792d6be
commit 55f56d27c9
19 files changed
+1225 -85

No files matched your search

+280
View File
@@ -0,0 +1,280 @@
/**
* grok-xai-mail-codes — hardened for clearer errors (no bare 1101 if possible)
*
* Bindings:
* KV name MUST be: CODES
* Secrets/vars:
* CODE_API_KEY (required)
* GMAIL_FORWARD_TO (optional)
* CODE_TTL_SEC (optional, default 600)
*/
const CODE_PATTERNS = [
/^([A-Z0-9]{3}-[A-Z0-9]{3})\s+xAI/i,
/\b([A-Z0-9]{3}-[A-Z0-9]{3})\b/i,
/verification\s+code[:\s]+(\d{4,8})/i,
/your\s+code[:\s]+(\d{4,8})/i,
/confirm(?:ation)?\s+code[:\s]+(\d{4,8})/i,
/验证码[::\s]+(\d{4,8})/,
];
const KEYWORDS = ["x.ai", "xai", "grok", "verification", "code", "confirm", "验证码", "确认"];
function json(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: { "content-type": "application/json; charset=utf-8" },
});
}
function extractCode(text, subject = "") {
const blob = `${subject || ""}\n${text || ""}`;
for (const re of CODE_PATTERNS) {
const m = blob.match(re);
if (m && m[1]) return String(m[1]).trim();
}
return null;
}
function emailsFromList(list) {
if (!list) return [];
const arr = Array.isArray(list) ? list : [list];
const out = [];
for (const item of arr) {
if (!item) continue;
if (typeof item === "string") {
const m = item.match(/[\w.+-]+@[\w.-]+\.\w+/g);
if (m) out.push(...m.map((x) => x.toLowerCase()));
continue;
}
if (item.address) out.push(String(item.address).toLowerCase());
if (item.email) out.push(String(item.email).toLowerCase());
}
return [...new Set(out.filter(Boolean))];
}
async function parseEmail(message) {
const raw = await new Response(message.raw).arrayBuffer();
const bytes = new Uint8Array(raw);
let rawText = "";
try {
rawText = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
} catch {
const n = Math.min(bytes.length, 500000);
let s = "";
for (let i = 0; i < n; i++) s += String.fromCharCode(bytes[i]);
rawText = s;
}
const headerBlob = rawText.split(/\r?\n\r?\n/, 1)[0] || "";
let subject = "";
const subjMatch = headerBlob.match(/^Subject:\s*(.+)$/im);
if (subjMatch) subject = subjMatch[1].trim();
let from = "";
const fromMatch = headerBlob.match(/^From:\s*(.+)$/im);
if (fromMatch) from = fromMatch[1].trim();
const recipients = new Set();
for (const h of ["To", "Cc", "Delivered-To", "X-Original-To", "Envelope-To"]) {
const re = new RegExp(`^${h}:\\s*(.+)$`, "gim");
let m;
while ((m = re.exec(headerBlob))) {
const found = m[1].match(/[\w.+-]+@[\w.-]+\.\w+/g) || [];
for (const e of found) recipients.add(e.toLowerCase());
}
}
try {
for (const e of emailsFromList(message.to)) recipients.add(e);
} catch (_) {}
return {
subject,
from,
recipients: [...recipients],
rawText: rawText.slice(0, 200000),
};
}
function isXaiMail(subject, from, body) {
const blob = `${subject}\n${from}\n${body}`.toLowerCase();
return KEYWORDS.some((k) => blob.includes(k));
}
function kv(env) {
// Accept common mistaken binding names
return env.CODES || env.GROK_MAIL_CODES || env.CODE || null;
}
async function storeCode(env, toEmail, payload) {
const ns = kv(env);
if (!ns || typeof ns.put !== "function") {
throw new Error("KV binding missing: add KV namespace with variable name CODES");
}
const ttl = Math.max(60, parseInt(String(env.CODE_TTL_SEC || "600"), 10) || 600);
const key = `code:${String(toEmail).toLowerCase().trim()}`;
await ns.put(key, JSON.stringify(payload), { expirationTtl: ttl });
try {
const prev = JSON.parse((await ns.get("recent")) || "[]");
prev.unshift({
to: toEmail,
code: payload.code,
ts: payload.ts,
subject: payload.subject,
});
await ns.put("recent", JSON.stringify(prev.slice(0, 30)), { expirationTtl: ttl });
} catch (_) {}
}
function checkKey(request, env, url) {
const key = url.searchParams.get("key") || request.headers.get("x-api-key") || "";
const expected = env.CODE_API_KEY || "";
return Boolean(expected) && key === expected;
}
export default {
async fetch(request, env, ctx) {
try {
const url = new URL(request.url);
const path = url.pathname.replace(/\/+$/, "") || "/";
if (path === "/health") {
const ns = kv(env);
return json({
ok: true,
service: "grok-xai-mail-codes",
kv_bound: Boolean(ns && typeof ns.get === "function"),
has_api_key: Boolean(env.CODE_API_KEY),
});
}
if (!checkKey(request, env, url)) {
return json({ error: "unauthorized" }, 401);
}
if (path === "/code" && request.method === "GET") {
const to = (url.searchParams.get("to") || url.searchParams.get("target") || "")
.trim()
.toLowerCase();
if (!to) return json({ error: "missing to" }, 400);
const ns = kv(env);
if (!ns || typeof ns.get !== "function") {
return json(
{
error: "kv not bound",
hint: "Workers → grok-xai-mail-codes → Settings → Bindings → add KV, Variable name must be CODES",
},
500,
);
}
const raw = await ns.get(`code:${to}`);
if (!raw) return json({ to, error: "not found" }, 404);
let data;
try {
data = JSON.parse(raw);
} catch {
return json({ to, error: "bad kv value" }, 500);
}
const consume = (url.searchParams.get("consume") || "1") !== "0";
if (consume) {
try {
await ns.delete(`code:${to}`);
} catch (_) {}
}
return json({
ok: true,
to,
code: data.code,
subject: data.subject || "",
from: data.from || "",
ts: data.ts || 0,
source: "worker-kv",
});
}
if (path === "/recent" && request.method === "GET") {
const ns = kv(env);
if (!ns) return json({ items: [], kv_bound: false });
const raw = await ns.get("recent");
return json({ items: raw ? JSON.parse(raw) : [], kv_bound: true });
}
if (path === "/ingest" && request.method === "POST") {
let body;
try {
body = await request.json();
} catch {
return json({ error: "invalid json" }, 400);
}
const to = String(body.to || body.target || "").toLowerCase().trim();
const code = String(body.code || "").trim();
if (!to || !code) return json({ error: "missing to/code" }, 400);
try {
await storeCode(env, to, {
code,
subject: body.subject || `${code} xAI confirmation code`,
from: body.from || "",
ts: Date.now(),
});
} catch (e) {
return json({ error: String(e && e.message ? e.message : e) }, 500);
}
return json({ ok: true, to, code });
}
return json({ error: "not found" }, 404);
} catch (e) {
return json(
{
error: "worker exception",
message: String(e && e.message ? e.message : e),
},
500,
);
}
},
async email(message, env, ctx) {
let parsed;
try {
parsed = await parseEmail(message);
} catch (_) {
const forwardTo = String(env.GMAIL_FORWARD_TO || "").trim();
if (forwardTo) {
try {
await message.forward(forwardTo);
} catch (_) {}
}
return;
}
const { subject, from, recipients, rawText } = parsed;
const code = extractCode(rawText, subject);
const interesting = isXaiMail(subject, from, rawText) || !!code;
if (interesting && code && recipients.length) {
const payload = { code, subject, from, ts: Date.now() };
try {
await Promise.all(recipients.map((to) => storeCode(env, to, payload)));
} catch (_) {
// still forward
}
}
const forwardTo = String(env.GMAIL_FORWARD_TO || "").trim();
if (forwardTo) {
try {
await message.forward(forwardTo);
} catch (e) {
try {
message.setReject?.(`forward failed: ${e}`);
} catch (_) {}
}
}
},
};