Avant : une seule image (Dockerfile.app) qui bundle AdonisJS + SPA static.
Après : deux images, deux deployments, deux workflows CI avec path filters
indépendants.
Architecture
- rubis-web (NodePort 30110, exposé via Traefik)
· nginx-alpine + SPA Vite dist + nginx.conf
· sert /assets/* (cache 1y immutable), / (try_files index.html SPA fallback)
· reverse-proxy /api/* → rubis-api.rubis.svc.cluster.local:3333
- rubis-api (ClusterIP, accessible uniquement depuis le cluster)
· AdonisJS V7 + workers BullMQ dans le même process
· init-container migrate (idempotent, depuis build/)
· /api/v1/health pour les probes K3s + healthcheck Docker
- rubis-redis (ClusterIP, inchangé)
Bénéfices
- Build/deploy indépendants : changement front ne reconstruit pas l'API,
changement API ne reconstruit pas le SPA
- nginx en frontal donne du gzip + cache long sur les assets fingerprintés
- API n'expose plus de surface publique (defense in depth)
- Routes plus simples : on retire le wildcard SPA fallback dans
start/routes.ts (nginx s'en charge), on retire @adonisjs/static aurait
été cohérent mais on le garde pour minimiser les diffs
Files
- Dockerfile.api (replaces Dockerfile.app, Node-only)
- Dockerfile.web (new, nginx)
- apps/web/nginx.conf (new)
- k3s/app/api.yml (replaces deployment.yml + service.yml, ClusterIP)
- k3s/app/web.yml (new, NodePort 30110)
- .gitea/workflows/deploy-{api,web}.yml (replaces deploy-app.yml)
- /api/v1/health route ajoutée
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
75 lines
2.2 KiB
Nginx Configuration File
75 lines
2.2 KiB
Nginx Configuration File
# nginx.conf — reverse proxy + SPA static pour rubis-web
|
|
#
|
|
# Sert :
|
|
# - / → assets SPA + index.html avec fallback try_files (TanStack Router)
|
|
# - /api/* → reverse proxy vers le service ClusterIP rubis-api:3333
|
|
#
|
|
# Le service rubis-api est interne au cluster K3s (pas de NodePort).
|
|
# Seul nginx (NodePort 30110 → Traefik) est exposé.
|
|
|
|
upstream rubis_api {
|
|
server rubis-api.rubis.svc.cluster.local:3333 max_fails=3 fail_timeout=10s;
|
|
keepalive 32;
|
|
}
|
|
|
|
# Compression gzip — Vite produit déjà du JS minifié, mais HTML/CSS/SVG
|
|
# bénéficient toujours du gzip on-the-fly.
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_min_length 1024;
|
|
gzip_types
|
|
application/javascript
|
|
application/json
|
|
application/xml
|
|
text/css
|
|
text/html
|
|
text/plain
|
|
image/svg+xml;
|
|
|
|
server {
|
|
listen 80 default_server;
|
|
server_name _;
|
|
root /var/www;
|
|
index index.html;
|
|
|
|
# Limite raisonnable pour les uploads de factures (PDF, photos).
|
|
client_max_body_size 25m;
|
|
|
|
# Désactive les logs sur les ressources qui spamment (favicon, robots).
|
|
location = /favicon.ico { log_not_found off; access_log off; }
|
|
location = /robots.txt { log_not_found off; access_log off; }
|
|
|
|
# Assets fingerprintés Vite : cache long, immutable.
|
|
location /assets/ {
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# API → reverse proxy vers AdonisJS (rubis-api ClusterIP).
|
|
# Inclut /api/v1/checkin/* qui sert les liens reçus par email.
|
|
location /api/ {
|
|
proxy_pass http://rubis_api;
|
|
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 https;
|
|
proxy_set_header Connection "";
|
|
|
|
# Timeouts adaptés au plus long endpoint (upload OCR Mistral).
|
|
proxy_connect_timeout 5s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
}
|
|
|
|
# SPA fallback : toute route non-asset, non-API → index.html
|
|
# (TanStack Router gère côté client).
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
# index.html lui-même : pas de cache (pour récupérer les nouveaux
|
|
# builds sans purger côté client).
|
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
}
|
|
}
|