From db65182dc1fbc5054720b94e5cae5cdf8ce51800 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 26 Mar 2026 11:08:35 -0500 Subject: [PATCH] 's' --- .env.example | 4 ++ app/api/vip/check-by-email/route.ts | 99 +++++++++++++++++------------ config/services.config.ts | 7 +- 3 files changed, 67 insertions(+), 43 deletions(-) diff --git a/.env.example b/.env.example index 6000a87..b202c6c 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,8 @@ # ========== PocketBase ========== NEXT_PUBLIC_POCKETBASE_URL=https://pocketbase.hackrobot.cn +# 服务端 API 可优先走内网/本机,避免公网超时 +# POCKETBASE_INTERNAL_URL=http://127.0.0.1:8090 POCKETBASE_EMAIL=xiaoshuang.eric@gmail.com POCKETBASE_PASSWORD=Xiao4669805@ @@ -15,6 +17,8 @@ NEXT_PUBLIC_API_URL=http://127.0.0.1:8007 # PAYMENT_API_INTERNAL_URL=http://127.0.0.1:8007 # 内网/慢网络可适当提高超时(毫秒):check_vip 默认 15s,payh5/redirect 默认 60s # PAYJSAPI_CHECK_TIMEOUT=15000 +# PAYJSAPI_CHECK_BY_EMAIL_TIMEOUT=15000 +# PAYJSAPI_CHECK_BY_EMAIL_RETRIES=1 # PAYJSAPI_PAYH5_TIMEOUT=60000 # PAYJSAPI_REDIRECT_TIMEOUT=60000 PAYMENT_PROVIDER=zpay diff --git a/app/api/vip/check-by-email/route.ts b/app/api/vip/check-by-email/route.ts index adf9d3c..dda9566 100644 --- a/app/api/vip/check-by-email/route.ts +++ b/app/api/vip/check-by-email/route.ts @@ -37,6 +37,8 @@ type RecoveryResult = { const PAYJS_CHECK_BY_EMAIL_TIMEOUT = Number(process.env.PAYJSAPI_CHECK_BY_EMAIL_TIMEOUT) || 5000; +const PAYJS_CHECK_BY_EMAIL_RETRIES = + Math.max(0, Number(process.env.PAYJSAPI_CHECK_BY_EMAIL_RETRIES) || 1); function resolvePayApiUrl(request: NextRequest): string { const payConfig = getPaymentConfig(); @@ -53,51 +55,64 @@ async function checkVipByEmailFromPayjsapi( password: string, userId?: string ): Promise { - try { - const apiUrl = resolvePayApiUrl(request); - const res = await fetch(`${apiUrl}/nomadvip/check_vip_by_email`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - email, - password, - ...(userId ? { user_id: userId } : {}), - }), - cache: "no-store", - signal: AbortSignal.timeout(PAYJS_CHECK_BY_EMAIL_TIMEOUT), - }); - if (!res.ok) { - return null; - } + const payConfig = getPaymentConfig(); + const primaryUrl = resolvePayApiUrl(request); + const candidates = Array.from( + new Set([primaryUrl, payConfig.apiUrl].map((x) => (x || "").trim().replace(/\/$/, "")).filter(Boolean)) + ); - const data = await res.json().catch(() => ({})); - if (!data?.vip) { - return { - vip: false, - error: - typeof data?.error === "string" && data.error.trim() - ? data.error.trim() - : undefined, - }; - } + for (const apiUrl of candidates) { + for (let i = 0; i <= PAYJS_CHECK_BY_EMAIL_RETRIES; i += 1) { + try { + const res = await fetch(`${apiUrl}/nomadvip/check_vip_by_email`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + email, + password, + ...(userId ? { user_id: userId } : {}), + }), + cache: "no-store", + signal: AbortSignal.timeout(PAYJS_CHECK_BY_EMAIL_TIMEOUT), + }); + if (!res.ok) { + continue; + } - return { - vip: true, - effectiveUserId: - typeof data?.effectiveUserId === "string" - ? data.effectiveUserId.trim() - : undefined, - anonymousUserId: - typeof data?.anonymousUserId === "string" - ? data.anonymousUserId.trim() - : undefined, - token: typeof data?.token === "string" ? data.token : undefined, - record: data?.record?.id ? data.record : undefined, - }; - } catch (error) { - console.log("[check-by-email] payjsapi request failed:", error); - return null; + const data = await res.json().catch(() => ({})); + if (!data?.vip) { + return { + vip: false, + error: + typeof data?.error === "string" && data.error.trim() + ? data.error.trim() + : undefined, + }; + } + + return { + vip: true, + effectiveUserId: + typeof data?.effectiveUserId === "string" + ? data.effectiveUserId.trim() + : undefined, + anonymousUserId: + typeof data?.anonymousUserId === "string" + ? data.anonymousUserId.trim() + : undefined, + token: typeof data?.token === "string" ? data.token : undefined, + record: data?.record?.id ? data.record : undefined, + }; + } catch (error) { + if (i >= PAYJS_CHECK_BY_EMAIL_RETRIES) { + console.log("[check-by-email] payjsapi request failed url=%s error=%o", apiUrl, error); + } else { + await new Promise((resolve) => setTimeout(resolve, 250 * (i + 1))); + } + } + } } + return null; } async function loginPocketBaseUser( diff --git a/config/services.config.ts b/config/services.config.ts index 8444509..6ea7b3d 100644 --- a/config/services.config.ts +++ b/config/services.config.ts @@ -31,10 +31,15 @@ export interface MinioConfig { } export function getPocketBaseConfig(): PocketBaseConfig { + const serverSide = typeof window === "undefined"; + const internal = process.env.POCKETBASE_INTERNAL_URL?.trim(); + if (serverSide && internal) { + return { url: internal.replace(/\/$/, "") }; + } return { url: - process.env.NEXT_PUBLIC_POCKETBASE_URL || process.env.POCKETBASE_URL || + process.env.NEXT_PUBLIC_POCKETBASE_URL || "https://pocketbase.hackrobot.cn", }; }