import axios from 'axios'; // Cookie-based auth (Google OAuth2 session), so send credentials with each call. const api = axios.create({ baseURL: '/api/v1', withCredentials: true }); api.interceptors.response.use( (r) => r, (err) => { if (err.response?.status === 401) { // Not signed in. Send the user to the public landing page (unless already // there) so they can read the features and choose to log in. if (window.location.pathname !== '/') window.location.href = '/'; } return Promise.reject(err); } ); // Top-level navigation that starts the Google flow and returns to the app. export const LOGIN_URL = '/api/v1/auth/login?returnUrl=/app'; export const AppApi = { info: () => api.get('/app/info').then((r) => r.data) }; export const AuthApi = { me: () => api.get('/auth/me').then((r) => r.data), logout: () => api.post('/auth/logout') }; export const SyncApi = { status: () => api.get('/sync/status').then((r) => r.data), full: () => api.post('/sync/full'), incremental: () => api.post('/sync/incremental') }; export const AnalyticsApi = { dashboard: () => api.get('/analytics/dashboard').then((r) => r.data), health: () => api.get('/analytics/health').then((r) => r.data), topSenders: (take = 20) => api.get(`/analytics/top-senders?take=${take}`).then((r) => r.data), volume: (days = 90) => api.get(`/analytics/volume?days=${days}`).then((r) => r.data), heatmap: () => api.get('/analytics/heatmap').then((r) => r.data), categoryHeatmap: () => api.get('/analytics/category-heatmap').then((r) => r.data), attachments: () => api.get('/analytics/attachments').then((r) => r.data), sidebarCounts: () => api.get('/analytics/sidebar-counts').then((r) => r.data), // Large take to retrieve all senders for the volume-tiers page. allSenders: () => api.get('/analytics/top-senders?take=5000').then((r) => r.data) }; export const CleanupApi = { preview: (req) => api.post('/cleanup/preview', req).then((r) => r.data), execute: (req) => api.post('/cleanup/execute', req).then((r) => r.data) }; // Bulk actions over an explicit set of email IDs (selection-driven, always confirmed — // the user already opted in by selecting rows and clicking the action). // Numeric values must match CleanupActionType in Domain/Enums/Enums.cs (no string // enum converter is configured on the API, so plain numbers are required here). const CLEANUP_ACTION = { Archive: 0, Trash: 1, MarkRead: 5, MarkUnread: 6, Star: 7, Unstar: 8 }; export const BulkApi = { markRead: (ids) => CleanupApi.execute({ action: CLEANUP_ACTION.MarkRead, emailIds: ids, confirmed: true }), markUnread: (ids) => CleanupApi.execute({ action: CLEANUP_ACTION.MarkUnread, emailIds: ids, confirmed: true }), star: (ids) => CleanupApi.execute({ action: CLEANUP_ACTION.Star, emailIds: ids, confirmed: true }), unstar: (ids) => CleanupApi.execute({ action: CLEANUP_ACTION.Unstar, emailIds: ids, confirmed: true }), archive: (ids) => CleanupApi.execute({ action: CLEANUP_ACTION.Archive, emailIds: ids, confirmed: true }), trash: (ids) => CleanupApi.execute({ action: CLEANUP_ACTION.Trash, emailIds: ids, confirmed: true }), }; export const UnsubscribeApi = { detect: () => api.post('/unsubscribe/detect'), safeList: () => api.get('/unsubscribe/safe-list').then((r) => r.data), process: (req) => api.post('/unsubscribe/process', req).then((r) => r.data) }; export const LayoutApi = { get: () => api.get('/widgetlayout').then((r) => r.data), save: (layout) => api.put('/widgetlayout', layout) }; export const SearchApi = { folder: (slug, page = 1, pageSize = 50) => api.post('/search', folderToRequest(slug, page, pageSize)).then((r) => r.data), query: (q, page = 1, pageSize = 50) => api.get('/search', { params: { q, page, pageSize } }).then((r) => r.data), }; function folderToRequest(slug, page, pageSize) { const base = { page, pageSize }; switch (slug) { // ── Mailbox ── case 'inbox': return { ...base, isInInbox: true, isTrashed: false }; case 'allmail': return { ...base }; case 'unread': return { ...base, isUnread: true }; case 'starred': return { ...base, isStarred: true }; case 'sent': return { ...base, gmailLabel: 'SENT' }; case 'drafts': return { ...base, gmailLabel: 'DRAFT' }; case 'archive': return { ...base, isInInbox: false, isTrashed: false }; case 'spam': return { ...base, gmailLabel: 'SPAM' }; case 'trash': return { ...base, isTrashed: true }; // ── Special filters ── case 'large': return { ...base, minSizeBytes: 5_000_000 }; case 'old': { const d = new Date(); d.setFullYear(d.getFullYear() - 1); return { ...base, to: d.toISOString().slice(0, 10) }; } // ── Smart folders ── case 'automated': return { ...base, category: 'Notification' }; case 'finance': return { ...base, category: 'Finance' }; case 'social': return { ...base, category: 'Social' }; case 'shopping': return { ...base, category: 'Shopping' }; case 'noreply': return { ...base, query: 'from:noreply' }; case 'gaming': return { ...base, category: 'Gaming' }; case 'sales': return { ...base, category: 'SeasonalSales' }; case 'ridesharing': return { ...base, category: 'RideSharing' }; case 'food': return { ...base, category: 'FoodDelivery' }; case 'wellness': return { ...base, category: 'Wellness' }; // New categories case 'travel': return { ...base, category: 'Travel' }; case 'subscriptions': return { ...base, category: 'Subscriptions' }; case 'parcels': return { ...base, category: 'Parcels' }; case 'recruitment': return { ...base, category: 'Recruitment' }; case 'events': return { ...base, category: 'Events' }; case 'security': return { ...base, category: 'SecurityAlerts' }; case 'healthcare': return { ...base, category: 'Healthcare' }; case 'education': return { ...base, category: 'Education' }; case 'news': return { ...base, category: 'NewsMedia' }; case 'property': return { ...base, category: 'PropertyUtilities' }; case 'charity': return { ...base, category: 'Charity' }; case 'government': return { ...base, category: 'Government' }; case 'crypto': return { ...base, category: 'CryptoInvesting' }; case 'family': return { ...base, category: 'FamilySchool' }; default: return { ...base }; } } export const EmailApi = { get: (id) => api.get(`/email/${id}`).then((r) => r.data), markRead: (id) => api.post(`/email/${id}/read`), markUnread: (id) => api.post(`/email/${id}/unread`), star: (id) => api.post(`/email/${id}/star`), unstar: (id) => api.post(`/email/${id}/unstar`), trash: (id) => api.post(`/email/${id}/trash`), untrash: (id) => api.post(`/email/${id}/untrash`), unsubscribe:(id) => api.post(`/email/${id}/unsubscribe`).then((r) => r.data), }; export const ExportApi = { reportUrl: (format) => `/api/v1/export/report?format=${format}` }; export default api;