import axios from "axios"; const api = axios.create({ baseURL: "/api" }); api.interceptors.request.use((config) => { const token = localStorage.getItem("token"); if (token) config.headers.Authorization = `Bearer ${token}`; return config; }); export default api; // --- Auth --- export const login = (email: string, password: string) => api .post<{ access_token: string }>("/auth/login", new URLSearchParams({ username: email, password })) .then((r) => r.data.access_token); export const register = (email: string, password: string, full_name?: string) => api.post("/auth/register", { email, password, full_name }).then((r) => r.data); // --- Users --- export interface UserData { id: string; email: string; full_name: string | null; is_active: boolean; is_admin: boolean; color_mode: string | null; } export const getMe = () => api.get("/users/me").then((r) => r.data); export interface DashboardPrefs { app_ids: string[]; } export const getDashboardPrefs = () => api.get("/users/me/preferences").then((r) => r.data); export const updateDashboardPrefs = (app_ids: string[]) => api.patch("/users/me/preferences", { app_ids }).then((r) => r.data); // --- Admin --- export interface AdminUserCreate { email: string; password: string; full_name?: string; is_admin?: boolean; } export const adminGetUsers = () => api.get("/admin/users").then((r) => r.data); export const adminCreateUser = (data: AdminUserCreate) => api.post("/admin/users", data).then((r) => r.data); export const adminDeleteUser = (userId: string) => api.delete(`/admin/users/${userId}`); export const adminToggleActive = (userId: string) => api.patch(`/admin/users/${userId}/active`).then((r) => r.data); // --- Profile --- export interface ProfileData { id: string; user_id: string; phone: string | null; date_of_birth: string | null; position: string | null; address: string | null; updated_at: string; } export interface ProfileUpdate { phone?: string | null; date_of_birth?: string | null; position?: string | null; address?: string | null; } export const getProfile = () => api.get("/profile/me").then((r) => r.data); export const updateProfile = (data: ProfileUpdate) => api.put("/profile/me", data).then((r) => r.data); // --- Documents --- export type DocumentStatus = "pending" | "processing" | "done" | "failed"; export interface CategoryOut { id: string; name: string; } export interface DocumentOut { id: string; user_id: string; filename: string; title: string | null; file_size: number; status: DocumentStatus; document_type: string | null; extracted_data: string | null; tags: string | null; error_message: string | null; created_at: string; processed_at: string | null; categories: CategoryOut[]; source: string; watch_path: string | null; suggested_folder: string | null; suggested_filename: string | null; share_count: number; } export interface SharedDocumentOut extends DocumentOut { shared_by_user_id: string; shared_via_group_id: string; } export interface DocumentShareOut { id: string; document_id: string; group_id: string; shared_by_user_id: string; created_at: string; } export interface DocumentPage { items: DocumentOut[]; total: number; page: number; pages: number; } export interface DocumentListParams { page?: number; per_page?: number; sort?: string; order?: "asc" | "desc"; status?: string; document_type?: string; search?: string; category_id?: string; } export interface DocumentStatusOut { id: string; status: DocumentStatus; document_type: string | null; error_message: string | null; processed_at: string | null; } export const listDocuments = (params: DocumentListParams = {}) => api.get("/documents", { params }).then((r) => r.data); export const listSharedWithMe = (params: DocumentListParams = {}) => api.get("/documents/shared-with-me", { params }).then((r) => r.data); export const getDocumentShares = (docId: string) => api.get(`/documents/${docId}/shares`).then((r) => r.data); export const addDocumentShare = (docId: string, groupId: string) => api.post(`/documents/${docId}/shares`, { group_id: groupId }).then((r) => r.data); export const removeDocumentShare = (docId: string, groupId: string) => api.delete(`/documents/${docId}/shares/${groupId}`); export const getDocument = (id: string) => api.get(`/documents/${id}`).then((r) => r.data); export const getDocumentStatus = (id: string) => api.get(`/documents/${id}/status`).then((r) => r.data); export const uploadDocument = (file: File) => { const form = new FormData(); form.append("file", file); return api.post("/documents/upload", form).then((r) => r.data); }; export const updateDocumentType = (id: string, document_type: string) => api.patch(`/documents/${id}/type`, { document_type }).then((r) => r.data); export const deleteDocument = (id: string) => api.delete(`/documents/${id}`); export const downloadDocument = async (id: string, filename: string) => { const response = await api.get(`/documents/${id}/file`, { responseType: "blob" }); const url = URL.createObjectURL(response.data); const a = document.createElement("a"); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); }; export const viewDocument = async (id: string): Promise => { const response = await api.get(`/documents/${id}/file`, { responseType: "blob" }); const url = URL.createObjectURL(response.data); const win = window.open(url, "_blank"); // Revoke after a generous delay — the new tab needs time to load the blob setTimeout(() => URL.revokeObjectURL(url), 60_000); if (!win) alert("Pop-up blocked. Please allow pop-ups for this site to preview PDFs."); }; export const updateDocumentTags = (id: string, tags: string[]) => api.patch(`/documents/${id}/tags`, { tags }).then((r) => r.data); export const updateDocumentTitle = (id: string, title: string) => api.patch(`/documents/${id}/title`, { title }).then((r) => r.data); export const reprocessDocument = (id: string) => api.post(`/documents/${id}/reprocess`).then((r) => r.data); export const assignCategory = (docId: string, catId: string) => api.post(`/documents/${docId}/categories/${catId}`); export const removeCategory = (docId: string, catId: string) => api.delete(`/documents/${docId}/categories/${catId}`); // --- Categories --- export const listCategories = () => api.get("/documents/categories").then((r) => r.data); export const createCategory = (name: string) => api.post("/documents/categories", { name }).then((r) => r.data); export const renameCategory = (id: string, name: string) => api.patch(`/documents/categories/${id}`, { name }).then((r) => r.data); export const deleteCategory = (id: string) => api.delete(`/documents/categories/${id}`); // --- Appearance & Themes --- export interface ThemeColors { primary: string; primary_hover: string; accent: string; accent_hover: string; background: string; surface: string; border: string; text_primary: string; text_muted: string; } export interface ThemeDefinition { id: string; label: string; builtin: boolean; light: ThemeColors; dark: ThemeColors; } export interface AppearanceSettings { theme: string; default_mode: string; } export const getAppearanceSettings = (): Promise => api.get("/settings/appearance").then((r) => r.data); export const updateAppearanceSettings = (data: AppearanceSettings): Promise => api.patch("/settings/appearance", data).then((r) => r.data); export const getThemes = (): Promise => api.get("/settings/themes").then((r) => r.data); export const createTheme = (data: Omit): Promise => api.post("/settings/themes", data).then((r) => r.data); export const updateTheme = ( id: string, data: { label?: string; light?: ThemeColors; dark?: ThemeColors } ): Promise => api.patch(`/settings/themes/${id}`, data).then((r) => r.data); export const deleteTheme = (id: string): Promise => api.delete(`/settings/themes/${id}`).then((r) => r.data); export const updateColorMode = (color_mode: string): Promise => api.patch("/users/me/color-mode", { color_mode }).then((r) => r.data); // --- Settings (admin only) --- export interface AIProviderUpdate { provider: string; anthropic_api_key?: string; anthropic_model?: string; ollama_base_url?: string; ollama_model?: string; ollama_api_key?: string; lmstudio_base_url?: string; lmstudio_model?: string; lmstudio_api_key?: string; } export const getAISettings = () => api.get>("/settings/ai").then((r) => r.data); export const updateAISettings = (data: AIProviderUpdate) => api.patch>("/settings/ai", data).then((r) => r.data); export const testAIConnection = () => api.post<{ ok: boolean; provider: string; response?: string; error?: string }>( "/settings/ai/test" ).then((r) => r.data); export const updateDocumentLimits = (max_pdf_mb: number) => api.patch>("/settings/documents/limits", { max_pdf_mb }).then( (r) => r.data ); export const getDocumentLimits = () => api.get>("/settings/documents/limits").then((r) => r.data); // --- User groups (current user's own memberships) --- export interface UserGroupOut { id: string; name: string; description: string | null; } export const getMyGroups = () => api.get("/users/me/groups").then((r) => r.data); // --- Groups (admin only) --- export interface GroupOut { id: string; name: string; description: string | null; created_at: string; member_count: number; } export interface GroupMemberOut { id: string; email: string; full_name: string | null; is_active: boolean; joined_at: string; } export interface GroupDetailOut extends GroupOut { members: GroupMemberOut[]; } export interface GroupCreate { name: string; description?: string | null; } export interface GroupUpdate { name?: string; description?: string | null; } export const adminListGroups = () => api.get("/admin/groups").then((r) => r.data); export const adminCreateGroup = (data: GroupCreate) => api.post("/admin/groups", data).then((r) => r.data); export const adminGetGroup = (groupId: string) => api.get(`/admin/groups/${groupId}`).then((r) => r.data); export const adminUpdateGroup = (groupId: string, data: GroupUpdate) => api.patch(`/admin/groups/${groupId}`, data).then((r) => r.data); export const adminDeleteGroup = (groupId: string) => api.delete(`/admin/groups/${groupId}`); export const adminAddGroupMember = (groupId: string, userId: string) => api.post(`/admin/groups/${groupId}/members/${userId}`); export const adminRemoveGroupMember = (groupId: string, userId: string) => api.delete(`/admin/groups/${groupId}/members/${userId}`); // --- Services --- export interface ServiceStatus { id: string; name: string; description: string; healthy: boolean; app_path: string; settings_path: string; } export const getServices = () => api.get("/services").then((r) => r.data); // --- System Prompts (admin only) --- export interface ServiceSystemPrompt { label: string; system: string; user_template: string; default_system: string; default_user_template: string; } export type SystemPromptsData = Record; export const getSystemPrompts = () => api.get("/settings/system-prompts").then((r) => r.data); export const updateSystemPrompt = ( serviceId: string, data: { system: string; user_template: string } ) => api .patch(`/settings/system-prompts/${serviceId}`, data) .then((r) => r.data); // --- Document suggestions (watch-ingested documents) --- export const confirmFolderSuggestion = (docId: string) => api.post(`/documents/${docId}/suggestions/folder/confirm`); export const rejectFolderSuggestion = (docId: string) => api.post(`/documents/${docId}/suggestions/folder/reject`); export const confirmFilenameSuggestion = (docId: string) => api.post(`/documents/${docId}/suggestions/filename/confirm`); export const rejectFilenameSuggestion = (docId: string) => api.post(`/documents/${docId}/suggestions/filename/reject`); // --- Plugins --- export interface PluginOut { id: string; name: string; icon: string; version: string; } export interface PluginSchemaProperty { type: string; title: string; description?: string; readOnly?: boolean; } export interface PluginManifest { id: string; name: string; icon: string; version: string; access: { allow_superuser: boolean; required_groups: string[]; }; settings_schema: { type: string; title?: string; properties: Record; }; } export const getPlugins = () => api.get("/plugins").then((r) => r.data); export const getPluginManifest = (id: string) => api.get(`/plugins/${id}/manifest`).then((r) => r.data); export const getPluginSettings = (id: string) => api.get>(`/plugins/${id}/settings`).then((r) => r.data); export const updatePluginSettings = (id: string, data: Record) => api.patch>(`/plugins/${id}/settings`, data).then((r) => r.data);