149 lines
5.0 KiB
TypeScript
149 lines
5.0 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { getPocketBaseConfig, SITE_ID } from "@/config/services.config";
|
|
|
|
async function getAdminToken(): Promise<string | null> {
|
|
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;
|
|
}
|
|
|
|
/** 将匿名 user_id 的支付记录关联到新创建的 PB 用户 */
|
|
async function linkPaymentsToUser(
|
|
adminToken: string,
|
|
fromUserId: string,
|
|
toUserId: string
|
|
): Promise<void> {
|
|
const pb = getPocketBaseConfig();
|
|
const filter = `user_id = "${fromUserId}"`;
|
|
const res = await fetch(
|
|
`${pb.url}/api/collections/payments/records?filter=${encodeURIComponent(filter)}&perPage=500`,
|
|
{ headers: { Authorization: `Bearer ${adminToken}` } }
|
|
);
|
|
if (!res.ok) return;
|
|
const data = await res.json();
|
|
const items = data?.items ?? [];
|
|
for (const item of items) {
|
|
await fetch(`${pb.url}/api/collections/payments/records/${item.id}`, {
|
|
method: "PATCH",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${adminToken}`,
|
|
},
|
|
body: JSON.stringify({ user_id: toUserId }),
|
|
});
|
|
}
|
|
}
|
|
|
|
async function linkSiteVipToUser(
|
|
adminToken: string,
|
|
fromUserId: string,
|
|
toUserId: string
|
|
): Promise<void> {
|
|
const pb = getPocketBaseConfig();
|
|
const filter = `user_id = "${fromUserId}" && site_id = "${SITE_ID}"`;
|
|
const res = await fetch(
|
|
`${pb.url}/api/collections/site_vip/records?filter=${encodeURIComponent(filter)}&perPage=500`,
|
|
{ headers: { Authorization: `Bearer ${adminToken}` } }
|
|
);
|
|
if (!res.ok) return;
|
|
const data = await res.json();
|
|
const items = data?.items ?? [];
|
|
for (const item of items) {
|
|
await fetch(`${pb.url}/api/collections/site_vip/records/${item.id}`, {
|
|
method: "PATCH",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${adminToken}`,
|
|
},
|
|
body: JSON.stringify({ user_id: toUserId }),
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function POST(request: NextRequest) {
|
|
const body = await request.json().catch(() => ({}));
|
|
const email = String(body?.email ?? "").trim();
|
|
const password = String(body?.password ?? "").trim();
|
|
const anonymousUserId = String(body?.anonymousUserId ?? "").trim();
|
|
|
|
if (!email || !password) {
|
|
return NextResponse.json({ ok: false, error: "请填写邮箱和密码" }, { status: 400 });
|
|
}
|
|
if (!anonymousUserId || !anonymousUserId.startsWith("user")) {
|
|
return NextResponse.json({ ok: false, error: "无效的匿名用户ID" }, { status: 400 });
|
|
}
|
|
if (password.length < 8) {
|
|
return NextResponse.json({ ok: false, error: "密码至少 8 位" }, { status: 400 });
|
|
}
|
|
|
|
const pb = getPocketBaseConfig();
|
|
|
|
// 1. 尝试注册,若已存在则登录
|
|
let token: string;
|
|
let record: { id: string; email?: string };
|
|
try {
|
|
const registerRes = await fetch(`${pb.url}/api/collections/users/records`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ email, password, passwordConfirm: password }),
|
|
});
|
|
if (registerRes.ok) {
|
|
const regData = await registerRes.json();
|
|
const loginRes = await fetch(`${pb.url}/api/collections/users/auth-with-password`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ identity: email, password }),
|
|
});
|
|
if (!loginRes.ok) throw new Error("登录失败");
|
|
const loginData = await loginRes.json();
|
|
token = loginData.token;
|
|
record = loginData.record;
|
|
} else {
|
|
const loginRes = await fetch(`${pb.url}/api/collections/users/auth-with-password`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ identity: email, password }),
|
|
});
|
|
if (!loginRes.ok) {
|
|
const err = await loginRes.json().catch(() => ({}));
|
|
return NextResponse.json(
|
|
{ ok: false, error: err?.message || "邮箱或密码错误" },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
const loginData = await loginRes.json();
|
|
token = loginData.token;
|
|
record = loginData.record;
|
|
}
|
|
} catch (e) {
|
|
return NextResponse.json(
|
|
{ ok: false, error: e instanceof Error ? e.message : "注册/登录失败" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
const newUserId = record.id;
|
|
|
|
// 2. 用管理员 token 将 payments、site_vip 的 user_id 从匿名改为新用户
|
|
const adminToken = await getAdminToken();
|
|
if (adminToken) {
|
|
await linkPaymentsToUser(adminToken, anonymousUserId, newUserId);
|
|
await linkSiteVipToUser(adminToken, anonymousUserId, newUserId);
|
|
}
|
|
|
|
return NextResponse.json({
|
|
ok: true,
|
|
token,
|
|
record: { id: record.id, email: record.email },
|
|
});
|
|
}
|