's'
This commit is contained in:
205
app/api/vip/check-by-user/route.ts
Normal file
205
app/api/vip/check-by-user/route.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getApiUrlFromRequest, getPaymentConfig } from "@/config/services.config";
|
||||
import {
|
||||
checkPaymentsByUserId,
|
||||
checkSiteVipByUserId,
|
||||
findLinkedMemberFromOrder,
|
||||
findVipEmailLinkByAnonymousUserId,
|
||||
getAdminToken,
|
||||
isAnonymousUserId,
|
||||
upsertVipEmailLink,
|
||||
} from "@/app/api/vip/_helpers";
|
||||
|
||||
type PayjsVipResult = {
|
||||
vip: boolean;
|
||||
email?: string;
|
||||
};
|
||||
|
||||
function resolvePayApiUrl(request: NextRequest): string {
|
||||
const payConfig = getPaymentConfig();
|
||||
const hostHeader = request.headers.get("host") || request.headers.get("x-forwarded-host");
|
||||
return (
|
||||
(hostHeader ? getApiUrlFromRequest(hostHeader) : null) ||
|
||||
process.env.PAYMENT_API_URL ||
|
||||
process.env.NEXT_PUBLIC_API_URL ||
|
||||
payConfig.apiUrl
|
||||
).replace(/\/$/, "");
|
||||
}
|
||||
|
||||
async function checkVipFromPayjsapi(
|
||||
request: NextRequest,
|
||||
userId: string
|
||||
): Promise<PayjsVipResult | null> {
|
||||
try {
|
||||
const apiUrl = resolvePayApiUrl(request);
|
||||
const url = `${apiUrl}/nomadvip/check_vip?user_id=${encodeURIComponent(
|
||||
userId
|
||||
)}`;
|
||||
console.log("[check-by-user] payjsapi request user_id=%s url=%s", userId, url);
|
||||
const res = await fetch(
|
||||
url,
|
||||
{ cache: "no-store", signal: AbortSignal.timeout(5000) }
|
||||
);
|
||||
if (!res.ok) {
|
||||
console.log(
|
||||
"[check-by-user] payjsapi non-ok user_id=%s status=%s",
|
||||
userId,
|
||||
res.status
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await res.json().catch(() => ({}));
|
||||
const result = {
|
||||
vip: !!data?.vip,
|
||||
email:
|
||||
typeof data?.email === "string" && data.email.trim()
|
||||
? data.email.trim().toLowerCase()
|
||||
: undefined,
|
||||
};
|
||||
console.log(
|
||||
"[check-by-user] payjsapi response user_id=%s vip=%s email=%s",
|
||||
userId,
|
||||
result.vip,
|
||||
result.email ?? "(none)"
|
||||
);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"[check-by-user] payjsapi request failed user_id=%s error=%s",
|
||||
userId,
|
||||
error instanceof Error ? error.message : String(error)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json().catch(() => ({}));
|
||||
const userId = String(body?.user_id ?? body?.userId ?? "").trim();
|
||||
|
||||
console.log("[check-by-user] request user_id=%s", userId || "(empty)");
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json(
|
||||
{ ok: false, vip: false, error: "缺少 user_id" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const payjsResult = await checkVipFromPayjsapi(request, userId);
|
||||
let vip = !!payjsResult?.vip;
|
||||
let email = payjsResult?.email ?? null;
|
||||
let linkedUserId: string | undefined;
|
||||
let vipFromSite = false;
|
||||
let vipFromPayments = false;
|
||||
let vipSource = vip ? "payjsapi" : "none";
|
||||
|
||||
try {
|
||||
if (vip) {
|
||||
console.log(
|
||||
"[check-by-user] hit payjsapi user_id=%s alreadyLinked=%s",
|
||||
userId,
|
||||
!!email
|
||||
);
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
vip: true,
|
||||
alreadyLinked: !!email,
|
||||
email: email ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
const adminToken = await getAdminToken();
|
||||
if (!adminToken) {
|
||||
console.error("[check-by-user] missing PocketBase admin token");
|
||||
return NextResponse.json({ ok: false, vip: false }, { status: 500 });
|
||||
}
|
||||
|
||||
if (!vip) {
|
||||
vipFromSite = await checkSiteVipByUserId(adminToken, userId);
|
||||
vip = vipFromSite;
|
||||
if (!vip) {
|
||||
vipFromPayments = await checkPaymentsByUserId(adminToken, userId);
|
||||
vip = vipFromPayments;
|
||||
}
|
||||
if (vipFromSite) {
|
||||
vipSource = "pocketbase:site_vip";
|
||||
} else if (vipFromPayments) {
|
||||
vipSource = "pocketbase:payments";
|
||||
}
|
||||
console.log(
|
||||
"[check-by-user] pb fallback user_id=%s site_vip=%s payments=%s",
|
||||
userId,
|
||||
vipFromSite,
|
||||
vipFromPayments
|
||||
);
|
||||
}
|
||||
|
||||
if (vip && !email && isAnonymousUserId(userId)) {
|
||||
const linkRecord = await findVipEmailLinkByAnonymousUserId(
|
||||
adminToken,
|
||||
userId
|
||||
);
|
||||
const linkEmail =
|
||||
typeof linkRecord?.email === "string"
|
||||
? linkRecord.email.trim().toLowerCase()
|
||||
: "";
|
||||
const linkUserId =
|
||||
typeof linkRecord?.user_id === "string" ? linkRecord.user_id.trim() : "";
|
||||
|
||||
if (linkEmail) {
|
||||
email = linkEmail;
|
||||
if (linkUserId && !isAnonymousUserId(linkUserId)) {
|
||||
linkedUserId = linkUserId;
|
||||
}
|
||||
vipSource = "vip_email_link";
|
||||
console.log(
|
||||
"[check-by-user] linked by vip_email_link user_id=%s linkedUserId=%s email=%s",
|
||||
userId,
|
||||
linkedUserId || "(none)",
|
||||
email
|
||||
);
|
||||
} else {
|
||||
const linkedMember = await findLinkedMemberFromOrder(adminToken, userId);
|
||||
if (linkedMember) {
|
||||
email = linkedMember.email;
|
||||
linkedUserId = linkedMember.userId;
|
||||
await upsertVipEmailLink(
|
||||
adminToken,
|
||||
linkedMember.email,
|
||||
linkedMember.userId,
|
||||
userId
|
||||
);
|
||||
vipSource = "linked_from_order";
|
||||
console.log(
|
||||
"[check-by-user] linked by order user_id=%s linkedUserId=%s email=%s",
|
||||
userId,
|
||||
linkedUserId,
|
||||
email
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[check-by-user] result user_id=%s vip=%s source=%s alreadyLinked=%s linkedUserId=%s",
|
||||
userId,
|
||||
vip,
|
||||
vipSource,
|
||||
!!email,
|
||||
linkedUserId || "(none)"
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
vip,
|
||||
alreadyLinked: !!email,
|
||||
email: email ?? undefined,
|
||||
linkedUserId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[check-by-user] failed:", error);
|
||||
return NextResponse.json({ ok: false, vip: false }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user