import { BookProject, Chapter, Entity, Idea, UserProfile } from '../types'; const API_BASE_URL = '/api'; // Proxied by Vite // --- API CLIENT GENERIC --- const api = { // Generic fetch with cookies // Generic fetch with cookies // Generic fetch with cookies async request(endpoint: string, options: RequestInit = {}) { // Append Instance to query params const instanceId = "54770_plumeia"; // Fallback if env missing in some contexts const separator = endpoint.includes('?') ? '&' : '?'; const url = `${API_BASE_URL}${endpoint}${separator}Instance=${instanceId}`; const headers = { 'Content-Type': 'application/json', 'X-Database-Instance': instanceId, // Keep header just in case ...options.headers, }; const response = await fetch(url, { ...options, headers, credentials: 'include', // IMPORTANT: Send cookies }); if (!response.ok) { let errorMsg = `Error ${response.status}: ${response.statusText}`; try { const errorJson = await response.json(); console.error("API Error JSON:", errorJson); if (errorJson.error) errorMsg = errorJson.error; if (errorJson.message) errorMsg = errorJson.message; } catch (e) { // Ignore json parse error } throw new Error(errorMsg); } // Return null if 204 No Content if (response.status === 204) return null; return response.json(); }, // --- AUTH ENDPOINTS --- auth: { async getSession() { try { return await api.request('/user-auth/get-session'); } catch (e) { return null; // No session } }, async signIn(email: string, password: string) { return api.request('/user-auth/sign-in/email', { method: 'POST', body: JSON.stringify({ email, password }), }); }, async signUp(email: string, password: string, name: string) { return api.request('/user-auth/sign-up/email', { method: 'POST', body: JSON.stringify({ email, password, name }), }); }, async signOut() { return api.request('/user-auth/sign-out', { method: 'POST', }); } }, // --- DATA ENDPOINTS --- data: { // Generic list (read all) async list(table: string) { // Swagger: GET /read/{table} -> { status: "success", data: [...] } const res = await api.request<{ data: T[] }>(`/data/read/${table}`); return res.data || []; }, // Generic get (read one) async get(table: string, id: string) { // Swagger: GET /read/{table}/{id} -> { status: "success", data: { ... } } const res = await api.request<{ data: T }>(`/data/read/${table}/${id}`); return res.data; }, // Generic create async create(table: string, data: any) { // Swagger: POST /create/{table} -> { status: "success", id: 123 } // Return the whole response so caller can get ID return api.request(`/data/create/${table}`, { method: 'POST', body: JSON.stringify(data), }) as Promise; }, // Generic update async update(table: string, id: string, data: any) { // Swagger: PUT /update/{table}/{id} // Note: Swagger for update usually expects "update" path prefix return api.request(`/data/update/${table}/${id}`, { method: 'PUT', body: JSON.stringify(data), }) as Promise; }, // Generic delete async delete(table: string, id: string) { // Swagger: DELETE /delete/{table}/{id} return api.request(`/data/delete/${table}/${id}`, { method: 'DELETE', }); }, // --- Specialized Project Methods --- // Fetch full project with related data (chapters, entities, etc) // NOTE: In a real app, you might want to fetch these separately or use a join if supported. // For now, we'll fetch the project and then fetch its children. async getFullProject(projectId: string): Promise { const project = await this.get('projects', projectId); // Fetch related data in parallel const [chapters, entities, ideas] = await Promise.all([ api.request(`/data/chapters/list?project_id=${projectId}`), api.request(`/data/entities/list?project_id=${projectId}`), api.request(`/data/ideas/list?project_id=${projectId}`), ]); return { ...project, chapters: chapters || [], entities: entities || [], ideas: ideas || [], // templates: [], // Not yet in DB // workflow: null // Not yet in DB }; } } }; export default api;