# /etc/nginx/conf.d/rob.tn.conf — rob.tn site config for moments. # # Lives on oolon (the per-site nginx ingress that terminates rob.tn 443 # traffic). Static frontend out of /var/www/moments; /api/* reverse- # proxied across the WG mesh to the moments-api binary on nikola. The # UI fetches /api/v1/... so the strip matches what Vite's dev proxy # does (drop the /api prefix before sending to axum, whose routes are # mounted at /v1/*). upstream moments_api { server nikola.kosherinata.internal:42424 max_fails=3 fail_timeout=30s; keepalive 8; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name rob.tn; ssl_certificate /etc/pki/tls/misc/oolon.hanzalova.internal.pem; ssl_certificate_key /etc/pki/tls/private/oolon.hanzalova.internal.pem; # Public forge — visitors are not on the internal mTLS mesh, so no # client-cert verification here. The X25519MLKEM768 default falls # back to classical curves for clients that don't speak PQ yet. ssl_protocols TLSv1.3; root /var/www/moments; index index.html; # Static SPA: serve the file if it exists, else fall back to index.html # so client-side routing works. location / { try_files $uri $uri/ /index.html; add_header Cache-Control "no-cache" always; } # Asset bundles are content-hashed by Vite — safe to cache aggressively. location ~* \.(js|css|woff2?|ttf|eot|svg|png|jpg|jpeg|gif|ico|webp|avif)$ { expires 30d; add_header Cache-Control "public, max-age=2592000, immutable"; try_files $uri =404; } location /api/ { # Strip /api so axum sees /v1/events, not /api/v1/events. rewrite ^/api/(.*)$ /$1 break; proxy_pass http://moments_api; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 30s; proxy_connect_timeout 5s; } access_log /var/log/nginx/rob.tn.access.log; error_log /var/log/nginx/rob.tn.error.log; } server { listen 80; listen [::]:80; server_name rob.tn; return 301 https://$host$request_uri; }