From 133f059f13b621e1a15104dfe1f8a04e90b91bfb Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 6 Aug 2026 16:30:20 +0000 Subject: [PATCH] feat(auth): centralized api client with Authorization header --- frontend/src/lib/api.ts | 60 +++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 70a4cbd..3c3dbaa 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -1,27 +1,59 @@ -import type { NetObject, ServiceInfo } from '../types'; +import { getToken, clearSession } from './session'; +import type { ServiceInfo } from '../types'; -const AGENT_BASE = '/api'; +const BACKEND = '/backend'; +const AGENT = '/api'; -export async function fetchObjects(): Promise { - return [ - { id: '1', name: 'Site HQ', slug: 'site-hq', type: 'site', status: 'active' }, - { id: '2', name: 'Rack A', slug: 'rack-a', type: 'rack', status: 'active' }, - { id: '3', name: 'Server Proxmox', slug: 'server-proxmox', type: 'physical_host', vendor: 'Dell', model: 'R730', status: 'active' }, - { id: '4', name: 'Switch Core', slug: 'switch-core', type: 'switch', vendor: 'Mikrotik', status: 'active' }, - { id: '5', name: 'Firewall Edge', slug: 'firewall-edge', type: 'firewall', status: 'active' }, - ]; +export async function apiFetch(path: string, init: RequestInit = {}): Promise { + const token = getToken(); + const headers: Record = { + 'Content-Type': 'application/json', + ...(init.headers as Record ?? {}), + }; + if (token) headers['Authorization'] = `Bearer ${token}`; + const res = await fetch(`${BACKEND}${path}`, { ...init, headers }); + if (res.status === 401) { clearSession(); window.location.reload(); } + return res; } +export async function apiGet(path: string): Promise { + const res = await apiFetch(path); + if (!res.ok) throw new Error(`GET ${path} failed: ${res.status}`); + return res.json(); +} + +export async function apiPost(path: string, body?: unknown): Promise { + const res = await apiFetch(path, { method: 'POST', body: body ? JSON.stringify(body) : undefined }); + if (!res.ok) { + const err = await res.json().catch(() => null); + throw new Error(err?.detail ?? `POST ${path} failed: ${res.status}`); + } + return res.json(); +} + +export async function apiPatch(path: string, body?: unknown): Promise { + const res = await apiFetch(path, { method: 'PATCH', body: body ? JSON.stringify(body) : undefined }); + if (!res.ok) { + const err = await res.json().catch(() => null); + throw new Error(err?.detail ?? `PATCH ${path} failed: ${res.status}`); + } + return res.json(); +} + +export async function apiDelete(path: string): Promise { + const res = await apiFetch(path, { method: 'DELETE' }); + if (!res.ok) throw new Error(`DELETE ${path} failed: ${res.status}`); +} + +// Legacy agent calls (unauthenticated local agent) export async function fetchServices(): Promise { - const res = await fetch(`${AGENT_BASE}/services`); + const res = await fetch(`${AGENT}/services`); if (!res.ok) throw new Error('Impossibile leggere lo stato dei servizi'); return res.json(); } export async function serviceAction(name: string, action: 'start' | 'stop' | 'restart') { - const res = await fetch(`${AGENT_BASE}/services/${encodeURIComponent(name)}/${action}`, { - method: 'POST', - }); + const res = await fetch(`${AGENT}/services/${encodeURIComponent(name)}/${action}`, { method: 'POST' }); if (!res.ok) throw new Error(`Azione ${action} fallita su ${name}`); return res.json(); }