fix(setup): nginx-Template im Installer auf Prod-Stand bringen
setup_server.sh schrieb eine veraltete Minimal-nginx-Config ohne /redoc, /static/-Alias (nötig für lokale Swagger/ReDoc-Assets) und Security-Header. Bei einem Neu-Setup wäre /docs sofort wieder kaputt gewesen. Jetzt 1:1 an die auf 137/164 laufende Config angeglichen. Zusätzlich inkonsistente Step-Nummerierung ([1/6]...[5/7]) korrigiert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Ahyx6D3r7G1EuAc42nezn
This commit is contained in:
+84
-16
@@ -5,10 +5,10 @@
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "==> [1/6] System-Pakete aktualisieren"
|
echo "==> [1/7] System-Pakete aktualisieren"
|
||||||
apt-get update && apt-get upgrade -y
|
apt-get update && apt-get upgrade -y
|
||||||
|
|
||||||
echo "==> [2/6] Abhängigkeiten installieren"
|
echo "==> [2/7] Abhängigkeiten installieren"
|
||||||
apt-get install -y \
|
apt-get install -y \
|
||||||
python3 python3-venv python3-dev python3-pip \
|
python3 python3-venv python3-dev python3-pip \
|
||||||
postgresql postgresql-contrib \
|
postgresql postgresql-contrib \
|
||||||
@@ -16,7 +16,7 @@ apt-get install -y \
|
|||||||
nginx \
|
nginx \
|
||||||
git curl build-essential libpq-dev
|
git curl build-essential libpq-dev
|
||||||
|
|
||||||
echo "==> [3/6] PostgreSQL einrichten"
|
echo "==> [3/7] PostgreSQL einrichten"
|
||||||
systemctl enable postgresql && systemctl start postgresql
|
systemctl enable postgresql && systemctl start postgresql
|
||||||
|
|
||||||
# Datenbank + User anlegen (Produktiv-DB + Test-DB)
|
# Datenbank + User anlegen (Produktiv-DB + Test-DB)
|
||||||
@@ -36,7 +36,7 @@ GRANT ALL PRIVILEGES ON DATABASE timemaster_db TO timemaster;
|
|||||||
GRANT ALL PRIVILEGES ON DATABASE timemaster_test TO timemaster;
|
GRANT ALL PRIVILEGES ON DATABASE timemaster_test TO timemaster;
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
echo "==> [4/6] Redis einrichten"
|
echo "==> [4/7] Redis einrichten"
|
||||||
systemctl enable redis-server && systemctl start redis-server
|
systemctl enable redis-server && systemctl start redis-server
|
||||||
|
|
||||||
echo "==> [5/7] Python venv + Abhängigkeiten"
|
echo "==> [5/7] Python venv + Abhängigkeiten"
|
||||||
@@ -50,35 +50,103 @@ echo "==> [6/7] Alembic Migrations ausführen"
|
|||||||
alembic upgrade head
|
alembic upgrade head
|
||||||
|
|
||||||
echo "==> [7/7] nginx für Frontend konfigurieren"
|
echo "==> [7/7] nginx für Frontend konfigurieren"
|
||||||
mkdir -p /opt/timemaster/frontend/dist
|
mkdir -p /opt/timemaster/frontend/dist /opt/timemaster/backend/static
|
||||||
cat > /etc/nginx/sites-available/timemaster << 'NGINX'
|
cat > /etc/nginx/sites-available/timemaster << 'NGINX'
|
||||||
|
# HTTP-only Konfiguration (SSL/HTTPS noch nicht eingerichtet)
|
||||||
|
# Sobald ein TLS-Zertifikat vorhanden ist:
|
||||||
|
# 1. Listen-Block auf 443 ssl http2 erweitern
|
||||||
|
# 2. ssl_certificate / ssl_certificate_key einkommentieren
|
||||||
|
# 3. HSTS-Header hinzufügen
|
||||||
|
# 4. HTTP->HTTPS-Redirect aktivieren
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|
||||||
root /opt/timemaster/frontend/dist;
|
client_max_body_size 20M;
|
||||||
index index.html;
|
|
||||||
|
|
||||||
# SPA fallback
|
# API Backend
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
# API proxy
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
proxy_pass http://127.0.0.1:8000;
|
proxy_pass http://127.0.0.1:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" 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' data:; connect-src 'self'; frame-ancestors 'none';" always;
|
||||||
}
|
}
|
||||||
|
|
||||||
# FastAPI docs (dev)
|
# FastAPI Docs (nur in dev aktiv – docs_url ist None in Production)
|
||||||
location /docs {
|
location /docs {
|
||||||
proxy_pass http://127.0.0.1:8000/docs;
|
proxy_pass http://127.0.0.1:8000/docs;
|
||||||
proxy_set_header Host $host;
|
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" 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' data:; connect-src 'self'; frame-ancestors 'none';" always;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /redoc {
|
||||||
|
proxy_pass http://127.0.0.1:8000/redoc;
|
||||||
|
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" 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' data:; connect-src 'self'; frame-ancestors 'none';" always;
|
||||||
|
}
|
||||||
|
|
||||||
location /openapi.json {
|
location /openapi.json {
|
||||||
proxy_pass http://127.0.0.1:8000/openapi.json;
|
proxy_pass http://127.0.0.1:8000/openapi.json;
|
||||||
|
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" 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' data:; connect-src 'self'; frame-ancestors 'none';" always;
|
||||||
|
}
|
||||||
|
|
||||||
|
# React Frontend (statische Dateien)
|
||||||
|
# HINWEIS: nginx-Regel: add_header in einem location-Block ueberschreibt
|
||||||
|
# alle add_header-Direktiven des parent server-Blocks. Daher Security-Header
|
||||||
|
# in jede location wiederholen.
|
||||||
|
location / {
|
||||||
|
root /opt/timemaster/frontend/dist;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
expires 1d;
|
||||||
|
|
||||||
|
add_header Cache-Control "public, must-revalidate";
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" 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' data:; connect-src 'self'; frame-ancestors 'none';" always;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Swagger/ReDoc-Assets (app/static/swagger-ui im Repo) + Uploads
|
||||||
|
location /static/ {
|
||||||
|
alias /opt/timemaster/backend/static/;
|
||||||
|
expires 7d;
|
||||||
|
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" 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' data:; connect-src 'self'; frame-ancestors 'none';" always;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NGINX
|
NGINX
|
||||||
@@ -93,7 +161,7 @@ echo ""
|
|||||||
echo "✓ Setup abgeschlossen!"
|
echo "✓ Setup abgeschlossen!"
|
||||||
echo " Backend: sudo systemctl start timemaster"
|
echo " Backend: sudo systemctl start timemaster"
|
||||||
echo " Frontend: http://$(hostname -I | awk '{print $1}')/"
|
echo " Frontend: http://$(hostname -I | awk '{print $1}')/"
|
||||||
echo " API-Docs: http://$(hostname -I | awk '{print $1}')/docs"
|
echo " API-Docs: http://$(hostname -I | awk '{print $1}')/docs (und /redoc)"
|
||||||
echo ""
|
echo ""
|
||||||
echo ""
|
echo ""
|
||||||
echo "Frontend deployen (nach lokalem Build):"
|
echo "Frontend deployen (nach lokalem Build):"
|
||||||
|
|||||||
Reference in New Issue
Block a user