Commit Graph
91 Commits
Author SHA1 Message Date
Johan LEROY c741ffc827 fix(auth): corrige la CI cassee par le nouvel endpoint de validation
Backend / Lint, typage et tests (push) Successful in 1m19s
/auth/reset-password/validate manquait a la liste explicite des routes
publiques (test_route_protection) et n'avait pas le modele de reponse
422 declare (openapi.json desynchronise du contrat genere).
2026-09-17 15:31:14 +02:00
Johan LEROY d7f775f9f7 feat(auth): verifie le lien de reset des le chargement, sans le consommer
Ajoute GET /auth/reset-password/validate (lecture seule, sans rate
limit : le jeton est un secret de 256 bits non brute-forcable) pour que
la page reset-password redirige immediatement vers /login si le lien
est invalide ou expire, plutot que d'attendre la soumission du
formulaire. La verification a la soumission (confirm_password_reset)
reste la seule source de verite atomique.
2026-09-17 15:28:50 +02:00
Johan LEROY e381e0de09 feat(frontend): checklist de complexite du mot de passe sur reset-password
Remplace l'indice statique sous le champ nouveau mot de passe par une
checklist qui coche chaque regle (longueur, majuscule, minuscule,
chiffre, caractere special) au fur et a mesure de la saisie. Les regles
individuelles (PASSWORD_REQUIREMENTS) sont exposees depuis le meme
validateur que PASSWORD_PATTERN pour rester la seule source de verite.
2026-09-17 14:57:05 +02:00
Johan LEROY 7674955637 fix(frontend): un lien de reset absent ou expire renvoie vers login avec un message standard
Avant, un token absent affichait un message inline sur /reset-password, et
un token invalide/expire ne se voyait qu'apres soumission du formulaire.
Les deux cas redirigent maintenant vers /login avec le motif
"lien-expire", qui y affiche le message standard "Ce lien de
reinitialisation est invalide ou a expire. Connectez-vous ou
redemandez-en un."
2026-09-17 14:49:46 +02:00
Johan LEROY 063092f2c7 fix(frontend): le rafraichissement de session au demarrage ne doit pas ecraser un lien de reset
Le refresh de session lance par provideAppInitializer echoue silencieusement
sans cookie valide, mais l'intercepteur forcait quand meme un
router.navigate(['/login']) sur le 401 resultant, ecrasant la navigation
vers /reset-password?token=... venue de l'email. L'intercepteur ne
redirige plus quand on est deja sur une route invitee (login,
forgot-password, reset-password).
2026-09-17 14:40:53 +02:00
Johan LEROY 921da48eb1 fix(auth): corrige 4 failles de la revue de securite sur la PR #90
Anti-enumeration cassee sur /auth/forgot-password : l'envoi SMTP etait
synchrone dans le chemin de reponse, donc un email existant prenait plus
de temps qu'un email inconnu (et pouvait renvoyer 500 si le relais SMTP
echouait, contre 202 sinon). L'envoi part desormais en BackgroundTasks,
apres que la reponse 202 a ete envoyee au client, avec un try/except qui
logue plutot que de laisser une exception SMTP remonter.

confirm_password_reset() ne revalidait pas is_active/kind du compte avant
de changer le mot de passe : un compte desactive dans les 15 minutes
suivant l'emission du lien pouvait quand meme voir son mot de passe
change et son must_change_password efface.

Les plages [A-ZA-Y]/[a-za-y] de la regle de complexite incluaient par
erreur x et / (U+00D7, U+00F7), donc un mot de passe sans aucune
majuscule ou minuscule pouvait passer la validation.

Le validateur frontend (JS, \w ASCII) et le validateur backend (Python,
\w Unicode) divergeaient sur les caracteres accentues : un mot de passe
comme "Securite1" passait cote front puis se faisait rejeter en 422 cote
back. Les deux cotes utilisent maintenant le meme jeu explicite de
caracteres speciaux (SPECIAL_CHARACTERS, partage aussi avec cli.py).
2026-09-17 14:35:14 +02:00
Johan LEROY 916b5d246a Merge remote-tracking branch 'origin/dev' into feat/password-policy-forgot-password
# Conflicts:
#	apps/backend/app/api/deps.py
#	apps/backend/pyproject.toml
2026-09-17 12:18:48 +02:00
Johan LEROYandGitHub 7913518c4b Merge pull request #94 from ineszang/feat/get-readings
feat(backend): expose GET /api/v1/readings avec fenetre bornee et pag…
2026-09-17 12:12:55 +02:00
Dorian a158d6f84c Merge remote-tracking branch 'origin/dev' into feat/get-readings 2026-09-17 11:58:27 +02:00
Dorian 8d28113f03 feat(backend): expose GET /api/v1/readings avec fenetre bornee et pagination 2026-09-17 11:50:20 +02:00
Johan LEROY 62932e57c3 style(backend): formatage ruff de cli.py 2026-09-17 11:42:44 +02:00
Johan LEROY 9161b74874 feat(auth): politique de complexite du mot de passe et flux de reinitialisation
Remplace la regle de longueur seule (12 caracteres) par une exigence de
composition (8 caracteres minimum, majuscule, minuscule, chiffre, caractere
special), non documentee dans les exigences officielles du projet, par une
regle explicite partagee entre le backend (validateur Pydantic) et le
frontend.

Ajoute un flux "mot de passe oublie" en libre-service, absent jusqu'ici :
jeton a usage unique hache en base (meme principe que les refresh tokens),
expirant a 15 minutes, envoye par email via un service SMTP (aiosmtplib,
Mailpit en dev), avec limitation de debit dediee et reponse generique pour
eviter l'enumeration des comptes.

Closes #87
2026-09-17 10:53:58 +02:00
Meryemel-gham 6798d35572 fix(data): corrige le typage de l'import historique
Backend / Lint, typage et tests (push) Successful in 1m16s
2026-09-17 10:41:18 +02:00
Meryemel-gham f03dce5fe3 style(data): applique le formatage Ruff 2026-09-17 10:06:54 +02:00
Meryemel-gham ebb72fb399 test(data): couvre l'import historique 2026-09-17 09:34:30 +02:00
Meryemel-gham b2d52823ba fix(data): aligne l'import historique avec les contraintes BDD 2026-09-17 09:34:30 +02:00
Meryemel-gham fcbfcc8eb2 feat(data): ajoute l'import historique des donnees 2026-09-17 09:34:30 +02:00
Johan LEROYandGitHub 3692d486c6 Merge pull request #83 from ineszang/feat/endpoint-sensors-status
feat(backend): expose GET /api/v1/sensors/status
2026-09-17 09:16:24 +02:00
ValentinDeFariaandGitHub 24bf8bf4b9 Update apps/backend/tests/services/test_sensor.py
Backend / Lint, typage et tests (push) Successful in 1m11s
2026-09-17 09:10:04 +02:00
Johan LEROYandGitHub 9f465538bf Merge pull request #85 from ineszang/feat/auth-front
feat(frontend): authentification frontend
2026-09-17 09:09:59 +02:00
Johan LEROY 515a92b395 fix(frontend): isole les fichiers de tests vitest pour eviter la pollution de mocks
Le test site-load-chart.spec.ts echouait de facon intermittente en CI : sans
isolation, vitest partage le registre de modules entre fichiers de spec, donc
le mock chart.js d'un fichier pouvait ecraser celui d'un autre selon l'ordre
d'execution.
2026-09-17 09:02:53 +02:00
ValentinDeFariaandGitHub 0174272bdd Update dashboard.html 2026-09-16 17:00:52 +02:00
ineszangandGitHub c740b61b24 Merge pull request #80 from ineszang/feat/pipeline-ci
Feat/pipeline ci
2026-09-16 16:41:55 +02:00
Valentin 5669cd63ec feat(frontend): authentification frontend
ajout de la page login, changement de mot de passe forcé, rafraîchissement de session en mémoire, intercepteur, déconnexion, bouton logout sur le dashboard
2026-09-16 16:07:28 +02:00
ineszang44 06cb60463c fix(frontend): droit d'accès au fichier de config de nginx, réduction de code smells 2026-09-16 15:12:04 +02:00
ineszang44 1c6b6105bd chore(frontend): ajout de la configuration nginx 2026-09-16 14:54:12 +02:00
Johan LEROY 77440281f8 feat(backend): expose GET /api/v1/sensors/status pour l'issue #32
Dérive l'état de santé de 5 capteurs par site et un statut overall depuis
la dernière lecture (data_quality, null_reasons, nullité des colonnes),
sur le gabarit d'agrégation de StatsService. Route réservée au rôle admin.
2026-09-16 14:53:54 +02:00
Johan LEROY 76fa90dfcb test(backend): exerce AlertSeverity comme enum plutot qu'une chaine dans les tests alerts
Backend / Lint, typage et tests (push) Successful in 1m8s
Le filtre severity passait par une chaine brute dans les tests, sans jamais
exercer le trajet reel AlertSeverity (enum) -> SQLAlchemy -> PostgreSQL.
2026-09-16 14:43:27 +02:00
ineszang44 6ecec1afef chore(frontend): ajout du dockerignore et du dockerfile 2026-09-16 14:40:55 +02:00
Johan LEROY e13096c62a Fusionne dev dans feat/endpoint-alertes-predictives
Resout les conflits additifs entre les routes alerts, recommendations
et stats mergees sur dev (PR #79, PR #82) pendant le developpement de
cette branche : deps.py, router.py, openapi.py, openapi.json,
test_openapi.py et 20-backend.md conservent desormais les trois routes.
2026-09-16 14:25:49 +02:00
Dorian PESCE c7490d01b3 Merge remote-tracking branch 'origin/dev' into feat/endpoint-recommandations 2026-09-16 14:03:24 +02:00
Johan LEROY 61e031fc16 Fusionne dev dans feat/stats-summary
Resout les conflits de deps.py, router.py, repositories/site.py et
test_site.py entre l'ajout de stats et le merge de sites/openapi-contrat
sur dev. Generalise ROUTES_A_ROLE dans test_openapi.py et documente la
checklist d'ajout d'une route metier, absentes de dev au moment du fork.
2026-09-16 13:37:03 +02:00
Dorian PESCE 781644b28e feat(backend): ajoute GET /recommendations et GET /recommendations/{recommendation_id} 2026-09-16 13:30:51 +02:00
Johan LEROY e50921c907 feat(backend): expose GET /api/v1/alerts
Consultation des alertes de consommation, filtrable par site_id et
severity a l'identique du contrat GET /alerts de l'API Mock. Reprend
le gabarit endpoints -> services -> repositories -> models pose par
sites, sur la table alert deja creee par la revision Alembic
e6d2026091501.

Generalise aussi le garde-fou OpenAPI du 403 (ROUTES_A_ROLE) au-dela
du seul tag users, pour que l'ajout d'alerts a la liste des routes
protegees par role soit reellement verifie.

Closes #59
2026-09-16 13:06:37 +02:00
Johan LEROY c83fd889b8 Fusionne dev dans feat/frontend-make-dev
Combine les cibles install-backend/install-frontend/dev-backend/dev-frontend
introduites ici avec la cible openapi ajoutee par PR #76 (merge de dev).
2026-09-16 12:21:20 +02:00
Johan LEROY 12c5cf87ad Merge branch 'dev' into feat/openapi-contrat
Complète le contrat OpenAPI de GET /sites et GET /sites/{site_id} (merges depuis dev via #78) :
tag sites décrit, REPONSES_LECTEUR (401 + 403 mot de passe provisoire) posée au niveau du
routeur, 404 et 422 documentés sur la route detail. openapi.json régénéré.
2026-09-16 12:04:32 +02:00
Johan LEROY ad149db0cb fix(backend): corrige une assertion tautologique dans test_list_all_returns_the_sites_sorted_by_identifier
Backend / Lint, typage et tests (push) Successful in 1m7s
L'assertion comparait le résultat à lui-même trié, donc vraie quel que
soit l'ordre réellement renvoyé par SiteRepository.list_all(). Compare
désormais à des identifiants connus à l'avance.
2026-09-16 11:55:03 +02:00
Johan LEROY 50dcb4de32 feat(backend): expose GET /api/v1/stats/summary
Ajoute le resume instantane de consommation du parc attendu par le
frontend (deja developpe contre ce contrat en mode mock). Nouveaux
SiteRepository et ReadingRepository (derniere lecture par site via
DISTINCT ON), StatsService pour l'agregation et les cas de repli
(capacite nulle, absence de lecture, data_quality inconnue), et le
endpoint lecteur-seul correspondant. Documentation des routes et du
schema des couches mises a jour.
2026-09-16 11:45:58 +02:00
Dorian PESCE d25e544db6 fix(backend): contourne un bug de ruff format sur le except à deux types de health.py 2026-09-16 11:41:53 +02:00
Dorian PESCE 22ff1d93f4 fix(backend): type le retour de SiteRepository.get_by_id pour mypy strict 2026-09-16 11:29:18 +02:00
Johan LEROY fabd073aaf fix(backend): documente le 403 CSRF de require_trusted_origin
Le contrat OpenAPI et 31-contrat-authentification.md passaient sous
silence le 403 leve par require_trusted_origin sur refresh, logout,
logout-all et password. Ajoute REPONSE_ORIGINE_REFUSEE, regenere
openapi.json et etend test_openapi.py pour verifier que ces quatre
routes le declarent.
2026-09-16 11:22:40 +02:00
Dorian PESCE 31a9cb109f fix(backend): corrige la syntaxe except invalide de la sonde /health/ready 2026-09-16 11:04:27 +02:00
Dorian PESCE 50dddf952b feat(backend): ajoute les endpoints GET /sites et GET /sites/{site_id} 2026-09-16 11:03:06 +02:00
Dorian PESCE fc6600aeaf Revert "feat(backend): ajoute les endpoints GET /sites et GET /sites/{site_id}"
This reverts commit 1325a75e9a.
2026-09-16 11:02:00 +02:00
Dorian PESCE 1325a75e9a feat(backend): ajoute les endpoints GET /sites et GET /sites/{site_id} 2026-09-16 11:01:13 +02:00
Johan LEROY 16a0cc4d3b fix(build): stabilise make dev pour le frontend
Desactive le prompt d'analytics Angular CLI (bloquait ng serve en
sous-processus non interactif) et affiche les URLs backend/frontend au
demarrage de make dev.
2026-09-16 10:36:31 +02:00
Johan LEROY 3347fa5bdb docs(architecture): acte le contrat OpenAPI dans la vue backend
20-backend.md gagne une section qui dit où vit le schéma, comment on le
régénère, pourquoi il est versionné en plus d'être servi, et pourquoi servers,
license_info et contact restent absents. La table des routes gagne la colonne
des codes d'erreur déclarés.

31-contrat-authentification.md renvoyait le frontend vers /docs, donc vers une
API qui tourne. Il renvoie maintenant vers le fichier, lisible sans rien
lancer.
2026-09-16 10:14:19 +02:00
Johan LEROY da481d7485 feat(backend): verse le contrat OpenAPI au dépôt et le garde honnête
`make openapi` écrit apps/backend/openapi.json, et un test compare le fichier
versionné au schéma généré. Une route qui change son contrat public le montre
donc dans la diff d'une pull request, et une PR qui oublie de régénérer échoue
en CI : le fichier vit sous apps/backend, que le filtre de chemins de
backend.yml couvre.

Le schéma exporté ne lit ni le .env du poste ni les variables APP_ : tout ce
qui l'atteint est posé par settings_du_contrat(), sans quoi le fichier
changerait de machine en machine.

main() réclamait un mot de passe avant de lire la commande. Le branchement
passe devant, sinon l'export serait resté bloqué sur getpass.
2026-09-16 10:12:50 +02:00
Johan LEROY 344f82fcdd feat(backend): documente le contrat d'erreur dans l'OpenAPI
Le schéma ne déclarait aucun code d'erreur : ni 401, ni 403, ni 404, ni 409,
ni 429. Swagger affirmait que /auth/login ne pouvait répondre que 200 ou 422,
alors que 31-contrat-authentification.md décrit ces codes comme le contrat que
le frontend doit traiter.

Le 422 publié était pire qu'absent : le schéma exposait HTTPValidationError,
le modèle par défaut de FastAPI avec sa clé `loc`, quand
validation_error_handler renvoie {"detail": [{"champ", "type"}]}. Un client
codé sur la documentation lisait une clé qui n'arrive jamais.

Les métadonnées arrivent avec : description, résumé et une description par
tag. `servers`, `license_info` et `contact` restent absents, ils poseraient
des décisions qui ne sont pas prises.

Le cookie de rafraîchissement devient visible par un APIKeyCookie en
auto_error=False, purement documentaire : lit_le_cookie() reste seul maître du
401 de /auth/refresh.

Au passage, health.py posait son tag deux fois, une fois sur son APIRouter et
une fois à l'include_router.
2026-09-16 10:12:41 +02:00
Johan LEROY e85c83972a chore(frontend): sort le rapport JUnit du suivi git
La ligne /test-results ajoutee au .gitignore n'avait aucun effet : le fichier
etait deja suivi, et un .gitignore ne s'applique pas a un fichier indexe. Il
reapparaissait donc modifie dans le diff de chacun a chaque execution de ng
test, qui le regenere a l'emplacement fixe par angular.json.
2026-09-16 09:20:48 +02:00