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

639 lines
28 KiB
Markdown

# Marche à suivre — Mise en production API de téléchargement des langues (modèles ONNX)
> **Objet** : procédure **complète** de mise en production de l'**API dédiée** de
> téléchargement des **modèles de langue ONNX** (traduction hors-ligne pour le
> mobile). Cette API est un **service Windows séparé** (`OmikaLanguesApi`, projet
> `src/Omika.Langues.Api`), distinct d'`OmikaApi`. Elle sert au mobile les fichiers
> de modèle + un manifeste versionné, **en accès anonyme** (fichiers génériques,
> aucune donnée client). L'**URL de base reste lue en base** (`ParametreSysteme`
> `PAS_Code = 'SRV_LANGUES'`) par `OmikaApi`, qui l'expose au bootstrap mobile.
>
> **Cible** : Windows Server `SRV-OMIKA`, PowerShell **administrateur**, runtime
> .NET 10. Aucune base de données, aucune authentification, aucun service externe.
>
> Ce document **complète** `T15-RUNBOOK-DEPLOIEMENT-PRODUCTION.md` et
> `MISE_EN_PRODUCTION_DIAGNOSTICS_V2.md` : il ne couvre que le **service Langues**.
> `OmikaApi` garde uniquement la lecture `SRV_LANGUES` → `LanguesUrl` au bootstrap.
---
## 0. Vue d'ensemble de l'API Langues
| Composant | Rôle | Référence |
|---|---|---|
| `Omika.Langues.Api` | **Service dédié** : endpoints `GET /api/langues*` (manifeste + streaming). | Windows Service `OmikaLanguesApi` |
| `Omika.Core` | `LanguageModelService` (lecture manifeste + résolution fichier) + `LanguageModelOptions` + contrats partagés. | — |
| `Langues:RootPath` | Dossier racine des modèles + du manifeste. | variable d'env. `Langues__RootPath` |
| `ParametreSysteme.SRV_LANGUES` | URL de base du service Langues, lue par `OmikaApi`. | HFSQL (Omika **et** Batik) |
Flux :
```
1. GET /api/mobile/bootstrap ──> OmikaApi lit SRV_LANGUES ──> LanguesUrl (→ URL du service Langues)
2. GET {LanguesUrl}/api/langues ──> manifeste (packs + fichiers + sha256) [anonyme]
3. GET {LanguesUrl}/api/langues/{code}/{sens}/{fichier} ──> streaming (reprise) [anonyme]
```
- `SRV_LANGUES` absent/vide → `LanguesUrl = null` → le mobile désactive la
traduction hors-ligne (aucun appel, aucun plantage).
- Les textes à traduire ne sortent **jamais** de l'appareil : traduction 100 %
locale une fois le modèle téléchargé.
---
## 1. Ce qui a changé
1. **Nouveau service dédié** `OmikaLanguesApi` (`src/Omika.Langues.Api`) : les
endpoints langues ne sont **plus** dans `OmikaApi`.
2. **Accès anonyme** : aucun token requis (les modèles sont des fichiers génériques).
3. **Nouvelle section de configuration** : `Langues` (`RootPath`, `ManifestFileName`),
portée par le service `OmikaLanguesApi`.
4. **`OmikaApi`** ne conserve que la lecture `SRV_LANGUES` → `MobileConfigurationDto.LanguesUrl`.
5. **Nouveaux fichiers à déployer sur disque** : `manifest.json` + modèles ONNX
(jamais embarqués dans l'APK/IPA ni dans un binaire).
> Le **client de téléchargement ONNX côté mobile** (téléchargement, reprise,
> vérification SHA-256, stockage local) relève d'un **ticket ultérieur** : ce
> document ne couvre que le **serveur** (service Langues + exposition `SRV_LANGUES`).
---
## 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 OmikaApi # Running (bootstrap métier)
Test-Path 'C:\Omika\Data' # dossier de données (langues)
```
> `OmikaLanguesApi` **n'a besoin d'aucun accès HFSQL** : uniquement du dossier de
> fichiers `Langues:RootPath`.
---
## 3. Publication (poste DEV)
```powershell
cd D:\Avalonia\Omika
# Omika.Api (métier : lit SRV_LANGUES → LanguesUrl ; endpoints langues retirés)
dotnet publish src\Omika.Api\Omika.Api.csproj `
-c Release -r win-x64 --self-contained false -o C:\Omika\Deploy\OmikaApi-next
# Omika.Langues.Api (NOUVEAU service dédié)
dotnet publish src\Omika.Langues.Api\Omika.Langues.Api.csproj `
-c Release -r win-x64 --self-contained false -o C:\Omika\Deploy\LanguesApi-next
```
**Contrôles** : chaque dossier contient son `.exe` ; `appsettings.Development.json`
absent ; aucun secret Dev dans les publications.
Transférer `OmikaApi-next` et `LanguesApi-next` vers `C:\Omika\Deploy\` sur SRV-OMIKA.
---
## 4. Base HFSQL — paramètre `SRV_LANGUES`
`OmikaApi` lit l'URL depuis la table `ParametreSysteme` (schéma Omika **et** Batik —
rubriques `PAS_Code` / `PAS_Valeur`, groupe `ProtectionPlus`).
| Rubrique | Valeur |
|---|---|
| `PAS_Code` | `SRV_LANGUES` |
| `PAS_Description` | ex. « URL du service de téléchargement des modèles de langue » |
| `PAS_Valeur` | URL de base du **service Langues**, **sans slash final** |
Exemples de `PAS_Valeur` (pointe vers le service `OmikaLanguesApi`) :
- Production derrière Apache (vhost/hôte dédié) : `https://langues.applilogik.fr`
- Chemin dédié derrière l'Apache existant : `https://omika.applilogik.fr/langues`
- API interne directe : `https://omika-langues.local:8443`
> Le slash final est toléré (l'API le normalise), mais **ne pas le mettre** évite
> toute ambiguïté. La valeur doit être une URL **HTTPS** joignable par le mobile.
**Écriture** : même mécanisme que `SRV_LICENCE_MOBILE` existant — côté WinDev
`ParametreSystemeEcrit("SRV_LANGUES", "<url>")`, ou insertion/UPDATE direct via
l'outil de gestion HFSQL. La valeur n'est **jamais** codée en dur dans l'application.
---
## 5. Fichiers modèles + manifeste (sur disque)
### 5.1 Arborescence sous `RootPath`
```
Langues/
├── manifest.json
├── es/
│ ├── versFrancais/
│ │ ├── encoder.onnx
│ │ └── vocab.spm
│ └── depuisFrancais/
│ ├── encoder.onnx
│ └── vocab.spm
├── pt/
│ ├── versFrancais/...
│ └── depuisFrancais/...
└── ar/ ...
```
- `{code}` : code ISO de la langue (`es`, `pt`, `ar`, `ru`, `pl`, …).
- `{sens}` : **deux valeurs canoniques** uniquement :
- `versFrancais` → langue secondaire → français (dictée, avant insertion) ;
- `depuisFrancais` → français → langue secondaire (lecture, affichage seul).
### 5.2 Fichier `manifest.json` (clés en camelCase)
```json
{
"packs": [
{
"code": "es",
"libelle": "Espagnol",
"version": "1.0.0",
"fichiers": [
{
"sens": "versFrancais",
"nomFichier": "encoder.onnx",
"tailleOctets": 122000000,
"sha256": "a1b2c3d4...<empreinte hexadécimale SHA-256>"
},
{
"sens": "versFrancais",
"nomFichier": "vocab.spm",
"tailleOctets": 804000,
"sha256": "e5f6a7b8..."
},
{
"sens": "depuisFrancais",
"nomFichier": "encoder.onnx",
"tailleOctets": 122000000,
"sha256": "11121314..."
}
]
}
]
}
```
- `url` n'est **pas** à renseigner : l'API la construit
(`/api/langues/{code}/{sens}/{nomFichier}`).
- `tailleOctets` = taille réelle en octets (progression mobile).
- `sha256` = empreinte SHA-256 **hexadécimale** (intégrité).
### 5.3 Générer les empreintes SHA-256 (PowerShell)
```powershell
Get-FileHash -Algorithm SHA256 .\es\versFrancais\encoder.onnx |
ForEach-Object { $_.Hash.ToLowerInvariant() }
```
### 5.4 Générer le manifeste (script — recommandé)
`tailleOctets` et `sha256` sont des valeurs **mesurées sur les fichiers**, jamais
saisies à la main : une empreinte fausse ou absente ne se voit **que sur le
téléphone**, où le pack est refusé sans qu'aucune erreur ne remonte au serveur.
```powershell
# Écrit <RootPath>\manifest.json depuis les fichiers réellement présents
.\infra\deploy\New-OmikaLanguesManifeste.ps1 -RootPathLangues C:\Omika\Data\Langues
# Contrôle après chaque ajout/remplacement de modèle : code 1 au moindre écart
.\infra\deploy\New-OmikaLanguesManifeste.ps1 -Verifier
```
- La racine par défaut est lue dans `Langues__RootPath` (MACHINE) : le manifeste
est donc écrit **là où l'API le lit** (§6).
- Le script **refuse d'écrire** (code 1) un manifeste vide, et signale en clair
un code de langue invalide, un dossier de sens inconnu, un fichier absent, une
taille ou une empreinte divergente. Ces cas produisent sinon un pack
**silencieusement ignoré** (code invalide) ou **refusé** (empreinte) par le
mobile.
- `-Verifier` ne demande aucun droit administrateur : c'est le contrôle à passer
dans la foulée de toute livraison de modèle.
- `-WhatIf` affiche le manifeste qui serait écrit, sans rien modifier.
> Le format de §5.2 reste la référence ; le script ne fait que le remplir avec
> des valeurs exactes. À noter : le mobile compare `sens` de façon **sensible à
> la casse** (`versFrancais` / `depuisFrancais`) — le script normalise cette
> casse et le signale s'il la corrige.
---
## 6. Configuration serveur (`Langues`)
Règle : en variable d'environnement, `:` devient `__`
(`Langues:RootPath` ⇔ `Langues__RootPath`).
Toute modification d'une variable du service exige un **redémarrage** d'`OmikaLanguesApi`.
| Variable | Défaut | Rôle |
|---|---|---|
| `Langues__RootPath` | `<dossier de l'application>\Langues` | Dossier racine des modèles + manifeste |
| `Langues__ManifestFileName` | `manifest.json` | Nom du fichier manifeste |
| `ASPNETCORE_URLS` | `http://localhost:5000` | Adresse/port d'écoute Kestrel (HTTP loopback derrière Apache) |
| `ASPNETCORE_ENVIRONMENT` | `Production` | Environnement (recommandé) |
> Le défaut de `RootPath` est le **dossier de l'application** (résolu via
> `AppContext.BaseDirectory`), jamais le répertoire de travail courant — un service
> Windows démarre dans `C:\Windows\System32`, ce qui rendrait ce dernier inutilisable.
>
> **Recommandation production** : fixer `Langues__RootPath` sur **`C:\Omika\Data\Langues`**
> (cohérent avec les autres données sous `C:\Omika\Data`). Ne **jamais** placer les
> modèles dans `C:\Omika\Apps\LanguesApi` : ils seraient écrasés à chaque
> redéploiement (voir §7.4).
>
> **Port** : voir **§6bis** — le port n'est **pas** imposé par ce document et ne
> doit **jamais** être repris d'un exemple : il se décide contre le plan de ports
> Apache, une fois, et s'écrit ensuite partout à l'identique.
---
## 6bis. Choix du port loopback (à décider AVANT §7.5bis)
**Aucun port n'est fixé par ce document.** La convention Apache/Kestrel du parc
`omika.applilogik.fr` n'est **pas consignée dans le dépôt** : elle ne peut donc pas
être devinée ici. Elle doit être relevée sur le serveur, puis appliquée.
Ports déjà connus du dépôt — à ne **pas** réutiliser :
| Port | Service | Source |
|---|---|---|
| 5097 | `Omika.Api` (développement) | `docs\start_omika_test.ps1` |
| 5098 | `Omika.Diagnostics.Api` | `DEPLOIEMENT_PRODUCTION_DIAGNOSTICS.md` §4 |
| 5080 | `Omika.Diagnostics.Web` | `DEPLOIEMENT_PRODUCTION_DIAGNOSTICS.md` §4 |
| 5000 | Kestrel par défaut (si `ASPNETCORE_URLS` absente) | — |
### Procédure (à exécuter sur le serveur)
```powershell
# <PORT_LANGUES> : à remplacer par le port candidat avant exécution.
$portLangues = <PORT_LANGUES>
# 1. Le port candidat est-il déjà en écoute ?
Get-NetTCPConnection -LocalPort $portLangues -State Listen -ErrorAction SilentlyContinue |
Select-Object LocalAddress,LocalPort,OwningProcess
# 2. Tranches réservées par Hyper-V / WSL / Docker (elles peuvent être n'importe où)
netsh int ipv4 show excludedportrange protocol=tcp
# 3. Plage éphémère Windows : le port fixe doit rester EN DESSOUS
netsh int ipv4 show dynamicport tcp
# 4. Ports Omika réellement en écoute (vue d'ensemble)
Get-NetTCPConnection -State Listen | Where-Object LocalPort -in 5097,5098,5080,$portLangues
```
### Critères d'acceptation du port
1. **Libre** : aucune écoute sur `127.0.0.1` (étapes 1 et 2).
2. **Hors plage éphémère** : en dessous de la borne basse de l'étape 3
(49152 par défaut) — sinon une socket sortante peut le préempter.
3. **Conforme au plan Apache** : le vhost proxy doit pointer vers **ce** port.
4. **Un seul chiffre dans tout le déploiement** : `ASPNETCORE_URLS`, `ProxyPass` et
`SRV_LANGUES` doivent décrire le même service. Une divergence produit un routage
silencieux vers le mauvais service (404, ou réponse d'un autre service).
> **Sécurité** : Kestrel reste lié à `127.0.0.1` (HTTP), jamais à `0.0.0.0`.
> L'exposition publique se fait **uniquement** en HTTPS par le reverse proxy Apache.
> Le numéro choisi n'a aucune incidence sur ce point : c'est l'adresse de liaison
> qui protège le service, pas le port.
---
## 7. Marche à suivre détaillée (fenêtre de maintenance)
### 7.1 Audit (lecture seule)
```powershell
Get-Service OmikaLanguesApi -ErrorAction SilentlyContinue | Select-Object Name,Status
[Environment]::GetEnvironmentVariable('Langues__RootPath','Machine')
Test-Path 'C:\Omika\Data\Langues\manifest.json'
```
### 7.2 Backup des binaires et des données (AVANT remplacement)
```powershell
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$root = 'C:\Omika\Backups'
# Binaires : seulement s'ils existent déjà
if (Test-Path 'C:\Omika\Apps\LanguesApi') {
Copy-Item 'C:\Omika\Apps\LanguesApi' -Destination "$root\LanguesApi_${stamp}" -Recurse -Force
}
if (Test-Path 'C:\Omika\Apps\OmikaApi') {
Copy-Item 'C:\Omika\Apps\OmikaApi' -Destination "$root\OmikaApi_${stamp}" -Recurse -Force
}
# Données langues : seulement si le dossier existe déjà
if (Test-Path 'C:\Omika\Data\Langues') {
Copy-Item 'C:\Omika\Data\Langues' -Destination "$root\Langues_${stamp}" -Recurse -Force
}
```
### 7.3 Arrêt des services
```powershell
Stop-Service OmikaLanguesApi -Force -ErrorAction SilentlyContinue
Stop-Service OmikaApi -Force
Get-Service OmikaApi,OmikaLanguesApi | Select-Object Name,Status
```
### 7.4 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\LanguesApi-next' 'C:\Omika\Apps\LanguesApi' /E /XF appsettings.json appsettings.Production.json web.config
```
> Ne **jamais** supprimer : `C:\Omika\Config\**`, `C:\Omika\Data\**` (dont
> `C:\Omika\Data\Langues\**`), certificats TLS, variables d'environnement MACHINE.
### 7.5 Création du service `OmikaLanguesApi` (NOUVEAU — premier déploiement)
Le service se crée par le **script dédié** (recommandé : idempotent, vérifie le
compte, le droit de connexion et la disponibilité du port, prend en charge
`-WhatIf` et `-Desinstaller`) :
```powershell
cd C:\Omika\Deploy\Deploy
.\Install-OmikaLanguesApi.ps1 -Port <PORT_LANGUES>
```
> Le script résout le compte, pose les ACL (**lecture seule** sur les modèles),
> vérifie que `<PORT_LANGUES>` est réellement libre, puis crée le service
> `OmikaLanguesApi` (Manual, non démarré). Il n'écrit **aucune** variable
> d'environnement : la configuration reste l'étape §7.5bis ci-dessous.
Équivalent manuel (si vous préférez ne pas utiliser le script) :
```powershell
$cred = Get-Credential -UserName '.\svc_omika_langues' -Message 'Compte service OmikaLanguesApi'
New-Service -Name 'OmikaLanguesApi' -DisplayName 'Omika Langues Api' `
-BinaryPathName '"C:\Omika\Apps\LanguesApi\Omika.Langues.Api.exe"' `
-Credential $cred -StartupType Manual
# ACL sur le dossier des modèles (lecture seule suffit)
icacls 'C:\Omika\Data\Langues' /grant:r "$env:COMPUTERNAME\svc_omika_langues:(OI)(CI)R"
```
> Le compte dédié doit avoir le droit « Ouvrir une session en tant que service »
> (le script le vérifie et avertit si le droit manque — erreur 1069 au démarrage).
>
> **Prérequis binaire** : le service doit embarquer `UseWindowsService()`. Sans
> lui, il démarre puis s'arrête aussitôt avec l'erreur **1053** (« le service n'a
> pas répondu à temps »). C'est le cas depuis l'ajout de
> `Microsoft.Extensions.Hosting.WindowsServices` à `Omika.Langues.Api`.
>
> `infra\deploy\Install-OmikaServices.ps1` couvre désormais ce service, via
> l'option `-InclureLangues` (voir §11).
### 7.5bis Configuration du service (variables d'environnement MACHINE)
**Premier déploiement uniquement** : poser la configuration en variables
d'environnement **MACHINE** (le robocopy §7.4 a exclu `appsettings.json`, donc le
service démarre sans fichier de config). Toute modification exige ensuite un
**redémarrage** du service.
```powershell
# <PORT_LANGUES> : port décidé en §6bis — NE PAS copier une valeur d'exemple.
$portLangues = <PORT_LANGUES>
[Environment]::SetEnvironmentVariable('Langues__RootPath', 'C:\Omika\Data\Langues', 'Machine')
[Environment]::SetEnvironmentVariable('ASPNETCORE_URLS', "http://127.0.0.1:$portLangues", 'Machine')
[Environment]::SetEnvironmentVariable('ASPNETCORE_ENVIRONMENT', 'Production', 'Machine')
# Contrôle
[Environment]::GetEnvironmentVariable('Langues__RootPath', 'Machine')
[Environment]::GetEnvironmentVariable('ASPNETCORE_URLS', 'Machine')
```
> - `Langues__RootPath` : **obligatoire** en production (défaut = dossier de
> l'application, qui serait écrasé au prochain déploiement).
> - `ASPNETCORE_URLS` : port loopback **explicite**, décidé en **§6bis** (ne jamais
> recopier un exemple : le port se déduit du plan Apache, pas de ce document).
> Sans cette variable, Kestrel écoute `http://localhost:5000`.
> - Le reverse proxy Apache expose ensuite ce port en **HTTPS** ; `SRV_LANGUES`
> pointe sur l'URL publique (voir §4 et §7.5ter).
### 7.5ter Exposition HTTPS par le reverse proxy Apache
Le service écoute en HTTP sur `127.0.0.1` : il est inatteignable depuis un
téléphone. L'exposition se fait dans le vhost 443 **existant**.
`SRV_LANGUES` décrit l'**URL de base** seule : le mobile construit ensuite
`{base}/api/langues` puis `{base}/api/langues/{code}/{sens}/{fichier}`
(`LanguagePackService.ConstruireUrl`, qui concatène — un chemin dans la base est
donc conservé). Pour `SRV_LANGUES = https://omika.applilogik.fr/langues`, Apache
doit donc **retirer** le préfixe `/langues` avant de transmettre au service :
```apache
# C:\Apache24\conf\extra\httpd-ssl.conf — dans le <VirtualHost ...:443>
# Service Langues : /langues/api/langues -> http://127.0.0.1:5099/api/langues
ProxyPass /langues/ http://127.0.0.1:5099/
ProxyPassReverse /langues/ http://127.0.0.1:5099/
ProxyPass /langues http://127.0.0.1:5099/
ProxyPassReverse /langues http://127.0.0.1:5099/
```
Puis :
```powershell
& C:\Apache24\bin\httpd.exe -t # doit dire « Syntaxe OK »
Restart-Service <service Apache>
```
Contrôles (le `--resolve` évite le DNS : depuis le serveur, le nom public résout
souvent vers une IP externe qui ne revient pas sur la machine) :
```powershell
$U = 'https://omika.applilogik.fr'
$C = @('--resolve','omika.applilogik.fr:443:127.0.0.1','-k','-s','-o','NUL','-w','%{http_code}\n')
curl.exe @C "$U/langues/api/langues" # 200
curl.exe @C "$U/langues/api/langues/gl/commun/vocab.json" # 200
curl.exe @C "$U/health" # 200 : API métier intacte
$j = (curl.exe -k -s --resolve omika.applilogik.fr:443:127.0.0.1 "$U/langues/api/langues") -join ''
($j | ConvertFrom-Json).packs.Count # 31
```
> - **Le préfixe `/langues` n'est pas décoratif** : la règle existante
> `ProxyPass /api/ http://127.0.0.1:5080/api/` capte `/api/langues` et
> l'envoie à l'API métier, qui ne sert plus les langues (404). Un préfixe
> dédié évite de toucher à cette règle métier et de dépendre de l'ordre des
> `ProxyPass`.
> - Variante vhost dédié (`https://langues.applilogik.fr`) : la règle devient
> `ProxyPass /api/langues http://127.0.0.1:5099/api/langues` (sans préfixe à
> retirer), et `SRV_LANGUES = https://langues.applilogik.fr`.
> - `$U/langues` seul répond `404` du service : il n'y a pas de route `/`, c'est
> normal et sans conséquence (le mobile n'appelle que les chemins `/api/...`).
> - Un `404` sur `$U/langues/api/langues` signifie que la règle n'a pas été
> chargée (fichier non réécrit, ou Apache non rechargé) — ce n'est pas un
> problème de service : `http://127.0.0.1:5099/api/langues` reste alors `200`.
### 7.6 Déploiement des fichiers modèles + manifeste
**Procédure complète : `LIVRAISON_MODELES_LANGUES.md`** (obtention des modèles,
arborescence, dépôt, génération du manifeste, contrôles). Résumé :
```powershell
$root = 'C:\Omika\Data\Langues'
New-Item -ItemType Directory -Force -Path $root | Out-Null
# 1. Copier UNIQUEMENT les dossiers de langue {code}\ (pas de manifest.json
# préparé ailleurs : il dépend des fichiers réellement présents ici)
robocopy 'C:\Omika\Deploy\Langues\es' "$root\es" /E /R:2 /W:2
# 2. GÉNÉRER le manifeste sur le serveur (taille + SHA-256 de chaque fichier)
cd C:\Omika\Deploy\Deploy
.\New-OmikaLanguesManifeste.ps1
# 3. Contrôler
.\New-OmikaLanguesManifeste.ps1 -Verifier
```
> Le manifeste n'est **jamais** recopié d'une autre machine : il contient la
> taille et l'empreinte SHA-256 des fichiers de **ce** disque. Un manifeste
> provenant d'ailleurs fait refuser chaque pack par le mobile, sans aucune
> erreur côté serveur.
>
> Le manifeste et les fichiers modèles sont relus à chaque requête : **aucun
> redémarrage n'est nécessaire**. Le mobile, lui, garde son index mémorisé :
> il faut « Actualiser la liste » dans Paramètres → Langue secondaire.
### 7.7 Création / mise à jour du paramètre `SRV_LANGUES` (HFSQL)
Écrire la ligne `ParametreSysteme` (voir §4) si elle n'existe pas, en pointant vers
l'URL du service Langues. Aucun redémarrage requis : relue à chaque bootstrap mobile.
### 7.8 Redémarrage (consommateurs avant producteurs)
```powershell
Start-Service OmikaLanguesApi # 1. service Langues (d'abord, car appelé par le mobile)
Start-Service OmikaApi # 2. API métier (expose LanguesUrl)
Get-Service OmikaApi,OmikaLanguesApi | Select-Object Name,Status
```
---
## 8. Smoke tests
> **Accès anonyme** : aucun token requis — les réponses sont directes.
> `<PORT_LANGUES>` = le port décidé en **§6bis** et posé en §7.5bis (aucun exemple
> n'est fourni ici : recopier un chiffre d'exemple est précisément l'erreur à éviter).
```powershell
# Racine du service
Invoke-WebRequest 'http://127.0.0.1:<PORT_LANGUES>/' -UseBasicParsing # 200 {status:ok}
# Manifeste
$manifest = Invoke-RestMethod 'http://127.0.0.1:<PORT_LANGUES>/api/langues'
$manifest.packs | Select-Object code,libelle,version
# Téléchargement d'un fichier (valide le streaming + la résolution)
Invoke-WebRequest 'http://127.0.0.1:<PORT_LANGUES>/api/langues/es/versFrancais/encoder.onnx' `
-OutFile "$env:TEMP\encoder.onnx"
Get-Item "$env:TEMP\encoder.onnx" | Select-Object Name,Length # taille = tailleOctets
# Cas d'erreur attendu
Invoke-WebRequest 'http://127.0.0.1:<PORT_LANGUES>/api/langues/es/versFrancais/absent.onnx' -SkipHttpErrorCheck
# → 404
```
Vérifier aussi côté métier : après un bootstrap mobile, `LanguesUrl` vaut bien
l'URL du service Langues (pas `null`).
---
## 9. Mise à jour (ajout de langue / nouvelle version de modèle)
### 9.1 Ajouter une nouvelle langue
1. Déposer les fichiers sous `C:\Omika\Data\Langues\{code}\versFrancais\` et
`{code}\depuisFrancais\`.
2. Ajouter l'objet `pack` dans `manifest.json` (avec `version`, `tailleOctets`, `sha256`).
3. **Aucun redémarrage** : le manifeste est relu à chaque `GET /api/langues`.
### 9.2 Mettre à jour un modèle existant
1. Remplacer le(s) fichier(s) `.onnx` / `.spm` sous `{code}\{sens}\`.
2. Recalculer `tailleOctets` et `sha256` (§5.3).
3. **Incrémenter `version`** du pack (ex. `1.0.0` → `1.0.1`).
> Le mobile compare `version` (et `sha256`) entre son modèle local et le manifeste
> serveur pour décider du re-téléchargement : tant que `version` et `sha256` sont
> inchangés, le mobile considère son modèle à jour.
### 9.3 Retirer une langue
1. Supprimer l'objet `pack` de `manifest.json`.
2. Optionnel : supprimer le dossier `{code}` de `C:\Omika\Data\Langues`.
---
## 10. Rollback
- **Binaires** : restaurer `C:\Omika\Backups\LanguesApi_<stamp>` (arrêt → copie →
démarrage §7.8). Idem `OmikaApi_<stamp>` si besoin.
- **Fichiers langues** : restaurer `C:\Omika\Backups\Langues_<stamp>` (simple copie,
aucune migration à annuler).
- **Paramètre HFSQL** : remettre l'ancienne valeur de `PAS_Valeur` (ou vider
`SRV_LANGUES` → `LanguesUrl = null` → la fonctionnalité est désactivée côté mobile).
---
## 11. Écarts constatés (scripts/documents à mettre à jour)
1. ~~**`infra\deploy\Install-OmikaServices.ps1`** : ne crée **pas** le service
`OmikaLanguesApi` ni ses ACL.~~ **RÉSOLU** : `infra\deploy` contient désormais
**un script par déploiement** (`Install-OmikaApi.ps1`, `Install-OmikaWorker.ps1`,
`Install-OmikaDiagnosticsApi.ps1`, `Install-OmikaDiagnosticsWorker.ps1`,
`Install-OmikaDiagnosticsWeb.ps1`, `Install-OmikaLanguesApi.ps1`) partageant le
module `Omika.Deploy.Common.psm1`. `Install-OmikaServices.ps1` devient un
orchestrateur qui les enchaîne, avec `-InclureLangues` pour le service Langues.
Le service embarque aussi `UseWindowsService()` (sans lui : erreur 1053 au
démarrage).
2. **`infra\deploy\Install-OmikaServerFiles.ps1`** : ne copie **pas** la publication
`LanguesApi` ni le dossier de livraison des modèles — à livrer séparément
(§3, §7.6). La livraison des modèles (obtention, arborescence, manifeste,
contrôles) est décrite pas à pas dans `LIVRAISON_MODELES_LANGUES.md`, et le
manifeste se génère avec `infra\deploy\New-OmikaLanguesManifeste.ps1` (§5.4).
3. **`Omika.slnx`** ne référence **pas** `Omika.Langues.Api` (comme les projets
Diagnostics) : on publie le `.csproj` directement.
4. **Client mobile** : le téléchargement des modèles est **implémenté** côté
application (voir `TRADUCTION_HORS_LIGNE_MOBILE.md`). Le **moteur d'inférence
ONNX Runtime reste à activer** (paquet NuGet optionnel, un symbole de
compilation) : sans lui, l'application télécharge et vérifie les modèles,
mais annonce la traduction comme indisponible au lieu d'échouer.
5. **Plan de ports Apache/Kestrel : non consigné dans le dépôt.** Les seuls ports
connus sont ceux de `DEPLOIEMENT_PRODUCTION_DIAGNOSTICS.md` §4 (5097, 5098,
5080), qui y sont eux-mêmes donnés comme « à définir / à confirmer » en
production. Aucun document ne fixe le port de `OmikaLanguesApi` : il se décide
sur le serveur (§6bis). Tant que ce plan n'existe pas par écrit, **tout port cité
en exemple dans un runbook doit être traité comme un espace réservé à
remplacer**, jamais comme une valeur à recopier.
---
## 12. Récapitulatif des points de configuration
1. **Base HFSQL** : ligne `ParametreSysteme` `PAS_Code = 'SRV_LANGUES'` → URL du service Langues.
2. **Service `OmikaLanguesApi`** : variables MACHINE `Langues__RootPath` → `C:\Omika\Data\Langues` **et** `ASPNETCORE_URLS` → `http://127.0.0.1:<PORT_LANGUES>` (port décidé en **§6bis**, posé §7.5bis).
3. **Fichiers** : arborescence `{code}/{sens}/{fichier}` sous `RootPath`, puis
`manifest.json` **généré** par `New-OmikaLanguesManifeste.ps1` (§5.4) — jamais
saisi à la main.
4. **Binaires** : `Omika.Langues.Api` (nouveau) + `Omika.Api` (republication).
5. **Accès** : anonyme — aucun token, aucun accès HFSQL.
6. **Mise à jour** : ajouter/remplacer les fichiers puis regénérer le manifeste ;
**aucun redémarrage** du service n'est requis (il relit le manifeste à chaque
appel de `GET /api/langues`). Le mobile, lui, garde son index mémorisé : il
faut un rafraîchissement de la liste des langues pour voir le changement.