Phases 6 et 7 : application SvelteKit, PWA, systemd, documentation
Application — rendu serveur sur adapter-node, conforme au §3bis : - tableau de bord, recherche à facettes, fiche détaillée, calendrier - /a-propos et /methode prérendues ; tout le reste en rendu serveur - API JSON ouverte : /api/textes, /api/textes/[slug], /api/echeances - recherche FTS5 avec surlignage et classement bm25 pondéré (le titre pèse dix fois plus que les points clés) - facettes recalculées sur le résultat filtré par les AUTRES facettes : cocher un thème doit recompter les statuts encore disponibles, sinon les compteurs mentent Identité OKI sobrifiée : tokens en CSS vanilla, thème sombre par défaut et clair opt-in, or comme seule couleur d'action, flag-bar une fois par écran, polices Archivo/Inter auto-hébergées. Politique de sécurité de contenu interdisant toute requête tierce. Les statuts ne reposent jamais sur la seule couleur : libellé en toutes lettres et pastille de forme distincte. Budget respecté très largement : 6,6 Ko de JavaScript sur l'accueil (3 Ko compressés) pour un plafond de 100 Ko. Les faits clés — AFD à 500 €, saisine 2026-915 DC, statut — sont lisibles sans JavaScript. Trois défauts trouvés en testant l'application, pas en relisant le code : - v_textes est une vue, et une vue SQLite n'a pas de rowid : la jointure avec l'index plein texte échouait (migration 005) - ppl-montagne remontait en « pertinence forte » alors que le corpus dit « sans portée pour la Guadeloupe » — la cotation comptait le mot sans lire la négation. Idem pour pjl-logement et accord-globe. Corrigé par une lecture du voisinage, avec quatre tests de non-régression. - les libellés d'affichage étaient importés depuis /server dans un composant, ce que SvelteKit interdit à raison PWA : service worker réseau-d'abord pour les pages, cache-d'abord pour le coffre du build. Une veille législative ne doit pas servir une page périmée quand le réseau répond — une date de promulgation change tout. Les réponses issues du cache portent un en-tête qui le dit. systemd : timer du pipeline (6 h, 18 h, dimanche 9 h, avec dispersion et rattrapage), application activée par socket avec mise en sommeil à 300 s. Unités durcies, validées par systemd-analyze. Documentation : README d'installation sur Debian/Ubuntu vierge, README-pipeline avec le piège des deux couples d'identifiants PISTE, et scripts/verifier-conformite.sh qui contrôle mécaniquement le §3bis et le §8. 172 tests, npm run check à 0 erreur, make verifier au vert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
feb5544599
commit
34d81c66a8
@@ -0,0 +1,47 @@
|
||||
# Passage de veille : collecte les sources officielles et met la base à jour.
|
||||
#
|
||||
# Unité oneshot déclenchée par veille-legislative.timer. Installation :
|
||||
# sudo cp systemd/veille-*.{service,timer,socket} /etc/systemd/system/
|
||||
# sudo systemctl daemon-reload
|
||||
# sudo systemctl enable --now veille-legislative.timer
|
||||
|
||||
[Unit]
|
||||
Description=Veille législative — passage de collecte
|
||||
Documentation=file:///opt/veille-legislative/README-pipeline.md
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=veille
|
||||
Group=veille
|
||||
WorkingDirectory=/opt/veille-legislative
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
EnvironmentFile=/opt/veille-legislative/.env
|
||||
ExecStart=/opt/veille-legislative/.venv/bin/python -m pipeline.update
|
||||
|
||||
# Un passage qui s'éternise a rencontré un problème réseau : on l'arrête.
|
||||
TimeoutStartSec=900
|
||||
|
||||
# Durcissement : le pipeline lit le réseau et écrit un seul fichier.
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/veille-legislative/data
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
RestrictNamespaces=true
|
||||
LockPersonality=true
|
||||
MemoryDenyWriteExecute=true
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallErrorNumber=EPERM
|
||||
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=veille-legislative
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,26 @@
|
||||
# Cadence de la veille : deux fois par jour, plus un passage renforcé le
|
||||
# dimanche pour rattraper les publications de fin de semaine au Journal officiel.
|
||||
#
|
||||
# Les décisions du Conseil constitutionnel tombent en semaine, les promulgations
|
||||
# suivent de un à trois jours : deux passages quotidiens suffisent largement, et
|
||||
# ménagent des services publics gratuits.
|
||||
|
||||
[Unit]
|
||||
Description=Déclenchement de la veille législative (6 h, 18 h, et dimanche 9 h)
|
||||
Documentation=file:///opt/veille-legislative/README-pipeline.md
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 06:00:00
|
||||
OnCalendar=*-*-* 18:00:00
|
||||
OnCalendar=Sun *-*-* 09:00:00
|
||||
|
||||
# Dispersion : inutile de frapper Légifrance à la seconde ronde en même temps
|
||||
# que tous les autres services qui l'interrogent.
|
||||
RandomizedDelaySec=900
|
||||
|
||||
# Rattrape le passage manqué si la machine était éteinte.
|
||||
Persistent=true
|
||||
Unit=veille-legislative.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -0,0 +1,52 @@
|
||||
# Application web de la veille législative (SvelteKit, adapter-node).
|
||||
#
|
||||
# Démarrée par veille-web.socket à la première requête, elle s'arrête d'elle-même
|
||||
# après IDLE_TIMEOUT secondes sans trafic, et systemd la réveille à la suivante.
|
||||
#
|
||||
# Node ≥ 20.6 requis : les variables ne sont PAS chargées automatiquement en
|
||||
# production, d'où --env-file.
|
||||
|
||||
[Unit]
|
||||
Description=Veille législative — application web
|
||||
Documentation=file:///opt/veille-legislative/README.md
|
||||
Requires=veille-web.socket
|
||||
After=network.target veille-web.socket
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=veille
|
||||
Group=veille
|
||||
WorkingDirectory=/opt/veille-legislative/web
|
||||
ExecStart=/usr/bin/node --env-file=/opt/veille-legislative/.env build
|
||||
|
||||
Environment=NODE_ENV=production
|
||||
Environment=VEILLE_DB=/opt/veille-legislative/data/veille.db
|
||||
# Mise en sommeil après cinq minutes sans requête.
|
||||
Environment=IDLE_TIMEOUT=300
|
||||
# Obligatoire derrière un reverse proxy, sinon les soumissions de formulaire
|
||||
# sont refusées (« Cross-site POST form submissions are forbidden »).
|
||||
# Alternative : PROTOCOL_HEADER=x-forwarded-proto + HOST_HEADER=x-forwarded-host
|
||||
Environment=ORIGIN=https://veille.example.org
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# Durcissement : l'application ne fait que lire la base.
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadOnlyPaths=/opt/veille-legislative/data
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
RestrictNamespaces=true
|
||||
LockPersonality=true
|
||||
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=veille-web
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,17 @@
|
||||
# Activation par socket de l'application web.
|
||||
#
|
||||
# systemd tient le port ouvert et ne démarre l'application qu'à la première
|
||||
# requête. Combinée à IDLE_TIMEOUT dans le service, l'application se met en
|
||||
# sommeil sans trafic : l'empreinte mémoire d'un site de veille peu fréquenté
|
||||
# tombe à zéro entre deux visites, ce qui compte sur un VPS à 5 €.
|
||||
|
||||
[Unit]
|
||||
Description=Socket d'écoute de la veille législative
|
||||
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:3971
|
||||
# Le service hérite du descripteur : adapter-node lit LISTEN_PID et LISTEN_FDS.
|
||||
Accept=no
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
Reference in New Issue
Block a user