Brings the Work Package Suite from a browser-local prototype to a multi-tenant, SQL-backed deployment hardened for customer IP. Auth & access control - Local username/password login (bcrypt + JWT in an HttpOnly cookie), admin-managed users, per-project membership, and project-scoped API access. - Admin console: change user roles, view the audit trail, manage settings. Security hardening - CSP / HSTS / X-Frame-Options / nosniff headers in nginx; Secure cookie via X-Forwarded-Proto; CSRF Origin check; attribute-safe output escaping. - Login lockout, token_version session revocation, stronger password policy, fail-closed secret loading, encrypted (AES-256) database backups. Persistence & schema - SOPs and Work Packages are now DB-backed and shared across users, written through a durable client sync outbox that queues offline edits. - Alembic migrations applied automatically on container start. New capabilities - Phase 2 dashboard (progress, gating, pagination, archive). - Phase 3 PWA "Field View" with offline caching and auth fallback. - WP owner assignment with OPTIONAL email notifications, OFF by default and toggled from the admin console. SMTP password is read only from the SMTP_PASSWORD env var (never stored); emails carry a WP number + deep link, never customer IP. Also: IBM Carbon restyle, Help section, and DEPLOYMENT.md brought up to date. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
3.0 KiB
Plaintext
60 lines
3.0 KiB
Plaintext
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Work Package Suite — NGINX site config
|
|
#
|
|
# Serves the static site and reverse-proxies /api/ to the Python API
|
|
# (FastAPI on 127.0.0.1:8000), which stores SOPs, Work Packages, and comments
|
|
# in PostgreSQL. Same-origin, so there is no CORS.
|
|
#
|
|
# Install:
|
|
# 1. Copy the project files to the web root (e.g. /var/www/wp-suite).
|
|
# 2. Run the API as a service (see server/README.md) listening on :8000.
|
|
# 3. Put this file at /etc/nginx/conf.d/wp-suite.conf
|
|
# (or /etc/nginx/sites-available/ + symlink into sites-enabled/).
|
|
# 4. Replace server_name and the ssl_certificate paths.
|
|
# 5. sudo nginx -t && sudo systemctl reload nginx
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
# Redirect plain HTTP to HTTPS
|
|
server {
|
|
listen 80;
|
|
server_name wp-suite.company.local; # <-- your internal hostname
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
server_name wp-suite.company.local; # <-- your internal hostname
|
|
|
|
# Internal certificate from your company CA
|
|
ssl_certificate /etc/nginx/ssl/wp-suite.crt; # <-- cert path
|
|
ssl_certificate_key /etc/nginx/ssl/wp-suite.key; # <-- key path
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
# Static site
|
|
root /var/www/wp-suite; # <-- web root
|
|
index index.html;
|
|
|
|
# ── Security response headers (defense-in-depth) ─────────────────────────
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header Referrer-Policy "no-referrer" always;
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'; form-action 'self'" always;
|
|
|
|
location / {
|
|
try_files $uri $uri/ =404;
|
|
}
|
|
|
|
# ── API proxy → Python (FastAPI) ─────────────────────────────────────────
|
|
# All /api/ calls (SOPs, Work Packages, comments) go to the local API
|
|
# service. Keep the /api/ prefix — the API routes are defined under /api.
|
|
location /api/ {
|
|
proxy_pass http://127.0.0.1:8000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-For $remote_addr;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
client_max_body_size 5m; # WP documents can be larger
|
|
}
|
|
}
|