Files
Omika/docs/MISE_EN_PRODUCTION_DIAGNOSTICS_V2.md
2026-10-23 18:46:50 +02:00

26 KiB
Raw Permalink Blame History

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)

cd D:\Avalonia\Omika
dotnet --version            # SDK 10 (global.json épingle 10.0.302)
git pull                    # branche à jour

SRV-OMIKA (PowerShell administrateur)

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.

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

$env:PGPASSWORD = '<SECRET_POSTGRES_ADMIN>'
$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)

$env:PGPASSWORD = '<SECRET_POSTGRES_ADMIN>'
$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)

$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)

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)

$env:PGPASSWORD = '<SECRET_POSTGRES_ADMIN>'
$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)

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) :

$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\) :
    { "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 :

Observability__ServerDiagnostics__Enabled = true
Observability__Remote__Directory          = <chemin JSONL partagé avec le Worker>
DiagnosticsIdentity__HmacKey              = <clé HMAC>
DiagnosticDeployment__ClientCode          = <CodeClient>        (sinon HFSQL)
DiagnosticDeployment__ServerInstanceId    = <id instance>

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 :
    DiagnosticsForwarding__Enabled  = true
    DiagnosticsForwarding__Endpoint = https://<central>/api/v1/diagnostics/batches
    DiagnosticsForwarding__ApiKey   = <clé copiée>
    
  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) :
    # sur le serveur du Worker
    DiagnosticsForwarding__Enabled  = true
    DiagnosticsForwarding__Endpoint = https://<central>/api/v1/diagnostics/batches
    DiagnosticsForwarding__ApiKey   = <clé copiée>
    Observability__Remote__Directory= <dossier LOCAL au Worker>
    
    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)

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

Invoke-WebRequest 'http://127.0.0.1:<PORT_API>/health' -UseBasicParsing        # 200 Healthy
Invoke-WebRequest 'http://127.0.0.1:<PORT_WEB>/Login' -UseBasicParsing          # 200
Invoke-WebRequest 'http://127.0.0.1:<PORT_DIAG_API>/' -UseBasicParsing          # 200 {status:ok}

SQL post-redémarrage :

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\*_<stamp> (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.