Aller au contenu

Backend API - Spécifications Techniques

Vue d'ensemble

API REST Go avec Fiber framework, authentification JWT, et architecture multi-tenant.


Routes API

Public (No Auth)

Méthode Endpoint Description
GET /health Health check basique
GET /status Status public des services (cache 60s)
GET /status/incidents 10 derniers incidents
GET /status/external Monitors externes (UptimeRobot)

Authentification

Méthode Endpoint Description Rate Limit
POST /auth/login Connexion 5 req/15min
POST /auth/refresh Refresh token 5 req/15min
POST /auth/logout Déconnexion -
POST /auth/request-password-reset Demande reset 5 req/15min
POST /auth/reset-password Reset password 5 req/15min
GET /auth/validate-reset-token Valider token reset -
POST /auth/mfa/verify Vérifier code MFA 5 req/15min

GET /auth/validate-reset-token

Valide un token de réinitialisation de mot de passe.

Query Parameters : | Param | Type | Requis | Description | |-------|------|--------|-------------| | token | string | Oui | Token de reset reçu par email |

Réponse (200) :

{
  "valid": true
}

Réponse (token invalide/expiré) :

{
  "valid": false
}

Exemple curl :

curl -s "https://api.optralis.com/auth/validate-reset-token?token=abc123"


POST /auth/mfa/verify

Vérifie le code MFA après login initial pour obtenir les tokens d'accès.

Request Body :

{
  "mfa_session_id": "session-uuid-from-login",
  "code": "123456"
}

Réponse (200) :

{
  "access_token": "eyJ...",
  "refresh_token": "eyJ...",
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "role": "admin",
    "client_id": "uuid",
    "client_name": "Acme Corp"
  }
}

Codes d'erreur : | Code | Description | |------|-------------| | 400 | Request invalide (champs manquants) | | 401 | Session MFA invalide/expirée ou code incorrect | | 429 | Rate limit dépassé | | 500 | Erreur serveur |

Exemple curl :

curl -s -X POST https://api.optralis.com/auth/mfa/verify \
  -H "Content-Type: application/json" \
  -d '{"mfa_session_id":"abc123","code":"123456"}'

API Publique (No Auth)

Endpoints publics accessibles sans authentification.

Méthode Endpoint Description Rate Limit
POST /api/public/contact Soumettre formulaire contact 3 req/5min/IP
GET /api/public/landing-stats Statistiques landing page Cache 60s
GET /api/public/latest-version Dernière version agent -
GET /api/public/crl Certificate Revocation List (DER) Cache 5min

POST /api/public/contact

Soumet un formulaire de contact. Envoie un email à l'équipe support.

Protection anti-spam : - Cloudflare Turnstile : Token de vérification requis (sauf si TURNSTILE_SECRET_KEY non configuré) - Honeypot : Le champ website doit rester vide (les bots le remplissent automatiquement) - Rate limiting : 5 requêtes/minute par IP

Request Body :

{
  "name": "Jean Dupont",
  "email": "jean@example.com",
  "company": "Acme Corp",
  "subject": "demo",
  "message": "Je souhaite une démonstration...",
  "language": "fr",
  "turnstile_token": "0.xxxx...",
  "website": ""
}

Champ Type Requis Description
name string Oui Nom complet
email string Oui Email valide
company string Non Nom entreprise
subject string Non Sujet : demo, technical, enterprise ou vide
message string Oui Message (max 5000 car.)
language string Non Langue (fr/en, défaut: fr)
turnstile_token string Oui* Token Cloudflare Turnstile (*optionnel en dev)
website string Non Honeypot - doit rester vide

Réponse (200) :

{
  "success": true,
  "message": "message sent successfully"
}

Codes d'erreur : | Code | Description | |------|-------------| | 400 | Champs requis manquants, email invalide, ou vérification Turnstile échouée | | 429 | Rate limit dépassé (5 req/min par IP) | | 500 | Erreur d'envoi email |

Variables d'environnement : | Variable | Description | |----------|-------------| | TURNSTILE_SECRET_KEY | Clé secrète Cloudflare Turnstile (backend) |

Exemple curl :

curl -s -X POST https://api.optralis.com/api/public/contact \
  -H "Content-Type: application/json" \
  -d '{"name":"Jean","email":"jean@example.com","subject":"demo","message":"Hello","turnstile_token":"xxx"}'


GET /api/public/landing-stats

Retourne les statistiques publiques pour la landing page (cachées 60s).

Réponse (200) :

{
  "total_machines": 1250,
  "active_clients": 45,
  "total_monitoring_hours": 2500000
}

Champ Description
total_machines Nombre total de machines supervisées
active_clients Nombre de clients actifs
total_monitoring_hours Heures cumulées de monitoring

Headers de cache :

Cache-Control: public, max-age=60

Exemple curl :

curl -s https://api.optralis.com/api/public/landing-stats


GET /api/public/latest-version

Retourne la dernière version disponible de l'agent pour une plateforme donnée. Utilisé par le dashboard pour afficher les versions sur la page d'installation.

Query Parameters :

Paramètre Type Défaut Description
channel string stable Canal de distribution (stable, beta)
platform string windows Plateforme (windows, linux)
architecture string amd64 Architecture (amd64, arm64)

Réponse (200) :

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "version": "1.0.0",
  "channel": "stable",
  "platform": "windows",
  "architecture": "amd64",
  "download_url": "https://optralis.2lacs-it.com/downloads/stable/optralis-agent-v1.0.0.exe",
  "checksum_sha256": "a8ea22f3...",
  "file_size_bytes": 34895872,
  "changelog": "- Fix: ...\n- Feature: ...",
  "is_active": true,
  "created_at": "2026-12-18T10:30:00Z"
}

Codes d'erreur : | Code | Description | |------|-------------| | 404 | Aucune version trouvée pour les critères |

Exemple curl :

curl -s "https://api.optralis.com/api/public/latest-version?channel=stable&platform=windows&architecture=amd64"


GET /api/public/crl

Retourne la Certificate Revocation List (CRL) au format DER. Utilisé par les clients externes pour vérifier si un certificat agent a été révoqué.

Réponse (200) : Fichier binaire DER

Headers de réponse :

Content-Type: application/pkix-crl
Content-Disposition: attachment; filename="optralis-agent.crl"
Cache-Control: public, max-age=300

Codes d'erreur : | Code | Description | |------|-------------| | 500 | Erreur génération CRL | | 503 | CA non configurée |

Vérification avec OpenSSL :

# Télécharger et afficher la CRL
curl -s https://optralis-agent-api.2lacs-it.com/api/public/crl -o optralis.crl
openssl crl -in optralis.crl -inform DER -text -noout

Caractéristiques : - Validité : 24 heures - Cache : 5 minutes (invalidé automatiquement lors des révocations) - Format : DER (binaire X.509 CRL)


API (Auth Required)

Méthode Endpoint Description
GET /api/me Info utilisateur
POST /api/change-password Changer password
GET /api/status Status authentifié
GET /api/overview Vue d'ensemble flotte
GET /api/machines Liste machines
GET /api/machines/:id Détails machine
GET /api/machines/by-hostname/:hostname Machine par hostname
GET /api/machines/by-hostname/:hostname/inventory Inventaire complet
GET /api/machines/by-hostname/:hostname/metrics Historique métriques
GET /api/machines/by-hostname/:hostname/network-metrics Métriques réseau
GET /api/machines/by-hostname/:hostname/risk-overrides Liste des overrides de risque
POST /api/machines/by-hostname/:hostname/risk-overrides Créer/modifier un override
DELETE /api/machines/by-hostname/:hostname/risk-overrides/:id Supprimer un override
POST /api/machines/by-hostname/:hostname/force-collect Forcer collecte (admin)
DELETE /api/machines/by-hostname/:hostname Supprimer machine (admin)
PUT /api/machines/:id/channel Modifier canal update (admin)
PUT /api/machines/:id/maintenance Mode maintenance (admin)
GET /api/machines/:id/effective-intervals Intervalles effectifs
POST /api/machines/:id/certificate/revoke Révoquer certificat mTLS (admin)
POST /api/machines/:id/certificate/renew Renouveler certificat mTLS (admin)
GET /api/groups Liste groupes
POST /api/groups Créer groupe
PUT /api/groups/:id Modifier groupe
DELETE /api/groups/:id Supprimer groupe
PUT /api/machines/assign-group Assigner à un groupe
PUT /api/machines/remove-group Retirer d'un groupe
GET /api/settings Paramètres client
GET /api/windows-versions Mapping versions Windows
POST /api/windows-versions/refresh Rafraîchir versions (admin)
GET /api/export/machines Export machines CSV

MFA (Multi-Factor Authentication)

Gestion de l'authentification à deux facteurs pour les utilisateurs.

Méthode Endpoint Description Permission
POST /api/users/mfa/setup Initialiser MFA Auth
POST /api/users/mfa/verify Activer MFA Auth
DELETE /api/users/mfa/disable Désactiver MFA Auth
GET /api/users/mfa/status Statut MFA Auth

POST /api/users/mfa/setup

Génère un nouveau secret TOTP et QR code pour l'utilisateur.

Headers requis :

Authorization: Bearer <token>

Réponse (200) :

{
  "secret": "JBSWY3DPEHPK3PXP",
  "qr_code_url": "otpauth://totp/Optralis:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Optralis",
  "backup_codes": [
    "12345678",
    "87654321",
    "11223344",
    "44332211"
  ]
}

Codes d'erreur : | Code | Description | |------|-------------| | 400 | MFA déjà activé | | 401 | Non authentifié | | 500 | Erreur génération secret |


POST /api/users/mfa/verify

Vérifie le code TOTP et active MFA pour l'utilisateur.

Request Body :

{
  "code": "123456"
}

Réponse (200) :

{
  "message": "MFA enabled successfully"
}

Codes d'erreur : | Code | Description | |------|-------------| | 400 | Code invalide, MFA déjà activé, ou setup non trouvé | | 401 | Non authentifié | | 500 | Erreur serveur |


DELETE /api/users/mfa/disable

Désactive MFA pour l'utilisateur (nécessite le mot de passe).

Request Body :

{
  "password": "current-password"
}

Réponse (200) :

{
  "message": "MFA disabled successfully"
}

Codes d'erreur : | Code | Description | |------|-------------| | 400 | Password requis ou MFA non activé | | 401 | Password incorrect | | 500 | Erreur serveur |


GET /api/users/mfa/status

Retourne le statut MFA actuel de l'utilisateur.

Réponse (200) :

{
  "enabled": true,
  "verified_at": "2026-01-15T10:30:00Z",
  "backup_codes_left": 4
}

Champ Description
enabled MFA actif ou non
verified_at Date d'activation (null si non activé)
backup_codes_left Nombre de codes backup restants

Notifications

Méthode Endpoint Description
GET /api/notifications/channels Liste des canaux
GET /api/notifications/channels/:id Détails d'un canal
POST /api/notifications/channels Créer un canal (admin)
PUT /api/notifications/channels/:id Modifier un canal (admin)
DELETE /api/notifications/channels/:id Supprimer un canal (admin)
POST /api/notifications/channels/:id/test Tester un canal (admin)
GET /api/notifications/history Historique notifications
GET /api/notifications/types Types d'alertes
GET /api/notifications/channel-types Types de canaux

Labels (Tags Machines)

Système de tags key-value pour organiser et filtrer les machines.

Méthode Endpoint Description Permission
GET /api/labels Liste des labels Auth
POST /api/labels Créer un label Admin
PUT /api/labels/:id Modifier un label Admin
DELETE /api/labels/:id Supprimer un label Admin
POST /api/labels/assign Assigner label aux machines Admin
DELETE /api/labels/assign Retirer label des machines Admin
GET /api/labels/:id/machines Machines avec ce label Auth
GET /api/machines/:id/labels Labels d'une machine Auth
POST /api/machines/labels/batch Labels de plusieurs machines Auth

GET /api/labels

Retourne tous les labels du client.

Réponse (200) :

[
  {
    "id": "uuid",
    "client_id": "uuid",
    "key": "environment",
    "color": "#3b82f6",
    "created_at": "2026-01-15T10:00:00Z"
  }
]


POST /api/labels

Crée un nouveau label.

Request Body :

{
  "key": "environment",
  "color": "#3b82f6"
}

Champ Type Requis Description
key string Oui Clé du label (max 50 car.)
color string Non Couleur hex (défaut: bleu)

Réponse (201) :

{
  "id": "uuid",
  "client_id": "uuid",
  "key": "environment",
  "color": "#3b82f6",
  "created_at": "2026-01-15T10:00:00Z"
}

Codes d'erreur : | Code | Description | |------|-------------| | 400 | Clé manquante ou trop longue | | 409 | Label avec cette clé existe déjà |


POST /api/labels/assign

Assigne un label avec une valeur à plusieurs machines.

Request Body :

{
  "label_id": "uuid",
  "value": "production",
  "machine_ids": ["uuid1", "uuid2"]
}

Champ Type Requis Description
label_id uuid Oui ID du label
value string Non Valeur (défaut: clé du label, max 100 car.)
machine_ids uuid[] Oui Liste des machines

Réponse (200) :

{
  "success": true,
  "assigned_count": 2
}

Codes d'erreur : | Code | Description | |------|-------------| | 400 | Limite max labels par machine atteinte (10) | | 404 | Label ou machine non trouvé | | 403 | Accès refusé |


DELETE /api/labels/assign

Retire un label de plusieurs machines.

Request Body :

{
  "label_id": "uuid",
  "machine_ids": ["uuid1", "uuid2"]
}

Réponse (200) :

{
  "success": true,
  "unassigned_count": 2
}


GET /api/labels/:id/machines

Retourne les machines ayant ce label.

Query Parameters : | Param | Type | Description | |-------|------|-------------| | value | string | Filtrer par valeur (optionnel) |

Réponse (200) :

[
  {
    "machine_id": "uuid",
    "label_id": "uuid",
    "value": "production",
    "assigned_at": "2026-01-15T10:00:00Z"
  }
]


POST /api/machines/labels/batch

Récupère les labels de plusieurs machines en une seule requête (optimisation N+1).

Request Body :

{
  "machine_ids": ["uuid1", "uuid2", "uuid3"]
}

Limites : Maximum 100 machines par batch.

Réponse (200) :

{
  "uuid1": [
    {"label_id": "uuid", "key": "env", "value": "prod", "color": "#3b82f6"}
  ],
  "uuid2": [],
  "uuid3": [
    {"label_id": "uuid", "key": "env", "value": "dev", "color": "#22c55e"}
  ]
}

Utilisateurs (Multi-Utilisateurs)

Méthode Endpoint Description
GET /api/users Liste des utilisateurs du client
POST /api/users Créer un utilisateur (admin)
PUT /api/users/:id Modifier un utilisateur (admin)
DELETE /api/users/:id Supprimer un utilisateur (admin)
POST /api/users/:id/reset-password Réinitialiser mot de passe (admin)

Alert Rules (Règles d'Alerte)

Configuration des règles d'alerte pour le monitoring automatique.

Méthode Endpoint Description Permission
GET /api/alert-rules Liste des règles Auth
GET /api/alert-rules/:id Détails d'une règle Auth
GET /api/alert-rules/history Historique des déclenchements Auth
GET /api/alert-rules/metadata Métriques, opérateurs, sévérités Auth
POST /api/alert-rules Créer une règle Admin
PUT /api/alert-rules/:id Modifier une règle Admin
DELETE /api/alert-rules/:id Supprimer une règle Admin

GET /api/alert-rules

Retourne toutes les règles d'alerte du client.

Réponse (200) :

{
  "rules": [
    {
      "id": "uuid",
      "client_id": "uuid",
      "name": "CPU critique",
      "metric": "cpu",
      "operator": ">",
      "threshold": 90,
      "severity": "critical",
      "enabled": true,
      "cooldown_minutes": 60,
      "machine_id": null,
      "group_id": null,
      "channel_ids": ["uuid1", "uuid2"],
      "created_at": "2026-01-15T10:00:00Z"
    }
  ]
}


POST /api/alert-rules

Crée une nouvelle règle d'alerte.

Request Body :

{
  "name": "CPU critique",
  "metric": "cpu",
  "operator": ">",
  "threshold": 90,
  "severity": "critical",
  "cooldown_minutes": 60,
  "machine_id": null,
  "group_id": null,
  "channel_ids": ["uuid1"]
}

Champ Type Requis Description
name string Oui Nom de la règle
metric string Oui Métrique (cpu, ram, disk, temperature, offline_minutes)
operator string Oui Opérateur (>, <, >=, <=, ==)
threshold float Oui Seuil de déclenchement
severity string Oui Sévérité (low, warning, critical)
cooldown_minutes int Non Délai min entre alertes (défaut: 60, min: 5)
machine_id uuid Non Cibler une machine spécifique
group_id uuid Non Cibler un groupe
channel_ids uuid[] Non Canaux de notification

GET /api/alert-rules/history

Retourne l'historique des déclenchements d'alertes.

Query Parameters : | Param | Type | Description | |-------|------|-------------| | rule_id | uuid | Filtrer par règle | | machine_id | uuid | Filtrer par machine | | limit | int | Limite (défaut: 50) | | offset | int | Offset pagination |

Réponse (200) :

{
  "history": [
    {
      "id": "uuid",
      "rule_id": "uuid",
      "machine_id": "uuid",
      "machine_hostname": "SRV-01",
      "triggered_value": 95.5,
      "triggered_at": "2026-01-15T10:00:00Z"
    }
  ]
}


GET /api/alert-rules/metadata

Retourne les valeurs valides pour créer des règles.

Réponse (200) :

{
  "metrics": ["cpu", "ram", "disk", "temperature", "offline_minutes"],
  "operators": [">", "<", ">=", "<=", "=="],
  "severities": ["low", "warning", "critical"]
}

Collection Settings (Intervalles de Collecte)

Configuration des intervalles de collecte personnalisés pour les machines.

Méthode Endpoint Description Permission
GET /api/collection-settings Liste des paramètres Auth
GET /api/collection-settings/:id Détails d'un paramètre Auth
POST /api/collection-settings Créer un paramètre Admin
PUT /api/collection-settings/:id Modifier un paramètre Admin
DELETE /api/collection-settings/:id Supprimer un paramètre Admin
GET /api/machines/:id/effective-intervals Intervalles effectifs d'une machine Auth

GET /api/collection-settings

Retourne les paramètres de collecte du client.

Réponse (200) :

{
  "settings": [
    {
      "id": "uuid",
      "client_id": "uuid",
      "name": "Production servers",
      "priority": 10,
      "scope": "group",
      "machine_id": null,
      "group_id": "uuid",
      "intervals": {
        "metrics": 5,
        "storage": 120,
        "network": 15,
        "services": 60
      },
      "created_at": "2026-01-15T10:00:00Z"
    }
  ],
  "count": 1
}


POST /api/collection-settings

Crée un nouveau paramètre de collecte.

Request Body :

{
  "name": "Production servers",
  "priority": 10,
  "machine_id": null,
  "group_id": "uuid",
  "intervals": {
    "metrics": 5,
    "storage": 120,
    "network": 15
  }
}

Champ Type Requis Description
name string Oui Nom du paramètre
priority int Non Priorité (défaut: 0, plus haut = prioritaire)
machine_id uuid Non Machine ciblée (scope: machine)
group_id uuid Non Groupe ciblé (scope: group)
intervals object Oui Intervalles en secondes par collector

Collectors disponibles : | Collector | Min | Max | Défaut | |-----------|-----|-----|--------| | metrics | 5 | 300 | 10 | | storage | 60 | 3600 | 300 | | storage_health | 1800 | 86400 | 7200 | | network | 10 | 600 | 30 | | services | 30 | 1800 | 120 | | security | 300 | 86400 | 900 | | software | 3600 | 86400 | 14400 | | patches | 3600 | 86400 | 21600 |


GET /api/machines/:id/effective-intervals

Retourne les intervalles effectifs pour une machine (debug).

Réponse (200) :

{
  "machine_id": "uuid",
  "intervals": {
    "metrics": 5,
    "storage": 120,
    "storage_health": 7200,
    "network": 15,
    "services": 60,
    "security": 900,
    "software": 14400,
    "patches": 21600
  },
  "source": "group_setting",
  "setting_id": "uuid",
  "setting_name": "Production servers"
}

Champ Description
source Origine des intervalles (default, client_setting, group_setting, machine_setting)

Agent

Méthode Endpoint Description
POST /agent/heartbeat Vérification mises à jour (retourne UpdateInfo si dispo)
POST /agent/collect Envoyer données collector (8 types : metrics, storage, etc.)
GET /agent/config Récupérer config serveur
GET /agent/config/collectors Récupérer config 8 collectors
GET /agent/latest-version Dernière version agent
GET /agent/requirements Prérequis OS (public, pour installer)
POST /agent/certificate/renew Renouveler certificat mTLS (auto-renewal)

Note : Les endpoints /agent/inventory et /agent/updates ont été supprimés. Toutes les données transitent via /agent/collect avec le paramètre collector (metrics, storage, storage_health, network, services, security, software, patches).

Machine Lookup

Lors du bootstrap et des heartbeats, le système recherche une machine existante dans cet ordre : 1. Par hardware_id (case-insensitive avec LOWER()) 2. Par hostname (fallback pour compatibilité)

Note technique : La recherche par hardware_id utilise LOWER() car le stub Windows envoie le GUID en minuscules tandis que l'agent peut l'envoyer avec la casse originale de la registry.

GET /agent/config/collectors

Retourne la configuration des 8 collectors pour une machine. Les intervalles sont résolus depuis collection_settings avec priorité : machine > label > group > global > default.

Query Parameters : | Paramètre | Type | Description | |-----------|------|-------------| | hostname | string | Hostname de la machine (optionnel) |

Réponse (200) :

{
  "collectors": [
    {"type": "metrics", "enabled": true, "interval": 10},
    {"type": "storage", "enabled": true, "interval": 300},
    {"type": "storage_health", "enabled": true, "interval": 7200},
    {"type": "network", "enabled": true, "interval": 30},
    {"type": "services", "enabled": true, "interval": 120},
    {"type": "security", "enabled": true, "interval": 900},
    {"type": "software", "enabled": true, "interval": 14400},
    {"type": "patches", "enabled": true, "interval": 21600}
  ]
}

Effet secondaire : Met à jour last_config_sync de la machine (asynchrone).

Usage agent : Appelé au démarrage et toutes les 5 minutes pour synchroniser la configuration.


POST /agent/certificate/renew

Permet à un agent de renouveler son certificat mTLS avant expiration. L'agent doit s'authentifier avec son certificat actuel (pas encore expiré). Le renouvellement génère un nouveau certificat unique pour cette machine spécifique.

Conditions de renouvellement : - Le certificat actuel doit expirer dans les 30 prochains jours - Le certificat actuel ne doit pas être révoqué - L'agent doit être authentifié via mTLS - Le nouveau certificat conserve le même format CN=client_id:machine_id

Réponse (200) :

{
  "certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
  "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
  "ca_certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
  "expires_at": "2027-01-02T00:00:00Z"
}

Champ Description
certificate Nouveau certificat client (PEM)
private_key Nouvelle clé privée (PEM)
ca_certificate Certificat CA pour validation (PEM)
expires_at Date d'expiration du nouveau certificat

Codes d'erreur : | Code | Description | |------|-------------| | 400 | Certificat n'expire pas dans les 30 jours | | 401 | Authentification mTLS requise | | 403 | Certificat révoqué | | 500 | Erreur génération certificat |

Comportement backend : 1. Génère un nouveau certificat avec validité 1 an 2. Stocke les métadonnées dans client_certificates 3. Soft-revoke l'ancien certificat avec grace period 24h 4. Invalide le cache Redis

Usage agent : - L'agent vérifie l'expiration toutes les 24 heures - Si expiration < 30 jours, appelle cet endpoint - Sauvegarde les nouveaux fichiers de manière atomique - Recharge le client HTTP avec le nouveau certificat


Gestion Certificats mTLS (Admin)

Ces endpoints permettent de gérer les certificats par machine. Chaque machine possède son propre certificat unique (format CN=client_id:machine_id), permettant une révocation granulaire.

Méthode Endpoint Description
POST /api/machines/:id/certificate/revoke Révoquer le certificat d'une machine
POST /api/machines/:id/certificate/renew Renouveler le certificat d'une machine

POST /api/machines/:id/certificate/revoke

Révoque le certificat mTLS de cette machine spécifique. Seule cette machine sera bloquée, les autres machines du client ne sont pas affectées.

Réponse (200) :

{
  "success": true,
  "message": "Certificate revoked successfully"
}

Codes d'erreur : | Code | Description | |------|-------------| | 403 | Accès admin requis | | 404 | Machine non trouvée | | 500 | Erreur serveur |


POST /api/machines/:id/certificate/renew

Renouvelle le certificat d'une machine. Génère un nouveau certificat avec validité 1 an.

Réponse (200) :

{
  "success": true,
  "message": "Certificate renewed successfully",
  "expires_at": "2027-01-01T00:00:00Z"
}

Codes d'erreur : | Code | Description | |------|-------------| | 403 | Accès admin requis | | 404 | Machine non trouvée | | 500 | Erreur serveur |


Install Tokens (Auth JWT)

Tokens temporaires pour installation via curl (Linux) ou PowerShell (Windows).

Méthode Endpoint Description
POST /api/install-tokens Créer un token d'installation
GET /api/install-tokens Lister les tokens actifs
DELETE /api/install-tokens/:id Révoquer un token
GET /api/install-tokens/windows/active Token Windows actif (pour EXE)

POST /api/install-tokens

Crée un token temporaire pour installation via curl ou PowerShell.

Request Body :

{
  "name": "Windows installer",
  "duration_minutes": 15,
  "max_usage": 10
}

Champ Type Défaut Description
name string "" Nom optionnel
duration_minutes int 15 Durée en minutes (max: 60)
max_usage int 0 Limite d'utilisations (0 = illimité)

Réponse (200) :

{
  "token": {
    "id": "uuid",
    "token": "abc123...",
    "expires_at": "2025-01-08T12:30:00Z",
    "max_usage": 10,
    "usage_count": 0
  },
  "curl_command": "curl -fsSL https://optralis.2lacs-it.com/install.sh | sudo bash -s -- abc123...",
  "powershell_command": "irm https://optralis.2lacs-it.com/install.ps1 -OutFile install.ps1; .\\install.ps1 -Token \"abc123...\""
}

Champs de réponse :

Champ Description
curl_command Commande prête à copier pour Linux (curl + bash)
powershell_command Commande prête à copier pour Windows (irm + exécution)

Installeur Personnalisé (Auth JWT)

Méthode Endpoint Description
GET /api/installer/windows Télécharger installeur Windows (.exe)
GET /api/installer/linux/amd64 Télécharger installeur Linux AMD64 (.sh)
GET /api/installer/linux/arm64 Télécharger installeur Linux ARM64 (.sh)

Fonctionnement : - Génère un certificat client unique (CN = client_id) - Embed le certificat dans l'installeur - Windows: Archive auto-extractible avec agent + cert - Linux: Script shell avec cert en base64

Authentification Agent mTLS

L'agent s'authentifie via certificat mTLS avec un certificat unique par machine.

Format du certificat : - CN : client_id:machine_id (format UUID:UUID) - Validité : 1 an par défaut - Émetteur : CA interne Optralis

Flow mTLS :

Agent --[cert.crt (CN=client:machine)]--> Caddy --[X-Client-CN: client_id:machine_id]--> Backend

Middleware : middleware.AgentMTLSAuth() parse le CN pour extraire : - mtls_client_id : UUID du client - mtls_machine_id : UUID de la machine

Vérification révocation : middleware.CheckCertificateRevocation() vérifie par machine_id si le certificat est révoqué.

Note : Les anciens certificats au format CN=client_id ne sont plus supportés et retournent une erreur ErrCertificateLegacy.

Collectors (8 types)

L'endpoint /agent/collect accepte les données de 8 collectors différents :

Collector Description Intervalle défaut
metrics CPU, RAM, uptime, température 10s
storage Usage disques, espace libre 5min
storage_health S.M.A.R.T. détaillé 2h
network Bandwidth, latence 30s
services État services Windows/Linux 2min
security AV, firewall, ports 15min
software Apps installées, utilisateurs 4h
patches Windows Update, KB 6h

Architecture de Stockage des Collectors

Les collectors utilisent deux stratégies de stockage distinctes :

Collector Snapshot (machine_collector_data) Time-Series (metrics hypertable) Backward Compat Notes
metrics ✅ CPU, RAM, uptime, température
storage disk_info table Dashboard lit disk_info
storage_health smart_data table Dashboard lit smart_data
network ✅ bandwidth, latence, packet loss
services machine_inventory
security machine_inventory AV, firewall, bitlocker, ports
software machine_inventory
patches machine_inventory

Explication : - Snapshot (machine_collector_data) : Stocke uniquement la dernière valeur (ON CONFLICT DO UPDATE). Utilisé pour l'affichage du statut actuel. - Time-Series (metrics hypertable) : Stocke chaque mesure avec timestamp. Utilisé pour les graphiques d'historique (CPU/RAM, réseau). - Backward Compat (machine_inventory) : Mise à jour pour compatibilité avec le code legacy.

Important : Les collectors metrics et network nécessitent l'insertion dans la hypertable metrics pour alimenter les graphiques d'historique du dashboard (voir GetCPUHistory(), GetNetworkMetricHistory()).

Fichier source : backend/internal/services/machines/collectors_storage.go

Modèles de Données des Collectors

Les structures de données sont alignées entre l'agent Go, le backend Go et le frontend TypeScript.

SmartDiskInfo (storage_health):

Champ Type Description
device string Identifiant du disque (C:, /dev/sda)
model string? Modèle du disque (Samsung SSD 980 PRO)
serial string? Numéro de série
firmware string? Version firmware
smart_status string État: healthy, warning, critical, virtual, unavailable
is_virtual bool true si disque VM (QEMU, VMware, Hyper-V, etc.)
overall_health_test string? Résultat du test santé SMART
temperature int? Température en °C
power_on_hours int? Heures de fonctionnement
power_cycle_count int? Cycles d'alimentation
reallocated_sectors int? Secteurs réalloués (CRITIQUE)
current_pending_sectors int? Secteurs en attente (CRITIQUE)
offline_uncorrectable int? Secteurs non corrigeables (CRITIQUE)
predicted_failure bool Prédiction de panne SMART

Note VM : Pour les disques virtuels, seuls device, model, smart_status: "virtual" et is_virtual: true sont renseignés.

PendingUpdateInfo (patches):

Champ Type Description
title string Titre de la mise à jour
kb string? Numéro KB (ex: "KB5028997")
category string Security, Critical, Definition, Other
size string? Taille lisible (ex: "45.2 MB")
is_important bool Mise à jour importante/sécurité

UserInfoData (software):

Champ Type Description
username string Nom d'utilisateur
full_name string? Nom complet
is_admin bool Membre du groupe Administrateurs
enabled bool Compte activé (pas désactivé)
last_logon string? Dernière connexion
groups string[]? Groupes (backend only)

Fichier source : backend/internal/models/collectors.go

AI Automation (Pro+ seulement)

Vue d'ensemble de toute l'automatisation IA : routines et triggers.

Méthode Endpoint Description Permission
GET /api/ai/automation Config complète routines + triggers Auth (Pro+)

GET /api/ai/automation

Retourne la configuration complète d'automatisation IA avec routines, triggers et statistiques.

Réponse (200) :

{
  "routines": [
    {
      "routine": {
        "id": "uuid",
        "routine_type": "daily_health_check",
        "enabled": true,
        "schedule": "0 8 * * *",
        "last_run_at": "2026-01-15T08:00:00Z"
      },
      "last_history": { ... }
    }
  ],
  "triggers": [
    {
      "trigger": {
        "id": "uuid",
        "trigger_type": "critical_machine",
        "enabled": true,
        "conditions": { ... },
        "last_triggered_at": "2026-01-15T10:00:00Z"
      },
      "last_history": { ... }
    }
  ],
  "routine_stats": {
    "total": 3,
    "enabled": 2,
    "last_24h_runs": 5
  },
  "trigger_stats": {
    "total": 2,
    "enabled": 2,
    "last_24h_triggers": 3
  }
}

Codes d'erreur : | Code | Description | |------|-------------| | 403 | Licence Pro ou Enterprise requise |


AI Routines (Essentials+ seulement)

Routines d'analyse IA planifiées (cron-like).

Méthode Endpoint Description Permission
GET /api/ai/routines Liste des routines Auth (Essentials+)
GET /api/ai/routines/stats Statistiques routines Auth (Essentials+)
GET /api/ai/routines/:id Détails d'une routine Auth (Essentials+)
GET /api/ai/routines/:id/history Historique exécutions Auth (Essentials+)
POST /api/ai/routines Créer une routine Admin (Essentials+)
PUT /api/ai/routines/:id Modifier une routine Admin (Essentials+)
DELETE /api/ai/routines/:id Supprimer une routine Admin (Essentials+)

POST /api/ai/routines

Crée une nouvelle routine d'analyse planifiée.

Request Body :

{
  "routine_type": "daily_health_check",
  "schedule": "0 8 * * *",
  "enabled": true,
  "config": {
    "analysis_type": "full",
    "machines": "all",
    "notify_channels": ["uuid1"]
  }
}

Champ Type Requis Description
routine_type string Oui Type (daily_health_check, weekly_report, trend_analysis)
schedule string Oui Expression cron (ex: "0 8 * * *" = 8h chaque jour)
enabled bool Non Activé (défaut: true)
config object Non Configuration spécifique au type

Types de routines : | Type | Description | |------|-------------| | daily_health_score | Analyse santé quotidienne des machines | | weekly_report | Rapport hebdomadaire par email | | daily_report | Synthèse quotidienne avec vue d'ensemble du parc | | drift_detection | Détection de dérives et analyse baseline (z-score) |

Drift Detection - Fonctionnement : 1. Calcul baseline sur 7 jours (moyenne, écart-type) 2. Calcul z-score = (valeur_actuelle - moyenne) / écart-type 3. Si z-score > 2 → warning, > 3 → critical 4. Appel Claude API pour expliquer le drift détecté 5. Stockage dans metric_baselines avec explication

Métriques surveillées : cpu_percent, ram_percent, disk_latency_ms

AI Scheduler (Interne)

Le scheduler est un service interne de l'AI service qui exécute les routines planifiées.

Fichier : ai-service/internal/services/scheduler.go

Comportement : - Démarré automatiquement au lancement de l'AI service - Vérifie les routines à exécuter toutes les 5 minutes - Exécute immédiatement au démarrage pour rattraper les routines en retard

Critères d'exécution :

WHERE r.enabled = true
  AND (r.next_run_at IS NULL OR r.next_run_at <= NOW())
  AND c.license_type IN ('pro', 'business', 'enterprise')
  AND c.license_status = 'active'

Logique de planification : - next_run_at est la source de vérité unique pour décider si une routine doit s'exécuter - schedule_hour sert uniquement à calculer le prochain next_run_at après exécution - Permet les exécutions forcées via UPDATE ai_routines SET next_run_at = NOW()

Après exécution : - last_run_at est mis à jour avec la date d'exécution - next_run_at est recalculé selon schedule_hour et schedule_day


GET /api/ai/routines/:id/history

Retourne l'historique d'exécution d'une routine.

Query Parameters : | Param | Type | Description | |-------|------|-------------| | limit | int | Limite (défaut: 20) |

Réponse (200) :

{
  "history": [
    {
      "id": "uuid",
      "routine_id": "uuid",
      "started_at": "2026-01-15T08:00:00Z",
      "completed_at": "2026-01-15T08:02:30Z",
      "status": "success",
      "machines_analyzed": 25,
      "issues_found": 3
    }
  ]
}


AI Triggers (Essentials+ seulement)

Triggers d'analyse IA basés sur des conditions (événements).

Méthode Endpoint Description Permission
GET /api/ai/triggers Liste des triggers Auth (Essentials+)
GET /api/ai/triggers/stats Statistiques triggers Auth (Essentials+)
GET /api/ai/triggers/:id Détails d'un trigger Auth (Essentials+)
GET /api/ai/triggers/:id/history Historique déclenchements Auth (Essentials+)
POST /api/ai/triggers Créer un trigger Admin (Essentials+)
PUT /api/ai/triggers/:id Modifier un trigger Admin (Essentials+)
DELETE /api/ai/triggers/:id Supprimer un trigger Admin (Essentials+)

POST /api/ai/triggers

Crée un nouveau trigger d'analyse automatique.

Request Body :

{
  "trigger_type": "critical_machine",
  "enabled": true,
  "conditions": {
    "health_score_below": 50,
    "offline_minutes": 15,
    "cpu_above": 95
  },
  "actions": {
    "analysis_type": "full",
    "notify_channels": ["uuid1"]
  }
}

Champ Type Requis Description
trigger_type string Oui Type (critical_machine, new_machine, degradation)
enabled bool Non Activé (défaut: true)
conditions object Oui Conditions de déclenchement
actions object Oui Actions à exécuter

Types de triggers : | Type | Description | |------|-------------| | critical_machine | Machine avec health score critique | | new_machine | Nouvelle machine détectée | | degradation | Dégradation progressive détectée |


GET /api/ai/triggers/:id/history

Retourne l'historique de déclenchement d'un trigger.

Query Parameters : | Param | Type | Description | |-------|------|-------------| | limit | int | Limite (défaut: 20) |

Réponse (200) :

{
  "history": [
    {
      "id": "uuid",
      "trigger_id": "uuid",
      "triggered_at": "2026-01-15T10:00:00Z",
      "machine_id": "uuid",
      "machine_hostname": "SRV-PROD-01",
      "trigger_reason": "Health score below 50",
      "analysis_result": { ... }
    }
  ]
}

Admin (Super Admin)

Tous les endpoints /api/admin/* requièrent le rôle super_admin.

Dashboard & Statistiques

Méthode Endpoint Description
GET /api/admin/dashboard Statistiques globales
GET /api/admin/stats/timeline Timeline clients/machines

Gestion des Clients

Méthode Endpoint Description
GET /api/admin/clients Liste clients
GET /api/admin/clients/:id Détails client
POST /api/admin/clients Créer client
PUT /api/admin/clients/:id Modifier client
DELETE /api/admin/clients/:id Supprimer client
POST /api/admin/clients/:id/regenerate-license-id Régénérer license ID
POST /api/admin/impersonate/:clientId Impersonation token

Gestion des Licences

Méthode Endpoint Description
GET /api/admin/licenses Liste licences
PUT /api/admin/clients/:clientId/extend Prolonger licence
GET /api/admin/license-types Types de licences
POST /api/admin/license-types Créer type licence
PUT /api/admin/license-types/:id Modifier type licence
DELETE /api/admin/license-types/:id Supprimer type licence

Gestion Docker

Méthode Endpoint Description
GET /api/admin/docker/containers Liste containers
GET /api/admin/docker/containers/:id Détails container
GET /api/admin/docker/containers/:id/logs Logs container
GET /api/admin/docker/containers/:id/stats Stats container
POST /api/admin/docker/containers/:id/start Démarrer container
POST /api/admin/docker/containers/:id/stop Arrêter container
POST /api/admin/docker/containers/:id/restart Redémarrer container

Gestion Agent Versions

Méthode Endpoint Description
GET /api/admin/agent-versions Liste des versions
GET /api/admin/agent-versions/:id Détails version
POST /api/admin/agent-versions Créer version
PUT /api/admin/agent-versions/:id Modifier version
DELETE /api/admin/agent-versions/:id Supprimer version + fichiers
POST /api/admin/agent-versions/:id/promote Promouvoir en stable
PUT /api/admin/machines/bulk-channel Maj canal machines en lot
GET /api/admin/machines/all Toutes machines (tous clients)
GET /api/admin/agent-downloads Téléchargements disponibles

DELETE /api/admin/agent-versions/:id - Suppression complète : - Supprime l'entrée en base de données - Supprime le fichier EXE sur le serveur - Met à jour/supprime les fichiers metadata selon la plateforme : - Windows : metadata-windows.json et metadata.json - Linux : metadata-linux.json - Si la version supprimée était active, active automatiquement la version suivante (même channel/platform/architecture)

Prérequis OS & Activity

Méthode Endpoint Description
GET /api/admin/agent-requirements Prérequis OS agent
PUT /api/admin/agent-requirements Modifier prérequis OS
GET /api/admin/windows-builds Builds Windows (Client/Server)
POST /api/admin/windows-builds/refresh Rafraîchir depuis endoflife.date
GET /api/admin/activity Journal d'activité
GET /api/admin/activity/stats Statistiques d'activité

Gestion des Certificats (Admin)

Méthode Endpoint Description
GET /api/admin/certificates Liste tous les certificats (tous clients)
GET /api/admin/certificates/stats Statistiques des certificats
DELETE /api/admin/certificates/purge Supprimer les certificats inactifs (expired/revoked)
POST /api/admin/certificates/:serial/restore Restaurer un certificat révoqué
POST /api/admin/certificates/refresh-cache Rafraîchir le cache Redis des certificats
DELETE /api/admin/certificates/purge

Supprime uniquement les certificats inactifs (expired ou revoked) de la base de données. Les certificats actifs sont préservés.

Certificats supprimés : - expired : Certificats dont expires_at <= NOW() et non révoqués - revoked : Certificats avec revoked_at IS NOT NULL

Certificats préservés : - valid : Expiration > 30 jours - expiring_soon : Expiration entre 7 et 30 jours - expiring : Expiration < 7 jours

Réponse (200) :

{
  "message": "inactive certificates purged successfully",
  "deleted": 42
}

Réponse (aucun certificat inactif) :

{
  "message": "no inactive certificates to purge",
  "deleted": 0
}


POST /api/admin/certificates/:serial/restore

Restaure un certificat précédemment révoqué (annule la révocation).

Réponse (200) :

{
  "success": true,
  "message": "Certificate restored successfully"
}

Codes d'erreur : | Code | Description | |------|-------------| | 404 | Certificat non trouvé ou non révoqué | | 500 | Erreur serveur |


POST /api/admin/certificates/refresh-cache

Vide le cache Redis des statuts de révocation des certificats. Utilisé pour corriger les désynchronisations entre Redis et PostgreSQL.

Cas d'usage : - Un agent affiche "certificate revoked" alors que le certificat est valide dans le dashboard - Après une migration ou restauration de base de données - Debug de problèmes de cache

Réponse (200) :

{
  "message": "certificate cache refreshed successfully"
}

Note technique : Cette opération supprime toutes les clés Redis avec le pattern cert:revoked:*. Les prochaines requêtes des agents reconstruiront le cache depuis PostgreSQL.


GET /api/admin/certificates

Liste tous les certificats mTLS avec filtrage et pagination.

Query Parameters : | Param | Type | Description | |-------|------|-------------| | status | string | Filtrer par statut (valid, expiring_soon, expiring, expired, revoked) | | client_id | uuid | Filtrer par client | | search | string | Rechercher par hostname ou serial | | limit | int | Limite (défaut: 50, max: 200) | | offset | int | Offset pagination |

Réponse (200) :

{
  "certificates": [
    {
      "id": "uuid",
      "client_id": "uuid",
      "client_name": "Acme Corp",
      "machine_id": "uuid",
      "machine_hostname": "SRV-PROD-01",
      "serial": "ABC123...",
      "issued_at": "2026-01-01T00:00:00Z",
      "expires_at": "2026-01-01T00:00:00Z",
      "revoked_at": null,
      "revocation_reason": "",
      "status": "valid"
    }
  ],
  "total": 150,
  "limit": 50,
  "offset": 0
}

Statuts possibles : | Statut | Description | |--------|-------------| | valid | Certificat valide (> 30 jours avant expiration) | | expiring_soon | Expire dans 7-30 jours | | expiring | Expire dans < 7 jours | | expired | Certificat expiré | | revoked | Certificat révoqué |


GET /api/admin/certificates/stats

Retourne les statistiques globales des certificats.

Réponse (200) :

{
  "total": 150,
  "valid": 120,
  "expiring_soon": 15,
  "expiring": 5,
  "expired": 3,
  "revoked": 7
}

Validation Conformité OS

Le backend valide automatiquement la conformité OS de chaque machine à chaque heartbeat (INSERT et UPDATE).

Logique de validation : 1. Parse le build number depuis os_version (patterns: "Build XXXXX", "10.0.XXXXX") 2. Détecte le type Windows (Server si contient "Server", sinon Client) 3. Compare avec WindowsClientMinBuild ou WindowsServerMinBuild des agent_requirements 4. Stocke le résultat dans is_os_compliant

Cas particuliers : - Linux : Toujours compliant si linux_enabled = true - Erreur de parsing : Considéré compliant (fail-safe) - Build = 0 (non parseable) : Considéré compliant

Cache : - Les agent_requirements sont mis en cache (TTL 5 minutes) - Évite une requête DB à chaque heartbeat

Configuration IA (Enterprise only)

Méthode Endpoint Description
GET /api/ai-config Récupérer la config IA du client
PUT /api/ai-config Mettre à jour la config (admin)
POST /api/ai-config/analyze Lancer l'auto-détection (admin)

Fonctionnalités : - Auto-détection de la criticité des machines basée sur les hostnames (patterns PROD, SQL, WMS, ERP, etc.) - Classification manuelle ajustable (critique, important, standard) - Contexte personnalisé injecté dans les prompts IA - Configuration par client (chaque client Enterprise a sa propre config)

Patterns auto-détectés : | Pattern | Criticité | Raison | |---------|-----------|--------| | *PROD*, *PRD* | Critique | Production | | *SQL*, *DB*, *BDD* | Critique | Base de données | | *WMS*, *ERP*, *SAP* | Critique | Métier critique | | *COMPTA*, *PAIE* | Critique | Finance | | *WEB*, *WWW*, *API* | Important | Services exposés | | *MAIL*, *SMTP* | Important | Communication | | *DEV*, *TEST*, *UAT* | Standard | Non-prod | | *BACKUP*, *BKP* | Standard | Secondaire |

AI Service (via service interne :3002)

Endpoints standards (toutes licences)

Méthode Endpoint Description Coût quota
GET /health Health check (DB, Redis, client Claude initialisé) 0
GET /ai/quota Vérifier quota restant 0
POST /ai/quota/set Définir quota personnalisé (admin) 0
POST /ai/analyze/logs Analyse des logs critiques 1
POST /ai/analyze/anomalies Détection d'anomalies métriques 1
POST /ai/analyze/full Analyse complète (logs + anomalies + reco) 3
POST /ai/recommendations Recommandations priorisées 1
POST /ai/health-score Score de santé IA (0-100) avec catégories 1
POST /ai/analyze/trends Analyse des tendances p95/p99 (7/30 jours) 1
POST /ai/scripts/generate Génération de scripts remédiation 1
POST /ai/reports/generate Génération rapport quotidien/hebdo/mensuel 2
GET /ai/reports/:id Récupérer un rapport (Essentials+) 0
GET /ai/reports/:id/download Télécharger un rapport HTML (Essentials+) 0
GET /ai/reports Lister les rapports (Essentials+) 0

Détail des endpoints standards :

Endpoint Fonctionnalité détaillée
/ai/quota/set Définit un quota mensuel personnalisé pour un client (admin uniquement). Body: {client_id, monthly_limit}. Permet de surcharger le quota par défaut du plan licence
/ai/analyze/logs Analyse les logs système (error, critical) et identifie les patterns problématiques, les erreurs récurrentes et les causes potentielles
/ai/analyze/anomalies Détecte les anomalies dans les métriques (CPU, RAM, disque) via analyse statistique par rapport aux baselines historiques
/ai/analyze/full Combine logs + anomalies + recommandations en une seule analyse exhaustive de la machine
/ai/recommendations Génère des recommandations d'actions priorisées (avec scripts optionnels) basées sur l'état actuel de la machine
/ai/health-score Calcule un score global 0-100 avec sous-scores par catégorie (CPU, RAM, disque, sécurité, stabilité) et tendance
/ai/analyze/trends Analyse les tendances sur 7 ou 30 jours : percentiles p95/p99, dérives et prédictions de saturation
/ai/scripts/generate Génère des scripts PowerShell/Bash pour remédier aux problèmes identifiés, avec avertissements de sécurité
/ai/reports/generate Crée un rapport quotidien, hebdomadaire ou mensuel (format exécutif ou technique) avec synthèse HTML stylée

Endpoints Maintenance Prédictive Avancée (Pro/Business/Enterprise)

Méthode Endpoint Description Coût quota
POST /ai/analyze/degradation Priorisation intelligente - dégradation progressive 1
POST /ai/analyze/intermittent Détection d'intermittence (latence disque, IO wait) 1
POST /ai/correlate Priorisation intelligente - corrélation événements/métriques 1
POST /ai/predictive/maintenance Analyse prédictive complète groupée 5

Détail des endpoints avancés :

Endpoint Fonctionnalité détaillée
/ai/analyze/degradation Identifie les dégradations progressives de performance (CPU/RAM/disque qui augmentent lentement) avec prédiction de date critique
/ai/analyze/intermittent Détecte les problèmes sporadiques : pics de latence disque >50ms, IO wait >15%, patterns (aléatoire, périodique, escalade)
/ai/correlate Corrèle les événements système avec les pics de métriques pour identifier les causes racines (ex: mise à jour Windows → pic CPU)
/ai/predictive/maintenance Analyse groupée complète : health-score + trends + degradation + correlations + intermittent en un seul appel avec synthèse globale

Notes : - Le header X-Client-ID est requis pour tous les endpoints (sauf /health). - Les endpoints Avancés retournent une erreur 403 PREMIUM_REQUIRED pour les licences Starter/Essentials/Trial.


Modèles de Données

type Client struct {
    ID        uuid.UUID
    Name      string
    LicenseID uuid.UUID
    CreatedAt time.Time
}

type User struct {
    ID                 uuid.UUID
    ClientID           uuid.UUID
    Email              string
    FirstName          string
    LastName           string
    Role               string  // super_admin, admin, observer
    MustChangePassword bool
    PasswordChangedAt  time.Time
    CreatedAt          time.Time
    UpdatedAt          time.Time
}

type Machine struct {
    ID            uuid.UUID
    ClientID      uuid.UUID
    GroupID       uuid.UUID
    Hostname      string
    HardwareID    string     // Identifiant matériel persistant
    OS            string
    OSVersion     string
    IP            string
    AgentVersion  string
    LastSeen      time.Time
    HealthScore   int
    IsOSCompliant bool       // true si OS respecte agent_requirements (build number)
    // mTLS certificate fields
    CertSerial    string     // Numéro de série du certificat
    CertIssuedAt  *time.Time // Date d'émission
    CertExpiresAt *time.Time // Date d'expiration
    CertRevokedAt *time.Time // Date de révocation (null si valide)
    CertStatus    string     // Calculé: valid, expiring, expired, revoked, legacy
}

type NotificationChannel struct {
    ID          uuid.UUID
    ClientID    uuid.UUID
    Type        string                 // email, webhook_teams, webhook_slack, webhook_discord
    Name        string
    Enabled     bool
    Config      map[string]interface{} // Configuration JSONB (emails ou webhook_url)
    CreatedAt   time.Time
    UpdatedAt   time.Time
}

type AlertRule struct {
    ID              uuid.UUID
    ClientID        uuid.UUID
    Name            string
    Metric          string      // cpu, ram, disk, temperature, offline_minutes
    Operator        string      // >, <, >=, <=, ==
    Threshold       float64
    Severity        string      // low, warning, critical
    Enabled         bool
    CooldownMinutes int         // Délai minimum entre deux alertes
    MachineID       *uuid.UUID  // Optionnel : cibler une machine spécifique
    GroupID         *uuid.UUID  // Optionnel : cibler un groupe
    ChannelIDs      []uuid.UUID // Canaux de destination pour cette règle
    CreatedAt       time.Time
    UpdatedAt       time.Time
}

type ActivityLog struct {
    ID        uuid.UUID
    Timestamp time.Time
    Level     string                 // info, warn, error
    Category  string                 // auth, email, alert, system, user, machine
    Message   string
    Metadata  map[string]interface{} // Données additionnelles (JSON)
    ClientID  *uuid.UUID
    UserID    *uuid.UUID
    IPAddress string
}

type ServiceIncident struct {
    ID          uuid.UUID
    ServiceName string     // api, database, ai
    Status      string     // degraded, offline
    StartedAt   time.Time
    ResolvedAt  *time.Time
    Description *string
    CreatedAt   time.Time
}

Page Status Publique

Endpoint GET /status

Retourne le status de tous les services avec cache de 60 secondes.

Optimisations (v2.3.12) : - Checks parallélisés avec goroutines (AI + uptime en parallèle) - Timeout service AI : 2s (au lieu de 5s) - Cache TTL : 60s (au lieu de 30s)

Réponse :

{
  "overall_status": "online",
  "services": {
    "api": {
      "status": "online",
      "response_time_ms": 0,
      "uptime_15d": [100.0, 100.0, ...]
    },
    "database": {
      "status": "online",
      "response_time_ms": 5,
      "uptime_15d": [100.0, 99.8, ...]
    },
    "ai": {
      "status": "online",
      "response_time_ms": 150,
      "uptime_15d": [100.0, 100.0, ...]
    }
  },
  "metrics": {
    "total_machines": 100,
    "online_machines": 95,
    "offline_machines": 5,
    "average_health_score": 87.5
  },
  "timestamp": "2026-01-01T12:00:00Z"
}

Services monitorés : - api : Toujours online si l'API répond - database : Ping PostgreSQL (degraded si >1s) - ai : Health check service AI (:3002) - vérifie DB, Redis et initialisation client Claude (sans appel API)

Uptime : - 30 valeurs = 15 jours × 2 demi-journées - Pourcentage calculé par service - Requête SQL optimisée (1 au lieu de 30)

Endpoint GET /status/incidents

Retourne les 10 derniers incidents de service.

Réponse :

{
  "incidents": [
    {
      "id": "uuid",
      "service_name": "database",
      "status": "degraded",
      "started_at": "2026-01-01T10:00:00Z",
      "resolved_at": "2026-01-01T10:15:00Z",
      "description": "Latence élevée"
    }
  ],
  "total": 1
}

Cache Status

Le cache en mémoire (TTL 30s) stocke : - Status des 3 services + temps de réponse - Métriques machines (total, online, health score) - Uptime 15 jours par service

Invalidation : Automatique après expiration TTL.

Endpoint GET /status/external

Retourne le status des monitors externes (UptimeRobot).

Configuration requise :

UPTIMEROBOT_API_KEY=ur1234567-xxxxxxxxxxxxxxxxxxxx

Réponse :

{
  "monitors": [
    {
      "id": "802079181",
      "name": "Optralis - API",
      "status": "online",
      "uptime_ratio": 99.95,
      "response_time": 150,
      "url": "https://optralis-api.2lacs-it.com/health",
      "uptime_15d": [100.0, 100.0, 99.8, ...]
    },
    {
      "id": "802079169",
      "name": "Optralis - Frontend",
      "status": "online",
      "uptime_ratio": 99.98,
      "response_time": 85,
      "url": "https://optralis.2lacs-it.com/status",
      "uptime_15d": [100.0, 100.0, 100.0, ...]
    }
  ],
  "timestamp": "2026-01-01T12:00:00Z"
}

Champs : | Champ | Description | |-------|-------------| | uptime_ratio | Uptime sur 15 jours (%) | | uptime_15d | 30 valeurs (15 jours × 2 demi-journées de 12h) calculées depuis les logs d'événements UptimeRobot |

Calcul uptime_15d : - L'API UptimeRobot est appelée avec logs=1&logs_limit=1000&custom_uptime_ratios=15 - Les logs sont triés chronologiquement (UptimeRobot les retourne du plus récent au plus ancien) - Les logs d'événements (type 1=down, 2=up) sont analysés pour calculer le temps de panne réel - Pour chaque période de 12 heures (AM: 00:00-11:59, PM: 12:00-23:59), on calcule: - Le temps de panne total en secondes (chevauchement des pannes avec le bucket) - Le pourcentage de disponibilité: (43200 - secondesPanne) / 43200 × 100 - Résultat: 30 valeurs représentant les 15 derniers jours, du plus ancien au plus récent

Types de logs UptimeRobot : - 1 → Monitor down (début de panne) - 2 → Monitor up (fin de panne) - 98 → Monitoring started - 99 → Monitoring paused

Status UptimeRobot : - 2 → online - 8 → degraded (seems_down) - 9 → offline (down)

Intégration Dashboard : Les moniteurs "Optralis - API" et "Optralis - Frontend" sont affichés dans le tableau principal des services (page /status), avec le même graphique d'uptime que les services internes.


Système de Notifications Multi-Canal

Vue d'Ensemble

Canal Format Configuration
Email HTML templated Liste d'adresses email
Microsoft Teams Adaptive Card v1.5 URL Power Automate Workflow
Slack Block Kit URL Webhook Incoming
Discord Embeds URL Webhook

Types d'Alertes

Type Clé Description
Machine hors ligne machine_offline Machine non vue depuis X minutes
Machine en ligne machine_online Machine revenue en ligne (après offline)
CPU élevé high_cpu Usage CPU > seuil configuré
RAM élevée high_ram Usage RAM > seuil configuré
Disque critique disk_critical Espace disque > seuil configuré
Alerte SMART smart_warning Erreur SMART détectée

Anti-Spam Machine Offline

Le système évite les notifications répétitives pour les machines constamment offline :

Machine online → offline : NOTIFICATION "Machine hors ligne" 🔴
Machine reste offline    : SILENCE (pas de re-notification)
Machine offline → online : NOTIFICATION "Retour en ligne" 🟢

Mécanisme : - Colonne machines.offline_notified_at : NULL = online/jamais notifié, timestamp = date notification - La routine monitoring ne notifie que si offline_notified_at IS NULL - Après notification offline : offline_notified_at = NOW() - Au heartbeat : si offline_notified_at était set → notification "retour en ligne" + reset à NULL

Architecture Simplifiée

Canaux = Destinations simples (Email, Teams, Slack, Discord) - Configuration : type + nom + config (emails ou webhook_url) - Pas de logique métier, juste des endpoints de livraison

Règles = Toute la logique - Métrique + opérateur + seuil - Scope : toutes machines / machine / groupe - Cooldown configurable par règle - Liste des canaux de destination (channel_ids)

┌─────────────────────────────────────────────────────────────────┐
│                    Monitoring Service                            │
│    Évalue les règles (cpu > 85%, offline_minutes > 5, etc.)     │
└─────────────────────────────────────┬───────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                      Alert Rule Triggered                        │
│   - Vérifie le cooldown de la règle                             │
│   - Récupère les channel_ids de la règle                        │
│   - Enregistre dans alert_rule_history                          │
└───────┬─────────────┬─────────────┬─────────────┬───────────────┘
        │             │             │             │
        ▼             ▼             ▼             ▼
    ┌───────┐     ┌───────┐     ┌───────┐     ┌───────┐
    │ Email │     │ Teams │     │ Slack │     │Discord│
    └───────┘     └───────┘     └───────┘     └───────┘

Cooldown

Chaque règle a son propre cooldown configurable (minimum: 5 minutes, défaut: 60 minutes). Le système vérifie la dernière alerte déclenchée pour: - La même règle - La même machine

Formats Webhook

Les 3 plateformes affichent les mêmes informations : machine, dernière connexion, OS, IP, version agent, date.

Microsoft Teams (Power Automate Workflows - Adaptive Card v1.5):

Note : Les webhooks Office 365 Connectors (MessageCard) sont dépréciés par Microsoft (fin 2025). Le nouveau format utilise les Adaptive Cards via Power Automate Workflows.

{
  "type": "message",
  "attachments": [{
    "contentType": "application/vnd.microsoft.card.adaptive",
    "contentUrl": null,
    "content": {
      "type": "AdaptiveCard",
      "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "version": "1.5",
      "body": [
        {"type": "TextBlock", "text": "🏢 MONCLIENT", "size": "Large", "weight": "Bolder", "color": "Accent"},
        {"type": "TextBlock", "text": "🔴 **ATTENTION** - Machine hors ligne", "size": "Medium", "color": "attention"},
        {"type": "TextBlock", "text": "━━━━━━━━━━━━━━━━━━━━", "size": "Small", "color": "Light"},
        {"type": "ColumnSet", "columns": [
          {"type": "Column", "width": "stretch", "items": [
            {"type": "TextBlock", "text": "🖥️ Machine", "size": "Small", "color": "Light", "weight": "Bolder"},
            {"type": "TextBlock", "text": "SRV-PROD-01", "size": "Medium", "weight": "Bolder"}
          ]},
          {"type": "Column", "width": "stretch", "items": [
            {"type": "TextBlock", "text": "📅 Date", "size": "Small", "color": "Light", "weight": "Bolder"},
            {"type": "TextBlock", "text": "08/01/2026 à 14:30", "size": "Medium"}
          ]}
        ]},
        {"type": "ColumnSet", "columns": [
          {"type": "Column", "width": "stretch", "items": [
            {"type": "TextBlock", "text": "🕐 Dernière connexion", "size": "Small", "color": "Light", "weight": "Bolder"},
            {"type": "TextBlock", "text": "Il y a 5 minutes"}
          ]},
          {"type": "Column", "width": "stretch", "items": [
            {"type": "TextBlock", "text": "💻 Système", "size": "Small", "color": "Light", "weight": "Bolder"},
            {"type": "TextBlock", "text": "Windows 11 Pro"}
          ]}
        ]},
        {"type": "TextBlock", "text": "🟠 **Optralis** Monitoring", "size": "Small", "color": "Light"}
      ],
      "actions": [
        {"type": "Action.OpenUrl", "title": "🔗 Voir sur Optralis", "url": "https://..."}
      ]
    }
  }]
}

Configuration Power Automate : 1. Teams → Apps → Workflows 2. Chercher "Post to a channel when a webhook request is received" 3. Sélectionner le channel cible 4. Copier l'URL générée (https://*.logic.azure.com/...)

Slack (Block Kit):

{
  "blocks": [
    {"type": "header", "text": {"type": "plain_text", "text": "🏢 MONCLIENT", "emoji": true}},
    {"type": "section", "text": {"type": "mrkdwn", "text": "🔴 *ATTENTION* - Machine hors ligne"}},
    {"type": "divider"},
    {"type": "section", "fields": [
      {"type": "mrkdwn", "text": "*🖥️ Machine*\n`SRV-PROD-01`"},
      {"type": "mrkdwn", "text": "*📅 Date*\n08/01/2026 à 14:30"}
    ]},
    {"type": "section", "fields": [
      {"type": "mrkdwn", "text": "*🕐 Dernière connexion*\nIl y a 5 minutes"},
      {"type": "mrkdwn", "text": "*💻 Système*\nWindows 11 Pro"}
    ]},
    {"type": "section", "fields": [
      {"type": "mrkdwn", "text": "*🌐 IP locale*\n`192.168.1.42`"},
      {"type": "mrkdwn", "text": "*📦 Version agent*\n2.1.0 (stable)"}
    ]},
    {"type": "actions", "elements": [{
      "type": "button", "style": "primary",
      "text": {"type": "plain_text", "text": "🔗 Voir sur Optralis", "emoji": true},
      "url": "https://..."
    }]},
    {"type": "divider"},
    {"type": "context", "elements": [
      {"type": "image", "image_url": "https://optralis-img.2lacs-it.com/assets/Optralis-white.png", "alt_text": "Optralis"},
      {"type": "mrkdwn", "text": "🟠 *Optralis* Monitoring • _Voir plus loin. Agir plus vite._"}
    ]}
  ]
}

Discord (Embeds) - Status Card Design (3 colonnes):

{
  "embeds": [{
    "author": {
      "name": "Client : MonClient"
    },
    "title": "🔴 MACHINE OFFLINE",
    "description": "SRV-PROD-01",
    "color": 16347926,
    "fields": [
      {"name": "🕐 Dernière connexion", "value": "Il y a 5 minutes", "inline": true},
      {"name": "💻 Système", "value": "Windows 11 Pro", "inline": true},
      {"name": "🌐 IP locale", "value": "192.168.1.42", "inline": true},
      {"name": "📦 Version agent", "value": "2.1.0", "inline": true},
      {"name": "🔄 Canal", "value": "stable", "inline": true},
      {"name": "📅 Détecté le", "value": "08/01/2026 à 14:30", "inline": true},
      {"name": "🔗 Lien", "value": "[Voir la machine](https://...)", "inline": false}
    ],
    "image": {"url": "https://optralis-img.2lacs-it.com/assets/Optralis-white.png"},
    "footer": {"text": "Optralis"},
    "timestamp": "2026-01-08T14:30:00Z"
  }]
}

Note: Les champs avec inline: true s'affichent en 3 colonnes. Le lien est en pleine largeur.

Types de titres Discord : | Type | Titre | |------|-------| | machine_offline | 🔴 MACHINE OFFLINE | | machine_online | 🟢 MACHINE ONLINE | | high_cpu | 🟠 CPU ÉLEVÉ | | high_ram | 🟠 RAM ÉLEVÉE | | disk_critical | 🔴 DISQUE CRITIQUE | | smart_warning | 🟡 ALERTE SMART | | brute_force | 🔴 TENTATIVE BRUTE FORCE |


Système Multi-Utilisateurs

Rôles

Rôle Description Accès
super_admin Administrateur global Optralis Tout (panel admin inclus)
admin Administrateur client Configuration, notifications, gestion utilisateurs
observer Observateur Lecture seule (dashboard, machines)

Matrice des Permissions

Action super_admin admin observer
Voir machines/métriques
Voir notifications
Créer/modifier notifications
Gérer groupes
Gérer utilisateurs
Supprimer machines
Panel admin global

Middleware

// AdminRequired - Autorise admin et super_admin
func AdminRequired() fiber.Handler {
    return func(c *fiber.Ctx) error {
        role := c.Locals("role").(string)
        if role == "super_admin" || role == "admin" {
            return c.Next()
        }
        return errors.Forbidden(c, "admin access required")
    }
}

Protections

Protection Description
Dernier admin Impossible de supprimer/changer le rôle du dernier admin
Auto-modification Un admin ne peut pas modifier son propre rôle
Auto-suppression Un admin ne peut pas se supprimer lui-même
Email unique Contrainte UNIQUE sur email
Validation rôle Seuls "admin" et "observer" sont acceptés

Activity Logging System

Architecture

Composant Description Fichier
Table activity_logs Hypertable TimescaleDB avec rétention 30 jours database/database.go
Service LogActivity, LogInfo, LogWarn, LogError services/activity.go
Handler GetActivityLogs, GetActivityStats handlers/activity.go

Catégories d'événements

Catégorie Logs actifs Messages loggés
auth 5+ Login succès/échec, demande reset password, création utilisateur
email 5 "Email de réinitialisation envoyé", "Email de bienvenue envoyé", "Email de nouveau mot de passe envoyé", "Email de contact envoyé", "Email d'alerte envoyé"
alert 2+ Machines offline détectées, alerte résolue
system 8 "Groupe créé/modifié/supprimé", "Label créé/modifié/supprimé", "Paramètres de collecte modifiés", "Configuration IA modifiée"
user 2+ Création compte, modification profil
machine 8 "Machine supprimée", "Mode maintenance activé/désactivé", "Certificat révoqué/renouvelé", "Canal de mise à jour modifié", "Machines assignées/retirées du groupe", "Label assigné aux machines"
certificate 2+ Création, renouvellement, révocation

Niveaux de log

Niveau Description Couleur dashboard
info Événement normal Bleu
warn Avertissement Jaune
error Erreur Rouge

Utilisation Backend

import "github.com/2lacs-informatique/optralis/backend/internal/services"

// Log simple
services.LogInfo("auth", "User logged in successfully", map[string]interface{}{
    "email": user.Email,
}, &clientID, &userID)

// Log erreur
services.LogError("email", "Failed to send alert email", map[string]interface{}{
    "error": err.Error(),
}, &clientID, nil)

AI Service

Vue d'Ensemble

Service Go/Fiber autonome pour l'analyse intelligente des métriques via Claude (Anthropic).

Stack technique : - Go + Fiber v2 - Claude API (Anthropic) - PostgreSQL (partage la même DB que l'API principale) - Redis (cache et rate limiting)

Architecture

┌──────────────────────────────────────────────────────────────┐
│                    optralis-ai (:3002)                        │
│                     (service interne)                         │
└─────────────────────────────┬────────────────────────────────┘
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
   ┌─────────┐          ┌──────────┐          ┌─────────┐
   │ Claude  │          │PostgreSQL│          │  Redis  │
   │   API   │          │   :5432  │          │  :6379  │
   └─────────┘          └──────────┘          └─────────┘

Configuration

Variable Description Défaut
ANTHROPIC_API_KEY Clé API Anthropic (obligatoire) -
AI_SERVICE_PORT Port du service 3002
AI_MODEL Modèle Claude claude-sonnet-4-20250514
AI_MAX_TOKENS Tokens max par requête 4096
AI_DEFAULT_LANGUAGE Langue par défaut fr
AI_RATE_LIMIT_PER_MINUTE Rate limit 60
AI_CACHE_TTL_MINUTES TTL cache Redis 30
DATABASE_URL URL PostgreSQL (partagée avec API)
REDIS_URL URL Redis redis://redis:6379

Système de Quota

Chaque client a un quota mensuel de requêtes AI (basé sur license_type dans la table clients).

Licence Quota mensuel
Trial 50
Starter 150
Essentials 300
Pro 1,500
Business 5,000
Enterprise 15,000

Référence code : ai-service/internal/services/quota.go:112-118

Système Anti-Spam

Le service AI implémente un double système de protection contre les requêtes inutiles :

1. Cooldown par Machine

Chaque type d'analyse a un cooldown spécifique par machine :

Endpoint Cooldown
health-score 30 min
analyze/logs 15 min
analyze/anomalies 15 min
analyze/full 2 heures
analyze/trends 15 min
analyze/degradation 15 min
correlate 15 min
recommendations 1 heure
reports/generate 6 heures
scripts/generate 30 min

Réponse si en cooldown (HTTP 429) :

{
  "error": "Analysis on cooldown",
  "cooldown": true,
  "remaining_secs": 420,
  "next_available": "2026-12-28T18:15:00Z"
}

Référence code : ai-service/internal/services/cooldown.go

2. Détection de Changements Significatifs

Avant de lancer une analyse, le système vérifie si des changements significatifs ont eu lieu depuis la dernière analyse :

Type de changement Seuil significatif
Alertes Nouvelle alerte ou alerte résolue
CPU/RAM/Disk Variation ≥ 15%
Logs erreur ≥ 3 nouveaux logs ERROR/CRITICAL
Événements Nouveaux événements severity high/critical

Réponse si aucune nouvelle donnée (HTTP 304) :

{
  "error": "No new data since last analysis",
  "code": "NO_NEW_DATA",
  "last_analysis": "2026-12-28T17:30:00Z"
}

Réponse si pas de changement significatif (HTTP 304) :

{
  "error": "No significant changes detected",
  "code": "NO_SIGNIFICANT_CHANGES",
  "message": "Les problèmes identifiés précédemment sont toujours présents...",
  "previous_issues": ["High CPU usage", "Low disk space"],
  "last_analysis": "2026-12-28T17:30:00Z"
}

Référence code : ai-service/internal/services/change_detection.go

Ordre des vérifications

1. Cooldown expiré ?          → Non → HTTP 429 (cooldown)
2. Nouvelles données ?        → Non → HTTP 304 (no_new_data)
3. Changements significatifs ? → Non → HTTP 304 (no_significant_changes)
4. Quota disponible ?         → Non → HTTP 429 (quota_exceeded)
5. OK → Lancer l'analyse

Fonctionnement

Mode Description Status
Automatique Page /status affiche le health de l'AI service ✅ Implémenté
Manuel (CLI) Appels curl depuis le VPS ✅ Implémenté
Dashboard Intégration UI pour déclencher analyses 🔜 À faire

Exemple d'appel

# Health check
curl -s http://localhost:3002/health | jq

# Score de santé d'une machine
curl -s -X POST http://localhost:3002/ai/health-score \
  -H "Content-Type: application/json" \
  -H "X-Client-ID: <CLIENT_UUID>" \
  -d '{
    "machine_id": "<MACHINE_UUID>",
    "language": "fr"
  }' | jq

Réponse type (health-score)

{
  "id": "uuid",
  "machine_id": "uuid",
  "overall_score": 92,
  "category_scores": {
    "cpu": 95,
    "ram": 85,
    "disk": 95,
    "security": 80,
    "stability": 95
  },
  "trend": "stable",
  "risk_level": "low",
  "summary": "Système en excellente santé...",
  "top_issues": [
    {
      "type": "issue",
      "title": "Issue #1",
      "description": "Description du problème",
      "severity": "high"
    }
  ],
  "confidence": 0.75,
  "created_at": "2026-12-28T17:27:51Z"
}

Déploiement

# Rebuild uniquement le service AI
bash scripts/deploy.sh --ai-only

# Vérifier les logs
docker logs optralis-ai --tail 50

Fichiers

Fichier Description
ai-service/cmd/main.go Point d'entrée
ai-service/internal/config/config.go Configuration
ai-service/internal/handlers/analyze.go Handlers d'analyse
ai-service/internal/handlers/health.go Health check
ai-service/internal/handlers/reports.go Génération de rapports
ai-service/internal/handlers/scripts.go Génération de scripts
ai-service/internal/services/analyzer.go Logique d'analyse
ai-service/internal/services/quota.go Gestion des quotas
ai-service/internal/services/cooldown.go Système de cooldown
ai-service/internal/services/change_detection.go Détection de changements
ai-service/internal/claude/client.go Client Claude API
ai-service/internal/claude/prompts.go Prompts système

Fichiers Backend

Fichier Description
models/notifications.go NotificationChannel, NotificationHistory
services/notifications.go SendNotification, GetEnabledChannels
services/webhook.go SendTeamsWebhook, SendSlackWebhook, SendDiscordWebhook
handlers/notifications.go CRUD API + test endpoint
services/users.go Gestion utilisateurs multi-tenant
handlers/users.go API utilisateurs
services/activity.go Logging activité
handlers/activity.go API journal activité

Architecture Backend

Pattern Handler → Service → Database

Le backend suit une architecture en couches stricte :

┌─────────────────────────────────────────────────────────────────┐
│                     handlers/ (HTTP Layer)                        │
│   - Parse requêtes HTTP, validation params                        │
│   - Appelle les services, retourne JSON                          │
└───────────────────────────────┬─────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                     services/ (Business Logic)                    │
│   - Logique métier, transactions, cache                          │
│   - JAMAIS d'import fiber, JAMAIS de c.JSON()                    │
└───────────────────────────────┬─────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                     database/ (Data Layer)                        │
│   - Connexion PostgreSQL via pgx                                 │
│   - SQL brut paramétré ($1, $2...)                               │
└─────────────────────────────────────────────────────────────────┘

Règles importantes : - Les handlers ne font JAMAIS de requêtes SQL directes - Les services retournent des structs Go, pas des fiber.Map - Les transactions utilisent defer tx.Rollback() systématiquement

Transactions

// Pattern correct avec defer rollback
func CreateResourceTx(tx *sql.Tx, data *Data) error {
    // tx.Rollback() est no-op si Commit() réussit
    defer tx.Rollback()

    _, err := tx.Exec(`INSERT INTO ...`)
    if err != nil {
        return err // Rollback automatique via defer
    }

    return tx.Commit()
}

Batch Operations

Pour éviter les patterns N+1, utiliser pq.Array et unnest() :

import "github.com/lib/pq"

// ❌ N+1 - À éviter
for _, id := range ids {
    database.DB.Exec(`INSERT INTO ... WHERE id = $1`, id)
}

// ✅ Batch - Correct
machineIDStrs := make([]string, len(machineIDs))
for i, id := range machineIDs {
    machineIDStrs[i] = id.String()
}

_, err = database.DB.Exec(`
    INSERT INTO machine_labels (machine_id, label_id, value)
    SELECT unnest($1::uuid[]), $2, $3
    ON CONFLICT (machine_id, label_id) DO UPDATE SET value = EXCLUDED.value
`, pq.Array(machineIDStrs), labelID, value)

Optimisation des COUNT

Utiliser FILTER pour éviter plusieurs requêtes :

// ❌ 6 requêtes - À éviter
database.DB.QueryRow(`SELECT COUNT(*) FROM certs WHERE status = 'valid'`)
database.DB.QueryRow(`SELECT COUNT(*) FROM certs WHERE status = 'expired'`)
// ...

// ✅ 1 requête avec FILTER - Correct
err := database.DB.QueryRow(`
    SELECT
        COUNT(*) as total,
        COUNT(*) FILTER (WHERE revoked_at IS NULL AND expires_at > $2) as valid,
        COUNT(*) FILTER (WHERE revoked_at IS NULL AND expires_at <= $1) as expired,
        COUNT(*) FILTER (WHERE revoked_at IS NOT NULL) as revoked
    FROM client_certificates
`, now, thirtyDays).Scan(&stats.Total, &stats.Valid, &stats.Expired, &stats.Revoked)

Cascade Deletes

Les foreign keys utilisent ON DELETE CASCADE. Ne pas supprimer manuellement les données dépendantes :

// ❌ Redondant - Les DELETEs sont inutiles
database.DB.Exec(`DELETE FROM metrics WHERE machine_id = $1`, machineID)
database.DB.Exec(`DELETE FROM disk_info WHERE machine_id = $1`, machineID)
database.DB.Exec(`DELETE FROM machines WHERE id = $1`, machineID)

// ✅ Correct - CASCADE gère le cleanup
database.DB.Exec(`DELETE FROM machines WHERE id = $1 AND client_id = $2`, machineID, clientID)

Éviter les N+1 avec CTE et DISTINCT ON

Pour récupérer des données associées (ex: admin user d'un client), utiliser une CTE :

// ❌ N+1 - 3 subqueries par ligne de résultat
rows, err := database.DB.Query(`
    SELECT c.id, c.name,
        (SELECT email FROM users WHERE client_id = c.id LIMIT 1),
        (SELECT first_name FROM users WHERE client_id = c.id LIMIT 1)
    FROM clients c
`)

// ✅ CTE avec DISTINCT ON - 1 seule requête
rows, err := database.DB.Query(`
    WITH admin_users AS (
        SELECT DISTINCT ON (client_id)
            client_id, email, first_name, last_name
        FROM users
        WHERE role IN ('admin', 'super_admin')
        ORDER BY client_id, role DESC
    )
    SELECT c.id, c.name, au.email, au.first_name
    FROM clients c
    LEFT JOIN admin_users au ON au.client_id = c.id
`)

Agrégations batch avec INSERT...SELECT

Pour les tâches périodiques, éviter les boucles avec requêtes individuelles :

// ❌ N+1 - 1 INSERT par machine
for _, machineID := range machines {
    database.DB.Exec(`INSERT INTO metric_aggregates ...`, machineID)
}

// ✅ Batch - 1 seule requête
database.DB.Exec(`
    INSERT INTO metric_aggregates (machine_id, cpu_avg, ...)
    SELECT
        machine_id, AVG(cpu_percent), ...
    FROM metrics
    WHERE recorded_at >= $1 AND recorded_at < $2
    GROUP BY machine_id
`, periodStart, periodEnd)

Index critiques

Table Index Usage
users idx_users_client_id Lookups admin par client
users idx_users_totp_enabled (partial) MFA activé
machines idx_machines_hostname_lower Recherche case-insensitive
metrics idx_metrics_machine_time Dashboard temps réel

Structure des Services

services/
├── auth/                    # Authentification (clients, users)
│   ├── clients.go           # CreateClient, CreateClientTx, GetClientByID
│   └── users.go             # CreateUser, CreateUserTx, GetUserByEmail
├── machines/                # Gestion machines
│   ├── heartbeat.go         # ProcessHeartbeatWithClientID
│   ├── inventory.go         # ProcessInventoryWithClientID
│   ├── queries.go           # GetMachinesByClientID, DeleteMachine
│   ├── storage.go           # storeMetricsTx, storeDisksTx (Tx only)
│   └── collectors_storage.go # Store*FromCollectorByClientID (mTLS)
├── certificates.go          # GetCertificateStats, PurgeInactiveCertificates
├── labels.go                # AssignLabelToMachines (batch)
├── admin.go                 # CreateClientWithAdmin (transactionnel)
└── *_wrapper.go             # Backward compatibility wrappers

Authentification Agent (mTLS)

Tous les endpoints agent utilisent l'authentification mTLS via ClientID :

// ✅ Pattern mTLS - Utiliser
func StoreMetricsFromCollectorByClientID(clientID uuid.UUID, ...) error

Note: Les fonctions legacy *AgentKey* ont été supprimées en v2.3.0.

Le middleware AgentMTLSAuth extrait le ClientID du certificat CN.


Email Templates

Structure des fichiers

backend/internal/services/templates/emails/
├── password-reset.html         # Réinitialisation mot de passe
├── welcome.html                # Bienvenue nouvel utilisateur
├── contact-confirmation.html   # Confirmation formulaire contact
├── contact-notification.html   # Notification admin (nouveau contact)
├── machine-alert.html          # Alerte machine hors ligne
├── password-expiration.html    # Rappel expiration mot de passe (90j)
├── license-expiration.html     # Rappel expiration licence
├── license-extended.html       # Confirmation prolongation licence
└── certificate-expiration.html # Alerte certificats mTLS expirés

Design System (Linear Theme)

Tous les templates utilisent le design system Linear, compatible Gmail/Outlook :

Élément Couleur Usage
Page background #09090b Fond de page (zinc-950)
Card background #0a0a0a Carte principale
Header/boxes #0f0f0f En-têtes et encarts
Bordure subtile #1f1f1f Séparateurs
Texte principal #fafafa Titres, noms
Texte secondaire #a1a1aa Paragraphes
Texte tertiaire #71717a Mentions légales
Accent orange #f97316 Boutons CTA, liens
Warning ambre #f59e0b Alertes expiration
Success vert #22c55e Confirmations
Danger rouge #ef4444 Alertes critiques

Accents par template

Chaque template a un accent de couleur spécifique (ligne en haut du header + couleur du titre) :

Template Accent Type
password-reset #f97316 Orange Standard
welcome #f97316 Orange Standard
contact-confirmation #22c55e Vert Succès
contact-notification #f97316 Orange Standard
machine-alert #ef4444 Rouge Danger
password-expiration #f59e0b Ambre Warning
license-expiration #f59e0b Ambre Warning
license-extended #22c55e Vert Succès
certificate-expiration #ef4444 Rouge Danger

Variables communes

Tous les templates reçoivent ces variables :

type EmailData struct {
    Language    string // "fr" ou "en"
    BaseURL     string // URL du site
    CompanyURL  string // URL de 2LACS IT
    CompanyName string // "2LACS IT"
    Year        int    // Année courante
    LogoCID     string // Content-ID pour logo inline
}

Variables machine-alert

Le template machine-alert.html reçoit ces variables additionnelles (champs conditionnels) :

Variable Type Description
MachineName string Nom de la machine
MachineStatus string Statut (Hors ligne, CPU élevé, etc.)
AlertTime string Heure de détection
LastSeen string Dernière connexion (optionnel)
MachineOS string Système d'exploitation (optionnel)
MachineIP string IP locale (optionnel)
AgentVersion string Version agent + canal (optionnel)
DashboardURL string Lien vers le dashboard

Les champs optionnels s'affichent uniquement si présents ({{if .Field}}).

Compatibilité Email

  • Pas de CSS externe : Tout est inline
  • Pas de rgba() : Couleurs hex uniquement
  • Pas de background-clip: text : Non supporté Gmail
  • Tables pour layout : Structure en tables imbriquées
  • Logo via CID : <img src="cid:{{.LogoCID}}" /> pour embedding inline

Email Queue Worker

Le backend utilise une queue Redis pour l'envoi asynchrone des emails. Le worker est démarré dans main.go via go services.StartEmailWorker().

Clé Redis : email:queue

Types de jobs supportés :

Type Source Description
welcome Backend Email de bienvenue nouvel utilisateur
password_reset Backend Lien de réinitialisation mot de passe
routine_notification AI Service Notification simple (texte) des routines IA
report_notification AI Service Rapport IA complet en HTML stylé

Format du job (JSON) :

{
  "type": "report_notification",
  "email": "user@example.com",
  "language": "fr",
  "data": {
    "subject": "[Optralis] Rapport Quotidien - ClientName (09/01/2026)",
    "textBody": "Bonjour, votre rapport est disponible...",
    "htmlBody": "<!DOCTYPE html>..."
  },
  "created_at": "2026-01-09T08:00:00Z",
  "retries": 0
}

Logique du worker :

func StartEmailWorker() {
    for {
        job := cache.DequeueEmail(5 * time.Second)
        if job == nil { continue }

        switch job.Type {
        case "welcome":
            sendEmail(job.Email, subject, htmlBody, textBody)
        case "report_notification":
            sendEmail(job.Email, job.Data["subject"], job.Data["htmlBody"], job.Data["textBody"])
        // ...
        }

        if err != nil && job.Retries < 3 {
            job.Retries++
            cache.EnqueueEmail(job) // Retry
        }
    }
}

Features Avancées par Tier

Cette section documente les features disponibles selon le plan de licence.

Alertes Intelligentes (Essentials+)

Système d'alertes avancé qui réduit le bruit en analysant le contexte.

Fonctionnalité Description
Suppression du bruit Regroupement des alertes similaires (même machine, même métrique)
Cooldown dynamique Délai entre alertes ajusté selon la criticité
Corrélation Ne pas alerter si la cause racine est déjà identifiée
Baseline adaptative Seuils qui s'adaptent au comportement normal de la machine

Endpoints :

Méthode Endpoint Description
GET /api/alerts/rules Lister les règles d'alertes
POST /api/alerts/rules Créer une règle intelligente
PUT /api/alerts/rules/:id Modifier une règle
DELETE /api/alerts/rules/:id Supprimer une règle

Logique de suppression du bruit :

// Regroupement : max 1 alerte par machine/métrique/15min
func shouldAlert(machine, metric string) bool {
    key := fmt.Sprintf("alert:%s:%s", machine, metric)
    if cache.Exists(key) {
        return false // Alerte déjà envoyée récemment
    }
    cache.Set(key, true, 15*time.Minute)
    return true
}


APM - Application Performance Monitoring (Pro+)

Surveillance des applications et services métier.

Niveau Fonctionnalités
Basique (Pro) Monitoring de 1 application, métriques de base
Complet (Business) Multi-applications, traces distribuées
Illimité (Enterprise) Applications illimitées, custom metrics

Métriques collectées :

Métrique Description Source
response_time_ms Temps de réponse moyen Agent HTTP probe
error_rate Taux d'erreurs (%) Logs applicatifs
throughput Requêtes/seconde Agent HTTP probe
availability Disponibilité (%) Health checks

Endpoints APM :

Méthode Endpoint Description
GET /api/apm/applications Lister les applications monitorées
POST /api/apm/applications Ajouter une application
GET /api/apm/applications/:id/metrics Métriques d'une application
GET /api/apm/applications/:id/traces Traces distribuées (Business+)

Monitoring Bases de Données (Pro+)

Surveillance des instances de base de données.

Niveau Bases supportées
Pro 1 base (PostgreSQL, MySQL, SQL Server)
Business Multi-DB (jusqu'à 10)
Enterprise Illimité

Métriques collectées :

Métrique Description
connections_active Connexions actives
connections_idle Connexions en attente
query_time_avg_ms Temps moyen des requêtes
slow_queries_count Nombre de requêtes lentes (>1s)
deadlocks_count Nombre de deadlocks
cache_hit_ratio Ratio de cache hit (%)
replication_lag_ms Lag de réplication (si applicable)

Endpoints :

Méthode Endpoint Description
GET /api/databases Lister les bases monitorées
POST /api/databases Ajouter une base
GET /api/databases/:id/metrics Métriques d'une base
GET /api/databases/:id/slow-queries Top requêtes lentes

Rapports Automatiques

Synthèse Quotidienne (Tous plans)

Email quotidien avec résumé du parc.

Contenu Description
Machines en alerte Liste des machines avec problèmes
Score de santé global Moyenne du parc
Top 5 incidents Alertes les plus fréquentes
Tendance 24h Évolution CPU/RAM/Disque

Configuration : Settings > Notifications > Synthèse quotidienne

Rapports Direction (Essentials+)

Rapports hebdomadaires/mensuels pour les décideurs.

Type Contenu Format
Simple (Starter) Stats de base, graphiques Email HTML
Complet (Essentials+) KPIs, tendances, recommandations Email HTML + PDF

Endpoints :

Méthode Endpoint Description
GET /api/reports/summary/daily Synthèse quotidienne
GET /api/reports/executive/weekly Rapport direction hebdo
GET /api/reports/executive/monthly Rapport direction mensuel
POST /api/reports/generate Générer un rapport personnalisé

Rapports PDF Automatiques (Pro+)

Génération automatique de rapports PDF planifiés.

Fréquence Destinataires Contenu
Hebdomadaire Admins Résumé technique
Mensuel Direction KPIs exécutifs
Trimestriel DSI Analyse tendances

Multi-Sites (Business+)

Gestion centralisée de plusieurs sites géographiques.

Fonctionnalité Description
Vision multi-sites Dashboard consolidé de tous les sites
Score santé agrégé Score global calculé à partir de tous les sites
Comparaison inter-sites Benchmark entre sites
Alertes par site Notifications configurables par site

Modèle de données :

CREATE TABLE sites (
    id UUID PRIMARY KEY,
    client_id UUID REFERENCES clients(id),
    name VARCHAR(100) NOT NULL,
    location VARCHAR(200),
    timezone VARCHAR(50) DEFAULT 'Europe/Paris',
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Machines associées à un site
ALTER TABLE machines ADD COLUMN site_id UUID REFERENCES sites(id);

Endpoints :

Méthode Endpoint Description
GET /api/sites Lister les sites
POST /api/sites Créer un site
GET /api/sites/:id/machines Machines d'un site
GET /api/sites/:id/health Score santé du site
GET /api/sites/overview Vue consolidée multi-sites

Calcul du score santé agrégé :

func CalculateAggregatedHealthScore(sites []Site) float64 {
    var totalScore float64
    var totalMachines int

    for _, site := range sites {
        for _, machine := range site.Machines {
            totalScore += machine.HealthScore
            totalMachines++
        }
    }

    if totalMachines == 0 {
        return 100.0
    }
    return totalScore / float64(totalMachines)
}

Corrélation Logs/Métriques (Pro+)

Analyse croisée des événements système et des pics de métriques.

Fonctionnalité Description
Timeline corrélée Affichage simultané logs + métriques
Détection de causes Identification automatique des causes racines
Patterns récurrents Détection de corrélations fréquentes

Endpoint : POST /ai/correlate (voir section AI Service)

Exemple de corrélation détectée :

{
  "correlation": {
    "metric_spike": {
      "type": "cpu",
      "value": 95,
      "timestamp": "2026-01-07T10:30:00Z"
    },
    "related_events": [
      {
        "source": "Windows Update",
        "event_id": 19,
        "message": "Installation started",
        "timestamp": "2026-01-07T10:29:55Z"
      }
    ],
    "confidence": 0.92,
    "root_cause": "Windows Update installation caused CPU spike"
  }
}


Planification de Capacité (Business+)

Prédiction des besoins futurs en ressources.

Fonctionnalité Description
Prédiction saturation Date estimée de saturation disque/RAM
Tendances long terme Analyse sur 30/90/180 jours
Recommandations Suggestions d'upgrade proactives

Endpoint : POST /ai/predictive/maintenance (voir section AI Service)

Exemple de prédiction :

{
  "capacity_planning": {
    "disk_c": {
      "current_usage_percent": 75,
      "growth_rate_gb_per_month": 5.2,
      "predicted_full_date": "2026-04-15",
      "days_until_critical": 98,
      "recommendation": "Ajouter 100GB ou nettoyer les fichiers temporaires"
    },
    "ram": {
      "avg_usage_percent": 82,
      "trend": "stable",
      "recommendation": "Surveiller - proche du seuil critique"
    }
  }
}


Liens