This commit is contained in:
eric
2026-03-12 03:55:17 -05:00
parent 7e8f0e3e40
commit 2a9bb8330c
18 changed files with 767 additions and 72 deletions

View File

@@ -8,6 +8,7 @@ import { usePayStatusPoll } from "@/app/lib/usePayStatusPoll";
import { VipLayout } from "@/app/vip/components/VipLayout";
import { LandingPage } from "@/app/vip/components/LandingPage";
import { DashboardPage } from "@/app/vip/components/DashboardPage";
import { PayAuthModal } from "@/app/vip/components/landing/PayAuthModal";
import styles from "@/app/vip/components/vip.module.css";
const PB_URL =
@@ -168,7 +169,19 @@ export default function Home() {
setUserName(name);
}, []);
const getOrCreateUserId = useCallback(() => {
const getOrCreateUserId = useCallback(async () => {
// 优先使用 PocketBase 用户 ID(已登录时)
try {
const meRes = await fetch("/api/auth/me");
const meData = await meRes.json();
if (meData?.user?.id) {
const pbUserId = meData.user.id;
setStorage("userId", pbUserId);
return pbUserId;
}
} catch {
/* ignore */
}
let userId = getStorage("userId");
if (userId?.startsWith("{") && userId.includes("data")) {
try {
@@ -215,6 +228,37 @@ export default function Home() {
[savePaidType, saveUserName]
);
/** 会员账号恢复:通过邮箱+密码验证,关联 users 后检查 site_vip / payments */
const checkVerifyByEmail = useCallback(
async (email: string, password: string): Promise<boolean> => {
try {
const { pbLogin, pbSaveAuth } = await import("@/app/lib/pocketbase");
const result = await pbLogin(email, password);
pbSaveAuth(result.token, result.record);
await fetch("/api/auth/sync-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: result.token, record: result.record }),
});
const userId = result.record.id;
const vipRes = await fetch("/api/vip/check");
const vipData = await vipRes.json();
if (vipData?.vip) {
setStorage("userId", userId);
savePaidType("vip");
saveUserName(email);
return true;
}
const found = await checkPaymentFromPB(userId);
if (found) return true;
return false;
} catch {
return false;
}
},
[checkPaymentFromPB, savePaidType, saveUserName]
);
const clearAllStorage = useCallback(() => {
try {
if (typeof window === "undefined") return;
@@ -236,8 +280,10 @@ export default function Home() {
}
}, []);
const handlePay = useCallback(async () => {
const userId = getOrCreateUserId();
const [authForPayOpen, setAuthForPayOpen] = useState(false);
const doPay = useCallback(async () => {
const userId = await getOrCreateUserId();
const returnUrl = `${window.location.origin}/paid`;
const env = getPayEnv();
const device = getDeviceFromEnv(env);
@@ -259,6 +305,20 @@ export default function Home() {
}
}, [getOrCreateUserId]);
const handlePay = useCallback(async () => {
try {
const meRes = await fetch("/api/auth/me");
const meData = await meRes.json();
if (meData?.user?.id) {
doPay();
return;
}
} catch {
/* ignore */
}
setAuthForPayOpen(true);
}, [doPay]);
const handleVerifySuccess = useCallback(() => {
window.location.reload();
}, []);
@@ -297,7 +357,7 @@ export default function Home() {
}
const init = async () => {
getOrCreateUserId();
await getOrCreateUserId();
const localType = getStorage("paidType");
const localUserName = getStorage("userName");
@@ -308,6 +368,26 @@ export default function Home() {
return;
}
// SSO: 优先检查根域 Cookie(跨站登录态)
try {
const meRes = await fetch("/api/auth/me");
const meData = await meRes.json();
if (meData?.user && meData?.token) {
const { pbSaveAuth } = await import("@/app/lib/pocketbase");
pbSaveAuth(meData.token, { id: meData.user.id, email: meData.user.email });
const vipRes = await fetch("/api/vip/check");
const vipData = await vipRes.json();
if (vipData?.vip) {
savePaidType("vip");
saveUserName(meData.user.email || meData.user.id);
setLoading(false);
return;
}
}
} catch {
/* ignore */
}
const userId = getStorage("userId");
if (userId) {
const found = await checkPaymentFromPB(userId);
@@ -355,17 +435,34 @@ export default function Home() {
);
}
const handleLogout = useCallback(async () => {
const { pbLogout } = await import("@/app/lib/pocketbase");
await pbLogout();
clearAllStorage();
}, [clearAllStorage]);
return (
<VipLayout isLoggedIn={isLoggedIn} userName={userName}>
{isLoggedIn ? (
<DashboardPage userName={userName} />
) : (
<LandingPage
onJoin={handlePay}
onVerify={checkPaymentFromPB}
onVerifySuccess={handleVerifySuccess}
/>
)}
</VipLayout>
<>
<VipLayout isLoggedIn={isLoggedIn} userName={userName} onLogout={handleLogout}>
{isLoggedIn ? (
<DashboardPage userName={userName} />
) : (
<LandingPage
onJoin={handlePay}
onVerify={checkPaymentFromPB}
onVerifyByEmail={checkVerifyByEmail}
onVerifySuccess={handleVerifySuccess}
/>
)}
</VipLayout>
<PayAuthModal
open={authForPayOpen}
onClose={() => setAuthForPayOpen(false)}
onSuccess={() => {
setAuthForPayOpen(false);
doPay();
}}
/>
</>
);
}