Skip to content

Client de bureau ​

Application WPF qui encapsule l'interface web dans une fenêtre native Windows via WebView2. Elle sert le frontend depuis le disque, indépendamment du service, et dirige les appels API et le temps réel vers le backend en boucle locale.

TechnologieWPF .NET 9 (net9.0-windows), WebView2, WinForms pour l'icône de notification
Point d'entréeApp.xaml.cs puis MainWindow.xaml.cs
Nom d'assemblySuperviseur
PaquetsMicrosoft.Web.WebView2, System.Management
Référence projetdotnet/shared — pour l'empreinte matérielle

Structure ​

Fichier ou dossierRôle
App.xaml.csMutex d'instance unique, contrôle de licence, choix de la fenêtre à ouvrir
MainWindow.xaml / .csFenêtre principale, WebView2, icône de notification, fermeture protégée
VirtualHost.csMappage de l'hôte virtuel et injection de l'adresse du backend
LoadingWindow.xamlAnneau de chargement, dans une fenêtre à part
LicenseWindow.xamlActivation du poste
HelpWindow.xamlNotices, servies par le même hôte virtuel
PasswordVerifyDialog.xamlSaisie du mot de passe administrateur à la fermeture
LanguagePreference.cs, Loc.csLangue des fenêtres natives
Configuration/AppConfig.csChargement de app.config.json
Licensing/LicenseChecker, LicenseMessages, SecurityVerifier

Architecture ​

LoadingWindow (anneau neutre, fenêtre propre)  ── estompée au signal « app-ready »
MainWindow
├── WebView2 ── hôte virtuel https://superviseur.local/ (wwwroot sur disque)
│   └── SPA Vue ──postMessage──► OnWebViewMessage → NotifyIcon / retrait de l'indicateur
│                 └── API + SignalR ──► https://localhost:5000 (backend, boucle locale)
├── NotifyIcon (barre des tâches)
└── OnClosing → mot de passe administrateur (POST /api/auth/verify-admin-password)

Mécanismes non évidents ​

Le front et le backend sont deux origines distinctes ​

ÉlémentValeur
Hôte virtuelsuperviseur.local, mappé sur wwwroot/ à côté de l'exécutable
Page d'entréehttps://superviseur.local/index.html
Adresse du backendInjectée dans la page comme window.__SUPERVISEUR_API_BASE__

Conséquence : la page de connexion s'affiche même si le service n'est pas démarré. Corollaire pour le frontend — une URL /api ou /uploads écrite en relatif viserait superviseur.local et échouerait sans message.

Toute fenêtre du client qui affiche l'interface doit appeler VirtualHost.ApplyAsync. Sans le mappage, superviseur.local n'est résolu par rien et la fenêtre affiche une erreur réseau ; sans le repli sur index.html, une route sans fichier correspondant — /notice, /login après un rafraîchissement — donne « fichier introuvable », le mappage WebView2 n'ayant pas d'index de répertoire.

Le certificat de bouclage est accepté dans WebView2, pas dans Windows ​

En Release, le client s'abonne à ServerCertificateErrorDetected et accepte le certificat uniquement lorsque l'URI est de bouclage. Le certificat auto-signé du service n'a donc pas à être placé dans le magasin racine de la machine.

L'indicateur de chargement est une fenêtre à part ​

Le WebView2 rend son HWND au-dessus de tout élément de la même fenêtre : un anneau posé dans MainWindow serait masqué. LoadingWindow est donc une fenêtre distincte, estompée au signal app-ready envoyé par le front. Aucune identité visuelle n'y est posée : l'habillage est celui que le front applique sur son propre écran de démarrage.

DEBUG et Release ne visent pas les mêmes adresses ​

CompilationInterface chargée depuisAPI visée
DEBUGhttp://localhost:5173 (serveur Vite)http://localhost:{DefaultPort}
Releasehttps://superviseur.local/index.html (disque)https://localhost:{DefaultPort}

La variable d'environnement SUPERVISEUR_WEB_URL, si elle est définie, passe devant. Les outils de développement et le menu contextuel de WebView2 ne sont activés qu'en DEBUG.

La langue des fenêtres natives est mémorisée par utilisateur ​

LanguagePreference écrit %APPDATA%\{application}\ui-language : aucun droit administrateur, à la différence du répertoire de données commun du service. Lecture comme écriture sont tolérantes à l'échec — la langue ne peut jamais empêcher l'application de démarrer.

app.config.json est copié auprès de l'exécutable ​

Le client est installé dans {app}\client, hors de portée du app.config.json déposé auprès du backend. Sans cette copie, AppConfig.Load() retombe sur ses valeurs par défaut — version, URL et port erronés.

Le pont vers le front est unidirectionnel côté page ​

La SPA envoie ses messages par window.chrome.webview.postMessage() : notifications système, signal de disponibilité de l'interface. Le client les traite dans OnWebViewMessage et déclenche les NotifyIcon (infobulles) et FlashWindowEx (clignotement de la barre des tâches), ce qui rend les alertes visibles même quand la fenêtre n'a pas le focus.

Comportement de la fenêtre ​

ActionRésultat
MinimiserAutorisé
RedimensionnerAutorisé — ResizeMode="CanResize", taille minimale 900 × 600
Fermer (×, Alt+F4)Mot de passe administrateur requis ; autorisé sans mot de passe si le backend est injoignable
Double-clic sur l'icône de notificationRestaure la fenêtre au premier plan
Seconde instanceRefusée par un mutex nommé {AppName}-Mutex

Développement ​

bash
dotnet run --project dotnet/client

Le serveur Vite doit tourner sur :5173 et le backend de développement sur :5264.

Contraintes actives ​

ContrainteConséquence si elle est ignorée
Le wwwroot du frontend doit être déposé à côté de l'exécutableVirtualHost.IsAvailable est faux et la fenêtre n'a rien à servir
Toute fenêtre affichant l'interface appelle VirtualHost.ApplyAsyncErreur réseau, ou « fichier introuvable » sur une route sans fichier
Le runtime WebView2 doit être présentPréinstallé sur Windows 10 1803+ et Windows 11 ; l'installateur le pose sinon
L'acceptation du certificat est strictement limitée au bouclageÉlargir la condition ouvrirait le client à n'importe quel certificat invalide
Le projet cible net9.0-windowsIl ne se compile pas hors Windows

Build de production ​

Piloté par deployment/build-all.ps1 : publication autonome en fichier unique, aucun runtime requis sur la machine cible. Le wwwroot du frontend est déposé à la fois auprès du backend et auprès du client.

Tests ​

dotnet/tests/client — voir son README.

Documentation Technique