# ========================================================================= # Strangler routing: Django (holy-backend) and Rust (holy-rust) run side by # side, sharing one SQLite DB + media dir + SECRET_KEY. Every endpoint group # below can be served by EITHER backend, so cutover is per-group and instant: # change a `proxy_pass` target, then `docker exec holy-frontend nginx -s reload` # (or recreate the container). Rollback is the same edit in reverse. # # Cutover order (see backend-rs/README.md): # Phase 1 reads -> categories, flavors, banners # Phase 2 auth+write -> auth/token, ratings, replies, users, notifications, tickets # Phase 3 admin+jobs -> admin-custom (THEN stop Django beat + set # RUST_ENABLE_SCHEDULER=true so exactly one scheduler runs) # Phase 4 retire -> point catch-all /api/ at rust; serve /media/ from # nginx; drop Django /admin/ + /static/ if unused. # ========================================================================= upstream django_backend { server holy-backend:8000; } upstream rust_backend { server holy-rust:8001; } server { listen 80; # --- SPA ------------------------------------------------------------- location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; } # --- API: per-group strangler routing -------------------------------- # Longest-prefix match wins, so these specific groups override the # catch-all /api/ below. To cut a group over, swap django_backend -> # rust_backend on its proxy_pass line. # Phase 1 — read-only (safe to move first): location /api/categories/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } location /api/flavors/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } location /api/banners/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } # Phase 2 — auth + writes: location /api/auth/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } location = /api/token/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } # legacy JWT alias location /api/ratings/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } location /api/replies/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } location /api/users/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } location /api/notifications/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } location /api/tickets/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } # Phase 3 — admin + jobs (cut over, THEN flip the scheduler ownership): location /api/admin-custom/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } # OpenAPI schema / Swagger UI (both backends serve these): location /api/schema { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } # Catch-all: anything not explicitly cut over stays on Django. location /api/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } # --- Django-only paths (retire last) --------------------------------- # Django admin site + its static assets. The app's own admin UI uses # /api/admin-custom/, so these are only the Django admin site + DRF UI. location /admin/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } location /static/ { proxy_pass http://django_backend; include /etc/nginx/proxy_params.conf; } # --- Media ----------------------------------------------------------- # Served straight from the shared bind mount (read-only) so media does not # depend on either backend being up — a prerequisite for retiring Django. location /media/ { alias /usr/share/nginx/media/; access_log off; expires 7d; add_header Cache-Control "public"; try_files $uri =404; } }