From 3eb0334114b00c564c33cc3de3bdba6054b61e3d Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 27 Mar 2026 19:07:24 -0500 Subject: [PATCH] 's' --- .env.example | 9 ++++--- app/HomeClient.tsx | 4 --- app/HomeClientLoader.tsx | 7 +---- app/api/vip/_helpers.ts | 4 ++- app/api/vip/check-by-email/route.ts | 17 +++++++++---- app/api/vip/check-by-user/route.ts | 6 ++--- app/vip/components/landing/LoginModal.tsx | 2 +- config/services.config.ts | 31 +++++++++++++++++------ 8 files changed, 49 insertions(+), 31 deletions(-) diff --git a/.env.example b/.env.example index 65c3492..c9db7e2 100644 --- a/.env.example +++ b/.env.example @@ -1,13 +1,16 @@ # ========== 与 payjsapi/digital/cnomadcna 同源 ========== # 复制为 .env.local,PB/Memos 与 payjsapi 保持一致 # -# 本地无 PocketBase:仓库已提供写好的 .env.local(与 payjsapi/.env.local 配套)。 -# payjsapi 侧需 PB_INTERNAL_URL;Next 侧需 POCKETBASE_INTERNAL_URL,勿只配 NEXT_PUBLIC_*。 +# 本地无 PocketBase:next dev 下可用 POCKETBASE_INTERNAL_URL 指远程 PB。 +# next start 生产同机部署:服务端只用 POCKETBASE_URL(默认 http://127.0.0.1:8090),不会读取 POCKETBASE_INTERNAL_URL,避免误配公网域名拖慢 API。 +# payjsapi 生产请配本机 PB(如 PB_INTERNAL_URL=http://127.0.0.1:8090),勿让 payjs 再绕公网打 pocketbase 域名。 # ========== PocketBase ========== NEXT_PUBLIC_POCKETBASE_URL=https://pocketbase.hackrobot.cn -# 服务端直连 PB(本地未起 8090 时必填线上域名) +# 仅 next dev 服务端生效:远程 PB POCKETBASE_INTERNAL_URL=https://pocketbase.hackrobot.cn +# 仅 next start 生产服务端生效(同机示例) +# POCKETBASE_URL=http://127.0.0.1:8090 POCKETBASE_EMAIL=xiaoshuang.eric@gmail.com POCKETBASE_PASSWORD=Xiao4669805@ diff --git a/app/HomeClient.tsx b/app/HomeClient.tsx index d4355cc..c0df242 100644 --- a/app/HomeClient.tsx +++ b/app/HomeClient.tsx @@ -1,7 +1,6 @@ "use client"; import { useCallback, useEffect, useState } from "react"; -import { HomeBootOverlay } from "@/app/components/HomeBootOverlay"; import { triggerPay } from "@/app/lib/triggerPay"; import { getPayEnv, getDeviceFromEnv } from "@/app/lib/payEnv"; import { usePayStatusPoll } from "@/app/lib/usePayStatusPoll"; @@ -692,9 +691,6 @@ export default function HomeClient() { return ( <> - {!initDone && !isLoggedIn ? ( - - ) : null} {isLoggedIn ? ( diff --git a/app/HomeClientLoader.tsx b/app/HomeClientLoader.tsx index 90b1a26..07188f1 100644 --- a/app/HomeClientLoader.tsx +++ b/app/HomeClientLoader.tsx @@ -1,15 +1,10 @@ "use client"; import dynamic from "next/dynamic"; -import { HomeBootOverlay } from "@/app/components/HomeBootOverlay"; const HomeClient = dynamic(() => import("./HomeClient"), { ssr: false, - loading: () => ( -
- -
- ), + loading: () =>
, }); export function HomeClientLoader() { diff --git a/app/api/vip/_helpers.ts b/app/api/vip/_helpers.ts index d84e00f..5114d7e 100644 --- a/app/api/vip/_helpers.ts +++ b/app/api/vip/_helpers.ts @@ -1,7 +1,9 @@ import { SITE_ID, getPocketBaseConfig } from "@/config/services.config"; const VIP_EMAIL_LINK_COLLECTION = "vip_email_link"; -const PB_FETCH_TIMEOUT = Number(process.env.POCKETBASE_FETCH_TIMEOUT) || 10000; +const PB_FETCH_TIMEOUT = + Number(process.env.POCKETBASE_FETCH_TIMEOUT) || + (process.env.NODE_ENV === "development" ? 30_000 : 15_000); const PB_ADMIN_TOKEN_TTL_MS = Number(process.env.POCKETBASE_ADMIN_TOKEN_TTL_MS) || 55_000; let adminTokenCache: { token: string; expiresAt: number } | null = null; diff --git a/app/api/vip/check-by-email/route.ts b/app/api/vip/check-by-email/route.ts index bea6701..3af278c 100644 --- a/app/api/vip/check-by-email/route.ts +++ b/app/api/vip/check-by-email/route.ts @@ -38,10 +38,14 @@ type RecoveryResult = { const inflightEmailChecks = new Map>(); const PAYJS_CHECK_BY_EMAIL_TIMEOUT = - Number(process.env.PAYJSAPI_CHECK_BY_EMAIL_TIMEOUT) || 20000; + Number(process.env.PAYJSAPI_CHECK_BY_EMAIL_TIMEOUT) || 20_000; const PAYJS_CHECK_BY_EMAIL_RETRIES = Math.max(0, Number(process.env.PAYJSAPI_CHECK_BY_EMAIL_RETRIES) || 1); +const PB_USER_LOGIN_TIMEOUT_MS = + Number(process.env.POCKETBASE_USER_LOGIN_TIMEOUT_MS) || + (process.env.NODE_ENV === "development" ? 30_000 : 15_000); + function resolvePayApiUrl(request: NextRequest): string { const payConfig = getPaymentConfig(); const hostHeader = request.headers.get("host") || request.headers.get("x-forwarded-host"); @@ -154,6 +158,7 @@ async function loginPocketBaseUser( headers: { "Content-Type": "application/json" }, body: JSON.stringify({ identity: email, password }), cache: "no-store", + signal: AbortSignal.timeout(PB_USER_LOGIN_TIMEOUT_MS), }); if (!res.ok) { @@ -301,6 +306,11 @@ export async function POST(request: NextRequest) { } try { + const adminTokenPromise = getAdminToken().catch((error) => { + console.error("[check-by-email] getAdminToken failed:", error); + return null; + }); + let authData: { token: string; record: PBAuthRecord } | null = null; let loginError: string | null = null; try { @@ -339,10 +349,7 @@ export async function POST(request: NextRequest) { ); } - const adminToken = await getAdminToken().catch((error) => { - console.error("[check-by-email] getAdminToken failed:", error); - return null; - }); + const adminToken = await adminTokenPromise; if (!adminToken) { // 管理员 token 获取失败时,降级到 payjsapi 结果,避免直接 500 if (!payjsResult) { diff --git a/app/api/vip/check-by-user/route.ts b/app/api/vip/check-by-user/route.ts index 80a1fe5..6c32549 100644 --- a/app/api/vip/check-by-user/route.ts +++ b/app/api/vip/check-by-user/route.ts @@ -84,7 +84,7 @@ async function checkVipFromPayjsapi( userId )}`; console.log("[check-by-user] payjsapi request user_id=%s url=%s", userId, url); - const checkTimeout = Number(process.env.PAYJSAPI_CHECK_TIMEOUT) || 15000; + const checkTimeout = Number(process.env.PAYJSAPI_CHECK_TIMEOUT) || 15_000; const retries = Math.max(0, Number(process.env.PAYJSAPI_CHECK_RETRIES) || 1); const res = await fetchPayjsVipWithRetry(url, checkTimeout, retries); if (!res) { @@ -166,7 +166,7 @@ export async function POST(request: NextRequest) { let linkedUserId: string | undefined; let vipFromSite = false; let vipFromPayments = false; - let vipSource = vip ? "payjsapi" : "none"; + let vipSource = "none"; try { const adminToken = await getAdminToken().catch((error) => { @@ -206,7 +206,7 @@ export async function POST(request: NextRequest) { ); } - // PB 未命中再请求 payjsapi,避免每次都先走远端网络 + // 同机 PB 命中则不再打 payjsapi;仅未命中时再走 payjs(与线上极致路径一致) if (!vip) { payjsResult = await checkVipFromPayjsapi(request, userId); if (payjsResult?.vip) { diff --git a/app/vip/components/landing/LoginModal.tsx b/app/vip/components/landing/LoginModal.tsx index 05008b2..7a88eab 100644 --- a/app/vip/components/landing/LoginModal.tsx +++ b/app/vip/components/landing/LoginModal.tsx @@ -90,7 +90,7 @@ export function LoginModal({ open, onClose, onVerify, onVerifyByEmail, onSuccess
{verifyOverlay ? ( - + ) : null} ); diff --git a/config/services.config.ts b/config/services.config.ts index d381dd8..56a8c87 100644 --- a/config/services.config.ts +++ b/config/services.config.ts @@ -30,23 +30,38 @@ export interface MinioConfig { publicUrl: string; } +/** + * 服务端 PocketBase 基址: + * - 生产(next start):只认 `POCKETBASE_URL`,否则本机 8090。忽略 `POCKETBASE_INTERNAL_URL`,避免误配公网域名把同机 API 拖慢。 + * - 本地 next dev:可用 `POCKETBASE_INTERNAL_URL` 指向远程 PB(本机未起 8090)。 + */ export function getPocketBaseConfig(): PocketBaseConfig { const serverSide = typeof window === "undefined"; - const internal = process.env.POCKETBASE_INTERNAL_URL?.trim(); - if (serverSide && internal) { - return { url: internal.replace(/\/$/, "") }; - } - // 生产同机部署默认走本机 PocketBase,避免 DNS/公网抖动导致 fetch failed + if (serverSide) { + if (isDev) { + const devServerUrl = + process.env.POCKETBASE_INTERNAL_URL?.trim() || + process.env.POCKETBASE_URL?.trim(); + if (devServerUrl) { + return { url: devServerUrl.replace(/\/$/, "") }; + } + return { url: "http://127.0.0.1:8090" }; + } return { - url: (process.env.POCKETBASE_URL || "http://127.0.0.1:8090").replace(/\/$/, ""), + url: (process.env.POCKETBASE_URL || "http://127.0.0.1:8090").replace( + /\/$/, + "" + ), }; } + return { - url: + url: ( process.env.NEXT_PUBLIC_POCKETBASE_URL || process.env.POCKETBASE_URL || - "https://pocketbase.hackrobot.cn", + "https://pocketbase.hackrobot.cn" + ).replace(/\/$/, ""), }; }