yavsc/CHANGELOG.md
Paul Schneider 4ec169ed71
Some checks failed
Dotnet build and test / build (pull_request) Has been cancelled
Forgejo Release / release (push) Successful in 26m22s
Improve PostIt draft attachment and save workflow
Insert attachment links on file addition, resolve provisional links on first save, preserve the editor selection, and highlight unsaved draft changes. Include regression tests and changelog updates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-11 02:03:48 +01:00

47 KiB

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] 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<ApplicationUser> : 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<ProfileService>() après AddAspNetIdentity<ApplicationUser>()). 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<IBlogPost> 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=<version> target: creates a release/<V> branch from main, bumps the <Version> 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.

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.