# Marche à suivre — Mise en production Omika Diagnostics (V2) > **Objet** : procédure **complète et à jour** de mise en production de la chaîne > Diagnostics, tenant compte du **premier déploiement d'il y a deux semaines** > (DiagnosticsWorker + DiagnosticsWeb + schéma `001→004/005`) et de tous les > changements intervenus depuis : **API centrale `Omika.Diagnostics.Api` (T19)**, > tables `diagnostics_clients`/`diagnostics_users`/`diagnostics_reference`/ > `diagnostics_tasks`, rétention télémétrie + erreurs non traitées, suppression de > groupes d'erreurs, etc. > > **Cible** : Windows Server 2025 `SRV-OMIKA`, PowerShell **administrateur**, > PostgreSQL 18, runtime .NET 10 (hébergement). > > Ce document **remplace** `MISE_A_JOUR_DIAGNOSTICS_SRV-OMIKA.md` (qui s'arrêtait à > `011`) et complète `T15-RUNBOOK-DEPLOIEMENT-PRODUCTION.md` (qui s'arrêtait à `005`). --- ## 0. Vue d'ensemble des projets Diagnostics | Projet | Rôle | Référence | |---|---|---| | `Omika.Diagnostics.Core` | Modèles, abstractions de repository, import JSONL, validation. | — | | `Omika.Diagnostics.Data` | Persistance Npgsql (pas d'ORM) + DI. | — | | `Omika.Diagnostics.Worker` | **Service** d'import JSONL → PostgreSQL + rétention (`OmikaDiagnosticsWorker`). | Windows Service | | `Omika.Diagnostics.Web` | **Service** interface de consultation/admin (`OmikaDiagnosticsWeb`). | Kestrel + service Windows | | `Omika.Diagnostics.Api` | **Service** API centrale (T19), reçoit les lots des serveurs clients (`OmikaDiagnosticsApi`). | Kestrel + service Windows | | `Omika.Api` | API métier/mobile **chez le client** : `POST /api/diagnostics` + `ServerDiagnosticMiddleware` + forwarder central. | Windows Service `OmikaApi` | | `Omika.Worker` | Worker métier (sync) ; émet des diagnostics serveur et forwarde son **propre** spool si hébergé sur un autre serveur que l'API. | Windows Service `OmikaWorker` | | `Omika.Observability.External` | Pont **optionnel** OpenTelemetry → Collector → SigNoz (hors chaîne Diagnostics). | — | | `Omika.App*` | App mobile/desktop : outbox SQLite + uploader `POST /api/diagnostics`. | — | > ⚠️ `Omika.slnx` **ne référence pas** les projets Diagnostics : on publie chaque > `.csproj` directement (voir §2). La chaîne Diagnostics est **indépendante** du > pont OpenTelemetry/SigNoz (`Observability:External`) — ne pas les confondre. --- ## 1. Ce qui a changé depuis le premier déploiement 1. **Nouveau service** : `OmikaDiagnosticsApi` (`src/Omika.Diagnostics.Api`) — API centrale multi-serveur. **Non présent** dans le premier déploiement. 2. **Base de données fortement évoluée** : migrations `005 → 016` ajoutées (contexte client, appareil/licence, usage, registre clients, utilisateurs Web, référentiel, tâches de traitement, rétention télémétrie/erreurs, DELETE groupe). 3. **Nouvelles tables** : `diagnostics_clients`, `diagnostics_users`, `diagnostics_reference`, `diagnostics_tasks`. 4. **Nouveaux privilèges** : rôle `omika_diagnostics_api` (009) ; DELETE Web sur `diagnostics_tasks` (013) et `diagnostic_event` (016) ; SELECT/DELETE worker sur `telemetry_metric_hourly` (014) ; SELECT colonne-ciblé worker sur `diagnostics_tasks` (015). 5. **Nouvelles fonctionnalités Web** : `/Clients`, `/Users`, `/Referentiel`, `/Taches`, boutons « Supprimer » (tâche + groupe d'erreurs). --- ## 2. Prérequis ### Poste DEV (publication) ```powershell cd D:\Avalonia\Omika dotnet --version # SDK 10 (global.json épingle 10.0.302) git pull # branche à jour ``` ### SRV-OMIKA (PowerShell administrateur) ```powershell net session # OK sinon STOP dotnet --list-runtimes # Microsoft.AspNetCore.App 10.x Get-Service postgresql* # Running Test-Path 'C:\Program Files\PostgreSQL\18\bin\psql.exe' Test-Path 'C:\Program Files\PostgreSQL\18\bin\pg_dump.exe' ``` --- ## 3. Publication (poste DEV) > Script prêt : `scripts\publish-diagnostics.ps1` (ajouter `-IncludeCentralApi`). > Équivalent manuel ci-dessous. ```powershell cd D:\Avalonia\Omika # 1. Omika.Api (métier + forwarder) dotnet publish src\Omika.Api\Omika.Api.csproj ` -c Release -r win-x64 --self-contained false -o C:\Omika\Deploy\OmikaApi-next # 2. Omika.Worker (sync métier) dotnet publish src\Omika.Worker\Omika.Worker.csproj ` -c Release -r win-x64 --self-contained false -o C:\Omika\Deploy\OmikaWorker-next # 3. Omika.Diagnostics.Worker (EnableWindowsService=true = défaut) dotnet publish src\Omika.Diagnostics.Worker\Omika.Diagnostics.Worker.csproj ` -c Release -r win-x64 --self-contained false -o C:\Omika\Deploy\DiagnosticsWorker-next # 4. Omika.Diagnostics.Web dotnet publish src\Omika.Diagnostics.Web\Omika.Diagnostics.Web.csproj ` -c Release -r win-x64 --self-contained false -o C:\Omika\Deploy\DiagnosticsWeb-next # 5. Omika.Diagnostics.Api (NOUVEAU — API centrale) dotnet publish src\Omika.Diagnostics.Api\Omika.Diagnostics.Api.csproj ` -c Release -r win-x64 --self-contained false -o C:\Omika\Deploy\DiagnosticsApi-next ``` **Contrôles** : chaque dossier contient son `.exe` ; `appsettings.Development.json` absent (ou jamais activé) ; aucun secret Dev dans les publications. Transférer ensuite les 5 dossiers `*-next` vers `C:\Omika\Deploy\` sur SRV-OMIKA. --- ## 4. Base de données — inventaire des migrations (`001 → 016`) Ordre d'application **strict** sur SRV-OMIKA, en **superuser** (`postgres`), avec `ON_ERROR_STOP=1`. Toutes sont **idempotentes** et **non destructives**. | # | Fichier | Objet | Rôle(s) impacté(s) | |---|---|---|---| | 001 | `001_diagnostics_schema.sql` | `diagnostic_event` + `telemetry_metric_hourly` + dédup `event_id` | — | | 002 | `002_actor_key.sql` | colonnes `actor_key`/`actor_key_version` | — | | 003 | `003_diagnostics_roles.sql` | rôles `owner`(NOLOGIN)/`worker`/`web` + ownership + grants | worker: INSERT ; web: SELECT | | 004 | `004_diagnostics_retention.sql` | rétention bornée | worker: +SELECT+DELETE `diagnostic_event` | | 005 | `005_diagnostics_client_context.sql` | colonnes `client_code`/`server_instance`/`environment` | — | | 006 | `006_diagnostics_device_license.sql` | colonnes appareil/licence | — | | 007 | `007_diagnostics_usage.sql` | colonnes `screen_code`/`feature_code` | — | | 008 | `008_diagnostics_clients.sql` | table `diagnostics_clients` | — | | 009 | `009_diagnostics_clients_grants.sql` | rôle `omika_diagnostics_api` + grants clients | api: SELECT + UPDATE(last_seen) ; web: SELECT/INSERT/UPDATE | | 010 | `010_diagnostics_web_users.sql` | table `diagnostics_users` | web: SELECT/INSERT/UPDATE | | 011 | `011_diagnostics_reference.sql` | table `diagnostics_reference` + seed 29+3 | web: SELECT/INSERT/UPDATE | | 012 | `012_diagnostics_tasks.sql` | table `diagnostics_tasks` | web: SELECT/INSERT/UPDATE | | 013 | `013_diagnostics_tasks_delete.sql` | DELETE tâche | web: +DELETE `diagnostics_tasks` | | 014 | `014_diagnostics_telemetry_retention.sql` | rétention télémétrie (180 j) | worker: +SELECT+DELETE `telemetry_metric_hourly` | | 015 | `015_diagnostics_retention_untreated.sql` | ne pas purger les erreurs non traitées | worker: +SELECT(2 col.) `diagnostics_tasks` | | 016 | `016_diagnostics_event_delete.sql` | suppression d'un groupe d'erreurs | web: +DELETE `diagnostic_event` | > ⚠️ `003_diagnostics_roles_preprod.sql` concerne **uniquement** la préproduction : > **ne jamais** l'appliquer en Production. ### Récapitulatif des privilèges par rôle (après 016) | Rôle (LOGIN) | Service | Privilèges | |---|---|---| | `omika_diagnostics_worker` | Diagnostics.Worker | `diagnostic_event` INSERT/SELECT/DELETE ; `telemetry_metric_hourly` SELECT/DELETE ; `diagnostics_tasks` SELECT(`group_url_identifier`,`status`) | | `omika_diagnostics_web` | Diagnostics.Web | `diagnostic_event` SELECT/DELETE ; `diagnostics_clients` SELECT/INSERT/UPDATE(cols) ; `diagnostics_users` SELECT/INSERT/UPDATE(cols) ; `diagnostics_reference` SELECT/INSERT/UPDATE(cols) ; `diagnostics_tasks` SELECT/INSERT/UPDATE(cols)/DELETE | | `omika_diagnostics_api` | Diagnostics.Api | `diagnostics_clients` SELECT + UPDATE(`last_seen_at_utc`) uniquement | --- ## 5. Marche à suivre détaillée (fenêtre de maintenance) ### 5.1 Audit (lecture seule) — déterminer les migrations déjà appliquées ```powershell $env:PGPASSWORD = '' $psql = 'C:\Program Files\PostgreSQL\18\bin\psql.exe' & $psql -h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -c ` "SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_name LIKE 'diagnostic%' ORDER BY 1;" & $psql -h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -c ` "SELECT column_name FROM information_schema.columns WHERE table_name='diagnostic_event' ORDER BY ordinal_position;" & $psql -h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -c ` "SELECT rolname FROM pg_roles ORDER BY rolname;" Remove-Item Env:PGPASSWORD ``` Repères : `diagnostic_event`=001 ; `actor_key`=002 ; rôles owner/worker/web=003 ; `client_code`=005 ; `diagnostics_clients`=008 ; rôle `omika_diagnostics_api`=009 ; `diagnostics_users`=010 ; `diagnostics_reference`=011 ; `diagnostics_tasks`=012. ### 5.2 Backup PostgreSQL (AVANT toute migration) ```powershell $env:PGPASSWORD = '' $stamp = Get-Date -Format 'yyyyMMdd_HHmm' $out = "C:\Omika\Backups\omika_diagnostics_${stamp}_before_upgrade.backup" & 'C:\Program Files\PostgreSQL\18\bin\pg_dump.exe' ` -h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -F c -v -f $out Remove-Item Env:PGPASSWORD Get-Item $out | Select-Object Name,Length # taille > 0 sinon NO-GO ``` ### 5.3 Backup des binaires (AVANT remplacement) ```powershell $stamp = Get-Date -Format 'yyyyMMdd-HHmmss' $root = 'C:\Omika\Backups' Copy-Item 'C:\Omika\Apps\OmikaApi' -Destination "$root\OmikaApi_${stamp}" -Recurse -Force Copy-Item 'C:\Omika\Apps\OmikaWorker' -Destination "$root\OmikaWorker_${stamp}" -Recurse -Force Copy-Item 'C:\Omika\Apps\DiagnosticsWorker' -Destination "$root\DiagnosticsWorker_${stamp}" -Recurse -Force Copy-Item 'C:\Omika\Apps\DiagnosticsWeb' -Destination "$root\DiagnosticsWeb_${stamp}" -Recurse -Force # DiagnosticsApi : nouveau — backup seulement s'il existe déjà if (Test-Path 'C:\Omika\Apps\DiagnosticsApi') { Copy-Item 'C:\Omika\Apps\DiagnosticsApi' -Destination "$root\DiagnosticsApi_${stamp}" -Recurse -Force } ``` ### 5.4 Arrêt des services (ordre : figer la lecture, puis les écrivains) ```powershell Stop-Service OmikaDiagnosticsWeb -Force Stop-Service OmikaApi -Force Stop-Service OmikaWorker -Force Stop-Service OmikaDiagnosticsWorker -Force # OmikaDiagnosticsApi : à arrêter aussi s'il existait déjà Stop-Service OmikaDiagnosticsApi -Force -ErrorAction SilentlyContinue Get-Service OmikaApi,OmikaWorker,OmikaDiagnosticsWorker,OmikaDiagnosticsWeb,OmikaDiagnosticsApi | Select-Object Name,Status ``` ### 5.5 Migrations (UNIQUEMENT les manquantes, dans l'ordre, TOUJOURS avant les binaires) ```powershell $env:PGPASSWORD = '' $psql = 'C:\Program Files\PostgreSQL\18\bin\psql.exe' $db = 'C:\Omika\Deploy\DiagnosticsDatabase' # dossier contenant les .sql livrés $migrations = @( '001_diagnostics_schema.sql','002_actor_key.sql','003_diagnostics_roles.sql', '004_diagnostics_retention.sql','005_diagnostics_client_context.sql', '006_diagnostics_device_license.sql','007_diagnostics_usage.sql', '008_diagnostics_clients.sql','009_diagnostics_clients_grants.sql', '010_diagnostics_web_users.sql','011_diagnostics_reference.sql', '012_diagnostics_tasks.sql','013_diagnostics_tasks_delete.sql', '014_diagnostics_telemetry_retention.sql','015_diagnostics_retention_untreated.sql', '016_diagnostics_event_delete.sql' ) foreach ($m in $migrations) { $f = Join-Path $db $m if (-not (Test-Path $f)) { Write-Host "ABSENT : $m" ; continue } Write-Host "Application : $m ..." & $psql -h 127.0.0.1 -p 5432 -U postgres -d omika_diagnostics -v ON_ERROR_STOP=1 -f $f if ($LASTEXITCODE -ne 0) { throw "ÉCHEC sur $m (exit $LASTEXITCODE). STOP." } } Remove-Item Env:PGPASSWORD ``` > **Règle d'ordre** : migration **avant** binaires (colonnes/tables additives ; > anciens binaires restent compatibles, l'inverse échouerait). > Idempotence ⇒ réappliquer une migration déjà en place est un no-op (sauf 003 : > ne pas rejouer l'ownership inutilement si les rôles existent déjà). ### 5.6 Mots de passe des rôles LOGIN (manuel, jamais en script/log) Après `003`/`009` : ``` \password omika_diagnostics_worker \password omika_diagnostics_web \password omika_diagnostics_api # nouveau rôle (009) — pour DiagnosticsApi ``` ### 5.7 Copie des binaires (préserve la config serveur existante) ```powershell robocopy 'C:\Omika\Deploy\OmikaApi-next' 'C:\Omika\Apps\OmikaApi' /E /XF appsettings.json appsettings.Production.json web.config robocopy 'C:\Omika\Deploy\OmikaWorker-next' 'C:\Omika\Apps\OmikaWorker' /E /XF appsettings.json appsettings.Production.json web.config robocopy 'C:\Omika\Deploy\DiagnosticsWorker-next' 'C:\Omika\Apps\DiagnosticsWorker' /E /XF appsettings.json appsettings.Production.json web.config robocopy 'C:\Omika\Deploy\DiagnosticsWeb-next' 'C:\Omika\Apps\DiagnosticsWeb' /E /XF appsettings.json appsettings.Production.json web.config robocopy 'C:\Omika\Deploy\DiagnosticsApi-next' 'C:\Omika\Apps\DiagnosticsApi' /E /XF appsettings.json appsettings.Production.json web.config robocopy 'C:\Omika\Deploy\LanguesApi-next' 'C:\Omika\Apps\LanguesApi' /E /XF appsettings.json appsettings.Production.json web.config ``` > Ne **jamais** supprimer : `C:\Omika\Data\Api\Diagnostics\Jsonl\**`, > `C:\Omika\Data\DiagnosticsWorker\Checkpoint\import-checkpoint.json`, > `C:\Omika\Config\**`, certificats TLS, variables d'environnement MACHINE. ### 5.8 Création du service `OmikaDiagnosticsApi` (NOUVEAU) Le service n'existe pas encore : le créer (compte dédié, jamais LocalSystem) : ```powershell $cred = Get-Credential -UserName '.\svc_omika_diagapi' -Message 'Compte service OmikaDiagnosticsApi' New-Service -Name 'OmikaDiagnosticsApi' -DisplayName 'Omika Diagnostics Api' ` -BinaryPathName '"C:\Omika\Apps\DiagnosticsApi\Omika.Diagnostics.Api.exe"' ` -Credential $cred -StartupType Manual # ACL dossier spool central + logs icacls 'C:\ProgramData\Omika\Diagnostics\Central' /grant:r 'svc_omika_diagapi:(OI)(CI)M' icacls 'C:\Omika\Logs\DiagnosticsApi' /grant:r 'svc_omika_diagapi:(OI)(CI)M' ``` > Le compte dédié doit avoir le droit « Ouvrir une session en tant que service ». > Ce service se crée par le script dédié > `infra\deploy\Install-OmikaDiagnosticsApi.ps1` (compte, ACL du spool central, > droit de connexion, `-WhatIf`, `-Desinstaller`). L'orchestrateur > `Install-OmikaServices.ps1` l'inclut par défaut — utilisez `-SansDiagnosticsApi` > pour l'exclure sur un serveur **mono-serveur**, où il est inutile. --- ## 6. Configuration serveur par service (secrets injectés SUR LE SERVEUR) Règle : en variable d'environnement, `:` devient `__` (`Diagnostics:Central:Directory` ⇔ `Diagnostics__Central__Directory`). `ConnectionStrings:DiagnosticsDatabase` **prime** sur `Diagnostics:ConnectionString`. Toute modif de variable d'un service exige un **redémarrage** du service. ### Omika.Api (`OmikaApi`) — émission + ingestion + forwarding | Variable | Rôle | |---|---| | `ASPNETCORE_ENVIRONMENT` | `Production` (obligatoire) | | `MobileAuth__SigningKey` | fail-fast en Prod (signature tokens mobile) | | `DiagnosticsIdentity__HmacKey` | clé HMAC ActorKey/DeviceKey (≥256 bits Base64) | | `DiagnosticDeployment__ClientCode` | CodeClient (sinon fallback HFSQL) | | `DiagnosticDeployment__ServerInstanceId` | id d'instance (sinon MachineName) | | `Observability__Remote__Directory` | **dossier JSONL partagé** (identique au Worker) | | `Observability__ServerDiagnostics__Enabled` | **`true` pour activer l'émission serveur 4xx/5xx/exceptions** (voir §7) | | `DiagnosticsForwarding__Enabled` | kill switch forwarder central (`true`/`false`) | | `DiagnosticsForwarding__Endpoint` | URL centrale **HTTPS** (si forwarding) | | `DiagnosticsForwarding__ApiKey` | clé d'accès centrale (**secret**) | | `LicenseConnection__*` / `SyncConnection__*` | secrets HFSQL existants (non touchés) | ### Omika.Worker (`OmikaWorker`) | Variable | Rôle | |---|---| | `ASPNETCORE_ENVIRONMENT` | `Production` (obligatoire) | | `DiagnosticDeployment__ClientCode` | CodeClient (sinon fallback HFSQL) | | `DiagnosticDeployment__ServerInstanceId` | id d'instance (sinon MachineName) | | `Observability__Remote__Directory` | **dossier JSONL** (voir note topologie ci-dessous) | | `DiagnosticsForwarding__Enabled` | forwarder du Worker (`true` si serveur séparé de l'API) | | `DiagnosticsForwarding__Endpoint` | URL centrale **HTTPS** (si forwarding Worker) | | `DiagnosticsForwarding__ApiKey` | clé d'accès centrale (**secret**) | | secrets HFSQL existants | non touchés | > **Topologie serveur client :** > - **Même serveur que l'API** : `Observability__Remote__Directory` = dossier **partagé** > avec l'API, lu par `OmikaDiagnosticsWorker`. Laisser `DiagnosticsForwarding__Enabled=false` > côté Worker (l'API forwarde déjà ce dossier) → un **seul** forwarder sur le dossier partagé. > - **Serveur séparé de l'API** : `Observability__Remote__Directory` = dossier **local au > Worker** (jamais un partage réseau) ; activer `DiagnosticsForwarding__Enabled=true` + > `Endpoint` + `ApiKey`. Le Worker forwarde alors son **propre** spool vers le central > (checkpoint `forwarder-checkpoint-worker.json`, distinct de celui de l'API). ### Omika.Diagnostics.Worker (`OmikaDiagnosticsWorker`) | Variable | Défaut | Rôle | |---|---|---| | `ConnectionStrings__DiagnosticsDatabase` | — | Npgsql `Username=omika_diagnostics_worker` | | `Observability__Remote__Directory` | `""` | dossier JSONL (**identique** à Omika.Api) | | `Diagnostics__Import__CheckpointDirectory` | `""` | dossier checkpoint | | `Diagnostics__Import__CheckpointFileName` | `import-checkpoint.json` | nom checkpoint | | `Diagnostics__RetentionDays` | `60` | rétention événements | | `Diagnostics__TelemetryRetentionDays` | `180` | rétention télémétrie | | `Diagnostics__RetentionIntervalHours` | `24` | fréquence purge | | `Diagnostics__RetentionBatchSize` | `1000` | lot de purge | ### Omika.Diagnostics.Web (`OmikaDiagnosticsWeb`) | Variable | Rôle | |---|---| | `ASPNETCORE_ENVIRONMENT` | `Production` (default-deny) | | `ConnectionStrings__DiagnosticsDatabase` | Npgsql `Username=omika_diagnostics_web` | | `DiagnosticsAuth__Operators__*` | bootstrap temporaire (hash uniquement) puis retrait | ### Omika.Diagnostics.Api (`OmikaDiagnosticsApi`) — NOUVEAU | Variable | Défaut | Rôle | |---|---|---| | `ASPNETCORE_ENVIRONMENT` | — | `Production` | | `ConnectionStrings__DiagnosticsDatabase` | — | Npgsql `Username=omika_diagnostics_api` | | `Diagnostics__Central__Directory` | `logs/diagnostics-central` | **spool central** (chemin explicite, ex. `C:\ProgramData\Omika\Diagnostics\Central`) | | `Diagnostics__Central__MaxRequestBodyBytes` | `524288` | corps borné | | `Diagnostics__Central__MaxEventsPerBatch` | `100` | événements max/lot | --- ## 7. Activer l'envoi des diagnostics (API Omika + application) Trois flux distincts, tous **opt-in** : ### 7.1 Application (mobile/desktop) → Omika.Api L'application écrit dans une outbox SQLite locale (`omika-diagnostics.db`, toujours active). L'**upload** vers `POST /api/diagnostics` est **désactivé avant inscription** (`DiagnosticUploadOptions.Enabled=false`) puis **activé automatiquement dès que l'utilisateur s'inscrit** (QR ou code manuel), sur toutes les plateformes (Android, iOS, Desktop). - Activation automatique : à l'inscription réussie, l'application écrit elle-même le fichier `diagnostics-upload.json` dans le dossier de données de l'app (`%LocalAppData%\Omika\`) : ```json { "Enabled": true } ``` et active l'upload en mémoire pour la session courante (reprise sous ~60 s). Champs optionnels : `Interval`, `Timeout`, `BatchSize`, `MaxBatchBytes`, `EndpointPath`. - Appareils déjà inscrits (avant cette version) : le fichier est migré au démarrage si la session est inscrite et que le fichier n'existe pas encore. - Kill-switch support : poser manuellement `{ "Enabled": false }` dans ce fichier désactive l'upload ; un fichier existant n'est jamais écrasé par la restauration de session. - L'app envoie vers l'`OmikaApi` **déjà configuré** (BaseUrl + token de session) : aucun endpoint/secret supplémentaire. - Token absent → aucun envoi, données conservées (reprise au prochain contexte). > ⚠️ **Point d'attention** : l'upload ne s'exécute que lorsque l'app est active > (aucun job OS en arrière-plan) et exige un token de session valide. ### 7.2 Omika.Api → chaîne Diagnostics (diagnostics serveur) Le `ServerDiagnosticMiddleware` capture 4xx/5xx/exceptions et les écrit dans le JSONL remote (relu par `OmikaDiagnosticsWorker`). **Désactivé par défaut.** Activation : ```text Observability__ServerDiagnostics__Enabled = true Observability__Remote__Directory = DiagnosticsIdentity__HmacKey = DiagnosticDeployment__ClientCode = (sinon HFSQL) DiagnosticDeployment__ServerInstanceId = ``` ### 7.3 Serveur client → API centrale (multi-serveur uniquement) Uniquement si vous recevez des diagnostics de **serveurs clients distants** : 1. Dans `OmikaDiagnosticsWeb` → **Clients Diagnostics** → **Nouveau client** (CodeClient exact), **créer** puis **copier immédiatement** la clé. 2. Sur le serveur client, dans `OmikaApi` : ```text DiagnosticsForwarding__Enabled = true DiagnosticsForwarding__Endpoint = https:///api/v1/diagnostics/batches DiagnosticsForwarding__ApiKey = ``` 3. **Si `OmikaWorker` est sur un AUTRE serveur que `OmikaApi`**, activer AUSSI le forwarding sur le Worker (même clé ou clé du client Worker, checkpoints distincts) : ```text # sur le serveur du Worker DiagnosticsForwarding__Enabled = true DiagnosticsForwarding__Endpoint = https:///api/v1/diagnostics/batches DiagnosticsForwarding__ApiKey = Observability__Remote__Directory= ``` Chaque service forwarde alors son **propre** dossier JSONL local. Sur un même serveur (dossier partagé), n'activer qu'**un seul** forwarder (l'API) pour éviter le double envoi. 4. Redémarrer `OmikaApi` (et `OmikaWorker` le cas échéant), puis vérifier `last_seen_at_utc` dans Clients Diagnostics. > Sur un SRV-OMIKA **mono-serveur** (Omika.Api écrit le JSONL localement), l'API > centrale `OmikaDiagnosticsApi` et le forwarding ne sont **pas nécessaires**. --- ## 8. Redémarrage + smoke tests ### 8.1 Ordre de redémarrage (consommateurs avant producteurs) ```powershell Start-Service OmikaDiagnosticsWorker # 1. importeur (consommateur JSONL) Start-Service OmikaDiagnosticsWeb # 2. UI Start-Service OmikaDiagnosticsApi # 3. API centrale (si multi-serveur) Start-Service OmikaWorker # 4. sync métier Start-Service OmikaApi # 5. API (producteur, dernier) Get-Service OmikaApi,OmikaWorker,OmikaDiagnosticsWorker,OmikaDiagnosticsWeb,OmikaDiagnosticsApi | Select-Object Name,Status ``` ### 8.2 Smoke tests ```powershell Invoke-WebRequest 'http://127.0.0.1:/health' -UseBasicParsing # 200 Healthy Invoke-WebRequest 'http://127.0.0.1:/Login' -UseBasicParsing # 200 Invoke-WebRequest 'http://127.0.0.1:/' -UseBasicParsing # 200 {status:ok} ``` SQL post-redémarrage : ```sql SELECT count(*) FROM diagnostic_event; SELECT max(ingested_at_utc) FROM diagnostic_event; SELECT reference_type, count(*) FROM diagnostics_reference GROUP BY reference_type; -- attendu : SCREEN_CODE = 29, FEATURE_CODE = 3 SELECT count(*) FROM diagnostics_tasks; ``` Dans `OmikaDiagnosticsWeb`, vérifier : Dashboard, Diagnostics, Erreurs (bouton « Supprimer le groupe »), Traces, Synchronisation, Telemetry, Versions, Référentiel, **Taches** (bouton « Supprimer »), et `/Clients` + `/Users` (Admin). --- ## 9. Écarts constatés (scripts/documents à mettre à jour) 1. **`infra\deploy\Install-OmikaServerFiles.ps1`** : ne copie **que 4** publications (OmikaApi, OmikaWorker, DiagnosticsWorker, DiagnosticsWeb) → **ne déploie pas `DiagnosticsApi`**. 2. ~~**`infra\deploy\Install-OmikaServices.ps1`** : ne crée **que 4** services → ne crée **pas** `OmikaDiagnosticsApi` ni ses ACL (spool central/logs).~~ **RÉSOLU** : un script par déploiement dans `infra\deploy` (`Install-OmikaDiagnosticsApi.ps1` incluse), et l'orchestrateur les enchaîne dans le bon ordre (le propriétaire d'un dossier est installé **avant** ses lecteurs, pour que les ACL inter-services puissent être posées). 3. **`infra\deploy\Install-DatabaseSchema.ps1`** : n'applique **que `001`+`002`**. **`Harden-DatabaseRoles.ps1`** : n'applique **que `003`**. Les migrations `004 → 016` restent manuelles (cette marche à suivre les couvre). 4. **`Omika.slnx`** ne référence pas les projets Diagnostics (build par `.csproj`). 5. **App** : l'activation de l'upload est désormais automatique dès l'inscription (fichier `diagnostics-upload.json` écrit par l'app elle-même ; kill-switch support via `{ "Enabled": false }`). Corrigé au passage : le fichier était nommé `development-upload.json` et documenté à tort comme « compilé hors Release ». 6. **Docs obsolètes** : `MISE_A_JOUR_DIAGNOSTICS_SRV-OMIKA.md` (→011) et `T15-RUNBOOK-DEPLOIEMENT-PRODUCTION.md` (→005). Recommandation : mettre à jour les scripts 1–3 pour couvrir le 5ᵉ service et la liste complète `001→016` (et générer le `SHA256SUMS.txt` incluant `DiagnosticsApi`). --- ## 10. Rollback - **Binaires** : restaurer les dossiers `C:\Omika\Backups\*_` (arrêt → copie → démarrage dans l'ordre §8.1). - **Base** : **pas de rollback SQL automatique** (migrations additives/idempotentes). `pg_restore` (backup §5.2) uniquement sur décision humaine explicite. - **Kill switch** : `DiagnosticsForwarding__Enabled=false` (+ redémarrage OmikaApi) coupe tout envoi central sans toucher au métier ni au spool.