153 lines
6.9 KiB
TypeScript
153 lines
6.9 KiB
TypeScript
import type {
|
|
AdminProfile,
|
|
AdminUser,
|
|
AgentDebugResult,
|
|
AiLogRecord,
|
|
ApiResponse,
|
|
ChatDetail,
|
|
ChatRecord,
|
|
ChatRecordQuery,
|
|
DashboardStats,
|
|
KnowledgeItem,
|
|
ModelItem,
|
|
SystemConfigItem,
|
|
UserImportResult,
|
|
} from "../types/api";
|
|
|
|
const API_BASE = import.meta.env.VITE_API_BASE_URL ?? "/api";
|
|
const TOKEN_KEY = "ai-kb-admin-token";
|
|
|
|
export function getToken() {
|
|
return window.localStorage.getItem(TOKEN_KEY);
|
|
}
|
|
|
|
export function saveToken(token: string) {
|
|
window.localStorage.setItem(TOKEN_KEY, token);
|
|
}
|
|
|
|
export function clearToken() {
|
|
window.localStorage.removeItem(TOKEN_KEY);
|
|
}
|
|
|
|
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
|
const headers = new Headers(init.headers);
|
|
headers.set("Content-Type", "application/json");
|
|
const token = getToken();
|
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
const response = await fetch(`${API_BASE}${path}`, { ...init, headers });
|
|
const body = (await response.json()) as ApiResponse<T>;
|
|
if (!response.ok || body.code !== 0) {
|
|
throw new Error(body.message || `${response.status} ${response.statusText}`);
|
|
}
|
|
return body.data;
|
|
}
|
|
|
|
async function download(path: string, filename: string) {
|
|
const headers = new Headers();
|
|
const token = getToken();
|
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
const response = await fetch(`${API_BASE}${path}`, { headers });
|
|
if (!response.ok) throw new Error("导出失败");
|
|
const blob = await response.blob();
|
|
const url = window.URL.createObjectURL(blob);
|
|
const anchor = document.createElement("a");
|
|
anchor.href = url;
|
|
anchor.download = filename;
|
|
anchor.click();
|
|
window.URL.revokeObjectURL(url);
|
|
}
|
|
|
|
async function upload<T>(path: string, formData: FormData): Promise<T> {
|
|
const headers = new Headers();
|
|
const token = getToken();
|
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
const response = await fetch(`${API_BASE}${path}`, { method: "POST", body: formData, headers });
|
|
const body = (await response.json()) as ApiResponse<T> & { detail?: string };
|
|
if (!response.ok || body.code !== 0) {
|
|
throw new Error(body.message || body.detail || `${response.status} ${response.statusText}`);
|
|
}
|
|
return body.data;
|
|
}
|
|
|
|
function queryString(query: object) {
|
|
const params = new URLSearchParams();
|
|
Object.entries(query).forEach(([key, value]) => {
|
|
if (value !== undefined && value !== null && value !== "") params.set(key, String(value));
|
|
});
|
|
const text = params.toString();
|
|
return text ? `?${text}` : "";
|
|
}
|
|
|
|
export const api = {
|
|
login: (username: string, password: string) =>
|
|
request<{ token: string; admin: AdminProfile }>("/admin/login", {
|
|
method: "POST",
|
|
body: JSON.stringify({ username, password }),
|
|
}),
|
|
profile: () => request<AdminProfile>("/admin/profile"),
|
|
dashboard: (start?: string, end?: string) => {
|
|
const params = new URLSearchParams();
|
|
if (start) params.set("start", start);
|
|
if (end) params.set("end", end);
|
|
const qs = params.toString();
|
|
return request<DashboardStats>(`/admin/dashboard${qs ? "?" + qs : ""}`);
|
|
},
|
|
users: (keyword = "") => request<AdminUser[]>(`/admin/user/list?keyword=${encodeURIComponent(keyword)}`),
|
|
createUser: (payload: Record<string, unknown>) =>
|
|
request<AdminUser>("/admin/user", { method: "POST", body: JSON.stringify(payload) }),
|
|
importUsers: (students: Record<string, unknown>[]) =>
|
|
request<UserImportResult>("/admin/user/import", { method: "POST", body: JSON.stringify({ students }) }),
|
|
downloadStudentTemplate: () => download("/admin/user/import/template", "学员导入模板.xlsx"),
|
|
importUsersExcel: (file: File) => {
|
|
const formData = new FormData();
|
|
formData.append("file", file);
|
|
return upload<UserImportResult>("/admin/user/import/excel", formData);
|
|
},
|
|
updateUser: (id: number, payload: Record<string, unknown>) =>
|
|
request<AdminUser>(`/admin/user/${id}`, { method: "PUT", body: JSON.stringify(payload) }),
|
|
deleteUser: (id: number) => request<null>(`/admin/user/${id}`, { method: "DELETE" }),
|
|
knowledge: () => request<KnowledgeItem[]>("/admin/knowledge/list"),
|
|
createKnowledge: (payload: Record<string, unknown>) =>
|
|
request<KnowledgeItem>("/admin/knowledge", { method: "POST", body: JSON.stringify(payload) }),
|
|
updateKnowledge: (id: number, payload: Record<string, unknown>) =>
|
|
request<KnowledgeItem>(`/admin/knowledge/${id}`, { method: "PUT", body: JSON.stringify(payload) }),
|
|
deleteKnowledge: (id: number) => request<null>(`/admin/knowledge/${id}`, { method: "DELETE" }),
|
|
importKnowledgeFromSpace: (body: { node_token: string }) =>
|
|
request<{ imported: number; skipped: number; id: number; name: string }>(
|
|
"/admin/knowledge/import",
|
|
{ method: "POST", body: JSON.stringify(body) },
|
|
),
|
|
prompt: () => request<{ promptContent: string }>("/admin/prompt"),
|
|
savePrompt: (promptContent: string) =>
|
|
request<{ promptContent: string }>("/admin/prompt", { method: "PUT", body: JSON.stringify({ promptContent }) }),
|
|
resetPrompt: () => request<{ promptContent: string }>("/admin/prompt/reset", { method: "POST", body: "{}" }),
|
|
debugAgent: (payload: Record<string, unknown>) =>
|
|
request<AgentDebugResult>("/admin/agent/debug", { method: "POST", body: JSON.stringify(payload) }),
|
|
models: () => request<ModelItem[]>("/admin/model/list"),
|
|
createModel: (payload: Record<string, unknown>) =>
|
|
request<ModelItem>("/admin/model", { method: "POST", body: JSON.stringify(payload) }),
|
|
updateModel: (id: number, payload: Record<string, unknown>) =>
|
|
request<ModelItem>(`/admin/model/${id}`, { method: "PUT", body: JSON.stringify(payload) }),
|
|
enableModel: (modelId: number) =>
|
|
request<null>("/admin/model/enable", { method: "POST", body: JSON.stringify({ modelId }) }),
|
|
deleteModel: (modelId: number) =>
|
|
request<null>(`/admin/model/${modelId}`, { method: "DELETE" }),
|
|
testModel: (modelId: number) =>
|
|
request<{ ok: boolean; message: string; answer: string }>(`/admin/model/${modelId}/test`, {
|
|
method: "POST",
|
|
body: "{}",
|
|
}),
|
|
configs: () => request<SystemConfigItem[]>("/admin/config"),
|
|
saveConfig: (payload: Record<string, unknown>) =>
|
|
request<SystemConfigItem>("/admin/config", { method: "PUT", body: JSON.stringify(payload) }),
|
|
chats: (query: ChatRecordQuery = {}) => request<ChatRecord[]>(`/admin/chat/list${queryString(query)}`),
|
|
chatDetail: (sessionId: number) => request<ChatDetail>(`/admin/chat/${sessionId}`),
|
|
exportChats: (query: ChatRecordQuery = {}) => download(`/admin/chat/export${queryString(query)}`, "chat_records.csv"),
|
|
aiLogs: (query: { sessionId?: number; userId?: number; status?: string } = {}) =>
|
|
request<AiLogRecord[]>(`/admin/ai-log/list${queryString(query)}`),
|
|
aiLogDetail: (id: number) => request<AiLogRecord>(`/admin/ai-log/${id}`),
|
|
operationLogs: () => request<Record<string, unknown>[]>("/admin/log/list"),
|
|
clearFeishuCache: () =>
|
|
request<{ cleared: number; message: string }>("/admin/feishu/cache/clear", { method: "POST", body: "{}" }),
|
|
};
|