53 Commits

Author SHA1 Message Date
Thomas Fransolet
7a80766d43 Le journal d'audit ne suit plus les ecritures de la machine
Regression introduite par le commit qui journalise les sections.
WeatherSyncService ecrit `section.WeatherResult` sur cron, a 6 h et a 13 h.
Le job existait et etait benin tant que les sections n'etaient pas auditees ;
depuis, chaque rafraichissement produisait une ligne portant la prevision
OpenWeather complete en avant ET en apres -- quelques dizaines de Ko, deux
fois par jour, par section meteo, indefiniment.

Le stockage est le moindre probleme : ce bruit noie les modifications
humaines que l'ecran d'audit existe pour montrer.

Correctif : une liste de colonnes machine (WeatherResult,
WeatherUpdatedDate, DateUpdate) exclues du journal, et surtout aucune ligne
produite quand une modification ne touche qu'elles -- plutot qu'une ligne au
diff vide, qui aurait deplace le bruit sans le retirer.

DateUpdate y est pour une raison distincte de la meteo : estampille a chaque
SaveChanges, il figurait dans tous les diffs sans jamais rien y apprendre. Le
signal etait deja la -- un test ecrit hier devait l'ecarter a la main pour
rester lisible (`newValues.Keys.Where(k => k != "DateUpdate")`). Quand un
test doit filtrer une donnee pour etre lisible, la donnee n'a rien a y faire.

Verifie au passage : AgendaSyncService ecrit des EventAgenda, qui ne sont pas
audites, et ne touche pas la ligne Section. Lui n'est pas concerne.

⚠️ Consequence sur la dette signalee hier : sans le flot meteo, la table ne
grossit plus qu'au rythme des editions humaines -- de l'ordre de quelques
dizaines de milliers de lignes par an sur 4 clients. L'index sur Timestamp
evoque hier n'a plus lieu d'etre, donc pas de migration et le gel du lot B
n'est pas remis en cause. La purge reste pertinente, mais comme sujet RGPD
(lot J), plus comme sujet de volume.

3 tests. dotnet test 203/203.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 16:01:01 +02:00
Thomas Fransolet
805ca5cf3d Le journal d'audit voyait tout sauf le contenu
AuditedTypes.Contains(entry.Entity.GetType()) exigeait l'egalite exacte de
type, or Section est abstraite : le type runtime est toujours SectionMap,
SectionQuiz... Aucune des 13 sortes de section n'etait journalisee -- soit
precisement ce que l'ecran d'audit livre le 12/08 devait tracer. Resource,
Configuration, Device, User et Instance, eux, passaient : ils sont concrets,
et c'est ce qui rendait le trou invisible.

Remplace par une remontee a la classe de base auditee (IsInstanceOfType).

EntityType porte « Section », pas « SectionMap ». Des deux sorties possibles,
c'est la normalisation cote serveur qui est retenue :

- le filtre « Section » est deja dans l'ecran et se met a rendre des lignes
  sans toucher manager-app, donc sans coordonner deux repos ;
- elargir le filtre front aurait coute 13 entrees de liste et 39 cles i18n,
  et surtout aurait laisse tout futur sous-type sortir du filtre en silence
  -- la meme classe de panne que celle qu'on ferme ici ;
- le sous-type concret n'est pas perdu : le discriminateur TPH est une
  propriete du modele, donc serialisee dans NewValues (« Discriminator »:
  « Article »). Un test le tient.

8 tests, dont un par reflexion qui affirme que les 13 sous-types concrets
resolvent bien vers Section : la liste de types d'origine n'obligeait
personne a la suivre, c'est ce qui l'a laissee devenir fausse.

dotnet test 171/171.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 14:53:21 +02:00
Thomas Fransolet
a80096d72b Aligne les plans d'abonnement sur la grille de tarifs
La base semait Starter / Standard / Premium / Essentiel : les noms ET les
quotas avaient divergé de la grille de myinfomate-landing, sans que rien ne
le signale puisqu'aucun code n'indexe sur ces noms.

Écarts corrigés, pas seulement des renommages :
- Essentiel portait 500 000 jetons IA alors que la grille dit « Assistant IA
  visiteur : non inclus ». Passé à 0.
- Standard → Pro : 10 GB au lieu des 15 GB vendus, et 5 M jetons alors que
  l'IA n'est pas incluse non plus. Corrigé aux deux endroits.
- Essentiel et Pro avaient 395 jours de rétention de stats alors que la
  grille annonce des stats basiques (30 jours).
- Premium était déjà conforme.
- plan-enterprise créé : stockage illimité, IA en quota sur mesure.
- plan-starter supprimé — aucun équivalent commercial, et son 0 jeton en
  faisait un piège pour qui cherchait « le plan d'entrée ».

L'assistant IA n'étant inclus qu'à partir de Premium, il se vend en add-on
sur les plans inférieurs : surcharger Instance.AiTokensPerMonth suffit,
ApplyPlanQuotas ne repartant du plan qu'au changement de plan.

⚠️ La migration a été réécrite : EF avait scaffoldé les DeleteData en tête,
ce qui aurait buté sur la clé étrangère Instances.SubscriptionPlanId — ou
laissé des instances sans plan, donc à quotas nuls, donc sans IA ni
stockage. Les nouveaux plans sont insérés d'abord, les instances repointées
ensuite, les suppressions en dernier.

Sémantique du 0 laissée intacte et documentée : illimité pour le stockage,
pas d'IA pour les jetons. D'où long.MaxValue sur Enterprise.

dotnet build vert, dotnet test 143/143, migration appliquée en local.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 16:06:36 +02:00
Thomas Fransolet
7625487806 Lot B : geler le schéma, plus sécurité rapide et calculateur de stockage
LOT B — une seule migration EF (LotB_FreezeSchema) :
- SectionMap.MapResourceId → IconResourceId. L'écart (g) de la bascule tombe
  avec. Il fallait renommer aussi la propriété de navigation MapResource :
  la convention EF l'appariait au FK, la laisser aurait fabriqué un FK
  fantôme. Elle n'était utilisée nulle part ailleurs.
- SectionEvent.ParcoursIds supprimé (champ, DTO, SectionFactory, et une
  initialisation dans un montage de test).
- Instance.IsImageWatermark remplace le `instanceId == "633ee379…"` en dur
  de ResourceController.

EF a généré un RenameColumn, pas un drop+add : les icônes déjà configurées
survivent. L'avertissement de perte de données ne porte que sur le DropColumn
de ParcoursIds, ce qui est l'intention.

Non fait, et c'était une erreur de doc : « supprimer SectionEvent.IconResourceId ».
Ce champ n'existe pas — la ligne visée appartient à la classe imbriquée
MapAnnotation, partagée par SectionEvent, SectionAgenda et SectionMap, lue par
cinq contrôleurs et par GetReferencedResourceIds. La supprimer aurait cassé
les icônes d'annotation des trois types et la collecte offline.

SÉCURITÉ (lot A, même repo) :
- AuthenticationController.Authenticate : un bloc #if DEBUG écrasait l'email
  et le mot de passe reçus par un compte de test, donc toute compilation en
  Debug authentifiait n'importe quelle saisie. Retiré.
- EnableSensitiveDataLogging (qui écrit les valeurs des paramètres dans les
  logs) passe sous #if DEBUG, l'idiome déjà employé dans Startup.cs pour le
  CORS et Hangfire. Le Dockerfile publiant en -c Release, c'est un verrou réel.

LOT C1 :
- Calculateur StoragePath/SizeBytes extrait dans Helpers/ResourceStorage.cs,
  avec 13 tests fixant l'invariant des types URL. Il ferme le lien L5 : le
  backfill (C2) et l'écart (e) de la migration appelleront le même code.
- L'extraction a révélé la divergence qu'elle devait empêcher : des deux
  chemins de création de ResourceController, le chemin multipart écrivait
  SizeBytes mais laissait StoragePath nul.

dotnet build Debug et Release verts, dotnet test 143/143 (130 + 13).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:32:19 +02:00
Thomas Fransolet
18e4240f0f RAG: pipeline d'ingestion, endpoints du guide IA, journalisation RGPD
Ingestion et indexation
- IIngestionService/IngestionService : chargement des collections filles par
  sous-type, un jeu de morceaux par langue, ChunkIndex continu.
- SectionIndexingInterceptor retenu comme unique déclencheur : les 5
  sous-contrôleurs totalisaient 30 SaveChanges et 0 Enqueue, donc ajouter des
  points d'intérêt à une carte ne réindexait rien.
- HTML retiré avant l'embedding et lignes trop longues recoupées : sans cela un
  article dépassait l'entrée max du modèle et emportait son lot de 50 morceaux.
- Gabarits de LanguageInit filtrés, DistinctBy(Text) avant le Take : ils
  occupaient les cinq premiers résultats d'une recherche en néerlandais.

Endpoints du guide IA
- GET /api/Ai/knowledge/{id} : agrégats sur ContentEmbedding, donc sur ce qui
  est réellement indexé — compter les sections publiées serait plus flatteur et faux.
- GET /api/Ai/insights/{id} : miroir de GuideIaInsights côté manager-app, c'est
  l'écran qui a fixé la forme pour que le job de thèmes la remplisse.

RGPD
- VisitorQuestion journalisée dans AiController.Chat. HasAnswer se déduit des
  sources du retrieval, pas du texte : un repli poli ressemble à une réponse.
  L'écriture n'échoue jamais la réponse au visiteur.
- VisitorQuestionPurgeService, 90 jours, actif sans condition de configuration :
  une durée écrite dans les CGU n'est pas un réglage commercial.

Corrections
- Updateinstance ne recopiait pas les quotas du nouveau plan.
- CheckQuota ne bloquait ni ne comptait à quota 0 — IA gratuite non comptée.
- StoragePath et SizeBytes renseignés à Create, types URL exclus.

dotnet build 0 erreur, dotnet test 130/130.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 10:48:10 +02:00
Thomas Fransolet
af6b5b76ef Self-service onboarding (Stripe + Resend) + Postgres v3 schema (pgvector, media, visitor questions)
75 fichiers, ~1 mois de travail depuis f222861 (17/07). Contenu :

Onboarding self-service — jamais exécuté de bout en bout
  OnboardingController, StripeWebhookController, StripeService, ResendEmailService
  + IEmailService (10 templates), EmailTemplates/, TrialLifecycleService (Hangfire),
  PasswordTokenHelper, SlugHelper, 5 DTOs. Essai 14 j, Stripe customer/Checkout/Tax,
  mot de passe oublié + invitation user, plafond IA d'essai.

Schéma Postgres v3 — passe pendant que la base est vide
  ContentEmbedding + index HNSW (vector_cosine_ops), IEmbeddingService +
  GoogleEmbeddingService (gemini-embedding-001, 768 dims), Deployment/Dockerfile.postgres
  (postgis 3.4.3 + pgvector 0.8.6, épinglé par digest — un tag mobile rejouerait le
  warning de collation glibc). Colonnes Resource : StoragePath, FileName,
  IncludeInAiKnowledge, AiIndexStatus + nouveaux ResourceType ajoutés EN FIN d'enum.

Guide IA
  Champs Guide* sur Instance + InstanceDTO, AssistantService lit la configuration client
  dans les 4 blocs de prompt (ton codé en dur retiré, règle hors-sujet ajoutée aux deux
  variantes qui n'en avaient pas), IHttpClientFactory à la place des new HttpClient().
  Table VisitorQuestion + ConversationId sur AiChatRequest.

Stats
  Rétention unifiée à 13 mois (instances ET plans), VisitEventPurgeService.

Nettoyage
  IsStepLocked / IsHiddenInitially / FactContent supprimés de GuidedStep — IsStepLocked
  rendait une étape définitivement infranchissable même après réussite.
  SectionMap allégé (-57 lignes).

Tests
  SectionParcoursControllerTests, FakeConfiguration, FakeEmailService.
  ContentEmbedding a cassé 116 tests sur 124 (EF InMemory ne connaît pas Vector) :
  l'entité est exclue quand le provider n'est pas Npgsql. Conséquence assumée —
  le vector store n'est couvert par aucun test. dotnet test 124/124.

10 migrations EF. Base locale à jour, dotnet build 0 erreur.
Rien n'est en prod : la bascule Mongo → Postgres est décrite dans DOCS/STATUS.md §1quinquies.

⚠️ appsettings.json contient les clés Stripe (test) et Resend (prod) en clair — à rotationner.
2026-08-09 22:11:48 +02:00
Thomas Fransolet
f2228617ca Add instance index + date update audit + ai quota to token 2026-07-17 15:21:51 +02:00
Thomas Fransolet
19a0157b19 Bento support + upgrade security (filter by instance mandatory) 2026-07-14 17:21:10 +02:00
Thomas Fransolet
3cd5f7573d misc (ia assistant, SectionParcours + others fixs) 2026-07-08 17:00:43 +02:00
Thomas Fransolet
674655eb80 Update agenda sync (image management) + update isStatistics to hasStats (subscription plan) 2026-05-07 22:00:42 +02:00
Thomas Fransolet
9633ae43b7 Add support for web + misc 2026-05-07 16:49:56 +02:00
Thomas Fransolet
e0ff0eeba6 Quota update in controller, audit log (serilog) + ai translator + Unit tests ! (to be tested) + migration update 2026-04-10 16:28:33 +02:00
Thomas Fransolet
eff4f7ba5c Add seed subscriptionplan + wip migration controller 2026-04-01 17:26:27 +02:00
Thomas Fransolet
f72d94f30f Subscription plans 2026-04-01 17:00:13 +02:00
Thomas Fransolet
d5353eea9c wip service, fix some assistant ia bug, wip section agenda update, section event annotation etc 2026-03-25 10:56:54 +01:00
Thomas Fransolet
bad25bf5b3 Add push notification 2026-03-17 09:10:56 +01:00
Thomas Fransolet
a452f4af04 Add migration, need to check if migration with apikey correct.. ! + add roles and security (role and apikey) - need to be tested + visitevent stats + ai etc + all need to be tested ! 2026-03-13 17:45:01 +01:00
Thomas Fransolet
bde1666b42 Update save geometry + other, id null etc (thx claude) 2026-03-06 15:22:02 +01:00
Thomas Fransolet
f17d72b34e Multiple fixs after help from ai 2026-03-04 16:36:13 +01:00
Thomas Fransolet
98f3606887 Fix geopoint doesn't working 2025-11-27 16:52:10 +01:00
Thomas Fransolet
94443fa411 Update puzzle to game + misc 2025-11-27 16:19:00 +01:00
Thomas Fransolet
e70b574091 Updated applicationInstance for sectionEvent in dto 2025-10-17 23:21:10 +02:00
Thomas Fransolet
8f16f1570f Added method to return all section from one type + misc 2025-09-18 23:23:36 +02:00
Thomas Fransolet
fd191ec647 Moved guided stuffs 2025-08-20 14:35:37 +02:00
Thomas Fransolet
cbf3a3c7f5 Misc 2025-08-14 23:24:30 +02:00
Thomas Fransolet
4d755549af Updated code for device creation (create appconfig) + misc 2025-08-14 21:31:26 +02:00
Thomas Fransolet
b81972f723 Updated for kiosk (can assign different config on different device) 2025-08-14 16:56:02 +02:00
Thomas Fransolet
c8c5902292 misc + fixs 2025-08-14 16:30:03 +02:00
Thomas Fransolet
5aabf35f9d Add isweb to instance 2025-08-01 16:24:01 +02:00
Thomas Fransolet
b3b6a5d6fc Fix fromdto methods + misc 2025-07-17 11:58:23 +02:00
Thomas Fransolet
b5396643ce to lowerCase in all DTO + update to GeometryDTO 2025-07-16 15:28:54 +02:00
Thomas Fransolet
62e302a0f0 Update code for event, agenda and map controllers -> added DTOs + misc 2025-07-16 14:57:13 +02:00
Thomas Fransolet
a0aaf6e601 ApplicationInstance done but not tested + SectionEvent done but to be tested + wip sectioncontroller update + misc 2025-07-15 17:31:39 +02:00
Thomas Fransolet
9671361a34 Update DB QuizQuestion and misc 2025-07-15 16:30:01 +02:00
Thomas Fransolet
a051ae5f70 list of coordinates to geometry support (for sql postgis support) 2025-07-10 21:57:54 +02:00
Thomas Fransolet
c89566c9a9 Added link EventAgenda and SectionEvent + event agenda dto 2025-07-10 17:08:08 +02:00
Thomas Fransolet
8801c56f75 Added EventAgenda 2025-07-10 16:43:29 +02:00
Thomas Fransolet
400084fad5 Added explaination SectionMap, guidedPath etc 2025-07-09 17:44:48 +02:00
Thomas Fransolet
d241720102 Breaking changes ! WIP new format (ApplicationInstance) + SectionEvent and updated SectionMap 2025-07-09 17:37:30 +02:00
Thomas Fransolet
75a6b0c323 fix puzzle + added order in back 2025-05-27 14:44:33 +02:00
Thomas Fransolet
90f256ed13 Fix device creation 2025-05-27 13:15:33 +02:00
Thomas Fransolet
5fb5563730 Add get from configuration detail endpoint (mobile) 2025-05-23 17:06:18 +02:00
Thomas Fransolet
0229793c88 MISC 2025-05-15 17:21:18 +02:00
Thomas Fransolet
8635e75c06 update service (ressource deletion) + EF update link 2025-05-14 15:24:43 +02:00
Thomas Fransolet
f4919801b7 Misc 2025-05-13 17:18:59 +02:00
Thomas Fransolet
cef69a61df section (to be tested when front) + section map controller + section quiz controller + update useless entities to DTO 2025-03-24 17:18:59 +01:00
Thomas Fransolet
a4bde1a8f4 wip section controller 2025-03-20 17:52:45 +01:00
Thomas Fransolet
9aa5e7df5e Resource controller done + section WIP 2025-03-20 16:25:23 +01:00
Thomas Fransolet
1242d6c790 clean configuration + user controller done 2025-03-19 15:01:58 +01:00
Thomas Fransolet
8b68ebe8c9 Device controller done 2025-03-19 11:57:53 +01:00