Reparer la publication de l API (index.html cassé)

Le publish de MaBibli.Api recopiait index.html tel quel, placeholders
compris, et l application publiee restait blanche.

Cause : dans le SDK .NET 10, la cible de publication
GenerateHtmlAssetPlaceholdersPublishStaticWebAssets lit la liste des
fichiers HTML dans @(_HtmlStaticWebAssets), item alimente uniquement par
la cible de *build*. Quand c est l API qui demande au client ses assets de
publication, la cible de build n a pas tourne : la liste est vide, aucun
asset HTML calcule n entre dans le manifeste, et c est le fichier source
qui est recopie par ComputeResolvedFilesToPublishList.

Correction : supprimer le besoin de reecriture plutot que la reparer.
WasmFingerprintAssets=false et pas de OverrideHtmlAssetPlaceholders (c est
lui qui, a true, forcait l empreinte de blazor.webassembly.js) ; index.html
reference des noms stables, sans import map ni preload placeholder. Le
contournement « ?m=1 » de l import du module scanner devient inutile.

Verifie : publish self-contained lance, scripts references en 200,
application chargee dans un navigateur (catalogue affiche, module JS du
scanner charge, aucun 404).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mathieu
2026-08-18 02:42:58 +02:00
co-authored by Claude Opus 5
parent ab878b4d3c
commit 9cb3ca535d
4 changed files with 36 additions and 27 deletions
+22 -16
View File
@@ -107,7 +107,7 @@ de la fonctionnalité.
- `RGBLuminanceSource` accepte directement le buffer RGBA du canvas (`BitmapFormat.RGBA32`) — **aucune bibliothèque d'image nécessaire** (pas de SkiaSharp ni ImageSharp).
- Le scan caméra exige **HTTPS** (garanti par YunoHost en prod ; en dev, `localhost` est considéré comme sûr). Corollaire vérifié : tester le scan depuis un téléphone en pointant l'IP locale du PC (`http://192.168.x.x`) **échouera toujours** — ce n'est pas un contexte sécurisé. Le composant détecte ce cas et le dit explicitement.
- **Une `DOMException` perd son `name` en traversant le pont JS→C#** : `getUserMedia` refusé remonte en C# sous la forme « Permission denied undefined », sans `NotAllowedError`. Or c'est ce nom qui distingue « permission refusée » de « pas de caméra » de « caméra occupée ». Le JS doit donc **attraper l'erreur et renvoyer un code de statut** ; parser le message côté C# ne marche pas.
- **L'import du module JS doit porter une chaîne de requête** (`./js/scanner-camera.js?m=1`). En développement, l'import map généré par Blazor réécrit le chemin vers un nom empreinté que l'API hôte ne sert pas (elle utilise `UseStaticFiles`, qui ignore les points d'entrée empreintés) : l'import part en 404 et le scan ne démarre jamais. La chaîne de requête empêche cette réécriture ; en publication rien n'est réécrit pour ce fichier, donc le comportement est identique.
- **L'import du module JS se fait sur un chemin nu** (`./js/scanner-camera.js`). Il a longtemps porté une chaîne de requête `?m=1` : l'import map généré par Blazor réécrivait le chemin vers un nom empreinté que l'API hôte ne servait pas (elle utilise `UseStaticFiles`, qui ignore les points d'entrée empreintés), et l'import partait en 404. Depuis que les empreintes WASM sont désactivées (voir « Empreintes WASM désactivées »), **il n'y a plus d'import map du tout** et le contournement a été retiré. Vérifié en développement et sur le publish self-contained : le module se charge dans les deux cas.
### Squelette validé
@@ -259,29 +259,35 @@ dotnet publish MaBibli.Api -c Release -r linux-x64 --self-contained
Le self-contained embarque le runtime .NET dans le binaire : **aucun `dotnet-runtime` à installer** côté serveur, pas de conflit de versions. C'est le modèle de `radarr_ynh`.
### ⚠️ BLOQUANT CONNU : le publish de l'API produit un `index.html` cassé
### Empreintes WASM désactivées — pourquoi (ex-bloquant du publish, résolu le 2026-08-18)
**Constaté le 2026-08-18, non résolu. À corriger impérativement avant tout packaging YunoHost.**
**Ne pas réactiver `OverrideHtmlAssetPlaceholders` ni les empreintes WASM sans relire ce qui suit.**
`dotnet publish MaBibli.Api -c Release` produit un `wwwroot/index.html` dont les placeholders ne sont **pas** substitués :
Le `index.html` de `MaBibli.Client` ne contient **ni import map, ni placeholder `#[.{fingerprint}]`** : il référence directement `_framework/blazor.webassembly.js`. C'est délibéré, et `MaBibli.Client.csproj` porte `<WasmFingerprintAssets>false</WasmFingerprintAssets>` pour que les fichiers servis portent bien ces noms stables.
```html
<script type="importmap"></script> <!-- vide -->
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"> <!-- littéral -->
```
#### Le symptôme d'origine
Or seuls les noms **empreintés** existent sur disque (`blazor.webassembly.958z1vx7fr.js`). Résultat vérifié en exécutant le binaire publié : la racine répond **200**, mais le script de démarrage et `_framework/dotnet.js` renvoient **404**. L'application publiée affiche une **page blanche** — elle ne démarre pas.
`dotnet publish MaBibli.Api -c Release` produisait un `wwwroot/index.html` aux placeholders **non substitués** (`<script type="importmap"></script>` vide, `src="_framework/blazor.webassembly#[.{fingerprint}].js"` littéral), alors que seuls les noms empreintés existaient sur disque. La racine répondait **200**, le script de démarrage **404**, l'application publiée restait **blanche**. `dotnet publish MaBibli.Client` seul, lui, produisait un `index.html` correct.
`dotnet publish MaBibli.Client` seul produit au contraire un `index.html` correct : le défaut vient de l'hébergement du client par l'API, qui embarque le `wwwroot` de *build* du client plutôt que son `wwwroot` *publié*.
#### La cause réelle (SDK .NET 10.0.300)
**Piège de diagnostic** : vérifier que `/` renvoie 200 ne prouve rien — c'est ce qui a fait passer le défaut inaperçu à la phase 1. Il faut vérifier les scripts que `index.html` référence réellement, ou charger la page dans un navigateur.
Elle est dans `Microsoft.NET.Sdk.StaticWebAssets.HtmlAssetPlaceholders.targets`. La cible de **publication** `GenerateHtmlAssetPlaceholdersPublishStaticWebAssets` reçoit la liste des fichiers HTML à réécrire via `HtmlFiles="@(_HtmlStaticWebAssets)"` — or cet item n'est **jamais alimenté par le chemin de publication** : il l'est uniquement par la cible de **build** `ResolveHtmlAssetPlaceholdersBuildConfiguration`.
Pistes déjà essayées **sans succès** (inutile de les retenter à l'identique) :
- `<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>` sur `MaBibli.Api` ;
- `<WasmFingerprintAssets>false</WasmFingerprintAssets>` sur le client — donne bien un `dotnet.js` stable, mais `blazor.webassembly.js` reste empreinté car il provient des assets du package framework ;
- `<StaticWebAssetsFingerprintContent>false</StaticWebAssetsFingerprintContent>` sur les deux projets.
Quand on publie le client seul, le build a tourné dans la même instance MSBuild, l'item est rempli, la réécriture a lieu. Quand c'est l'**API** qui publie, elle demande au projet client ses assets de publication dans une instance où la cible de build n'a **pas** tourné : `@(_HtmlStaticWebAssets)` est vide, la tâche de réécriture ne produit **aucun fichier**, aucun asset HTML calculé n'entre dans `staticwebassets.publish.json` — et c'est alors le fichier source `MaBibli.Client/wwwroot/index.html` qui est recopié tel quel par `ComputeResolvedFilesToPublishList`, placeholders compris. Vérifié : le manifeste de publication de l'API ne contient **aucun** asset `.html`.
Le développement n'est pas affecté : `dotnet run --project MaBibli.Api` fonctionne normalement.
C'est pour cela que `<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>` sur l'API ne changeait rien : la cible s'exécutait bien, mais sur une liste vide.
#### La correction retenue
Supprimer le besoin de réécriture plutôt que réparer la réécriture : sans import map ni placeholder, il n'y a plus rien à substituer, et le fichier source recopié tel quel est déjà le bon.
- `<WasmFingerprintAssets>false</WasmFingerprintAssets>` sur le client ;
- **pas** de `OverrideHtmlAssetPlaceholders` (c'est lui qui, à `true`, force `BlazorFingerprintBlazorJs` et empreinte `blazor.webassembly.js` — la tentative précédente n'avait échoué que parce que les deux propriétés étaient combinées) ;
- `index.html` : `<script src="_framework/blazor.webassembly.js">`, sans `<script type="importmap">` ni `<link rel="preload" id="webassembly">` (deux balises que seule la machinerie de placeholders sait remplir).
Le cache-busting reste assuré par le service worker, qui compare les empreintes de `service-worker-assets.js`.
**Piège de diagnostic à conserver** : vérifier que `/` renvoie 200 ne prouve rien — c'est ce qui a fait passer le défaut inaperçu à la phase 1. Il faut vérifier les scripts que `index.html` référence **réellement**, ou charger la page dans un navigateur.
### Deux dépôts distincts
@@ -67,13 +67,11 @@
private enum Etat { Demarrage, Actif, Erreur }
// Le « ?m=1 » n'est pas cosmétique. En développement, l'import map généré par Blazor
// fait pointer « ./js/scanner-camera.js » vers un nom empreinté que l'API hôte ne sert
// pas (elle utilise UseStaticFiles, qui ignore les points d'entrée empreintés) : l'import
// échoue en 404 et le scan ne démarre jamais. Une chaîne de requête empêche l'import map
// de réécrire le chemin ; en publication, aucune réécriture n'est générée pour ce fichier,
// donc le comportement est identique. Vérifié dans les deux modes.
private const string CheminModule = "./js/scanner-camera.js?m=1";
// Chemin nu : il n'y a plus d'import map depuis que les empreintes WASM sont désactivées
// (voir MaBibli.Client.csproj), donc plus de réécriture vers un nom empreinté que l'hôte
// ne servirait pas. Le contournement « ?m=1 » qui neutralisait cette réécriture n'a plus
// lieu d'être. Vérifié en développement et en publication.
private const string CheminModule = "./js/scanner-camera.js";
// ~12 images/s : bien assez pour scanner, et deux fois moins de travail que 25 fps.
private const int IntervalleMs = 80;
+8 -1
View File
@@ -4,7 +4,14 @@
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
<!--
Empreintes WASM désactivées, et donc pas d'import map ni de placeholders dans index.html.
Raison : quand c'est MaBibli.Api qui publie ce projet, le SDK .NET 10 n'exécute jamais la
réécriture des placeholders et recopie index.html brut (voir CLAUDE.md). Des noms de
fichiers stables suppriment le problème à la racine. Le cache-busting reste assuré par le
service worker, qui compare les empreintes de service-worker-assets.js.
-->
<WasmFingerprintAssets>false</WasmFingerprintAssets>
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
</PropertyGroup>
+1 -3
View File
@@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MaBibli</title>
<base href="/" />
<link rel="preload" id="webassembly" />
<link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="css/app.css" />
<link rel="icon" type="image/png" href="favicon.png" />
@@ -14,7 +13,6 @@
<link href="manifest.webmanifest" rel="manifest" />
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
<script type="importmap"></script>
</head>
<body>
@@ -31,7 +29,7 @@
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
<script src="_framework/blazor.webassembly.js"></script>
<script>navigator.serviceWorker.register('service-worker.js', { updateViaCache: 'none' });</script>
</body>