Le src="logo.png" relatif resolvait mal sur les routes autres que "/" :
chemin absolu "/logo.png". Titre de page "Frontend" -> "EnerVision", favicon
regenere depuis l'icone reelle du logo. Pages login/change-password
retravaillees (fond degrade de marque, carte plus large, logo et titre plus
presents) via une classe .auth-page partagee plutot que dupliquee par page.
Centralise les couleurs/rayons/espacements dispersés en dur dans chaque page
(login, change-password, dashboard) en tokens CSS partagés, ajoute un petit
set de composants standalone réutilisables (ev-button, ev-card, ev-alert,
ev-badge) et intègre le logo EnerVision en en-tête des pages ainsi que dans
Swagger/ReDoc côté backend.
Refs #91
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
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.
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
CI en échec sur ruff format (ligne trop longue) et mypy (retour Any non
annoté, assignation Literal non étroite). Corrige sans changer le
comportement.
Ajoute la dernière mesure d'un site (SiteService.current), en réutilisant
la vérification d'existence déjà en place pour GET /sites/{site_id} :
SiteService gagne une dépendance ReadingRepository, sur le modèle de
composition déjà utilisé par StatsService/SensorService. Un site connu
sans lecture rend 200 avec les champs de mesure à null et
data_quality="critical" ; seul un site_id absent rend 404.
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.
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.
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.
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
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é.
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.
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.
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.
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.
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.
`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.
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.
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.
Les onze fichiers non conformes au .prettierrc du projet etaient exactement
ceux introduits ou modifies par cette branche ; les vingt-deux autres du
frontend etaient deja propres.
Aucune modification de comportement : indentation, virgules finales et
longueur de ligne a 100 caracteres.
Chart.js conserve chaque instance dans un registre lie au canvas et lui
attache un observateur de redimensionnement. Sans destroy, tout survit a la
destruction du composant, et une re-creation sur le meme canvas echoue avec
"Canvas is already in use".
Les doubles de test gagnent destroy : TestBed detruit les fixtures apres
chaque test, un mock sans cette methode fait tomber les specs existantes.
Sans catchError, la premiere reponse en erreur terminait le flux du timer :
le rafraichissement ne repartait jamais et l'ecran restait fige sur des
chiffres perimes, sans rien signaler.
Le catchError porte sur l'observable interne du switchMap. Place sur le flux
externe il terminerait le timer tout autant. Un signal error alimente un
bandeau, efface des qu'une reponse valide revient.
La convention de docs/architecture/40-data.md impose des noms de tables au
singulier, que les quatre tables d'authentification respectent déjà. Les six
tables data passent donc au singulier, avec leurs contraintes et leurs index.
La révision n'étant appliquée que sur des bases locales, elle est modifiée sur
place plutôt que doublée d'une migration de renommage.
alert_id désignait deux colonnes différentes : la clé métier text de l'API Mock
et la clé étrangère bigint de recommendation. La première devient
source_alert_id, la seconde pointe désormais vers alert.alert_id.