import axios from "axios"; const api = axios.create({ baseURL: `/api/v1`, headers: { "Content-Type": "application/json", }, }); // Attach auth token to every request api.interceptors.request.use((config) => { if (typeof window !== "undefined") { const token = localStorage.getItem("access_token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } } return config; }); // Handle 401 responses by clearing auth state api.interceptors.response.use( (response) => response, (error) => { if (error.response?.status === 401 && typeof window !== "undefined") { localStorage.removeItem("access_token"); localStorage.removeItem("user"); window.location.href = "/login"; } return Promise.reject(error); } ); // ── Types ─────────────────────────────────────────────────────────────── export interface User { id: number; email: string; full_name: string | null; is_active: boolean; is_superuser: boolean; subscription_tier: string; subscription_status: string; created_at: string; google_id?: string | null; oauth_provider?: string | null; stripe_customer_id?: string | null; subscription_expires_at?: string | null; last_login_at?: string | null; } export interface MailAccount { id: number; user_id: number; name: string; email_address: string; protocol: string; host: string; port: number; use_ssl: boolean; use_tls: boolean; username: string; forward_to: string; delivery_method: string; is_enabled: boolean; check_interval_minutes: number; max_emails_per_check: number; delete_after_forward: boolean; status: string; provider_name?: string | null; auto_detected: boolean; total_emails_processed: number; total_emails_failed: number; last_check_at?: string | null; last_successful_check_at?: string | null; last_error_at?: string | null; last_error_message?: string | null; created_at: string; updated_at: string; } export interface MailAccountCreate { name: string; email_address: string; protocol: string; host: string; port: number; use_ssl: boolean; use_tls?: boolean; username: string; password: string; forward_to: string; delivery_method?: string; is_enabled?: boolean; check_interval_minutes?: number; max_emails_per_check?: number; delete_after_forward?: boolean; } export interface MailAccountUpdate { name?: string; email_address?: string; protocol?: string; host?: string; port?: number; use_ssl?: boolean; use_tls?: boolean; username?: string; password?: string; forward_to?: string; delivery_method?: string; is_enabled?: boolean; check_interval_minutes?: number; max_emails_per_check?: number; delete_after_forward?: boolean; } export interface ProcessingRun { id: number; mail_account_id: number; started_at: string; completed_at?: string | null; duration_seconds?: number | null; emails_fetched: number; emails_forwarded: number; emails_failed: number; status: string; error_message?: string | null; account_name?: string | null; account_email?: string | null; } export interface ProcessingLog { id: number; timestamp: string; level: string; message: string; email_subject?: string | null; email_from?: string | null; success: boolean; mail_account_id: number; processing_run_id?: number | null; email_size_bytes?: number | null; error_details?: Record | null; } export interface PaginatedProcessingRuns { items: ProcessingRun[]; total: number; page: number; page_size: number; pages: number; } export interface PaginatedProcessingLogs { items: ProcessingLog[]; total: number; page: number; page_size: number; pages: number; } export interface AdminProcessingRun extends ProcessingRun { user_id?: number | null; user_email?: string | null; } export interface AdminProcessingLog extends ProcessingLog { user_id: number; user_email?: string | null; } export interface PaginatedAdminRuns { items: AdminProcessingRun[]; total: number; page: number; page_size: number; pages: number; } export interface PaginatedAdminLogsResponse { items: AdminProcessingLog[]; total: number; page: number; page_size: number; pages: number; } export interface AutoDetectSuggestion { protocol?: string; host?: string; port?: number; use_ssl?: boolean; [key: string]: unknown; } export interface GmailCredential { id: number; user_id: number; gmail_email: string; is_valid: boolean; import_label_templates: string[]; default_import_label_templates: string[]; last_verified_at?: string | null; created_at: string; updated_at: string; } export interface GmailDebugEmailResponse { message: string; message_id: string | null; thread_id: string | null; label_ids: string[]; } export interface UserSmtpConfig { id: number; user_id: number; host: string; port: number; username: string; use_tls: boolean; has_password: boolean; created_at: string; updated_at: string; } interface TokenResponse { access_token: string; refresh_token: string; token_type: string; user?: User; } // ── Auth API ──────────────────────────────────────────────────────────── export const authApi = { async login(credentials: { username: string; password: string; }): Promise { const formData = new URLSearchParams(); formData.append("username", credentials.username); formData.append("password", credentials.password); const response = await api.post("/auth/login", formData, { headers: { "Content-Type": "application/x-www-form-urlencoded" }, }); return response.data; }, async register(data: { email: string; password: string; full_name?: string; }): Promise { const response = await api.post("/auth/register", data); return response.data; }, async googleAuth( code: string, redirectUri: string ): Promise { const response = await api.post("/auth/google", { code, redirect_uri: redirectUri, }); return response.data; }, async getGoogleAuthUrl(redirectUri: string): Promise { const response = await api.get<{ authorization_url: string }>( "/auth/google/authorize-url", { params: { redirect_uri: redirectUri } } ); return response.data.authorization_url; }, }; // ── User API ──────────────────────────────────────────────────────────── export const userApi = { async getCurrentUser(): Promise { const response = await api.get("/users/me"); return response.data; }, async updateProfile(data: { full_name?: string; email?: string }): Promise { const response = await api.put("/users/me", data); return response.data; }, }; // ── Mail Accounts API ─────────────────────────────────────────────────── export const mailAccountsApi = { async list(): Promise { const response = await api.get("/mail-accounts"); return response.data; }, async create(data: MailAccountCreate): Promise { const response = await api.post("/mail-accounts", data); return response.data; }, async update( id: number, data: MailAccountUpdate ): Promise { const response = await api.put(`/mail-accounts/${id}`, data); return response.data; }, async toggle(id: number): Promise { const response = await api.patch(`/mail-accounts/${id}/toggle`); return response.data; }, async delete(id: number): Promise { await api.delete(`/mail-accounts/${id}`); }, async test(config: { host: string; port: number; protocol: string; username: string; password: string; use_ssl?: boolean; use_tls?: boolean; }): Promise<{ success: boolean; message: string }> { const response = await api.post<{ success: boolean; message: string }>( "/mail-accounts/test", config ); return response.data; }, async autoDetect( emailAddress: string ): Promise<{ success: boolean; suggestions: AutoDetectSuggestion[] }> { const response = await api.post<{ success: boolean; suggestions: AutoDetectSuggestion[]; }>("/mail-accounts/auto-detect", { email_address: emailAddress }); return response.data; }, }; // ── Processing Runs API ───────────────────────────────────────────────── export const processingRunsApi = { async list(params?: { page?: number; page_size?: number; account_id?: number; status?: string; }): Promise { const response = await api.get("/processing-runs", { params, }); return response.data; }, async get(runId: number): Promise { const response = await api.get(`/processing-runs/${runId}`); return response.data; }, async getLogs( runId: number, params?: { page?: number; page_size?: number } ): Promise { const response = await api.get( `/processing-runs/${runId}/logs`, { params } ); return response.data; }, async listForAccount( accountId: number, params?: { page?: number; page_size?: number; status?: string } ): Promise { const response = await api.get( `/mail-accounts/${accountId}/processing-runs`, { params } ); return response.data; }, async listLogsForAccount( accountId: number, params?: { page?: number; page_size?: number; level?: string } ): Promise { const response = await api.get( `/mail-accounts/${accountId}/logs`, { params } ); return response.data; }, }; // ── Gmail API ─────────────────────────────────────────────────────────── export const gmailApi = { /** Returns the Google OAuth2 URL the user should be redirected to. */ async getAuthorizeUrl(redirectUri: string): Promise { const response = await api.get<{ authorization_url: string }>( '/providers/gmail/authorize-url', { params: { redirect_uri: redirectUri } } ); return response.data.authorization_url; }, /** Exchange an OAuth2 code for Gmail tokens and persist them. */ async saveCallback(code: string, redirectUri: string): Promise { const response = await api.post('/providers/gmail/callback', { code, redirect_uri: redirectUri, }); return response.data; }, /** Get the current user's stored Gmail credential status. */ async getCredential(): Promise { const response = await api.get('/providers/gmail-credential'); return response.data; }, /** Remove stored Gmail credentials. */ async disconnect(): Promise { await api.delete('/providers/gmail-credential'); }, /** Inject a debug test email into the user's Gmail inbox. */ async sendDebugEmail(): Promise { const response = await api.post('/providers/gmail/debug-email'); return response.data; }, /** Update the labels applied to imported Gmail messages. */ async updateImportLabels(importLabelTemplates: string[]): Promise { const response = await api.put('/providers/gmail-credential/labels', { import_label_templates: importLabelTemplates, }); return response.data; }, }; // ── SMTP Config API ───────────────────────────────────────────────────── export const smtpApi = { async get(): Promise { const response = await api.get('/users/smtp-config'); return response.data; }, async save(data: { host: string; port: number; username: string; password?: string; use_tls: boolean; }): Promise { const response = await api.put('/users/smtp-config', data); return response.data; }, async remove(): Promise { await api.delete('/users/smtp-config'); }, }; // ── Admin Types ───────────────────────────────────────────────────────── export interface AdminUser { id: number; email: string; full_name: string | null; is_active: boolean; is_superuser: boolean; subscription_tier: string; subscription_status: string; google_id?: string | null; oauth_provider?: string | null; last_login_at?: string | null; created_at: string; mail_account_count: number; } export interface AdminUserUpdate { full_name?: string | null; email?: string; is_active?: boolean; is_superuser?: boolean; subscription_tier?: string; subscription_status?: string; } export interface SubscriptionPlan { id: number; tier: string; name: string; description?: string | null; price_monthly: number; price_yearly?: number | null; max_mail_accounts: number; max_emails_per_day: number; check_interval_minutes: number; support_level: string; features?: Record | null; is_active: boolean; } export interface SubscriptionPlanCreate { tier: string; name: string; description?: string; price_monthly: number; price_yearly?: number; max_mail_accounts: number; max_emails_per_day: number; check_interval_minutes: number; support_level: string; features?: Record; is_active: boolean; } export interface SubscriptionPlanUpdate { name?: string; description?: string; price_monthly?: number; price_yearly?: number; max_mail_accounts?: number; max_emails_per_day?: number; check_interval_minutes?: number; support_level?: string; features?: Record; is_active?: boolean; } export interface AdminStats { total_users: number; total_mail_accounts: number; total_processing_runs: number; } // ── Admin API ─────────────────────────────────────────────────────────── export const adminApi = { async getStats(): Promise { const response = await api.get('/admin/stats'); return response.data; }, async listUsers(skip = 0, limit = 100): Promise { const response = await api.get('/admin/users', { params: { skip, limit }, }); return response.data; }, async getUser(id: number): Promise { const response = await api.get(`/admin/users/${id}`); return response.data; }, async updateUser(id: number, data: AdminUserUpdate): Promise { const response = await api.put(`/admin/users/${id}`, data); return response.data; }, async deleteUser(id: number): Promise { await api.delete(`/admin/users/${id}`); }, async listPlans(): Promise { const response = await api.get('/admin/plans'); return response.data; }, async createPlan(data: SubscriptionPlanCreate): Promise { const response = await api.post('/admin/plans', data); return response.data; }, async updatePlan(id: number, data: SubscriptionPlanUpdate): Promise { const response = await api.put(`/admin/plans/${id}`, data); return response.data; }, async deletePlan(id: number): Promise { await api.delete(`/admin/plans/${id}`); }, async listProcessingRuns(params?: { page?: number; page_size?: number; user_id?: number; account_id?: number; status?: string; }): Promise { const response = await api.get('/admin/processing-runs', { params, }); return response.data; }, async listProcessingLogs(params?: { page?: number; page_size?: number; user_id?: number; account_id?: number; run_id?: number; level?: string; }): Promise { const response = await api.get('/admin/processing-logs', { params, }); return response.data; }, }; // ── Notification Types ────────────────────────────────────────────────── export interface NotificationConfig { id: number; user_id: number; name: string; channel: string; apprise_url: string | null; is_enabled: boolean; config: Record; notify_on_errors: boolean; notify_on_success: boolean; notify_threshold: number; created_at: string; updated_at: string; } export interface NotificationConfigCreate { name: string; channel: string; apprise_url?: string | null; is_enabled?: boolean; config?: Record; notify_on_errors?: boolean; notify_on_success?: boolean; notify_threshold?: number; } export interface NotificationConfigUpdate { name?: string; channel?: string; apprise_url?: string | null; is_enabled?: boolean; config?: Record; notify_on_errors?: boolean; notify_on_success?: boolean; notify_threshold?: number; } export interface AdminNotificationConfig { id: number; name: string; apprise_url: string; is_enabled: boolean; notify_on_errors: boolean; notify_on_system_events: boolean; description: string | null; created_at: string; updated_at: string; } export interface AdminNotificationConfigCreate { name: string; apprise_url: string; is_enabled?: boolean; notify_on_errors?: boolean; notify_on_system_events?: boolean; description?: string | null; } export interface AdminNotificationConfigUpdate { name?: string; apprise_url?: string; is_enabled?: boolean; notify_on_errors?: boolean; notify_on_system_events?: boolean; description?: string | null; } // ── Notifications API ─────────────────────────────────────────────────── export const notificationsApi = { async list(): Promise { const response = await api.get('/notifications'); return response.data; }, async create(data: NotificationConfigCreate): Promise { const response = await api.post('/notifications', data); return response.data; }, async update(id: number, data: NotificationConfigUpdate): Promise { const response = await api.put(`/notifications/${id}`, data); return response.data; }, async delete(id: number): Promise { await api.delete(`/notifications/${id}`); }, async test(apprise_url: string): Promise<{ success: boolean; message: string }> { const response = await api.post<{ success: boolean; message: string }>( '/notifications/test', { apprise_url } ); return response.data; }, }; // ── Admin Notifications API ───────────────────────────────────────────── export const adminNotificationsApi = { async list(): Promise { const response = await api.get('/admin/notifications'); return response.data; }, async create(data: AdminNotificationConfigCreate): Promise { const response = await api.post('/admin/notifications', data); return response.data; }, async update(id: number, data: AdminNotificationConfigUpdate): Promise { const response = await api.put(`/admin/notifications/${id}`, data); return response.data; }, async delete(id: number): Promise { await api.delete(`/admin/notifications/${id}`); }, async test(apprise_url: string): Promise<{ success: boolean; message: string }> { const response = await api.post<{ success: boolean; message: string }>( '/admin/notifications/test', { apprise_url } ); return response.data; }, }; export default api;