/** * API Service * * This service centralizes all API communication with the backend. * It provides methods for authentication, projects, persons, and tags. */ const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; /** * Base fetch function with error handling and authentication */ async function fetchAPI(endpoint: string, options: RequestInit = {}) { // Set default headers const headers: Record = { 'Content-Type': 'application/json', ...(options.headers as Record || {}), }; // Get token from localStorage if available (client-side only) if (typeof window !== 'undefined') { const token = localStorage.getItem('auth_token'); if (token) { headers['Authorization'] = `Bearer ${token}`; } } // Prepare fetch options const fetchOptions: RequestInit = { ...options, headers, credentials: 'include', // Include cookies for session management }; try { const response = await fetch(`${API_URL}${endpoint}`, fetchOptions); // Handle HTTP errors if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || `API error: ${response.status}`); } // Parse JSON response if content exists const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } return response; } catch (error) { console.error('API request failed:', error); throw error; } } /** * Authentication API */ export const authAPI = { /** * Get GitHub OAuth URL */ getGitHubOAuthUrl: async () => { return fetchAPI('/auth/github', { method: 'GET' }); }, /** * Exchange code for access token */ githubCallback: async (code: string) => { return fetchAPI('/auth/github/callback', { method: 'POST', body: JSON.stringify({ code }), }); }, /** * Logout user */ logout: async () => { return fetchAPI('/auth/logout', { method: 'POST' }); }, /** * Get current user */ getCurrentUser: async () => { return fetchAPI('/auth/me', { method: 'GET' }); }, }; /** * Projects API */ export const projectsAPI = { /** * Get all projects */ getProjects: async () => { return fetchAPI('/projects', { method: 'GET' }); }, /** * Get project by ID */ getProject: async (id: string) => { return fetchAPI(`/projects/${id}`, { method: 'GET' }); }, /** * Create new project */ createProject: async (data: any) => { return fetchAPI('/projects', { method: 'POST', body: JSON.stringify(data), }); }, /** * Update project */ updateProject: async (id: string, data: any) => { return fetchAPI(`/projects/${id}`, { method: 'PATCH', body: JSON.stringify(data), }); }, /** * Delete project */ deleteProject: async (id: string) => { return fetchAPI(`/projects/${id}`, { method: 'DELETE' }); }, }; /** * Persons API */ export const personsAPI = { /** * Get all persons for a project */ getPersons: async (projectId: string) => { return fetchAPI(`/projects/${projectId}/persons`, { method: 'GET' }); }, /** * Get person by ID */ getPerson: async (id: string) => { return fetchAPI(`/persons/${id}`, { method: 'GET' }); }, /** * Create new person */ createPerson: async (projectId: string, data: any) => { return fetchAPI(`/projects/${projectId}/persons`, { method: 'POST', body: JSON.stringify(data), }); }, /** * Update person */ updatePerson: async (id: string, data: any) => { return fetchAPI(`/persons/${id}`, { method: 'PATCH', body: JSON.stringify(data), }); }, /** * Delete person */ deletePerson: async (id: string) => { return fetchAPI(`/persons/${id}`, { method: 'DELETE' }); }, }; /** * Tags API */ export const tagsAPI = { /** * Get all tags */ getTags: async () => { return fetchAPI('/tags', { method: 'GET' }); }, /** * Get tag by ID */ getTag: async (id: string) => { return fetchAPI(`/tags/${id}`, { method: 'GET' }); }, /** * Create new tag */ createTag: async (data: any) => { return fetchAPI('/tags', { method: 'POST', body: JSON.stringify(data), }); }, /** * Update tag */ updateTag: async (id: string, data: any) => { return fetchAPI(`/tags/${id}`, { method: 'PATCH', body: JSON.stringify(data), }); }, /** * Delete tag */ deleteTag: async (id: string) => { return fetchAPI(`/tags/${id}`, { method: 'DELETE' }); }, }; /** * Groups API */ export const groupsAPI = { /** * Get all groups for a project */ getGroups: async (projectId: string) => { return fetchAPI(`/projects/${projectId}/groups`, { method: 'GET' }); }, /** * Get group by ID */ getGroup: async (id: string) => { return fetchAPI(`/groups/${id}`, { method: 'GET' }); }, /** * Create new group */ createGroup: async (projectId: string, data: any) => { return fetchAPI(`/projects/${projectId}/groups`, { method: 'POST', body: JSON.stringify(data), }); }, /** * Update group */ updateGroup: async (id: string, data: any) => { return fetchAPI(`/groups/${id}`, { method: 'PATCH', body: JSON.stringify(data), }); }, /** * Delete group */ deleteGroup: async (id: string) => { return fetchAPI(`/groups/${id}`, { method: 'DELETE' }); }, /** * Add person to group */ addPersonToGroup: async (groupId: string, personId: string) => { return fetchAPI(`/groups/${groupId}/persons/${personId}`, { method: 'POST', }); }, /** * Remove person from group */ removePersonFromGroup: async (groupId: string, personId: string) => { return fetchAPI(`/groups/${groupId}/persons/${personId}`, { method: 'DELETE', }); }, }; export default { auth: authAPI, projects: projectsAPI, persons: personsAPI, tags: tagsAPI, groups: groupsAPI, };