Premiere commit - Omika

This commit is contained in:
2026-10-23 18:46:50 +02:00
commit 8ea76c1b24
2155 changed files with 984301 additions and 0 deletions
+515
View File
@@ -0,0 +1,515 @@
# 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 = '<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)
```powershell
$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)
```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 = '<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)
```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 = <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` :
```text
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) :
```text
# 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)
```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:<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 :
```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\*_<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.