import axios, { AxiosError } from "axios"; // Default to production URL, override with VITE_API_URL for local dev const API_URL = import.meta.env.VITE_API_URL || "https://rmm-api.azcomputerguru.com"; export const api = axios.create({ baseURL: API_URL, headers: { "Content-Type": "application/json", }, }); // Token management - use sessionStorage (cleared on tab close) instead of localStorage // This provides better security against XSS attacks as tokens are not persisted const TOKEN_KEY = "gururmm_auth_token"; export const getToken = (): string | null => { return sessionStorage.getItem(TOKEN_KEY); }; export const setToken = (token: string): void => { sessionStorage.setItem(TOKEN_KEY, token); }; export const clearToken = (): void => { sessionStorage.removeItem(TOKEN_KEY); }; // Request interceptor - add auth header api.interceptors.request.use((config) => { const token = getToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // Response interceptor - handle 401 unauthorized api.interceptors.response.use( (response) => response, (error: AxiosError) => { if (error.response?.status === 401) { clearToken(); // Use a more graceful redirect that preserves SPA state if (window.location.pathname !== "/login") { window.location.href = "/login"; } } return Promise.reject(error); } ); // API types export interface Agent { id: string; hostname: string; os_type: string; os_version: string | null; agent_version: string | null; status: "online" | "offline" | "error"; last_seen: string | null; created_at: string; device_id: string | null; site_id: string | null; site_name: string | null; client_id: string | null; client_name: string | null; } export interface Metrics { id: number; agent_id: string; timestamp: string; cpu_percent: number; memory_percent: number; memory_used_bytes: number; disk_percent: number; disk_used_bytes: number; network_rx_bytes: number; network_tx_bytes: number; // Extended metrics uptime_seconds?: number; boot_time?: number; logged_in_user?: string; user_idle_seconds?: number; public_ip?: string; memory_total_bytes?: number; disk_total_bytes?: number; } export interface NetworkInterface { name: string; mac_address?: string; ipv4_addresses: string[]; ipv6_addresses: string[]; } export interface AgentState { agent_id: string; network_interfaces?: NetworkInterface[]; network_state_hash?: string; uptime_seconds?: number; boot_time?: number; logged_in_user?: string; user_idle_seconds?: number; public_ip?: string; network_updated_at?: string; metrics_updated_at?: string; } export interface Command { id: string; agent_id: string; command_type: string; command_text: string; status: "pending" | "running" | "completed" | "failed"; exit_code: number | null; stdout: string | null; stderr: string | null; created_at: string; completed_at: string | null; } export interface User { id: string; email: string; name: string | null; role: string; } export interface Client { id: string; name: string; code: string | null; notes: string | null; is_active: boolean; created_at: string; site_count: number; } export interface Site { id: string; client_id: string; client_name: string | null; name: string; site_code: string; address: string | null; notes: string | null; is_active: boolean; created_at: string; agent_count: number; } export interface CreateSiteResponse { site: Site; api_key: string; message: string; } export interface LoginRequest { email: string; password: string; } export interface LoginResponse { token: string; user: User; } export interface RegisterRequest { email: string; password: string; name?: string; } // API functions export const authApi = { login: async (data: LoginRequest): Promise => { const response = await api.post("/api/auth/login", data); if (response.data.token) { setToken(response.data.token); } return response.data; }, register: async (data: RegisterRequest): Promise => { const response = await api.post("/api/auth/register", data); if (response.data.token) { setToken(response.data.token); } return response.data; }, me: () => api.get("/api/auth/me"), logout: (): void => { clearToken(); }, isAuthenticated: (): boolean => { return !!getToken(); }, }; export const agentsApi = { list: () => api.get("/api/agents"), listUnassigned: () => api.get("/api/agents/unassigned"), get: (id: string) => api.get(`/api/agents/${id}`), delete: (id: string) => api.delete(`/api/agents/${id}`), move: (id: string, siteId: string | null) => api.post(`/api/agents/${id}/move`, { site_id: siteId }), getMetrics: (id: string, hours?: number) => api.get(`/api/agents/${id}/metrics`, { params: { hours } }), getState: (id: string) => api.get(`/api/agents/${id}/state`), }; export const commandsApi = { send: (agentId: string, command: { command_type: string; command: string }) => api.post(`/api/agents/${agentId}/command`, command), list: () => api.get("/api/commands"), get: (id: string) => api.get(`/api/commands/${id}`), }; export const clientsApi = { list: () => api.get("/api/clients"), get: (id: string) => api.get(`/api/clients/${id}`), create: (data: { name: string; code?: string; notes?: string }) => api.post("/api/clients", data), update: (id: string, data: { name?: string; code?: string; notes?: string; is_active?: boolean }) => api.put(`/api/clients/${id}`, data), delete: (id: string) => api.delete(`/api/clients/${id}`), }; export const sitesApi = { list: () => api.get("/api/sites"), get: (id: string) => api.get(`/api/sites/${id}`), listByClient: (clientId: string) => api.get(`/api/clients/${clientId}/sites`), create: (data: { client_id: string; name: string; address?: string; notes?: string }) => api.post("/api/sites", data), update: (id: string, data: { name?: string; address?: string; notes?: string; is_active?: boolean }) => api.put(`/api/sites/${id}`, data), delete: (id: string) => api.delete(`/api/sites/${id}`), regenerateApiKey: (id: string) => api.post<{ api_key: string; message: string }>(`/api/sites/${id}/regenerate-key`), };