This commit is contained in:
eric
2026-03-26 11:32:13 -05:00
parent 8c865cf0e8
commit f5068c0ead
3 changed files with 40 additions and 1 deletions

View File

@@ -35,6 +35,8 @@ type RecoveryResult = {
source?: string; source?: string;
}; };
const inflightEmailChecks = new Map<string, Promise<PayjsVipEmailResult | null>>();
const PAYJS_CHECK_BY_EMAIL_TIMEOUT = const PAYJS_CHECK_BY_EMAIL_TIMEOUT =
Number(process.env.PAYJSAPI_CHECK_BY_EMAIL_TIMEOUT) || 20000; Number(process.env.PAYJSAPI_CHECK_BY_EMAIL_TIMEOUT) || 20000;
const PAYJS_CHECK_BY_EMAIL_RETRIES = const PAYJS_CHECK_BY_EMAIL_RETRIES =
@@ -62,6 +64,13 @@ async function checkVipByEmailFromPayjsapi(
password: string, password: string,
userId?: string userId?: string
): Promise<PayjsVipEmailResult | null> { ): Promise<PayjsVipEmailResult | null> {
const inflightKey = `${email}::${userId || ""}`;
const existed = inflightEmailChecks.get(inflightKey);
if (existed) {
return existed;
}
const task = (async () => {
const payConfig = getPaymentConfig(); const payConfig = getPaymentConfig();
const primaryUrl = resolvePayApiUrl(request); const primaryUrl = resolvePayApiUrl(request);
const localUrl = getLocalPayApiUrl(); const localUrl = getLocalPayApiUrl();
@@ -125,6 +134,14 @@ async function checkVipByEmailFromPayjsapi(
} }
} }
return null; return null;
})();
inflightEmailChecks.set(inflightKey, task);
try {
return await task;
} finally {
inflightEmailChecks.delete(inflightKey);
}
} }
async function loginPocketBaseUser( async function loginPocketBaseUser(

View File

@@ -15,6 +15,8 @@ type PayjsVipResult = {
email?: string; email?: string;
}; };
const inflightUserChecks = new Map<string, Promise<PayjsVipResult | null>>();
async function fetchPayjsVipWithRetry( async function fetchPayjsVipWithRetry(
url: string, url: string,
timeoutMs: number, timeoutMs: number,
@@ -51,6 +53,12 @@ async function checkVipFromPayjsapi(
request: NextRequest, request: NextRequest,
userId: string userId: string
): Promise<PayjsVipResult | null> { ): Promise<PayjsVipResult | null> {
const existed = inflightUserChecks.get(userId);
if (existed) {
return existed;
}
const task = (async () => {
try { try {
const apiUrl = resolvePayApiUrl(request); const apiUrl = resolvePayApiUrl(request);
const url = `${apiUrl}/nomadvip/check_vip?user_id=${encodeURIComponent( const url = `${apiUrl}/nomadvip/check_vip?user_id=${encodeURIComponent(
@@ -95,6 +103,14 @@ async function checkVipFromPayjsapi(
); );
return null; return null;
} }
})();
inflightUserChecks.set(userId, task);
try {
return await task;
} finally {
inflightUserChecks.delete(userId);
}
} }
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {

View File

@@ -15,6 +15,8 @@ const ANONYMOUS_USER_COOKIE = "nomadvip_anon_uid";
const ANONYMOUS_USER_COOKIE_MAX_AGE = 60 * 60 * 24 * 30; const ANONYMOUS_USER_COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
const PAY_ORDER_COOKIE = "nomadvip_pay_order"; const PAY_ORDER_COOKIE = "nomadvip_pay_order";
const PAY_ORDER_MAX_AGE = 60 * 15; const PAY_ORDER_MAX_AGE = 60 * 15;
const EMAIL_VERIFY_TS_KEY = "emailVerifyAt";
const EMAIL_VERIFY_COOLDOWN_MS = 60 * 1000;
function getStorage(key: string): string | null { function getStorage(key: string): string | null {
if (typeof window === "undefined") return null; if (typeof window === "undefined") return null;
@@ -174,6 +176,7 @@ export default function Home() {
savePaidType("vip"); savePaidType("vip");
setUserEmail(normalizedEmail); setUserEmail(normalizedEmail);
saveUserName(normalizedEmail); saveUserName(normalizedEmail);
setStorage(EMAIL_VERIFY_TS_KEY, String(Date.now()));
return true; return true;
}, },
[savePaidType, saveUserName] [savePaidType, saveUserName]
@@ -485,7 +488,10 @@ export default function Home() {
// 校验逻辑:有本地存储(登录资料+VIP)时优先使用并校验;无身份/VIP 时点击按钮直接进入支付,不校验 // 校验逻辑:有本地存储(登录资料+VIP)时优先使用并校验;无身份/VIP 时点击按钮直接进入支付,不校验
const anonymousCandidate = getStoredAnonymousUserId(); const anonymousCandidate = getStoredAnonymousUserId();
const hadStoredUserId = !!anonymousCandidate; const hadStoredUserId = !!anonymousCandidate;
if (anonymousCandidate) { const emailVerifiedAt = Number(getStorage(EMAIL_VERIFY_TS_KEY) || "0");
const skipAnonymousRecover =
!!(getStorage("emailLinked") === "1" && Date.now() - emailVerifiedAt < EMAIL_VERIFY_COOLDOWN_MS);
if (anonymousCandidate && !skipAnonymousRecover) {
const recovered = await tryRecoverByAnonymousUserId(anonymousCandidate); const recovered = await tryRecoverByAnonymousUserId(anonymousCandidate);
if (recovered) { if (recovered) {
setStorage("userId", anonymousCandidate); setStorage("userId", anonymousCandidate);