's'
This commit is contained in:
@@ -32,6 +32,9 @@ type RecoveryResult = {
|
|||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PAYJS_CHECK_BY_EMAIL_TIMEOUT =
|
||||||
|
Number(process.env.PAYJSAPI_CHECK_BY_EMAIL_TIMEOUT) || 5000;
|
||||||
|
|
||||||
function resolvePayApiUrl(request: NextRequest): string {
|
function resolvePayApiUrl(request: NextRequest): string {
|
||||||
const payConfig = getPaymentConfig();
|
const payConfig = getPaymentConfig();
|
||||||
const hostHeader = request.headers.get("host") || request.headers.get("x-forwarded-host");
|
const hostHeader = request.headers.get("host") || request.headers.get("x-forwarded-host");
|
||||||
@@ -58,7 +61,7 @@ async function checkVipByEmailFromPayjsapi(
|
|||||||
...(userId ? { user_id: userId } : {}),
|
...(userId ? { user_id: userId } : {}),
|
||||||
}),
|
}),
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
signal: AbortSignal.timeout(15000),
|
signal: AbortSignal.timeout(PAYJS_CHECK_BY_EMAIL_TIMEOUT),
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
return null;
|
return null;
|
||||||
@@ -121,23 +124,26 @@ async function resolveRecovery(
|
|||||||
requestedAnonymousUserId?: string,
|
requestedAnonymousUserId?: string,
|
||||||
payjsResult?: PayjsVipEmailResult | null
|
payjsResult?: PayjsVipEmailResult | null
|
||||||
): Promise<RecoveryResult> {
|
): Promise<RecoveryResult> {
|
||||||
if (
|
if (isAnonymousUserId(requestedAnonymousUserId)) {
|
||||||
isAnonymousUserId(requestedAnonymousUserId) &&
|
const requestUserHasVip = await hasVipByUserId(adminToken, requestedAnonymousUserId);
|
||||||
(await hasVipByUserId(adminToken, requestedAnonymousUserId))
|
if (requestUserHasVip) {
|
||||||
) {
|
return {
|
||||||
return {
|
vip: true,
|
||||||
vip: true,
|
matchedUserId: requestedAnonymousUserId,
|
||||||
matchedUserId: requestedAnonymousUserId,
|
anonymousUserId: requestedAnonymousUserId,
|
||||||
anonymousUserId: requestedAnonymousUserId,
|
source: "request_user_id",
|
||||||
source: "request_user_id",
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const linkRecord = await findVipEmailLinkByEmail(adminToken, email);
|
const [linkRecord, fallbackAnonymousUserId] = await Promise.all([
|
||||||
|
findVipEmailLinkByEmail(adminToken, email),
|
||||||
|
findAnonymousUserIdForUser(adminToken, pbUserId),
|
||||||
|
]);
|
||||||
const linkedAnonymousUserId =
|
const linkedAnonymousUserId =
|
||||||
typeof linkRecord?.anonymous_user_id === "string"
|
typeof linkRecord?.anonymous_user_id === "string"
|
||||||
? linkRecord.anonymous_user_id.trim()
|
? linkRecord.anonymous_user_id.trim()
|
||||||
: (await findAnonymousUserIdForUser(adminToken, pbUserId)) ?? "";
|
: fallbackAnonymousUserId ?? "";
|
||||||
const linkedIds = Array.from(
|
const linkedIds = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
[
|
[
|
||||||
@@ -148,24 +154,30 @@ async function resolveRecovery(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const linkedId of linkedIds) {
|
if (linkedIds.length) {
|
||||||
if (!(await hasVipByUserId(adminToken, linkedId))) {
|
const linkedVipChecks = await Promise.all(
|
||||||
continue;
|
linkedIds.map(async (linkedId) => ({
|
||||||
|
linkedId,
|
||||||
|
vip: await hasVipByUserId(adminToken, linkedId),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const hit = linkedVipChecks.find((item) => item.vip);
|
||||||
|
if (hit) {
|
||||||
|
return {
|
||||||
|
vip: true,
|
||||||
|
matchedUserId: hit.linkedId,
|
||||||
|
anonymousUserId: isAnonymousUserId(linkedAnonymousUserId)
|
||||||
|
? linkedAnonymousUserId
|
||||||
|
: isAnonymousUserId(hit.linkedId)
|
||||||
|
? hit.linkedId
|
||||||
|
: undefined,
|
||||||
|
source: "vip_email_link",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
vip: true,
|
|
||||||
matchedUserId: linkedId,
|
|
||||||
anonymousUserId: isAnonymousUserId(linkedAnonymousUserId)
|
|
||||||
? linkedAnonymousUserId
|
|
||||||
: isAnonymousUserId(linkedId)
|
|
||||||
? linkedId
|
|
||||||
: undefined,
|
|
||||||
source: "vip_email_link",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await hasVipByUserId(adminToken, pbUserId)) {
|
const pbUserHasVip = await hasVipByUserId(adminToken, pbUserId);
|
||||||
|
if (pbUserHasVip) {
|
||||||
return {
|
return {
|
||||||
vip: true,
|
vip: true,
|
||||||
matchedUserId: pbUserId,
|
matchedUserId: pbUserId,
|
||||||
@@ -186,22 +198,27 @@ async function resolveRecovery(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const candidate of payjsCandidates) {
|
if (payjsCandidates.length) {
|
||||||
if (!(await hasVipByUserId(adminToken, candidate))) {
|
const payjsVipChecks = await Promise.all(
|
||||||
continue;
|
payjsCandidates.map(async (candidate) => ({
|
||||||
|
candidate,
|
||||||
|
vip: await hasVipByUserId(adminToken, candidate),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const hit = payjsVipChecks.find((item) => item.vip);
|
||||||
|
if (hit) {
|
||||||
|
return {
|
||||||
|
vip: true,
|
||||||
|
matchedUserId: hit.candidate,
|
||||||
|
anonymousUserId: isAnonymousUserId(hit.candidate)
|
||||||
|
? hit.candidate
|
||||||
|
: typeof payjsResult?.anonymousUserId === "string" &&
|
||||||
|
isAnonymousUserId(payjsResult.anonymousUserId.trim())
|
||||||
|
? payjsResult.anonymousUserId.trim()
|
||||||
|
: undefined,
|
||||||
|
source: "payjsapi",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
vip: true,
|
|
||||||
matchedUserId: candidate,
|
|
||||||
anonymousUserId: isAnonymousUserId(candidate)
|
|
||||||
? candidate
|
|
||||||
: typeof payjsResult?.anonymousUserId === "string" &&
|
|
||||||
isAnonymousUserId(payjsResult.anonymousUserId.trim())
|
|
||||||
? payjsResult.anonymousUserId.trim()
|
|
||||||
: undefined,
|
|
||||||
source: "payjsapi",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { vip: false };
|
return { vip: false };
|
||||||
@@ -229,8 +246,10 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authData = await loginPocketBaseUser(email, password);
|
const [authData, adminToken] = await Promise.all([
|
||||||
const adminToken = await getAdminToken();
|
loginPocketBaseUser(email, password),
|
||||||
|
getAdminToken(),
|
||||||
|
]);
|
||||||
if (!adminToken) {
|
if (!adminToken) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ ok: false, vip: false, error: "PocketBase 管理员认证失败" },
|
{ ok: false, vip: false, error: "PocketBase 管理员认证失败" },
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
|
|
||||||
:root,
|
:root,
|
||||||
.light {
|
.light {
|
||||||
|
--font-geist-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
|
||||||
|
"Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif,
|
||||||
|
"Apple Color Emoji", "Segoe UI Emoji";
|
||||||
|
--font-geist-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||||
|
"Liberation Mono", "Courier New", monospace;
|
||||||
--background: #fafafa;
|
--background: #fafafa;
|
||||||
--foreground: #171717;
|
--foreground: #171717;
|
||||||
--card: #ffffff;
|
--card: #ffffff;
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { GeistSans } from "geist/font/sans";
|
|
||||||
import { GeistMono } from "geist/font/mono";
|
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { ThemeProvider } from "./providers/ThemeProvider";
|
import { ThemeProvider } from "./providers/ThemeProvider";
|
||||||
import { themeInitScript } from "./theme-init";
|
import { themeInitScript } from "./theme-init";
|
||||||
|
|
||||||
const geistSans = GeistSans;
|
|
||||||
const geistMono = GeistMono;
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "异度星球",
|
title: "异度星球",
|
||||||
description: "数字游民社群",
|
description: "数字游民社群",
|
||||||
@@ -20,9 +15,7 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="zh-CN" suppressHydrationWarning>
|
<html lang="zh-CN" suppressHydrationWarning>
|
||||||
<body
|
<body className="antialiased">
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
||||||
>
|
|
||||||
<script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
|
<script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
|
||||||
<ThemeProvider>{children}</ThemeProvider>
|
<ThemeProvider>{children}</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user