const getBaseUrl = (): string => { if (typeof window !== 'undefined') { const host = window.location.hostname; if (host !== 'localhost' && host !== '127.0.0.1') { return ''; } } if (process.env.NODE_ENV === 'production') { return ''; } return process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; }; const rawBaseUrl = getBaseUrl(); const API_BASE_URL = rawBaseUrl.replace(/\/api\/v1\/?$/, '').replace(/\/+$/, ''); export const API_ENDPOINTS = { // Auth AUTH_LOGIN: '/api/v1/auth/login', AUTH_REFRESH: '/api/v1/auth/refresh', AUTH_LOGOUT: '/api/v1/auth/logout', // Users USERS_CREATE: '/api/v1/users/create', USERS_ALL: '/api/v1/users/all', USERS_PROFILE: (id: string) => `/api/v1/users/profile/${id}`, USERS_UPDATE: (id: string) => `/api/v1/users/update/${id}`, USERS_DELETE: (id: string) => `/api/v1/users/delete/${id}`, // Admin Security ADMIN_LIST_SESSIONS: '/api/v1/admin/list_sessions', ADMIN_REVOKE_SESSION: '/api/v1/admin/revoke_session', ADMIN_KILL_SWITCH: '/api/v1/admin/request_kill_switch', ADMIN_CANCEL_KILL_SWITCH: '/api/v1/admin/cancel_kill_switch', ADMIN_SELF_DESTRUCT: '/api/v1/admin/self_destruct', ADMIN_FAILED_LOGINS: '/api/v1/admin/failed-logins', ADMIN_AUDIT_LOGS: '/api/v1/admin/audit-logs', DASHBOARD_STATS: '/api/v1/dashboard/stats', // Settings SETTINGS_CREATE: '/api/v1/settings/create', SETTINGS_PUBLIC: '/api/v1/settings/public', SETTINGS_BY_KEY: (key: string) => `/api/v1/settings/key/${key}`, SETTINGS_UPDATE: (key: string) => `/api/v1/settings/update/${key}`, // MFA MFA_SETUP: '/api/v1/mfa/setup', MFA_VERIFY: '/api/v1/mfa/verify', // Files FILES_UPLOAD: '/api/v1/files/upload', FILES_BY_ENTITY: (type: string, id: string) => `/api/v1/files/entity/${type}/${id}`, FILES_DELETE: (id: string) => `/api/v1/files/delete/${id}`, // Geo GEO_COUNTRIES: '/api/v1/geo/countries', GEO_STATES: (countryId: number) => `/api/v1/geo/countries/${countryId}/states`, GEO_CITIES: (stateId: number) => `/api/v1/geo/states/${stateId}/cities`, // Role & Permissions Control ROLE_PERM_ROLES_ALL: '/api/v1/role-permissions/roles/all', ROLE_PERM_ROLES_CREATE: '/api/v1/role-permissions/roles/create', ROLE_PERM_ROLES_DELETE: (id: string) => `/api/v1/role-permissions/roles/delete/${id}`, ROLE_PERM_PERMISSIONS_ALL: '/api/v1/role-permissions/permissions/all', ROLE_PERM_ROLE_GET_PERMISSIONS: (id: string) => `/api/v1/role-permissions/roles/${id}/permissions`, ROLE_PERM_ROLE_SET_PERMISSIONS: (id: string) => `/api/v1/role-permissions/roles/${id}/permissions`, // Catalog, Devices, and Inventory Control CATALOG_BRANDS_ALL: '/api/v1/catalog/brands/all', CATALOG_BRANDS_CREATE: '/api/v1/catalog/brands/create', CATALOG_CATEGORIES_ALL: '/api/v1/catalog/categories/all', CATALOG_CATEGORIES_CREATE: '/api/v1/catalog/categories/create', CATALOG_PARTS_ALL: '/api/v1/catalog/parts/all', CATALOG_PARTS_CREATE: '/api/v1/catalog/parts/create', CATALOG_DEVICE_SERIES_ALL: '/api/v1/catalog/device-series/all', CATALOG_DEVICE_SERIES_CREATE: '/api/v1/catalog/device-series/create', CATALOG_DEVICE_MODELS_ALL: '/api/v1/catalog/device-models/all', CATALOG_DEVICE_MODELS_CREATE: '/api/v1/catalog/device-models/create', CATALOG_SERVICE_TYPES_ALL: '/api/v1/catalog/service-types/all', CATALOG_SERVICE_TYPES_CREATE: '/api/v1/catalog/service-types/create', CATALOG_REPAIR_SERVICES_ALL: '/api/v1/catalog/repair-services/all', CATALOG_REPAIR_SERVICES_CREATE: '/api/v1/catalog/repair-services/create', CATALOG_REPAIR_VARIANTS_ALL: '/api/v1/catalog/repair-variants/all', CATALOG_REPAIR_VARIANTS_CREATE: '/api/v1/catalog/repair-variants/create', CATALOG_STOCK_HISTORY: '/api/v1/catalog/stock-movements/history', CATALOG_STOCK_ADJUST: '/api/v1/catalog/stock-movements/adjust', CATALOG_PO_ALL: '/api/v1/catalog/purchase-orders/all', CATALOG_PO_CREATE: '/api/v1/catalog/purchase-orders/create', CATALOG_PO_RECEIVE: (id: string) => `/api/v1/catalog/purchase-orders/${id}/receive`, CATALOG_ATTRIBUTES_ALL: '/api/v1/catalog/attributes/all', CATALOG_ATTRIBUTES_CREATE: '/api/v1/catalog/attributes/create', CATALOG_PRODUCTS_ALL: '/api/v1/catalog/products/all', CATALOG_PRODUCTS_CREATE: '/api/v1/catalog/products/create', CATALOG_BRANDS_UPDATE: (id: string) => `/api/v1/catalog/brands/update/${id}`, CATALOG_CATEGORIES_UPDATE: (id: string) => `/api/v1/catalog/categories/update/${id}`, CATALOG_DEVICE_SERIES_UPDATE: (id: string) => `/api/v1/catalog/device-series/update/${id}`, CATALOG_DEVICE_MODELS_UPDATE: (id: string) => `/api/v1/catalog/device-models/update/${id}`, CATALOG_SERVICE_TYPES_UPDATE: (id: string) => `/api/v1/catalog/service-types/update/${id}`, CATALOG_REPAIR_SERVICES_UPDATE: (id: string) => `/api/v1/catalog/repair-services/update/${id}`, CATALOG_REPAIR_VARIANTS_UPDATE: (id: string) => `/api/v1/catalog/repair-variants/update/${id}`, CATALOG_ATTRIBUTES_UPDATE: (id: string) => `/api/v1/catalog/attributes/update/${id}`, CATALOG_PRODUCTS_UPDATE: (id: string) => `/api/v1/catalog/products/update/${id}`, CATALOG_PRODUCTS_DELETE: (id: string) => `/api/v1/catalog/products/${id}`, CATALOG_CATEGORIES_DELETE: (id: string) => `/api/v1/catalog/categories/${id}`, CATALOG_BRANDS_DELETE: (id: string) => `/api/v1/catalog/brands/${id}`, CATALOG_DEVICE_SERIES_DELETE: (id: string) => `/api/v1/catalog/device-series/${id}`, CATALOG_DEVICE_MODELS_DELETE: (id: string) => `/api/v1/catalog/device-models/${id}`, CATALOG_ATTRIBUTES_DELETE: (id: string) => `/api/v1/catalog/attributes/${id}`, INVENTORY_SKUS: '/api/v1/inventory/skus', INVENTORY_ADJUST: '/api/v1/inventory/adjust', } as const; export { API_BASE_URL }; /** * Resolves a media URL returned by the backend. * If already absolute (http/https), returned as-is. * If relative (e.g. /uploads/...), returned as-is so Next.js rewrites * can proxy it transparently to the production media server. */ export function getMediaUrl(url: string | null | undefined): string { if (!url) return ''; const cleanUrl = url.trim(); if (!cleanUrl) return ''; if (cleanUrl.startsWith('http://') || cleanUrl.startsWith('https://') || cleanUrl.startsWith('//')) return cleanUrl; if (cleanUrl.startsWith('/uploads/')) return cleanUrl; if (cleanUrl.startsWith('uploads/')) return `/${cleanUrl}`; const path = cleanUrl.startsWith('/') ? cleanUrl.slice(1) : cleanUrl; return `/uploads/${path}`; }