Files
Presencia/frontend/nginx.conf
Claude 3df521ac30 Revue complète : sécurité, optimisations et UI/UX mobile
Sécurité
- JWT : un JWT_SECRET d'exemple ou trop court est ignoré au profit d'un
  secret aléatoire conservé en base (avant, la valeur publique de
  docker-compose.yml permettait de forger un jeton admin).
- Le compte est relu en base à chaque requête : désactivation, changement
  de rôle et réinitialisation du mot de passe prennent effet immédiatement.
- Connexion : 10 échecs max par e-mail / 15 min, temps constant que l'e-mail
  existe ou non, mot de passe admin retiré des logs.
- Validation des entrées (ids, dates, mois, longueurs, e-mail) : 400 au
  lieu de 500. Un admin ne peut plus se désactiver ni se rétrograder.
- Mots de passe : 8 caractères minimum ; changement en libre-service.
- Export : nom de fichier conforme RFC 5987 (un nom de société avec « — »
  faisait planter l'export).
- esc() échappe aussi les guillemets (injection d'attributs HTML).
- En-têtes CSP / X-Frame-Options / nosniff (Nginx + API), API et Postgres
  publiés sur 127.0.0.1 seulement, image backend non-root via npm ci.

Optimisations
- Export : une requête pour tout le mois au lieu d'une par cadre.
- Saisie groupée : un seul INSERT (unnest), doublons dédupliqués.
- Dates renvoyées en chaînes (plus de décalage d'un jour selon le TZ).
- Planning : chaque clic met à jour l'affichage localement au lieu de
  recharger le mois ; les réponses de mois périmées sont ignorées.
- Recherche Utilisateurs / Plannings filtrée localement (plus une requête
  par touche) ; statuts des sociétés chargés en parallèle.

UI/UX
- Mobile : toutes les destinations dans la barre basse (Sociétés et Ma
  saisie étaient inaccessibles à l'admin) ; tableaux affichés en cartes
  (les boutons d'action étaient hors écran).
- Boutons désactivés pendant l'envoi, focus et Échap dans les dialogues,
  retour à l'écran de connexion quand la session est révoquée, erreurs
  d'export affichées au lieu d'un fichier JSON téléchargé, actions
  destructives signalées, confirmation avant désactivation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D5Bdayziw6tybgqETZoNSt
2026-10-09 07:17:54 +00:00

59 lines
2.3 KiB
Nginx Configuration File

server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
server_tokens off;
# Resolve the backend hostname at request time (via Docker's embedded DNS)
# instead of once at startup, so a recreated backend container is picked
# up without needing to restart/reload this nginx.
resolver 127.0.0.11 valid=30s;
location /api/ {
set $backend_upstream presencia-backend;
# $request_uri (the raw, un-normalized original URI) must be appended
# explicitly: when the proxy_pass target is built from a variable,
# nginx does NOT replace the matched location prefix with the rest of
# the request URI like it does for a static proxy_pass value — it
# would otherwise always forward the literal "/api/" with nothing
# appended, regardless of the actual path requested.
proxy_pass http://$backend_upstream:4790$request_uri;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# The API sets these too (for direct access); keep a single copy.
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header Referrer-Policy;
include /etc/nginx/snippets/security-headers.conf;
}
# index.html, app.js and the stylesheets keep the same filenames across
# releases, so without this a browser happily pairs a freshly fetched
# index.html with a cached app.js from a previous deploy. That mismatch
# throws on the first missing element id and kills the whole script — which
# looks exactly like a server-side failure. "no-cache" still allows caching,
# it only forces a revalidation, so unchanged files cost one 304.
location ~* \.(?:html|js|css)$ {
add_header Cache-Control "no-cache";
include /etc/nginx/snippets/security-headers.conf;
try_files $uri $uri/ /index.html;
}
# Fonts are content-stable and large; let them sit in the browser cache.
location ~* \.(?:woff2|woff|ttf)$ {
add_header Cache-Control "public, max-age=2592000";
include /etc/nginx/snippets/security-headers.conf;
}
location / {
add_header Cache-Control "no-cache";
include /etc/nginx/snippets/security-headers.conf;
try_files $uri $uri/ /index.html;
}
}