Phase 1 (Foundation): project skeleton, TOML config + Pydantic validation, MQTT bus wrapper, SQLite schema (9 tables), Click CLI, process supervisor. Phase 2 (Camera): RTSP capture via OpenCV, MOG2 motion detection with configurable sensitivity/zones, adaptive FPS recording (2fps idle/30fps motion) via FFmpeg subprocess, HLS live streaming, pre-motion ring buffer. Phase 3 (Web UI): Flask + Bootstrap 5 dark theme, 6 blueprints, Jinja2 templates (dashboard, kiosk 2x2 grid, events, sensors, recordings, settings), PWA with service worker + Web Push, full admin settings UI with config persistence. Remote Access: WireGuard tunnel configs, nginx reverse proxy with HLS caching + rate limiting, bandwidth-optimized remote HLS stream (426x240 @ 500kbps), DO droplet setup script, certbot TLS. 29 tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
166 lines
5.5 KiB
Plaintext
166 lines
5.5 KiB
Plaintext
# Nginx reverse proxy config for Digital Ocean droplet
|
|
# Proxies HTTPS traffic to Vigilar at home via WireGuard tunnel
|
|
#
|
|
# Install: cp vigilar.conf /etc/nginx/sites-available/vigilar
|
|
# ln -s /etc/nginx/sites-available/vigilar /etc/nginx/sites-enabled/
|
|
# nginx -t && systemctl reload nginx
|
|
#
|
|
# TLS: certbot --nginx -d vigilar.yourdomain.com
|
|
|
|
# Rate limiting zones
|
|
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
|
|
limit_req_zone $binary_remote_addr zone=stream:10m rate=5r/s;
|
|
limit_conn_zone $binary_remote_addr zone=connlimit:10m;
|
|
|
|
# HLS segment cache — reduces repeat requests hitting the home uplink
|
|
proxy_cache_path /var/cache/nginx/vigilar_hls
|
|
levels=1:2
|
|
keys_zone=hls_cache:10m
|
|
max_size=256m
|
|
inactive=30s
|
|
use_temp_path=off;
|
|
|
|
# Upstream: Vigilar on home server via WireGuard tunnel
|
|
upstream vigilar_home {
|
|
server 10.99.0.2:49735;
|
|
# If home server goes down, fail fast
|
|
keepalive 4;
|
|
}
|
|
|
|
# Redirect HTTP → HTTPS
|
|
server {
|
|
listen 80;
|
|
server_name vigilar.yourdomain.com;
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name vigilar.yourdomain.com;
|
|
|
|
# TLS (managed by certbot)
|
|
ssl_certificate /etc/letsencrypt/live/vigilar.yourdomain.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/vigilar.yourdomain.com/privkey.pem;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
|
ssl_prefer_server_ciphers off;
|
|
ssl_session_cache shared:SSL:10m;
|
|
ssl_session_timeout 1d;
|
|
|
|
# Security headers
|
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
|
|
add_header X-Frame-Options DENY always;
|
|
add_header X-Content-Type-Options nosniff always;
|
|
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
|
|
|
# Connection limits — protect 22 Mbps home uplink
|
|
# Max 10 simultaneous connections per IP
|
|
limit_conn connlimit 10;
|
|
|
|
# --- HLS streams (bandwidth-critical path) ---
|
|
# Cache .ts segments on the droplet to avoid re-fetching from home
|
|
# when multiple remote viewers request the same segment
|
|
location ~ ^/cameras/.+/hls/.+\.ts$ {
|
|
proxy_pass http://vigilar_home;
|
|
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;
|
|
|
|
# Cache segments for 10s — they're 2s segments, so this covers
|
|
# multiple viewers watching the same feed without re-fetching
|
|
proxy_cache hls_cache;
|
|
proxy_cache_valid 200 10s;
|
|
proxy_cache_key $uri;
|
|
add_header X-Cache-Status $upstream_cache_status;
|
|
|
|
# Rate limit: 5 segment requests/sec per IP
|
|
limit_req zone=stream burst=20 nodelay;
|
|
}
|
|
|
|
# HLS playlists — don't cache (they update every segment)
|
|
location ~ ^/cameras/.+/hls/.+\.m3u8$ {
|
|
proxy_pass http://vigilar_home;
|
|
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;
|
|
|
|
# No cache — playlists must be fresh
|
|
proxy_cache off;
|
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
}
|
|
|
|
# --- SSE event stream ---
|
|
location /events/stream {
|
|
proxy_pass http://vigilar_home;
|
|
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;
|
|
|
|
# SSE: disable buffering, long timeout
|
|
proxy_buffering off;
|
|
proxy_cache off;
|
|
proxy_read_timeout 3600s;
|
|
proxy_send_timeout 3600s;
|
|
chunked_transfer_encoding on;
|
|
proxy_set_header Connection '';
|
|
proxy_http_version 1.1;
|
|
}
|
|
|
|
# --- API endpoints ---
|
|
location ~ ^/system/api/ {
|
|
proxy_pass http://vigilar_home;
|
|
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;
|
|
|
|
limit_req zone=api burst=10 nodelay;
|
|
}
|
|
|
|
# --- Static assets (cache aggressively on droplet) ---
|
|
location /static/ {
|
|
proxy_pass http://vigilar_home;
|
|
proxy_set_header Host $host;
|
|
|
|
proxy_cache hls_cache;
|
|
proxy_cache_valid 200 1h;
|
|
add_header X-Cache-Status $upstream_cache_status;
|
|
}
|
|
|
|
# --- Service worker (must not be cached stale) ---
|
|
location = /static/sw.js {
|
|
proxy_pass http://vigilar_home;
|
|
proxy_set_header Host $host;
|
|
proxy_cache off;
|
|
add_header Cache-Control "no-cache";
|
|
}
|
|
|
|
# --- Everything else (pages, PWA manifest, etc.) ---
|
|
location / {
|
|
proxy_pass http://vigilar_home;
|
|
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;
|
|
|
|
proxy_http_version 1.1;
|
|
|
|
limit_req zone=api burst=20 nodelay;
|
|
}
|
|
|
|
# Deny access to config/sensitive paths
|
|
location ~ ^/(config|migrations|scripts|tests) {
|
|
deny all;
|
|
}
|
|
|
|
# Max upload size (for config changes, etc.)
|
|
client_max_body_size 1m;
|
|
|
|
# Logging
|
|
access_log /var/log/nginx/vigilar_access.log;
|
|
error_log /var/log/nginx/vigilar_error.log;
|
|
}
|