/** * 支付成功后存储 email -> user_id 映射,供「已有账号登录」按邮箱查库 * 同时创建 PB 用户(邮箱+12345678),以便后续用邮箱+密码登录 * 需 PocketBase 存在 vip_email_link 集合(email, user_id) */ import { NextRequest, NextResponse } from "next/server"; import { getPocketBaseConfig } from "@/config/services.config"; const VIP_EMAIL_LINK_COLLECTION = "vip_email_link"; const DEFAULT_PASSWORD = "12345678"; async function getAdminToken(): Promise { const email = process.env.POCKETBASE_EMAIL; const password = process.env.POCKETBASE_PASSWORD; if (!email || !password) return null; const pb = getPocketBaseConfig(); const res = await fetch(`${pb.url}/api/admins/auth-with-password`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ identity: email, password }), }); if (!res.ok) return null; const data = await res.json(); return data?.token ?? null; } /** 若 PB 无该邮箱用户则创建(密码 12345678),供后续邮箱登录 */ async function ensurePbUserExists(pbUrl: string, email: string): Promise { const registerRes = await fetch(`${pbUrl}/api/collections/users/records`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password: DEFAULT_PASSWORD, passwordConfirm: DEFAULT_PASSWORD, live_allowed: true, }), }); if (registerRes.ok) { console.log("[store-email-link] 已创建 PB 用户 email=%s", email); return; } const err = await registerRes.json().catch(() => ({})); const alreadyExists = err?.data?.email?.message?.includes("unique") || err?.message?.toLowerCase?.().includes("already") || err?.message?.toLowerCase?.().includes("unique"); if (alreadyExists) { console.log("[store-email-link] PB 用户已存在 email=%s", email); return; } console.warn("[store-email-link] 创建 PB 用户失败 email=%s status=%s", email, registerRes.status, err); } export async function POST(request: NextRequest) { const body = await request.json().catch(() => ({})); const email = String(body?.email ?? "").trim().toLowerCase(); const userId = String(body?.user_id ?? body?.userId ?? "").trim(); console.log("[store-email-link] 请求 email=%s user_id=%s", email || "(空)", userId || "(空)"); if (!email || !userId) { return NextResponse.json({ ok: false, error: "请填写邮箱" }, { status: 400 }); } const adminToken = await getAdminToken(); if (!adminToken) { return NextResponse.json({ ok: false, error: "服务未配置" }, { status: 500 }); } const pb = getPocketBaseConfig(); try { await ensurePbUserExists(pb.url, email); } catch (e) { console.warn("[store-email-link] ensurePbUserExists 异常:", e); } const safeEmail = email.replace(/"/g, '\\"'); const filter = `email = "${safeEmail}"`; try { const listRes = await fetch( `${pb.url}/api/collections/${VIP_EMAIL_LINK_COLLECTION}/records?filter=${encodeURIComponent(filter)}&perPage=1`, { headers: { Authorization: `Bearer ${adminToken}` } } ); if (!listRes.ok) { return NextResponse.json({ ok: false, error: "vip_email_link 集合不存在,请在 PocketBase 中创建" }, { status: 500 }); } const listData = await listRes.json().catch(() => ({})); const items = listData?.items ?? []; const recordBody: Record = { email, user_id: userId }; if (/^user\d+$/.test(userId)) { recordBody.anonymous_user_id = userId; } if (items.length > 0) { await fetch(`${pb.url}/api/collections/${VIP_EMAIL_LINK_COLLECTION}/records/${items[0].id}`, { method: "PATCH", headers: { "Content-Type": "application/json", Authorization: `Bearer ${adminToken}`, }, body: JSON.stringify(recordBody), }); } else { const createRes = await fetch(`${pb.url}/api/collections/${VIP_EMAIL_LINK_COLLECTION}/records`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${adminToken}`, }, body: JSON.stringify(recordBody), }); if (!createRes.ok) { const err = await createRes.json().catch(() => ({})); return NextResponse.json({ ok: false, error: err?.message || "写入失败" }, { status: 500 }); } } console.log("[store-email-link] 成功 email=%s user_id=%s", email, userId); return NextResponse.json({ ok: true }); } catch (e) { console.error("[store-email-link] error:", e); return NextResponse.json( { ok: false, error: e instanceof Error ? e.message : "操作失败" }, { status: 500 } ); } }