Files
Front-DataSentinel/DataSentinel/src/services/api.js
T
2026-06-20 12:54:01 +02:00

334 lines
10 KiB
JavaScript

/* Service centralisé pour tous les appels API vers le backend FastAPI */
/* Utilise la variable d'environnement Vite si définie, sinon localhost par défaut */
const BASE_URL = import.meta.env.VITE_API_URL ?? 'http://localhost:8000';
/* Clés de stockage local pour le token JWT et l'utilisateur connecté */
const TOKEN_KEY = 'ds_token';
const USER_KEY = 'ds_user';
/* Helpers de gestion du token JWT dans le localStorage */
export function getToken() {
return localStorage.getItem(TOKEN_KEY);
}
export function setToken(token) {
localStorage.setItem(TOKEN_KEY, token);
}
export function clearToken() {
localStorage.removeItem(TOKEN_KEY);
}
/* Construit les en-têtes d'authentification si un token est présent */
function authHeaders(extra = {}) {
const token = getToken();
const headers = { ...extra };
if (token) {
headers.Authorization = `Bearer ${token}`;
}
return headers;
}
/* Gestion d'une réponse 401 : purge de la session et redirection vers /login */
function handleUnauthorized() {
clearToken();
localStorage.removeItem(USER_KEY);
/* Évite une boucle de redirection si on est déjà sur la page de connexion */
if (window.location.pathname !== '/login') {
window.location.href = '/login';
}
}
/* Fonction utilitaire pour les requêtes fetch (GET) avec gestion d'erreur et auth */
async function fetchApi(path, params = {}) {
const url = new URL(`${BASE_URL}${path}`);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
url.searchParams.append(key, value);
}
});
try {
const response = await fetch(url.toString(), { headers: authHeaders() });
if (response.status === 401) {
handleUnauthorized();
throw new Error('Session expirée, veuillez vous reconnecter.');
}
if (!response.ok) {
const errorText = await response.text();
console.error(`Erreur API [${response.status}] ${path}: ${errorText}`);
throw new Error(`Erreur ${response.status}: ${errorText}`);
}
return await response.json();
} catch (error) {
console.error(`Erreur lors de l'appel API ${path}:`, error);
throw error;
}
}
/* Helper générique pour les requêtes JSON (POST / PUT / DELETE) avec auth */
async function sendJson(path, method, body) {
const url = `${BASE_URL}${path}`;
const options = {
method,
headers: authHeaders(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
};
if (body !== undefined) {
options.body = JSON.stringify(body);
}
try {
const response = await fetch(url, options);
if (response.status === 401) {
handleUnauthorized();
throw new Error('Session expirée, veuillez vous reconnecter.');
}
if (!response.ok) {
const errorText = await response.text();
console.error(`Erreur API [${response.status}] ${path}: ${errorText}`);
throw new Error(`Erreur ${response.status}: ${errorText}`);
}
/* Certaines réponses (204, DELETE) peuvent ne pas contenir de corps JSON */
const text = await response.text();
return text ? JSON.parse(text) : null;
} catch (error) {
console.error(`Erreur lors de l'appel API ${path}:`, error);
throw error;
}
}
/* Authentification : POST /auth/login en x-www-form-urlencoded (OAuth2) */
export async function login(username, password) {
const body = new URLSearchParams();
body.append('username', username);
body.append('password', password);
const response = await fetch(`${BASE_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString(),
});
if (response.status === 401) {
throw new Error('Identifiants invalides');
}
if (response.status === 429) {
throw new Error('Trop de tentatives. Veuillez réessayer dans une minute.');
}
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Erreur ${response.status}: ${errorText}`);
}
const data = await response.json();
setToken(data.access_token);
return data.user;
}
/* Récupère le profil de l'utilisateur connecté */
export async function getMe() {
return fetchApi('/auth/me');
}
/* ---------- Administration des utilisateurs ---------- */
export async function getUsers() {
return fetchApi('/admin/users');
}
export async function createUser({ username, email, password, role }) {
return sendJson('/admin/users', 'POST', { username, email, password, role });
}
export async function updateUser(id_user, changes) {
return sendJson(`/admin/users/${id_user}`, 'PUT', changes);
}
export async function deleteUser(id_user) {
return sendJson(`/admin/users/${id_user}`, 'DELETE');
}
export async function resetUserPassword(id_user, password) {
return sendJson(`/admin/users/${id_user}/reset-password`, 'POST', { password });
}
/* Journal d'audit */
export async function getJournal(limit = 200) {
return fetchApi('/admin/journal', { limit });
}
/* ---------- RGPD : données personnelles ---------- */
export async function exportMyData() {
return fetchApi('/me/data-export');
}
export async function deleteMyAccount() {
return sendJson('/me', 'DELETE');
}
/* Santé de l'API */
export async function getHealth() {
return fetchApi('/health');
}
/* Récupère la liste des catégories */
export async function getCategories() {
return fetchApi('/categories');
}
/* Récupère la liste des services */
export async function getServices() {
return fetchApi('/services');
}
/* Récupère les contacts, filtrable par service */
export async function getContacts(id_service) {
return fetchApi('/contacts', { id_service });
}
/* Récupère la nomenclature (monitorings), filtrable par service et catégorie */
export async function getMonitorings(id_service, id_categorie) {
const data = await fetchApi('/monitorings', { id_service, id_categorie });
return Array.isArray(data) ? data.map(m => ({
id: m.id_monito,
id_monito: m.id_monito,
nom: m.monito_intitule,
monito_intitule: m.monito_intitule,
...m
})) : data;
}
/* Récupère un monitoring par son identifiant */
export async function getMonitoringById(id) {
const data = await fetchApi(`/monitorings/${id}`);
return data ? {
id: data.id_monito,
id_monito: data.id_monito,
nom: data.monito_intitule,
monito_intitule: data.monito_intitule,
...data
} : data;
}
/* Récupère les lignes d'erreur d'un monitoring avec pagination et filtres */
export async function getMonitoringDetails(id, { search, limit = 500, offset = 0 } = {}) {
return fetchApi(`/monitorings/${id}/details`, { search, limit, offset });
}
/* Récupère le nombre d'erreurs d'un monitoring */
export async function getMonitoringCount(id) {
const data = await fetchApi(`/monitorings/${id}/count`);
return {
id_monito: data.id_monito,
count: data.nb_erreurs,
nb_erreurs: data.nb_erreurs,
...data
};
}
/* Récupère les colonnes dynamiques d'un monitoring */
export async function getMonitoringColumns(id) {
return fetchApi(`/monitorings/${id}/columns`);
}
/* Récupère les données du dashboard (VUE_CONSO), filtrable par service et catégorie */
export async function getDashboard(service, categorie) {
const data = await fetchApi('/dashboard', { service, categorie });
return Array.isArray(data) ? data.map(m => ({
id: m.id_monito,
id_monito: m.id_monito,
nom: m.nom_monito,
nb_erreurs: m.nb_erreurs,
...m
})) : data;
}
/* Récupère le résumé global KPI du dashboard */
export async function getDashboardSummary() {
const data = await fetchApi('/dashboard/summary');
return {
total: data.nb_monitorings ?? 0,
ok: data.monitorings_ok ?? 0,
warning: 0, // À calculer ou ajouter à l'API
critical: data.monitorings_en_erreur ?? 0,
nb_monitorings: data.nb_monitorings,
total_erreurs: data.total_erreurs,
max_erreurs: data.max_erreurs,
monitoring_critique: data.monitoring_critique,
...data
};
}
/* Récupère l'historique, filtrable par monitoring, dates et service */
export async function getHistorique(id_monito, date_debut, date_fin, service) {
const data = await fetchApi('/historique', { id_monito, date_debut, date_fin, service });
return Array.isArray(data) ? data.map(h => ({
date: h.date_sauvegarde,
date_sauvegarde: h.date_sauvegarde,
...h
})) : data;
}
/* Récupère les données d'évolution pour un graphique */
export async function getHistoriqueEvolution(id, date_debut, date_fin) {
const data = await fetchApi(`/historique/${id}/evolution`, { date_debut, date_fin });
if (data?.evolution) {
return data.evolution.map(point => ({
date: point.date,
erreurs: point.nb_erreurs,
nb_erreurs: point.nb_erreurs
}));
}
return [];
}
export async function getEvolutionGlobal(date_debut, date_fin) {
const data = await fetchApi('/evolution/global', { date_debut, date_fin });
if (data?.series) {
return data.series.map(point => ({
date: point.date,
erreurs: point.nb_erreurs,
nb_erreurs: point.nb_erreurs
}));
}
return [];
}
/* Export CSV utilitaire */
export function exportToCSV(data, filename = 'export.csv') {
if (!data || data.length === 0) {
console.warn('Aucune donnée à exporter');
return;
}
// Récupère les clés du premier objet
const headers = Object.keys(data[0]);
// Crée le CSV : en-têtes + lignes
const csvLines = [
headers.join(','),
...data.map(row =>
headers.map(header => {
const value = row[header];
// Échappe les valeurs qui contiennent des virgules ou des guillemets
if (value === null || value === undefined) return '';
const str = String(value);
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
return `"${str.replace(/"/g, '""')}"`;
}
return str;
}).join(',')
)
].join('\n');
// Crée un blob et télécharge
const blob = new Blob([csvLines], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
URL.revokeObjectURL(link.href);
}