Files
gitlab-instance-0a899031_cn…/app/lib/api-client.ts
2026-06-07 22:50:42 -05:00

101 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { getStoredToken, getStoredUser } from "@/app/lib/pocketbase";
export const API_BASE = (process.env.NEXT_PUBLIC_API_BASE_URL || "").replace(/\/$/, "");
export function apiUrl(path: string): string {
if (/^https?:\/\//i.test(path)) return path;
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
if (API_BASE.endsWith("/api") && normalizedPath.startsWith("/api/")) {
return `${API_BASE}${normalizedPath.slice(4)}`;
}
return `${API_BASE}${normalizedPath}`;
}
export async function apiFetch<T>(
path: string,
init?: RequestInit
): Promise<T> {
const res = await fetch(apiUrl(path), {
credentials: "include",
...init,
headers:
init?.body instanceof FormData
? init.headers
: {
"Content-Type": "application/json",
...(init?.headers || {}),
},
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(data?.detail || data?.error || `HTTP ${res.status}`);
}
return data as T;
}
export type AuthUser = {
id: string;
email: string;
vip?: boolean;
role?: string;
[key: string]: unknown;
};
export type AuthMeResponse = {
user?: AuthUser | null;
token?: string;
};
/** 读取当前登录用户;失败时返回 null不抛错 */
export async function fetchAuthMe(): Promise<AuthMeResponse | null> {
try {
return await apiFetch<AuthMeResponse>("/api/auth/me", { cache: "no-store" });
} catch {
return null;
}
}
/** 将本地 PocketBase token 同步到 httpOnly cookie */
export async function syncAuthSession(
token: string,
record: Record<string, unknown>
): Promise<boolean> {
try {
await apiFetch("/api/auth/sync-session", {
method: "POST",
body: JSON.stringify({ token, record }),
});
return true;
} catch {
return false;
}
}
/**
* 解析当前用户:优先 cookie 会话,其次本地 token 同步后再读。
* 返回 user + token若有未登录返回 null。
*/
export async function resolveAuthUser(): Promise<{
user: AuthUser;
token: string;
} | null> {
let data = await fetchAuthMe();
if (data?.user?.id && data?.token) {
return { user: data.user, token: data.token };
}
const token = getStoredToken();
const record = getStoredUser();
if (token && record?.id && record?.email) {
const synced = await syncAuthSession(token, record as Record<string, unknown>);
if (synced) {
data = await fetchAuthMe();
if (data?.user?.id && data?.token) {
return { user: data.user, token: data.token };
}
}
}
return null;
}