# Nginx config — audiocontrol frontend # Variables substituted at container start via envsubst: # $BEOCREATE_HOST — hostname/IP of the BeoCreate Pi # $BEOCREATE_PORT — hifiberrydsp REST API port (default 13141) server { listen 80; server_name _; # Docker's internal DNS — resolves all upstreams lazily so nginx starts # even if a backend is temporarily unreachable at boot resolver 127.0.0.11 valid=10s ipv6=off; # ── Serve React SPA ────────────────────────────────────────────────────── root /var/www/audiocontrol; index index.html; location / { try_files $uri $uri/ /index.html; } # ── HiFiBerry DSP REST API (BeoCreate Pi) ──────────────────────────────── location /api/hifiberry/ { set $beocreate http://${BEOCREATE_HOST}:${BEOCREATE_PORT}; rewrite ^/api/hifiberry/(.*)$ /$1 break; proxy_pass $beocreate; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 10s; proxy_connect_timeout 5s; } # ── Mopidy JSON-RPC (HTTP) ─────────────────────────────────────────────── location /api/mopidy/ { set $mopidy http://mopidy:6680; rewrite ^/api/mopidy/(.*)$ /mopidy/$1 break; proxy_pass $mopidy; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 10s; } # ── Mopidy library images ─────────────────────────────────────────────── location /api/mopidy-image/ { set $mopidy http://mopidy:6680; rewrite ^/api/mopidy-image/(.*)$ /$1 break; proxy_pass $mopidy; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 10s; } # ── Mopidy WebSocket (real-time events) ────────────────────────────────── location /ws/mopidy { set $mopidy_ws http://mopidy:6680; proxy_pass $mopidy_ws/mopidy/ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 86400s; proxy_send_timeout 86400s; } # ── Snapcast JSON-RPC (HTTP) ───────────────────────────────────────────── location /api/snapcast/rpc { set $snapserver_http http://snapserver:1780; rewrite ^/api/snapcast/rpc$ /jsonrpc break; proxy_pass $snapserver_http; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 10s; } # ── Spotify credential store (served by bandcamp-api) ─────────────────── location /api/spotify/ { set $bandcamp_api http://bandcamp-api:8091; rewrite ^/api/spotify/(.*)$ /spotify/$1 break; proxy_pass $bandcamp_api; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 15s; proxy_connect_timeout 10s; } # ── Bandcamp API (credential store + login test) ───────────────────────── location /api/bandcamp/ { set $bandcamp_api http://bandcamp-api:8091; rewrite ^/api/bandcamp/(.*)$ /$1 break; proxy_pass $bandcamp_api; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 30s; proxy_connect_timeout 10s; } # ── Snapcast now playing recognition ───────────────────────────────────── # host.docker.internal lives in /etc/hosts (extra_hosts), not in Docker's # embedded DNS (127.0.0.11), so we must NOT use a set/$var here — a static # proxy_pass resolves via /etc/hosts at config load time and always works. location /api/now-playing/ { rewrite ^/api/now-playing/(.*)$ /$1 break; proxy_pass http://host.docker.internal:8090; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 45s; proxy_connect_timeout 5s; } # ── Snapcast JSON-RPC WebSocket ────────────────────────────────────────── location /ws/snapcast { set $snapserver_ws http://snapserver:1780; rewrite ^/ws/snapcast$ /jsonrpc break; proxy_pass $snapserver_ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 86400s; proxy_send_timeout 86400s; } # ── Gzip ───────────────────────────────────────────────────────────────── gzip on; gzip_types text/plain text/css application/javascript application/json; gzip_min_length 1024; }