const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'https://api.sojorn.net'; class ApiClient { private token: string | null = null; setToken(token: string | null) { this.token = token; if (token) { if (typeof window !== 'undefined') localStorage.setItem('admin_token', token); } else { if (typeof window !== 'undefined') localStorage.removeItem('admin_token'); } } getToken(): string | null { if (this.token) return this.token; if (typeof window !== 'undefined') { this.token = localStorage.getItem('admin_token'); } return this.token; } private async request(path: string, options: RequestInit = {}): Promise { const token = this.getToken(); const headers: Record = { 'Content-Type': 'application/json', ...(options.headers as Record), }; if (token) { headers['Authorization'] = `Bearer ${token}`; } const res = await fetch(`${API_BASE}${path}`, { ...options, headers, }); if (res.status === 401) { this.setToken(null); if (typeof window !== 'undefined') { window.location.href = '/login'; } throw new Error('Unauthorized'); } if (!res.ok) { const body = await res.json().catch(() => ({ error: res.statusText })); throw new Error(body.error || `Request failed: ${res.status}`); } return res.json(); } // Auth async login(email: string, password: string) { const data = await this.request<{ access_token: string; user: any }>('/api/v1/auth/login', { method: 'POST', body: JSON.stringify({ email, password }), }); this.setToken(data.access_token); return data; } // Dashboard async getDashboardStats() { return this.request('/api/v1/admin/dashboard'); } async getGrowthStats(days = 30) { return this.request(`/api/v1/admin/growth?days=${days}`); } // Users async listUsers(params: { limit?: number; offset?: number; search?: string; status?: string; role?: string } = {}) { const qs = new URLSearchParams(); if (params.limit) qs.set('limit', String(params.limit)); if (params.offset) qs.set('offset', String(params.offset)); if (params.search) qs.set('search', params.search); if (params.status) qs.set('status', params.status); if (params.role) qs.set('role', params.role); return this.request(`/api/v1/admin/users?${qs}`); } async getUser(id: string) { return this.request(`/api/v1/admin/users/${id}`); } async updateUserStatus(id: string, status: string, reason: string) { return this.request(`/api/v1/admin/users/${id}/status`, { method: 'PATCH', body: JSON.stringify({ status, reason }), }); } async updateUserRole(id: string, role: string) { return this.request(`/api/v1/admin/users/${id}/role`, { method: 'PATCH', body: JSON.stringify({ role }), }); } async updateUserVerification(id: string, isOfficial: boolean, isVerified: boolean) { return this.request(`/api/v1/admin/users/${id}/verification`, { method: 'PATCH', body: JSON.stringify({ is_official: isOfficial, is_verified: isVerified }), }); } async resetUserStrikes(id: string) { return this.request(`/api/v1/admin/users/${id}/reset-strikes`, { method: 'POST' }); } // Posts async listPosts(params: { limit?: number; offset?: number; search?: string; status?: string; author_id?: string } = {}) { const qs = new URLSearchParams(); if (params.limit) qs.set('limit', String(params.limit)); if (params.offset) qs.set('offset', String(params.offset)); if (params.search) qs.set('search', params.search); if (params.status) qs.set('status', params.status); if (params.author_id) qs.set('author_id', params.author_id); return this.request(`/api/v1/admin/posts?${qs}`); } async getPost(id: string) { return this.request(`/api/v1/admin/posts/${id}`); } async updatePostStatus(id: string, status: string, reason?: string) { return this.request(`/api/v1/admin/posts/${id}/status`, { method: 'PATCH', body: JSON.stringify({ status, reason }), }); } async deletePost(id: string) { return this.request(`/api/v1/admin/posts/${id}`, { method: 'DELETE' }); } // Moderation async getModerationQueue(params: { limit?: number; offset?: number; status?: string } = {}) { const qs = new URLSearchParams(); if (params.limit) qs.set('limit', String(params.limit)); if (params.offset) qs.set('offset', String(params.offset)); if (params.status) qs.set('status', params.status || 'pending'); return this.request(`/api/v1/admin/moderation?${qs}`); } async reviewModerationFlag(id: string, action: string, reason?: string) { return this.request(`/api/v1/admin/moderation/${id}/review`, { method: 'PATCH', body: JSON.stringify({ action, reason }), }); } // Appeals async listAppeals(params: { limit?: number; offset?: number; status?: string } = {}) { const qs = new URLSearchParams(); if (params.limit) qs.set('limit', String(params.limit)); if (params.offset) qs.set('offset', String(params.offset)); if (params.status) qs.set('status', params.status || 'pending'); return this.request(`/api/v1/admin/appeals?${qs}`); } async reviewAppeal(id: string, decision: string, reviewDecision: string, restoreContent = false) { return this.request(`/api/v1/admin/appeals/${id}/review`, { method: 'PATCH', body: JSON.stringify({ decision, review_decision: reviewDecision, restore_content: restoreContent }), }); } // Reports async listReports(params: { limit?: number; offset?: number; status?: string } = {}) { const qs = new URLSearchParams(); if (params.limit) qs.set('limit', String(params.limit)); if (params.offset) qs.set('offset', String(params.offset)); if (params.status) qs.set('status', params.status || 'pending'); return this.request(`/api/v1/admin/reports?${qs}`); } async updateReportStatus(id: string, status: string) { return this.request(`/api/v1/admin/reports/${id}`, { method: 'PATCH', body: JSON.stringify({ status }), }); } // Algorithm async getAlgorithmConfig() { return this.request('/api/v1/admin/algorithm'); } async updateAlgorithmConfig(key: string, value: string) { return this.request('/api/v1/admin/algorithm', { method: 'PUT', body: JSON.stringify({ key, value }), }); } // Categories async listCategories() { return this.request('/api/v1/admin/categories'); } async createCategory(data: { slug: string; name: string; description?: string; is_sensitive?: boolean }) { return this.request('/api/v1/admin/categories', { method: 'POST', body: JSON.stringify(data), }); } async updateCategory(id: string, data: { name?: string; description?: string; is_sensitive?: boolean }) { return this.request(`/api/v1/admin/categories/${id}`, { method: 'PATCH', body: JSON.stringify(data), }); } // System async getSystemHealth() { return this.request('/api/v1/admin/health'); } async getAuditLog(params: { limit?: number; offset?: number } = {}) { const qs = new URLSearchParams(); if (params.limit) qs.set('limit', String(params.limit)); if (params.offset) qs.set('offset', String(params.offset)); return this.request(`/api/v1/admin/audit-log?${qs}`); } } export const api = new ApiClient();