# Changelog ## [1.0.8-rc25] - unstable ### Added * [Yavsc.Api] Notification du performer à la création d'un rendez-vous depuis PostIt, selon ses préférences de contact, sans supprimer l'envoi Org. * [Yavsc.Api][Yavsc.Server] Notification du performer à la signature client d'un devis (JSON ou image), enregistrée pour l'accueil PostIt et transmise par e-mail et SignalR. * [Yavsc.Api] Nouveau point d'entrée `GET /api/v1/notifications/me` pour exposer les notifications publiques et ciblées à PostIt sur la Home page ; les notifications d'administration restent filtrées. * [PostIt] Affichage des notifications sur la page d'accueil, avec actualisation, indicateur de chargement et message d'erreur. Les notifications publiques sont accessibles sans connexion et la liste est rechargée lors d'un changement de session. ### Changed * [PostIt] Insertion des liens de fichiers dès leur ajout au brouillon ; les liens provisoires des nouveaux billets sont résolus au premier enregistrement. Save conserve l'édition ouverte et est mis en valeur lorsque le titre, le corps ou les pièces jointes sont modifiés. * [Projet] Instructions Copilot persistantes : mise à jour du changelog par défaut à chaque commit, avec justification explicite des exceptions. ### Fixed * [PostIt.Tests] Les tests du bandeau de session utilisent le bandeau réellement monté dans la fenêtre headless, au lieu d'un contrôle détaché. Les fenêtres sont fermées après chaque test et les quatre boutons de navigation sont vérifiés. * [PostIt] Les liens Markdown des pièces jointes utilisent désormais le même filtre de nom que l'upload serveur, avant l'encodage URL. Le libellé conserve le nom original, notamment ses accents. * [Yavsc.Abstract][Yavsc.Blogs] Encodage des caractères non supportés dans les noms de fichiers téléversés avec `_` au lieu de `#`, pour éviter les fragments d'URL et permettre le téléchargement via les fichiers statiques, notamment pour les noms accentués. * [Yavsc.Api][Yavsc.Blogs] Les appels API et la bande passante sont désormais enregistrés côté serveur pour les requêtes authentifiées : le middleware de télémétrie est maintenant branché après l'authentification, ce qui permet de comptabiliser `ApiCalls` et `BandwidthMb` au lieu d'enregistrer des zéros. ## [1.0.8-rc24] - unstable ### Added * [PostIt] Reconnection automatique aux salons de discussion au reconnect ### Changed * [PostIt] La liste des billets et l'éditeur Markdown se partagent l'espace disponible et défilent séparément ; un séparateur permet de redimensionner les deux zones. ### Fixed * [PostIt] Défilement des articles longs en édition et en aperçu : suppression de la limite de hauteur qui masquait le contenu Markdown et du défilement imbriqué autour d'AvaloniaEdit. Six tests headless couvrent les barres automatiques, le redimensionnement et le défilement indépendant de la liste. ## [1.0.8-rc23] - unstable ### Added * [PostIt] Badge « Publié » à côté du titre des billets publiés dans la liste, mis à jour lors de la publication ou du retour en brouillon. * [PostIt] Gestion native du compte inspirée de `/Manage` : modification du nom d'utilisateur, du nom complet et de l'adresse, téléversement d'avatar, confirmation e-mail, préférence de courriel mensuel, création ou changement de mot de passe et activation/désactivation de l'authentification à deux facteurs. * [PostIt][Yavsc.Api] Sélection d'un agenda Google, gestion séparée des comptes bancaires, dissociation des connexions externes et suppression du compte avec confirmation du nom d'utilisateur. * [PostIt] Configuration du profil professionnel et des activités, navigation vers les cercles et les publications depuis le compte. * [PostIt][Yavsc.Api] Administration des utilisateurs avec recherche dans les métadonnées du profil et consultation de la consommation de ressources. * [Yavsc.Server][Yavsc.Api][Yavsc.Blogs] Suivi de la consommation de ressources, API de synthèse et migration `ResourceUsageRecords`. * [Tests] Couverture des parcours profil et professionnel, de l'API de consommation de ressources et du renouvellement des jetons : rotation à usage unique, appels simultanés, réponses 401 concurrentes et rejet d'un jeton de renouvellement révoqué. ### Changed * [PostIt] Réorganisation de la page de compte en sections de profil, sécurité et préférences ; actions de modification désactivées tant que le profil n'est pas chargé ou qu'une opération est en cours. * [PostIt] Utilisation de `PlaceholderText` à la place de `Watermark` dans les pages de profil et de configuration professionnelle. * [Yavsc.Api] `GET account/me` renvoie le DTO partagé attendu par PostIt, avec l'état de sécurité, les connexions externes, les comptes bancaires et les compteurs du compte. ### Fixed * [Yavsc.Api] Enregistrement des services ASP.NET Identity et des stores EF nécessaires à `UserManager` : leur absence empêchait l'activation du contrôleur de compte et provoquait une 500. * [Yavsc.Api] Chargement du profil : réponse adaptée lorsqu'un jeton ne contient pas d'identifiant utilisateur ou que le compte n'existe plus, au lieu d'une exception serveur. * [PostIt] Synchronisation de tous les chemins de renouvellement des jetons, y compris après une 401 et au démarrage : les appels concurrents ne réutilisent plus le même `refresh_token`. Un rejet `invalid_grant` efface la session et invite à se reconnecter. * [PostIt] Effacement des informations obsolètes après un échec de rechargement du profil et normalisation des valeurs de calendrier lors de la sauvegarde. * [PostIt][Yavsc.Api] Préservation de l'identité du profil professionnel lors de son enregistrement. * [Yavsc.Api] Les coordonnées bancaires ne sont plus modifiées à partir d'un résumé agrégé : opérations dédiées avec contrôle de propriété et validation de longueur des champs. ### Known limitations * [PostIt] L'ajout d'un nouveau fournisseur OAuth et la gestion des crédits ne sont pas encore disponibles dans l'interface native. ## [1.0.8-rc22] - unstable ### Fixed User experience login into PostIt ## [1.0.8-rc21] - unstable ### Fixed User experience editing the blog article. ## [1.0.8-rc20] - unstable Chat SignalR fonctionnel de bout en bout (connexion, salons multiples, envoi/réception de messages), enregistreur audio et localisation dans PostIt, intégration Live.Avalonia démarrée, et reverse-proxy nginx apprêté pour WebSocket. ### Added * [PostIt] Interface de chat : page `ChatPage` (connexion au hub, saisie du nom de salon, liste des messages et des membres présents), entrée depuis la page d'accueil. * [PostIt] Envoi de messages dans un salon joint : DTO `ChatMessage` (Name/Room/Message) pour l'événement `ReceiveMessage`, saisie avec envoi sur `Enter`, commande inactive tant qu'aucune salle n'est en focus (le hub refuse l'envoi hors salon, `LabNoJoinNoSend`). * [PostIt] Modèle multi-salles côté client : `JoinedRooms`, `SelectedRoom` (salle en focus), messages et utilisateurs indexés par salle (`FocusedMessages`/`FocusedUsers`), ComboBox de choix de la salle active ; un `Join` sur une salle déjà jointe ne fait que changer le focus. * [PostIt] Enregistreur audio (`AudioRecorder`, interface `IPCMProcessor`) et ressources de localisation (`Resources.{resx,en-EN,pt-PT}`). * [PostIt][WIP] Début d'intégration Live.Avalonia (preview XAML à chaud ; encore KO). * [contrib] Bloc `location /chathub` dans `template.vhost` : upgrade WebSocket (`Upgrade`/`Connection`), `proxy_read_timeout 3600s` et `proxy_buffering off` — sans quoi la connexion SignalR tombait au premier silence ou l'upgrade échouait derrière nginx. ### Changed * [PostIt] `StatusNotice` déplacé de `Models` vers `ViewModels` et enrichi d'une commande d'action optionnelle (`CheckCommand` + `CheckCommandLabel`, interface `IWithLabel`) : le bandeau de statut propose désormais l'action contextuelle (Connecter / Déconnecter / Reconnecter / Vérifier la connexion). * [PostIt] `ChatViewModel` suit le cycle de vie complet de la connexion : handlers `Reconnecting` / `Reconnected` / `Closed` de `WithAutomaticReconnect`, pour refléter l'état réel au lieu d'un `IsConnected` optimiste. * [Yavsc.Api] `AddSignalR().AddJsonProtocol(IncludeFields = true)` : `ChatRoomInfo` n'expose que des champs publics, que System.Text.Json ne sérialise pas par défaut — le client recevait des objets vides (`Name` null, `Users` vide) et la liste des salons joints n'était jamais alimentée. * [Yavsc.Server] `IConnexionManager.OnConnected` prend le `userName` en paramètre (résolu depuis les claims dans `ChatHub`) au lieu de le relire depuis un dictionnaire jamais alimenté. * [PostIt.Android] `network_security_config.xml` déplacé sous `Resources/xml/` (chemin canonique Android). ### Fixed * [Yavsc.Server] `KeyNotFoundException` dans `HubConnectionManager.OnConnected` à chaque connexion SignalR : `ChatUserNames` (et `ChatCxIds`) n'étaient jamais alimentés — relique d'un `SetUserName` disparu lors d'une refactorisation. L'exception dans `OnConnectedAsync` faisait fermer la connexion par le hub, et PostIt affichait « Déconnecté (The server closed the connection…) » immédiatement après la connexion. * [PostIt] URL du hub SignalR : retrait du préfixe `/api/v1` de `ApiUrl` avant d'apposer `Constants.ChatHubPath` (le hub est mappé à la racine du service, pas sous le préfixe versionné). * [PostIt] La `StatusBar` de la page chat référençait `Grid.Row="4"` sur une grille à 3 rangées (rangée hors limites, bandeau invisible). ## [1.0.8-rc19] - unstable [PostIt] Blogs UX ### Fixed La saisie du billet est maintenant plutôt possible, en petit écran. ## [1.0.8-rc18] - unstable * [PostIt] Page "About" avec lines vers le dépôt et les issues Forgejo * Accès aux fichiers personnels PostIt rendu fonctionnel de bout en bout (arbre de stockage indexé par le login, pas par le `sub`), boutons d'action des listes réparés (binding `$parent`), revendication `name` enfin présente dans les access tokens (branchement du `ProfileService`), et négociation HTTP/3 côté client PostIt. ### Added * [PostIt] Négociation HTTP/3 (QUIC) pour les appels API Yavsc : `DefaultRequestVersion = 3.0`, `DefaultVersionPolicy = RequestVersionOrLower` sur le `HttpClient` partagé de `YavscApiClient`, avec repli h2/h1.1 où QUIC n'est pas disponible (WASM fetch, Android/ desktop sans libmsquic). * [Tests] `MyFilesPageHeadlessTests` : monte `MyFilesPage` via `NavigationPage` + `Window.Show()` et vérifie que chaque bouton de ligne (Télécharger / Supprimer) a sa `Command` liée, est armé (`CanExecute`) et déclenche l'appel API au clic. * [Tests] `Remoting.PasswordToken_carries_name_claim_equal_to_login` : mint un vrai JWT via le token endpoint IdentityServer (password grant, `openid profile`) et asserte que l'access token porte `name`=login et que `GetUserName()` résout vers ce login. * [Tests] `FileSystemApiTests.Get_fs_lists_files_stored_under_the_user_login` : découple `sub` et login, téléverse sous le login, et asserte que `GET /api/v1/fs` retourne le fichier (et que le répertoire nommé d'après le `sub` n'existe pas). ### Changed * [Yavsc.Org] Branchement du `ProfileService` custom (`AddProfileService()` après `AddAspNetIdentity()`). La classe était du code mort : IdentityServer8 retombait sur son profile service par défaut, qui n'émet que les claims listés dans les `UserClaims` des API resources. `name` n'en faisait jamais partie, donc tout access token était sans `name` et `GetUserName()` résolvait à `null` sur les hosts ressources (Blogs/Api) — cause du 500 sur l'upload MyFiles et du `User.Identity.Name` injoignable. Le service émet désormais `name`=`user.UserName`, `email` et les rôles pour tout client dont les `AllowedScopes` contiennent `profile`, indépendamment du caller. * [Yavsc.Server] `ServiceExtensions` : `NameClaimType = JwtClaimTypes.Name`, `MapInboundClaims = false` (conserver les noms courts de claims tels qu'émis). * [Yavsc.Blogs] `FileSystemHelpers.EnsureDestinationDirectory` utilise `GetUserName()` (claim `name`) au lieu de `User.Identity.Name` pour la racine de stockage par utilisateur. * [Tests] `WebServerFixture` : `AccessTokenType = 0` (Jwt dans IS8 ; 1 = Reference, l'inverse de Duende) ; `openid`+`profile` sur le client de test ; réensemencement des identity resources openid/profile dans `ReseedAuthTestData` (un `ResetAndMigrateDatabase` vide le store in-memory partagé et les laissait absentes → `invalid_scope`). * [Tests] `TestTokenIssuer.Issue` gagne un paramètre `name` optionnel (défaut = subject) pour refléter la forme production où `sub` (GUID) ≠ `name` (login). * [Tests] `BillingServiceTests` : `[Collection("Yavsc Server")]` pour sérialiser avec le build d'hôte qui mute les registres statiques `BillingService`/`Config` (race parallèle pré-existante). ### Fixed * [Yavsc.Blogs] La liste `GET /api/v1/fs` cherchait sous `{Blog}/{sub-GUID}` (`User.GetUserId()`), alors que l'arbre de stockage perso est indexé par le login partout ailleurs (`EnsureDestinationDirectory` écrit sous `{Blog}/{name-claim}`, le endpoint de téléchargement et les avatars utilisent `user.UserName`). Une fois l'upload corrigé, les fichiers atterrissaient sous `{Blog}/{login}` tandis que la liste cherchait sous `{Blog}/{GUID}` — un arbre vide — d'où la page PostIt « Mes fichiers » qui ne listait rien. `GetDir` résout désormais l'utilisateur en base et liste sous `user.UserName`. `UploadedFile.OwnerId` reste `sub`, donc `EnrichWithFileIds` interpole toujours par `uid`. * [PostIt] Boutons d'action des lignes de liste grisés et inactifs au clic (Télécharger / Supprimer / Joindre sur MyFiles ; download/detach sur les listes de pièces jointes de `BillingQueryDetailsPage` et `EstimateEditionPage`). Le binding `$parent[vm:XxxViewModel].Cmd` cherchait un ancêtre CONTRÔLE de type `XxxViewModel` — un viewmodel, pas un contrôle — donc résolvait à `null` et le `Button` restait désactivé (grisé) et inerte au clic. Corrigé en `$parent[ContentPage].((vm:XxxViewModel)DataContext).Cmd` (syntaxe déjà en vigueur dans `CirclesPage` / `EstimateListPage`). * [PostIt] Les messages de la barre de statut (`StatusBar`) n'étaient pas sélectionnables/copiables : le texte était un `TextBlock` (non sélectionnable dans Avalonia). Remplacé par `SelectableTextBlock`. * [Yavsc.Blogs] La suppression d'un fichier (`DELETE api/v1/fs/{name}`) renvoyait un 40x alors que le fichier était bien supprimé du disque. `DeleteUserDirOrFile` (et `DeleteUserFile`) lisaient `FileInfo.Length` **après** `fi.Delete()` ; or `FileInfo.Length` lève `FileNotFoundException` une fois le fichier disparu — le `catch` du contrôleur transformait cette exception en 400, d'où le message perçu « le fichier n'existe pas sur le serveur » tandis que la suppression avait réussi. La taille est désormais capturée avant la suppression. * PostIt default settings ## [1.0.8-rc17] - unstable Fichiers utilisateur et pièces jointes sur les devis/demandes (PostIt + API), système de génération `contrib` (vhosts nginx HTTP/3 edge + appsettings jq + QUIC logiciel Kestrel), et correction du bug de production où `IsPublished` disparaissait du payload de liste du blog API. ### Added * [PostIt] Espace de stockage personnel (« MyFiles ») : page de gestion des fichiers de l'utilisateur, entrée depuis la page d'accueil. * [PostIt] Pièces jointes aux devis et aux demandes : attachement depuis le détail de demande et la page d'édition de devis. * [Yavsc.Api] Endpoints d'attachement de fichiers sur les devis (`EstimateApiController`) et les demandes front-office (`FrontOfficeApiController`). * [Yavsc.Api.Client] `UserFilesApiClient` + DTOs associés ; méthodes d'attachement sur `EstimateApiClient` et `FrontOfficeApiClient`. * [Yavsc.Server] Modèles `EstimateAttachedFile`, `QueryAttachedFile`, `BlogAttachedFile`, `AttachFileRequest` ; migration EF Core `UserFileAttachments`. * [contrib] Système de génération `Makefile` : vhosts nginx HTTP/3 edge (`template.vhost`, `QUIC_OPTS`), appsettings générés via jq (`template.appsettings-{org,api,blogs}.json`, échappement JSON-safe des secrets), QUIC logiciel Kestrel (`Http1AndHttp2AndHttp3`), et `README.nginx.md`. * [Tests] Projet `Yavsc.Server.Tests` : tests du service `BlogSpotService.Details` + `ServerServicesFixture` (SQLite `:memory:` par défaut, Npgsql opt-in via `YAVSC_SERVER_TEST_DB_PROVIDER`). * [Tests] `IBlogPostWireShapeTests` (forme du wire blog), `EstimateAttachmentsApiTests`, `FrontOfficeQueryAttachmentsApiTests`, `FileSystemApiTests` (pièces jointes et stockage perso). * [CI] Intégration Gitleaks / analyse de secrets (`.pre-commit-config.yaml`). ### Changed * [contrib] `QUIC_OPTS` (`reuseport`) désormais activé uniquement en production sur le vhost Org. La preprod et la prod partageant la même IP, `reuseport` — légal une seule fois par `addr:port` (443/udp) — serait sinon en conflit entre preprod-Org et prod-Org et ferait refuser la config par nginx. * [Yavsc.Api.Test] `ApiWebServerFixture` : la connexion admin PostgreSQL provient de la variable d'environnement `YAVSC_API_TEST_NPGSQL_ADMIN_CONNECTION` ; plus de mot de passe en source. ### Fixed * [Yavsc.Blogs] Bug de production : `IsPublished` était absent du payload de la liste du blog API. Le endpoint `GetBlogspot` retourne `IEnumerable` sérialisé par System.Text.Json d'après le type déclaré (l'interface), qui omet les propriétés du type concret — `IsPublished` vivait sur `BlogPost` / `BlogPostDto` mais pas sur `IBlogPost`. PostIt lisait donc `IsPublished == false` pour tout billet, y compris publiés (https://yavsc.pschneider.fr/BlogSpot/Details/13). Corrigé en déclarant `IsPublished` sur `IBlogPost`. * [Yavsc.Org] Accès au Swagger UI : les actions MVC scaffoldées de `CommentsController` (Index/Details/…) atterrissaient sur la même route `POST api/v1/blogcomments` que l'action d'API `Post`, provoquant une `SwaggerGeneratorException` (conflit method/path) au chargement de `/swagger`. Corrigé par `[ApiExplorerSettings(IgnoreApi = true)]` sur les actions scaffoldées. ## [1.0.8-rc16] - unstable Flux de signature et de génération de devis : un devis peut désormais être signé par les deux parties (fournisseur et client) puis téléchargé en PDF ou TeX depuis PostIt, avec les signatures dessinées en vecteur. ### Added * [PostIt] Pad de signature sur la page de validation d'un devis : on valide ou on refuse en joignant une signature (côté fournisseur ou client), capturée au format filaire PostIt (strokes) et envoyée sur l'acceptation. * [Yavsc.Api] Finalisation de `POST /api/v1/front/query/accept` et `.../reject` : autorisation selon le rôle (fournisseur/client/admin), signature optionnelle persistée sur l'estimate avec horodatage `ProviderValidationDate` / `ClientValidationDate` selon le côté signataire. * [Yavsc.Api] `QueryStatus.Accepted` éclaté en `ProAccepted` / `ClientAccepted` pour distinguer l'acceptation de chaque partie. * [Yavsc.Api] Endpoints de devis `GET /api/v1/front/query/{id}/estimate.tex` et `estimate.pdf` (clé par id de query). Le flux de génération TeX/PDF est rapatrié de Yavsc.Org vers Yavsc.Api (`TeXHelpers`, `PdfGenerationViewModel`, `BillViewComponent`, modèles Bill/Estimate, `_ViewImports` ; `Program.cs` câble `AddControllersWithViews` + `SiteSettings`). * [Yavsc.Api] Signatures du devis dessinées en TikZ vectoriel (`TeXHelpers.SignatureToTikz`) en bas du document, fournisseur et client côte à côte dans deux minipages ; plus de dépendance à un PNG de signature. * [Yavsc.Api.Client] `DownloadAsync` sur le client + `GetEstimateTexAsync` / `GetEstimatePdfAsync` ; boutons « Télécharger PDF » / « Télécharger TeX » dans PostIt sur la page de validation du devis et le détail de commande, avec un helper `StorageProvider` d'enregistrement de fichier. * [PostIt] La liste des devis se rafraîchit au retour de navigation (pop-back), de sorte qu'un devis validé quitte immédiatement la liste du signataire. * [PostIt] Renommage des titres de liste : « Devis en attente de signature fournisseur » / « Devis en attente de signature client ». * [Yavsc.Abstract] `SignatureType` déplacé dans `Yavsc.Abstract.Billing`. * [CLI] Option `-c configuration-file.json` au lancement. * [Tests] `EstimatePdfGenerationTests` : tests unitaires du pipeline signature → TikZ → PDF (devis signé des deux côtés), assertant que le PDF existe au chemin de sortie attendu et que seul le PDF persiste (aux/log nettoyés) ; skip propre si `lualatex` est absent. * [README] Dépendances système au runtime pour la génération PDF : `texlive-binaries` (lualatex), `texlive-luatex` (luaotfload), `texlive-pictures` (tikz), `texlive-fonts-extra` (bera). ### Changed * [Yavsc.Api] `GetOngoingEstimatesAsProvider` filtre désormais sur `ProviderValidationDate` : un devis signé par le fournisseur quitte sa liste « en attente ». * [Yavsc.Api] La génération PDF compile via le `lualatex` système, le TeX fourni sur l'entrée standard (`/dev/stdin`), le PDF écrit directement dans le répertoire des factures (`Site.Bills`), les artefacts `.aux`/`.log` nettoyés afin que seul le `.pdf` persiste. ### Fixed * [Yavsc.Api] `Emergency stop` à la génération PDF : `lualatex` sans argument fichier lit stdin en mode terminal et s'arrête après la première ligne d'un document multi-lignes. Corrigé en passant `/dev/stdin` comme nom de fichier d'entrée — stdin est alors traité comme un fichier régulier et le document multi-lignes compile. C'était la cause racine de l'échec en production, pas `bera` ni `luaotfload` (problèmes réels mais séparés, comblés côté paquets). * [Yavsc.Api] `I can't write on file 'estimate-1.log'` : le chemin relatif `Site.Bills` (`"bills"`) était imbriqué quand il servait à la fois de `-output-directory` et de répertoire de travail ; résolu en chemin absolu. * [Yavsc.Api] NRE dans `RenderViewToString` (RouteData / `ViewData.Model` nuls) : reconstruit sur le `ControllerContext` du contrôleur ; `IViewEngine` résolu via `IRazorViewEngine` (.NET 10 n'enregistre pas de `IViewEngine` unique). * [Yavsc.Api] `Layout = "null"` (chaîne) dans les modèles TeX cherchait `null.cshtml` ; corrigé en `Layout = null` (null C#). * [Yavsc.Api] `texi2pdf` (outil Texinfo, inadapté au LaTeX) remplacé par `lualatex`. * [Yavsc.Api] Les diagnostics lualatex étaient perdus (écrits dans le `.log` qu'on supprimait) ; stdout/stderr sont désormais capturés dans le message d'erreur, et le `.log` est conservé en cas d'échec. ## [1.0.8-rc15] - unstable ### Added * [PostIt] Saisie et envoi de pieces jointes sur un billet de blog: le client monte les fichiers en `multipart/form-data` lors du `Save`, ajoute un lien Markdown par piece jointe a l'article, puis re-PUT le billet. Une collection `DraftAttachments` porte les fichiers en cours d'edition. * [PostIt] Apercu Markdown dans l'editeur de blogs (integration `MarkdownViewer.Core`) avec bascule edition/preview sur `BlogsPage`. * [PostIt] Gestion des devis: page de liste `EstimateListPage` (vues client et prestataire), page d'edition `EstimateEditionPage` avec lignes de devis (`EstimateLineItemViewModel`), et flux de validation d'un devis. * [PostIt] Flux des requetes en cours cote prestataire (`ProviderOngoingRequestsPage`) avec tri persistant. * [Yavsc.Api] Endpoints devis `GET /api/v1/estimate/asclient` et `GET /api/v1/estimate/asprovider` pour lister les devis en cours selon le role. * [Yavsc.Api] Endpoint `GET /api/v1/billing/provider/ongoing` et `POST /api/v1/billing/prosign/{billingCode}/{id}` pour le flux prestataire. * [Yavsc.Api.Client] `EstimateApiClient` + DTOs (`EstimateDto`, `EstimateLineDto`) pour le pipeline devis cote client lourd. * [Yavsc.Api.Client] Reprise de l'upload de fichiers blog dans `BlogApiClient` (`CreateMultipartContent`, `BlogUploadFile`) — la meme forme filaire que PostIt utilise en production. * [Yavsc.Abstract] `FileServerUrlHelpers` pour deriver les URLs publiques des fichiers utilisateur depuis l'autorite OIDC (alignement sur `UserFilesPath`). * [Yavsc.Server] `BlogSpotService.AttachFiles` : ecrit les pieces jointes sous la racine des fichiers utilisateur (`{UserFilesDirName}/{user}/blogs/{postId}/{fileName}`). * [Yavsc.Blogs] `BlogApiController` accepte `multipart/form-data` (champ `blog` + parts `file`) sur `POST` et `PUT /api/v1/blogspot`, en plus du payload JSON existant. * [PostIt] Variable d'environnement `POSTIT_SETTINGS_JSON` pour surcharger les parametres au lancement (utile en CI/test). * [Yavsc.Server] Migration EF `fileACL`: `CircleAuthorizationToFile` gagne `OwnerId` + `Access` et un graphe proprieetaire, base du controle d'acces fichier par cercle. * [Yavsc.Server] Migration EF `genericEstimate`: `Estimate.Query` passe de `RdvQuery` a `NominativeServiceCommand?` pour supporter des devis non lies a un RDV. * [Yavsc.Server] Migrations EF `NominativeServiceCommand` et `AddHairCutQueryLocationId` (localisation de la coupe). * [CI] Integration du scan de secrets Gitleaks dans le pipeline Forgejo + `.gitleaksignore`. * [Tests] Non-regressions: round-trip multipart blog (`BlogApiTests`, `CreateMultipartContentTests` forme + serveur), `BlogAttachmentLinkTests`, `FileServerUrlHelpersTests`, `EstimateApiControllerTests`, VMs `EstimateList`/`EstimateEdition`/`ProviderOngoingRequests`, `BillingControllerTests` (filtrage par utilisateur), `GetOpenIdConfiguration` contre l'hote de test. ### Changed * [PostIt] Renommage `MainPage` -> `BlogsPage` (et `PushMainPageAsync` -> `PushBlogsPageAsync`) pour refletter le role de la page. * [PostIt] Refacto des `SiteSettings` : reorganisation des parametres, nouvelles valeurs par defaut, et indications plus claires dans les `appsettings`. * [Yavsc.Api] `BillingController` ne liste plus que les codes billing de l'utilisateur authentifie (filtre par `UserId`), au lieu de l'ensemble des requetes prestataire. * [Yavsc.Api] Mutualisation d'un `YavscMessageSender` partage dans l'hote API (au lieu d'une instance par controller). * [Yavsc.Org] La detection du doublon d'email a l'inscription utilise desormais le code erreur generic `DbException` 23505 (fournisseur-agnostique) au lieu de `PostgresException`, pour rester correct en test SQLite. * [PostIt] `YavscApiClient` tolere les corps vides (`204 No Content` sur `PUT /blogspot/{id}`) au lieu de lever un `JsonException`. * [CI] Le pipeline compile avant les tests, installe les dependances natives, et retire le `ItemGroup.Using Include=Xunit` superflu. ### Fixed * [PostIt] Suppression d'un faux message d'erreur affiche apres un `PUT /blogspot/{id}` reussi (204 traite comme succes, plus comme une reponse vide invalide). * [PostIt] Correction de la creation des liens de pieces jointes (`TryAppendAttachmentLinks`) : segment proprietaire et chemin `blogs/{id}/{fileName}` alignes sur ce que `AttachFiles` ecrit reellement. * [Yavsc.Blogs] Correction de l'upload / de la persistance des pieces jointes (`FileSystemHelpers` + `BlogApiController` multipart). * [Tests] Stabilisation des suites: seeds billing avec metadonnees d'audit, `GetOpenIdConfiguration_returns_ok` contre l'hote de test, retraits d'assertions abusives, tests SQLite verts. * [PostIt] Nettoyage de fichiers temporaires accidentels et ignore des repertoires `tmp` dans le depot. ## [1.0.8-rc14] - unstable ### Added * [PostIt] Ajout d'un `BillingQueryDetailsPageViewModel` et de sa page associee pour afficher le detail d'une commande billing depuis l'historique. * [PostIt] Ajout d'un mode detail avec section metier (statut, date, description, motif, infos) et section technique repliable (code, client, provision, lieu, prestations). * [PostIt] Ajout d'un badge de statut enrichi (couleur + pictogramme) sur le detail d'une commande pour visualiser l'etat en un coup d'oeil. * [PostIt] Ajout d'un bloc d'actions rapide en tete du detail (`Retour`, `Ouvrir en edition`) pour eviter le scroll jusqu'au bas de page. * [PostIt] Ajout d'un style monospace sur les metadonnees techniques (code billing, client, provision, lieu, prestations) pour faciliter la lecture des identifiants et valeurs brutes. ### Changed * [PostIt] Le bouton d'ouverture depuis la liste billing ouvre maintenant une page de detail dediee avant l'eventuelle edition. * [PostIt] Amelioration UX des pages billing: badges de statut colores, actions remontees en haut de page, et typographie monospace sur les metadonnees techniques. ### Fixed * [Yavsc.Api] Correction d'un 500 sur le refresh du catalogue d'activites lorsque `Activity.Description` est `NULL` en base (nullabilite explicite + projection null-safe + gardes sur codes vides). * [Yavsc.Api] Correction des erreurs 400/500 sur les routes billing (`Rdv`, `Brush`, `MBrush`) en imposant `ClientId` depuis l'utilisateur authentifie et en ignorant les champs server-owned lors de la validation modele. * [Yavsc.Api] Correction du `PUT /api/v1/billing/Rdv/{id}`: mise a jour controlee de l'entite existante (et non remplacement brut du graphe JSON), ce qui supprime les `BadRequest` parasites. * [Yavsc.Api] Correction PostgreSQL `timestamptz` sur RDV: normalisation UTC de `EventDate` sur `POST/PUT /api/v1/billing/Rdv` pour eviter l'erreur `Cannot write DateTime with Kind=Local`. * [Yavsc.Api] Correction du flux FrontOffice accept/reject de query: sauvegarde avec contexte utilisateur et fallback d'injection pour `IBillingService` afin d'eviter les erreurs serveur en environnement de test. * [Yavsc.Blogs] Correction des `BadRequest` sur `POST/PUT /api/v1/blogspot` avec payload JSON (PostIt): les proprietes de navigation/serveur (`Author`, `Tags`, `Comments`, audit) ne bloquent plus la validation. * [Yavsc.Org] Correction du flux MVC de creation de commentaire: `SaveChangesAsync(userId)` est utilise pour renseigner les champs d'audit requis (`UserCreated`/`UserModified`). * [Yavsc.Api.Test] Stabilisation des fixtures de seed billing: remplissage des metadonnees d'audit (`UserCreated`, `UserModified`, dates) pour eviter les echecs SQLite `NOT NULL`. ## [1.0.8-rc13] - unstable ### Added * [PostIt] Integration d'un selecteur de lieu RDV base sur Mapsui (carte interactive dans le formulaire `Rdv`). * [PostIt] Ajout d'un marqueur de position et d'une action de recentrage sur la carte RDV. * [PostIt] Ajout d'un service de reverse geocoding pour suggerer une adresse a partir des coordonnees carte. * [PostIt] Cache et debounce des resolutions d'adresse RDV pour limiter les appels reseau et lisser l'UX. * [PostIt.Tests] Nouvelles non-regressions sur le panneau d'adresse suggeree RDV et le comportement de la carte. * [Yavsc.Abstract] Activation de `#nullable enable annotations` sur les fichiers legacy avec annotations nullable. * [Yavsc.Server] Activation de `#nullable enable annotations` sur les fichiers legacy avec annotations nullable. ### Changed * [PostIt] Generalisation de la barre de statut d'action (severite explicite) sur pages principales, dialogues et formulaires billing. * [PostIt] Harmonisation des messages de statut utilisateur en francais. * [PostIt] Renforcement des gardes de navigation dans les flux de gestion des membres de cercle. * [PostIt] Le flux RDV conserve l'adresse saisie manuellement et propose l'adresse resolue comme suggestion explicite. * [PostIt] Le flux de geolocalisation RDV tolere les positions proches dans le cache de suggestion d'adresse. ### Fixed * [PostIt.Desktop] Correction d'un crash au demarrage OIDC (`No authority specified`) via durcissement des valeurs par defaut de configuration d'authentification. * [PostIt] Correction de la persistance des settings: l'etat runtime de statut n'est plus serialize dans le JSON utilisateur. * [PostIt.Tests] Ajout d'un verrou de non-regression sur le premier chargement des settings. * [PostIt] Correction du binding de la date RDV: `DatePicker.SelectedDate` est aligne sur un proxy `DateTimeOffset?` (`EventDateSelection`). ## [1.0.8-rc12] - unstable ### Added * [PostIt] Nouveau helper d'image `ImageHelper` pour charger des bitmaps depuis les ressources et depuis le web. * [PostIt] Affichage de l'avatar XS dans la liste des performers d'activites, avec fallback visuel (initiale utilisateur). * [PostIt.Tests] Nouveaux tests autour des URLs avatar et de la source d'autorite. * [contrib] Ajout d'un `README.md` utilitaire pour les symboles/icones. ### Changed * [PostIt] Les avatars ne sont plus relies en string sur `Image.Source`: ils sont telecharges et lies en `Bitmap`. * [Yavsc.Api.Client] `ActivityApiClient` accepte une base d'avatar dediee et construit les URLs avatar depuis l'autorite d'identification. * [PostIt] Les clients Activites/Billing utilisent maintenant `ApiUrl` en lecture dynamique: un changement via Parametres prend effet sans redemarrer l'application (apres sauvegarde et rafraichissement de la page). * [PostIt] Le header de `MainPage` n'utilise plus `ScrollViewer`; remplacement par une barre de commandes basee sur `WrapPanel`. * [PostIt] Alignement de la navigation blogs: renommage `PushMainPageAsync` -> `PushBlogsPageAsync` et ajustement de `HomePageViewModel`. ### Fixed * [PostIt.Android] Correction d'un 404 sur la page Activites au premier lancement: la configuration embarquee pointait `ApiUrl` vers le host Blogs au lieu de l'API metier. * [PostIt] Correction du bouton Sauver de la page Parametres: binding vers `SaveCommand` pour persister correctement `ApiUrl`/`BlogsApiUrl`. ## [1.0.8-rc11] - unstable ### Added nothing ### Changed * [Yavsc.Api.Test] Mise a jour de `Microsoft.EntityFrameworkCore.Sqlite` vers `10.0.11` afin de supprimer l'alerte NU1903 liee a `SQLitePCLRaw.lib.e_sqlite3` 2.1.11. * [Yavsc.Org] Nettoyage de la configuration NuGet pour le restore: suppression du fichier local `Directory.Packages.props` au profit du fichier racine centralise. * [Yavsc.Org] Suppression de references de packages redondantes dans le projet, sans impact fonctionnel attendu. ### Fixed * [Yavsc.Api.Test] Le restore n'emet plus le warning de vulnerabilite `NU1903` sur `SQLitePCLRaw.lib.e_sqlite3`. * [Yavsc.Org] Suppression d'une vulnerabilite de severite elevee sur AutoMapper apres publication et consommation de la nouvelle version candidate de `HigginsSoft.IdentityServer8`. ## [1.0.8-rc10] - unstable ### Added * [PostIt] Une page d'historique des commandes billing permet maintenant d'ouvrir une commande existante. * [PostIt] Une vue "Demandes en cours" en lecture seule est disponible pour le performer, filtrée sur les statuts actifs (Inserted, Accepted, InProgress). * [Yavsc.Org] Nouvelles entités `Country` et `PerformerCodeInputValidation` pour piloter la validation du code entreprise performer par pays. ### Changed * [PostIt] La page détail billing se préremplit depuis une commande existante (Rdv, Brush, MBrush) et passe en mode mise à jour. * [Yavsc.Org] Le formulaire `Manage/SetActivity` inclut désormais le pays d'exercice (`fr`, `en`, `pt`) et applique la regex associée au champ `SIREN`. * [Yavsc.Org] La vérification externe du numéro d'entreprise est conservée uniquement pour le pays `fr`. ### Fixed * [PostIt] Le flux historique n'est plus limité à une simple liste: l'action d'ouverture charge la commande cible puis navigue vers la page détail. * [Yavsc.Org] Le champ `SIREN` n'est plus validé avec une règle unique indépendante du pays d'exercice. ## [1.0.8-rc9] - unstable ### Added nothing ### Changed masquage non-owner côté backend de l'ACL du billet ### Fixed On a maintenant le comportement attendu bout en bout: ACL chargée depuis le BlogPostDto noms de cercles affichés dans le dialogue ACL côté PostIt ## [1.0.8-rc8] - unstable ### Added nothing ### Changed nothing ### Fixed The PostIt publish toggle button ## [1.0.8-rc7] - unstable ### Added * [PostIt] The search pattern now persists ### Changed * The blog spot path is now `/api/v1/blogspot` (yet in last release) ### Fixed * [Yavsc.Org] (Ticket #45) La forme de l'email de l'utilisateur est maintenant validée avant l'envoi du formulaire d'enregistrement ## [1.0.8-rc6] - unstable ### Added * a code cleanup, * a first Xamarin.UITest is successful, but disabled, because breaking the actual CI process, * Android app starts, the login process succeeds ### Changed L'identifiant de l'application client Android a changé, il passe en minuscules : `fr.pschneider.postit` ### Fixed a bug posting and retrieving ACL from the backend, the ACL now comes along with the article, [TODO][PostIt] keep ACL along with the article ## [1.0.8-rc1] - unstable ### Added - `BlogAclApiTests.PostCircleAuthorization_returns_201_when_payload_mirrors_PostIt_shape_against_existing_circle_named_test` : test de non-régression qui épingle la forme exacte du payload que PostIt envoie à `POST /api/v1/blogacl` (un objet `PostAccessControlRulePayload` avec `CircleId` et `BlogPostId`). C'est le verrou côté test du fix applicatif PostIt + serveur. - `BlogAclApiTests.PostCircleAuthorization_never_returns_500` : une `[Theory]` couvrant quatre shapes de payload (`{ circleId }`, corps vide, `{ blogPostId }` seul, `{ circleId, blogPostId: 0 }`) qui doivent tous retourner un statut différent de 500. Toute réintroduction d'un chemin 500 dans le futur fera rougir ce test. - `BlogAclApiTests.PostCircleAuthorization_dosent_return_500` et `..._dosent_return_500_on_success` : entry points `[Fact]` qui appellent la `[Theory]` ci-dessus avec un payload spécifique chacun, pour pouvoir filtrer en isolation depuis la ligne de commande ou le CI. - Règle « Pas de `object` dans le code source applicatif » ajoutée à `CONTRIBUTING.md` : types de retour, paramètres, champs, propriétés, variables locales doivent être typés statiquement. `dynamic` est interdit pour les mêmes raisons. ### Changed - `BlogAclApiController.CheckOwner` devient `CheckOwnerAsync` et utilise `FirstOrDefaultAsync` au lieu de `First`, supprimant l'appel LINQ synchrone sur le fil de la requête et retournant `false` sur cercle manquant (le contrôleur mappe déjà cela vers `ChallengeResult`). - `BlogsWebServerFixture` seed `alice`, son `Circle` et son `BlogPost` une seule fois au démarrage du host, sur la `SqliteConnection` partagée (`Cache=Shared`). Le précédent `EnsureDeleted` au début de chaque test fermait la connexion statique et détruisait le store `:memory:` pour tous les autres `DbContext` ; il est retiré au profit d'un `EnsureCreated` idempotent. ### Fixed - `POST /api/v1/blogacl` ne retourne plus 500 sur les payloads dont `BlogPostId` est absent ou à zéro. Le contrôleur rejette `BlogPostId <= 0` avec `400 BadRequest` avant que la requête n'atteigne `SaveChangesAsync`. L'incident de prod du 2026-08-21 sur mercure (PostIt envoyant seulement `circleId`, le serveur voyant `BlogPostId = default(long) = 0` et EF Core levant `InvalidOperationException` sur l'INSERT) n'est plus atteignable. - PostIt `PostAclDialogViewModel.AddAsync` envoie désormais le payload explicite `PostAccessControlRulePayload { CircleId, BlogPostId }` au lieu de l'ancien `CircleAuthorization { CircleId }`. Le DTO serveur `PostAccessControlRulePayload` est introduit dans `Yavsc.Abstract` pour porter le contrat. ## [1.0.7] - preview ### Added - Per-post ACL in PostIt: a new “Manage ACL” page, opened from the ACL button on a selected post, lets the post author grant or revoke grants for individuals or circles. The server scopes each grant operation to `caller == post.AuthorId` and returns `404` (not `403`) for posts the caller does not own, so the existence of another user's post is not leaked. - Circle membership API + UI: three new REST endpoints under `/api/circle/{id}/members` (`GET` list, `POST` add, `DELETE` remove) and a new “Members” column on the *My Circles* page with an “Add a member” button that opens a search modal. The search modal reuses `IUserDirectory` (introduced by the `IContactService` split in this same release) — exactly the use case the abstraction was carved out for. - Publish toggle for blog posts: a new `PUT /api/BlogApi/{id}/publish` endpoint, and a `Published` checkbox in the post toolbar that toggles a `BlogSpotPublication` row for the post. The publish signal flows through the pre-existing `PermissionHandler.IsPublic` path, so no new column was needed and the server-side authorisation logic is unchanged. - `UserSearchApiController` in `Yavsc.Blogs`: `GET /api/user-search?q=...&e=...&take=...`. Any-authenticated- caller endpoint that exposes the user's email under a closed- community assumption (documented in the controller's XML doc). Wired to the PostIt Desktop address book so the user search modal picks it up. - `IYavscApiClient` abstraction in `Yavsc.Api.Client`. The transport for the blog/circle/blog-acl/user-search clients is now accessed through this interface, so `PostIt.Tests` can stub the HTTP layer without spinning up a real WebAPI host. - Forgejo Actions release workflow: a `.forgejo/workflows/release.yml` pipeline that builds and publishes a release with the PostIt APK on tag push. Written in pure bash (the runner image has no Node), uses `jq` for JSON body construction and response parsing, uses the runner-provided `GITHUB_TOKEN` (no repo-level secret needed), validates the CHANGELOG section heading before allowing the tag to ship. - `make release V=` target: creates a `release/` branch from `main`, bumps the `` property in every `.csproj` via `dotnet-gitversion /updateprojectfiles`, commits the bump on the release branch, and pushes to `origin`. Fails fast if the working tree is dirty or if `HEAD` is not on `main`. - Forgejo status badges in the README. ### Changed - The new Publish toggle replaces the “Visibility enum” approach originally drafted in this branch: the existing `BlogSpotPublication` table already carried enough information to expose a publish switch, so no schema change was needed. The original `feat(blog): add Visibility { Private, Public }` commit and its EF migration were reverted in favour of the endpoint-only toggle. - `BlogPost` DTO and `IBlogPost` moved from `PostIt.Models` to `Yavsc.Abstract.Blogspot`, the shared assembly where the server-side entity and the wire DTO both live. Renamed `Yavsc.Blogspot.BlogPost` to `BlogPostDto` to make the wire/entity distinction explicit. - `BlogAclApiController` and `CircleApiController` moved from `Yavsc.Api` (not yet enabled in production) to `Yavsc.Blogs`, where they belong next to the `BlogSpotService` they depend on. - `IContactService` split from `IUserDirectory`: the two interfaces previously conflated the local address-book access (mobile-only, via `Contacts.Default`) and the Yavsc user-search access (Desktop-only, via `/api/user-search`) behind a single facade. The split restores the `ContactDto.Emails` multi-value shape that was being silently flattened to a single string before. - CI: the Forgejo Actions build now compiles `.csproj` projects directly inside the runner container (which ships the .NET SDK + Android workload), instead of relying on a separate Docker build step. Node-based third-party actions were replaced with bash + curl + `jq`. The validate-release job parses the CHANGELOG section heading to derive the channel (`stable` / `preview` / `unstable`) rather than the patch-version parity alone. ### Fixed - `CircleApiController` used to read the caller's user id via `FindFirstValue(ClaimTypes.NameIdentifier)`, which does not match when JWT Bearer middleware has `MapInboundClaims = false`. Switched to `User.GetUserId()` (tries `sub` first, then `ClaimTypes.NameIdentifier`, then `nameid`). This was a latent bug visible in tests but easy to ship to production if a host ever disabled the remap. - `CircleApiController` and `BlogAclApiController` reads and writes were not always scoped to the caller's own data. Tightened the authorisation checks: cross-user reads now return `404`, not the raw record. - `validate-release` CHANGELOG channel check used to parse the patch-version parity only, which disagreed with the channel suffix in the section heading (e.g. `## [1.0.7] - preview` would be flagged as `stable` from the parity alone). The job now inspects the heading line and trusts the suffix when present. - `.forgejo/workflows/release.yml`: the asset-upload URL now carries the asset name as a query-string parameter instead of a `curl` positional argument. The previous shape triggered Forgejo's “Missing `name` parameter” 400 in some cases. ### Removed - The `## [Unreleased]` block has been moved into this section. - The abandoned `Visibility { Private, Public }` enum and its EF migration, reverted in this release. The publish toggle covers the same user-visible switch without a schema change. [Unreleased]: https://forgejo.pschneider.fr/notazof/yavsc/compare/HEAD [1.0.8-rc16]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.8-rc15...1.0.8-rc16 [1.0.8-rc17]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.8-rc16...1.0.8-rc17 [1.0.8-rc18]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.8-rc17...1.0.8-rc18 [1.0.8-rc15]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.8-rc14...1.0.8-rc15 [1.0.8-rc1]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.7...1.0.8-rc1 [1.0.7]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.6...1.0.7 [1.0.6]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.5...1.0.6 ## [1.0.6] - stable ### Added - Self-hosted Forgejo Actions runner now drives the CI build for the yavsc repository, using the `pazof/yavsc-build-env:debian12-dotnet10-android36-v2` image pulled from Docker Hub. Workflow runs end-to-end: clone, restore, build, test, with NuGet.config picking up the `isn.pschneider.fr` feed. - The build-env image now ships `jq` (Debian package, ≥ 1.7), so the release workflow can build JSON bodies and parse API responses without a hand-rolled `sed`-based extractor that was matching the wrong `id` field on minified responses. ### Changed - CI workflow `.forgejo/workflows/buildAndTest.yml` no longer relies on `actions/checkout` (the runner image has no Node); clones yavsc via `git`, fetches the ref under test, and initializes submodules over HTTPS. ### Fixed - `Dockerfile` and `Dockerfile.backend` no longer carry a redundant `dotnet nuget add source` step that conflicted with the GitHub Actions APK build (`--allow-insecure-connections` on an HTTPS endpoint, exit 1). `NuGet.config` at the repo root supplies the `isn.pschneider.fr` feed for every restore, including inside Docker. - `.forgejo/workflows/release.yml`: PATCH on `/releases/{id}` no longer 404s on existing releases. The previous `sed`-based `json_field` matched the last `id` on the line (the author's), so it tried to PATCH `/releases/1` (the first user of the instance) instead of the actual release id. Switched to `jq` for both body construction and field extraction. [1.0.6]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.5...1.0.6