's'
This commit is contained in:
@@ -1,31 +1,20 @@
|
||||
/**
|
||||
* 课程支付状态 - localStorage(来自 nomadlms)
|
||||
* 与 join 报名支付分离,课程解锁用此模块
|
||||
* 课程解锁 - 校验登录态 + VIP(/api/vip/check)
|
||||
* 与 join 报名支付分离,课程解锁用 VIP 状态
|
||||
*/
|
||||
|
||||
const PAY_STORAGE_KEY = "lms-pay";
|
||||
|
||||
/** 是否已支付(课程已解锁) */
|
||||
export function isPaid(): boolean {
|
||||
if (typeof window === "undefined") return false;
|
||||
return localStorage.getItem(PAY_STORAGE_KEY) === "ok";
|
||||
}
|
||||
|
||||
/** 设置已支付(支付成功后调用) */
|
||||
export function setPaid(): void {
|
||||
if (typeof window === "undefined") return;
|
||||
localStorage.setItem(PAY_STORAGE_KEY, "ok");
|
||||
window.dispatchEvent(new CustomEvent("pay:updated"));
|
||||
}
|
||||
|
||||
/** 前 2 章为试看,无需支付 */
|
||||
export function isFreeTrial(moduleIndex: number, lessonIndex: number): boolean {
|
||||
if (moduleIndex === 0 && lessonIndex < 2) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 章节是否可观看 */
|
||||
export function canWatchLesson(moduleIndex: number, lessonIndex: number): boolean {
|
||||
/** 章节是否可观看(需传入 vip 状态,来自 useAuthAndVip 或 /api/vip/check) */
|
||||
export function canWatchLesson(
|
||||
moduleIndex: number,
|
||||
lessonIndex: number,
|
||||
vip: boolean
|
||||
): boolean {
|
||||
if (isFreeTrial(moduleIndex, lessonIndex)) return true;
|
||||
return isPaid();
|
||||
return vip;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
/**
|
||||
* 环境配置:区分本地调试与生产部署
|
||||
* 生产:https://api.hackrobot.cn(payjsapi)
|
||||
* 域名更换:设置 ROOT_DOMAIN(如 nomadro.com)可推导默认值
|
||||
*/
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
import { DEFAULT_PAYMENT_API_URL, DEFAULT_SITE_URL } from "@/config/domain.config";
|
||||
|
||||
/** 支付 API 地址(payjsapi) */
|
||||
export const PAYMENT_API_URL =
|
||||
process.env.PAYMENT_API_URL ||
|
||||
(isDev ? "http://127.0.0.1:8700" : "https://api.hackrobot.cn");
|
||||
process.env.PAYMENT_API_URL || DEFAULT_PAYMENT_API_URL;
|
||||
|
||||
/** 站点根地址(用于 return_url 等回跳,生产需配置) */
|
||||
export const SITE_URL =
|
||||
process.env.NEXT_PUBLIC_SITE_URL ||
|
||||
(isDev ? "http://localhost:3001" : "https://hackrobot.cn");
|
||||
process.env.NEXT_PUBLIC_SITE_URL || DEFAULT_SITE_URL;
|
||||
|
||||
/** 是否为本地开发 */
|
||||
export const IS_DEV = isDev;
|
||||
export const IS_DEV = process.env.NODE_ENV === "development";
|
||||
|
||||
@@ -67,11 +67,16 @@ export function pbSaveAuth(token: string, record: AuthUser): void {
|
||||
localStorage.setItem(PB_STORAGE_KEYS.user, JSON.stringify(record));
|
||||
}
|
||||
|
||||
/** 登出 - 清除本地存储 */
|
||||
export function pbLogout(): void {
|
||||
/** 登出 - 清除本地存储和根域 Cookie */
|
||||
export async function pbLogout(): Promise<void> {
|
||||
if (typeof window === "undefined") return;
|
||||
localStorage.removeItem(PB_STORAGE_KEYS.token);
|
||||
localStorage.removeItem(PB_STORAGE_KEYS.user);
|
||||
try {
|
||||
await fetch("/api/auth/logout", { method: "POST" });
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取当前存储的用户邮箱 */
|
||||
|
||||
45
app/lib/useAuthAndVip.ts
Normal file
45
app/lib/useAuthAndVip.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
|
||||
export interface AuthAndVipState {
|
||||
user: { id: string; email: string } | null;
|
||||
vip: boolean;
|
||||
loading: boolean;
|
||||
refetch: () => Promise<{ user: { id: string; email: string } | null; vip: boolean }>;
|
||||
}
|
||||
|
||||
export function useAuthAndVip(): AuthAndVipState {
|
||||
const [user, setUser] = useState<{ id: string; email: string } | null>(null);
|
||||
const [vip, setVip] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const refetch = useCallback(async (): Promise<{ user: typeof user; vip: boolean }> => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [meRes, vipRes] = await Promise.all([
|
||||
fetch("/api/auth/me", { credentials: "include" }),
|
||||
fetch("/api/vip/check", { credentials: "include" }),
|
||||
]);
|
||||
const meData = await meRes.json();
|
||||
const vipData = await vipRes.json();
|
||||
const newUser = meData?.user ?? null;
|
||||
const newVip = vipData?.vip === true;
|
||||
setUser(newUser);
|
||||
setVip(newVip);
|
||||
return { user: newUser, vip: newVip };
|
||||
} catch {
|
||||
setUser(null);
|
||||
setVip(false);
|
||||
return { user: null, vip: false };
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
return { user, vip, loading, refetch };
|
||||
}
|
||||
Reference in New Issue
Block a user