people-reading/web/lib/api.ts
2026-05-12 11:07:45 +08:00

107 lines
3.0 KiB
TypeScript

export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "/api/v1";
const TOKEN_KEY = "cyber_mister_token";
const CLIENT_KEY = "cyber_mister_client_id";
export type HandSide = "left" | "right" | "unknown";
export type Dimension = {
name: string;
observations: string[];
interpretation: string;
confidence: number;
advice: string;
};
export type ReportData = {
quality_check: {
can_analyze: boolean;
reason: string;
confidence: number;
};
overall_summary: string;
dimensions: Dimension[];
strengths: string[];
challenges: string[];
suggestions: string[];
lucky_keywords: string[];
disclaimer: string;
};
export type Report = {
id: string;
status: "pending" | "processing" | "completed" | "failed";
hand_side: HandSide;
error_message?: string | null;
report_data?: ReportData | null;
created_at: string;
updated_at: string;
};
export type ReportSummary = {
id: string;
status: Report["status"];
hand_side: HandSide;
created_at: string;
overall_summary?: string | null;
};
export function getStoredToken() {
if (typeof window === "undefined") return "";
return localStorage.getItem(TOKEN_KEY) || "";
}
export async function ensureToken() {
const existing = getStoredToken();
if (existing) return existing;
let clientId = localStorage.getItem(CLIENT_KEY);
if (!clientId) {
clientId = crypto.randomUUID();
localStorage.setItem(CLIENT_KEY, clientId);
}
const response = await fetch(`${API_BASE_URL}/auth/anonymous-login`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ client_id: clientId }),
});
if (!response.ok) throw new Error("匿名会话创建失败");
const data = await response.json();
localStorage.setItem(TOKEN_KEY, data.access_token);
return data.access_token as string;
}
export async function apiFetch<T>(path: string, options: RequestInit = {}): Promise<T> {
const token = await ensureToken();
const headers = new Headers(options.headers);
if (!(options.body instanceof FormData) && !headers.has("content-type")) {
headers.set("content-type", "application/json");
}
headers.set("authorization", `Bearer ${token}`);
const response = await fetch(`${API_BASE_URL}${path}`, { ...options, headers });
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.detail || "请求失败");
}
return response.json() as Promise<T>;
}
export async function uploadPalmImage(file: File) {
const token = await ensureToken();
const formData = new FormData();
formData.append("file", file);
const response = await fetch(`${API_BASE_URL}/uploads/palm`, {
method: "POST",
headers: { authorization: `Bearer ${token}` },
body: formData,
});
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.detail || "上传失败");
}
return response.json() as Promise<{ image_id: string; quality_check: Record<string, unknown>; expires_at: string }>;
}