diff --git a/deploy/dak-backend.service b/deploy/dak-backend.service new file mode 100644 index 0000000..6cae648 --- /dev/null +++ b/deploy/dak-backend.service @@ -0,0 +1,17 @@ +[Unit] +Description=DAK Zweitmeinungs-Portal Backend (FastAPI/Uvicorn) +After=network.target mariadb.service + +[Service] +Type=simple +User=dak +Group=dak +WorkingDirectory=/opt/dak-portal/backend +Environment="PATH=/opt/dak-portal/backend/venv/bin:/usr/local/bin:/usr/bin" +EnvironmentFile=/opt/dak-portal/backend/.env +ExecStart=/opt/dak-portal/backend/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 2 +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/deploy/dak-portal.nginx.conf b/deploy/dak-portal.nginx.conf new file mode 100644 index 0000000..c2a39a0 --- /dev/null +++ b/deploy/dak-portal.nginx.conf @@ -0,0 +1,52 @@ +server { + listen 443 ssl http2; + server_name dak.complexcaresolutions.de; + + # SSL certificates (managed by Plesk/Let's Encrypt) + ssl_certificate /etc/letsencrypt/live/dak.complexcaresolutions.de/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/dak.complexcaresolutions.de/privkey.pem; + + # Frontend — serve static files from Vite build + root /opt/dak-portal/frontend/dist; + index index.html; + + # API proxy to FastAPI backend + location /api/ { + proxy_pass http://127.0.0.1:8000/api/; + 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_read_timeout 120s; + client_max_body_size 20M; + } + + # FastAPI docs (optional, remove in production) + location /docs { + proxy_pass http://127.0.0.1:8000/docs; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /openapi.json { + proxy_pass http://127.0.0.1:8000/openapi.json; + } + + # SPA fallback — all other routes serve index.html + location / { + try_files $uri $uri/ /index.html; + } + + # Security headers + add_header X-Content-Type-Options nosniff always; + add_header X-Frame-Options DENY always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy strict-origin-when-cross-origin always; +} + +server { + listen 80; + server_name dak.complexcaresolutions.de; + return 301 https://$host$request_uri; +} diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100755 index 0000000..b72c72b --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# DAK Zweitmeinungs-Portal — Deployment Script for Hetzner 1 +# Run as root on the target server +set -euo pipefail + +APP_DIR="/opt/dak-portal" +REPO_URL="https://github.com/complexcaresolutions/dak.c2s.git" +BRANCH="main" +SERVICE_USER="dak" + +echo "=== DAK Portal Deployment ===" + +# 1. Create service user if needed +if ! id "$SERVICE_USER" &>/dev/null; then + echo "Creating service user '$SERVICE_USER'..." + useradd --system --shell /bin/false --home-dir "$APP_DIR" "$SERVICE_USER" +fi + +# 2. Clone or update repository +if [ -d "$APP_DIR/.git" ]; then + echo "Updating existing installation..." + cd "$APP_DIR" + git fetch origin + git checkout "$BRANCH" + git pull origin "$BRANCH" +else + echo "Fresh install — cloning repository..." + git clone --branch "$BRANCH" "$REPO_URL" "$APP_DIR" + cd "$APP_DIR" +fi + +# 3. Backend setup +echo "Setting up backend..." +cd "$APP_DIR/backend" + +if [ ! -d "venv" ]; then + python3 -m venv venv +fi + +source venv/bin/activate +pip install --quiet --upgrade pip +pip install --quiet -r requirements.txt + +# 4. Create .env if it doesn't exist +if [ ! -f ".env" ]; then + echo "Creating .env from template..." + cat > .env << 'ENVEOF' +DB_HOST=localhost +DB_PORT=3306 +DB_NAME=dak_c2s +DB_USER=dak_c2s_admin +DB_PASSWORD=CHANGE_ME + +JWT_SECRET_KEY=CHANGE_ME_GENERATE_A_SECURE_KEY +JWT_ALGORITHM=HS256 +ACCESS_TOKEN_EXPIRE_MINUTES=15 +REFRESH_TOKEN_EXPIRE_DAYS=7 + +SMTP_HOST=smtp.complexcaresolutions.de +SMTP_PORT=465 +SMTP_USER=noreply@complexcaresolutions.de +SMTP_PASSWORD=CHANGE_ME +SMTP_FROM=noreply@complexcaresolutions.de + +APP_NAME=DAK Zweitmeinungs-Portal +CORS_ORIGINS=https://dak.complexcaresolutions.de +ENVEOF + echo "!! IMPORTANT: Edit $APP_DIR/backend/.env with real credentials !!" +fi + +# 5. Run database migrations +echo "Running Alembic migrations..." +alembic upgrade head + +# 6. Frontend build +echo "Building frontend..." +cd "$APP_DIR/frontend" + +if ! command -v pnpm &>/dev/null; then + echo "Installing pnpm..." + npm install -g pnpm +fi + +pnpm install --frozen-lockfile +pnpm build + +# 7. Set ownership +echo "Setting file ownership..." +chown -R "$SERVICE_USER":"$SERVICE_USER" "$APP_DIR" + +# 8. Install systemd service +echo "Installing systemd service..." +cp "$APP_DIR/deploy/dak-backend.service" /etc/systemd/system/ +systemctl daemon-reload +systemctl enable dak-backend +systemctl restart dak-backend + +# 9. Install nginx config +echo "Installing nginx config..." +cp "$APP_DIR/deploy/dak-portal.nginx.conf" /etc/nginx/conf.d/dak-portal.conf +nginx -t && systemctl reload nginx + +echo "" +echo "=== Deployment complete ===" +echo "" +echo "Next steps:" +echo " 1. Edit /opt/dak-portal/backend/.env with real DB password and JWT secret" +echo " 2. Create admin user: cd /opt/dak-portal/backend && source venv/bin/activate && python scripts/create_admin.py" +echo " 3. Check service: systemctl status dak-backend" +echo " 4. Check logs: journalctl -u dak-backend -f" +echo " 5. Test: curl https://dak.complexcaresolutions.de/api/health"