107 lines
2.5 KiB
TypeScript
107 lines
2.5 KiB
TypeScript
|
|
// ============================================================
|
|||
|
|
// HTTP API 客户端
|
|||
|
|
// 职责:封装 REST API 请求(auth、conversations 等)
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
const API_BASE = "/api";
|
|||
|
|
|
|||
|
|
interface ApiResponse<T> {
|
|||
|
|
data?: T;
|
|||
|
|
error?: { code: string; message: string };
|
|||
|
|
status: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function request<T>(
|
|||
|
|
path: string,
|
|||
|
|
options: RequestInit = {}
|
|||
|
|
): Promise<ApiResponse<T>> {
|
|||
|
|
const url = `${API_BASE}${path}`;
|
|||
|
|
const headers: Record<string, string> = {
|
|||
|
|
"Content-Type": "application/json",
|
|||
|
|
...(options.headers as Record<string, string>),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const res = await fetch(url, { ...options, headers });
|
|||
|
|
const status = res.status;
|
|||
|
|
|
|||
|
|
if (res.status === 204) {
|
|||
|
|
return { status };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const body = await res.json();
|
|||
|
|
|
|||
|
|
if (!res.ok) {
|
|||
|
|
return {
|
|||
|
|
error: { code: body.error || "UNKNOWN", message: body.message || "请求失败" },
|
|||
|
|
status,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { data: body as T, status };
|
|||
|
|
} catch (err) {
|
|||
|
|
return {
|
|||
|
|
error: { code: "NETWORK_ERROR", message: "网络连接失败,请检查网络" },
|
|||
|
|
status: 0,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function authHeaders(accessToken: string): Record<string, string> {
|
|||
|
|
return { Authorization: `Bearer ${accessToken}` };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---- Auth API ----
|
|||
|
|
|
|||
|
|
export interface AuthUser {
|
|||
|
|
id: string;
|
|||
|
|
username: string;
|
|||
|
|
created_at: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface AuthResponse {
|
|||
|
|
user: AuthUser;
|
|||
|
|
access_token: string;
|
|||
|
|
refresh_token: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export async function register(
|
|||
|
|
username: string,
|
|||
|
|
password: string
|
|||
|
|
): Promise<ApiResponse<AuthResponse>> {
|
|||
|
|
return request<AuthResponse>("/auth/register", {
|
|||
|
|
method: "POST",
|
|||
|
|
body: JSON.stringify({ username, password }),
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export async function login(
|
|||
|
|
username: string,
|
|||
|
|
password: string
|
|||
|
|
): Promise<ApiResponse<AuthResponse>> {
|
|||
|
|
return request<AuthResponse>("/auth/login", {
|
|||
|
|
method: "POST",
|
|||
|
|
body: JSON.stringify({ username, password }),
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export async function refreshToken(
|
|||
|
|
refresh_token: string
|
|||
|
|
): Promise<ApiResponse<AuthResponse>> {
|
|||
|
|
return request<AuthResponse>("/auth/refresh", {
|
|||
|
|
method: "POST",
|
|||
|
|
body: JSON.stringify({ refresh_token }),
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export async function logout(
|
|||
|
|
accessToken: string,
|
|||
|
|
refreshToken: string
|
|||
|
|
): Promise<ApiResponse<{ message: string }>> {
|
|||
|
|
return request<{ message: string }>("/auth/logout", {
|
|||
|
|
method: "POST",
|
|||
|
|
headers: authHeaders(accessToken),
|
|||
|
|
body: JSON.stringify({ refresh_token: refreshToken }),
|
|||
|
|
});
|
|||
|
|
}
|