From 225e930e4c3eab3f080790c526e585c260bb9774 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 7 Jan 2026 17:42:04 +0100 Subject: [PATCH] Update deployment script to ensure persistent data management, enhance backup and restore processes, and improve error handling. Modify .gitignore to exclude sensitive production data and update deployment documentation to reflect changes. Add environment variable loading for production secrets in configuration files. --- .gitignore | 11 ++--- DEPLOYMENT.md | 2 +- deploy-production.sh | 94 +++++++++++++++++++++++++++++++++--------- harheimertc.config.cjs | 35 +++++++++++++++- harheimertc.simple.cjs | 12 +++++- package-lock.json | 6 +-- 6 files changed, 127 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index ac35ba4..ff8aab3 100644 --- a/.gitignore +++ b/.gitignore @@ -149,11 +149,8 @@ gitleaks gitleaks.tar.gz osv-scanner -# Sensitive data (DO NOT commit production sessions!) -# server/data/sessions.json - uncomment for production -public/uploads -public/uploads/* - -# Server data files (all data files to prevent overwriting) -server/data/ +# Sensitive / production data (DO NOT commit!) +server/data/** !server/data/.gitkeep +public/data/** +public/uploads/** diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index ae148a2..7778c60 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -119,7 +119,7 @@ pm2 save ## Notizen - **`.output/` ist NICHT im Git** (steht in `.gitignore`) -- **Produktivdaten SIND im Git** (werden versioniert) +- **Produktivdaten SOLLEN NICHT im Git sein** (werden per `.gitignore` ausgeschlossen und in Produktion außerhalb des Repos persistiert, z.B. unter `/var/lib/harheimertc`) - **Bei Deployment:** Immer Backup → Build → Restore - **Bei Problemen:** Script verwenden oder manuell Daten sichern diff --git a/deploy-production.sh b/deploy-production.sh index ebe5e8c..7bdc713 100755 --- a/deploy-production.sh +++ b/deploy-production.sh @@ -1,10 +1,55 @@ #!/bin/bash +set -euo pipefail + +# Immer im Repo-Verzeichnis arbeiten (wichtig für Backup/Restore mit relativen Pfaden) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" # Deployment Script für Harheimer TC Website # Sichert Produktivdaten vor dem Build und stellt sie danach wieder her echo "=== Harheimer TC Deployment ===" echo "" +echo "Working directory: $(pwd)" +echo "" + +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "ERROR: Dieses Script muss im Git-Repository ausgeführt werden (kein .git gefunden)." + exit 1 +fi + +# Optional (empfohlen): Persistente Daten außerhalb des Git-Repos halten und per Symlink einbinden. +# Das verhindert zuverlässig, dass Git jemals Produktivdaten überschreibt. +DATA_ROOT="${DATA_ROOT:-/var/lib/harheimertc}" +mkdir -p "$DATA_ROOT" + +ensure_symlink_dir() { + local src="$1" # z.B. server/data + local target="$2" # z.B. /var/lib/harheimertc/server-data + + mkdir -p "$(dirname "$src")" + mkdir -p "$target" + + if [ -L "$src" ]; then + return 0 + fi + + if [ -d "$src" ]; then + echo " Moving $src -> $target (first-time migration)" + # Merge existing content into target + cp -a "$src/." "$target/" || true + rm -rf "$src" + fi + + ln -s "$target" "$src" + echo " Linked $src -> $target" +} + +echo "0. Ensuring persistent data directories (recommended)..." +ensure_symlink_dir "server/data" "$DATA_ROOT/server-data" +ensure_symlink_dir "public/data" "$DATA_ROOT/public-data" +ensure_symlink_dir "public/uploads" "$DATA_ROOT/public-uploads" +echo "" # 1. BACKUP: Laufende Produktivdaten VOR allen Git-Operationen sichern echo "1. Backing up current production data (pre-git)..." @@ -14,9 +59,21 @@ rm -rf .backup mkdir -p .backup # Backup server data (JSON) und CSVs immer vom Dateisystem, nicht aus 'stash' -cp -a server/data .backup/data_backup 2>/dev/null || echo " No server/data to backup" +if [ -d server/data ]; then + cp -a server/data .backup/data_backup + echo " Backed up server/data -> .backup/data_backup" +else + echo "ERROR: server/data existiert nicht. Abbruch, damit wir keine Repo-Defaults ausrollen." + exit 1 +fi + mkdir -p .backup/public_data -cp -a public/data/*.csv .backup/public_data/ 2>/dev/null || echo " No public CSVs to backup" +if ls public/data/*.csv >/dev/null 2>&1; then + cp -a public/data/*.csv .backup/public_data/ + echo " Backed up public/data/*.csv -> .backup/public_data/" +else + echo " No public CSVs to backup (public/data/*.csv not found)" +fi # 2. Handle local changes and Git Pull echo "2. Handling local changes and pulling latest from git..." @@ -29,15 +86,11 @@ fi # Stash any local changes (including production data) echo " Stashing local changes..." -git stash push -m "Production deployment stash $(date)" +git stash push -m "Production deployment stash $(date)" || true # Pull latest changes echo " Pulling latest changes..." git pull -if [ $? -ne 0 ]; then - echo "ERROR: Git pull failed!" - exit 1 -fi # Reset any accidental changes from stash restore (should be none now) git reset --hard HEAD >/dev/null 2>&1 @@ -46,10 +99,6 @@ git reset --hard HEAD >/dev/null 2>&1 echo "" echo "3. Installing dependencies..." npm install -if [ $? -ne 0 ]; then - echo "ERROR: npm install failed!" - exit 1 -fi # 4. Remove old build (but keep data!) echo "" @@ -60,31 +109,36 @@ rm -rf .output echo "" echo "5. Building application..." npm run build -if [ $? -ne 0 ]; then - echo "ERROR: Build failed!" - exit 1 -fi # 6. Restore Production Data (überschreibe Repo-Defaults mit Backup) echo "" echo "6. Restoring production data..." # Stelle server/data vollständig wieder her (inkl. config.json, users.json, news.json, sessions.json, members.json, membership-applications) -if [ -d .backup/data_backup ]; then - mkdir -p server/data - cp -a .backup/data_backup/. server/data/ -else - echo "No server/data to restore" +if [ ! -d .backup/data_backup ]; then + echo "ERROR: Backup-Verzeichnis .backup/data_backup fehlt. Abbruch." + exit 1 fi +mkdir -p server/data +cp -a .backup/data_backup/. server/data/ +echo " Restored server/data from backup." + # Stelle alle CSVs wieder her if ls .backup/public_data/*.csv >/dev/null 2>&1; then mkdir -p public/data cp -a .backup/public_data/*.csv public/data/ + echo " Restored public/data/*.csv from backup." else echo "No public CSVs to restore" fi +# Sanity Check: users.json muss existieren und darf nicht leer sein +if [ ! -s server/data/users.json ]; then + echo "ERROR: server/data/users.json fehlt oder ist leer nach Restore. Abbruch." + exit 1 +fi + # 7. Cleanup backup and stash echo "" echo "7. Cleaning up backup and stash..." diff --git a/harheimertc.config.cjs b/harheimertc.config.cjs index 98c13da..8785ae1 100644 --- a/harheimertc.config.cjs +++ b/harheimertc.config.cjs @@ -1,3 +1,11 @@ +// Load environment variables from .env (production secrets) +try { + // eslint-disable-next-line global-require + require('dotenv').config({ path: '/var/www/harheimertc/.env' }) +} catch (_e) { + // If dotenv isn't available or .env missing, continue (process.env may be set elsewhere) +} + module.exports = { apps: [{ name: 'harheimertc', @@ -10,7 +18,32 @@ module.exports = { max_memory_restart: '1G', env: { NODE_ENV: 'production', - PORT: 3100 + PORT: 3100, + // Secrets/Config (loaded from .env above, if present) + ENCRYPTION_KEY: process.env.ENCRYPTION_KEY, + JWT_SECRET: process.env.JWT_SECRET, + SMTP_HOST: process.env.SMTP_HOST, + SMTP_PORT: process.env.SMTP_PORT, + SMTP_USER: process.env.SMTP_USER, + SMTP_PASS: process.env.SMTP_PASS, + SMTP_FROM: process.env.SMTP_FROM, + SMTP_ADMIN: process.env.SMTP_ADMIN, + NUXT_PUBLIC_BASE_URL: process.env.NUXT_PUBLIC_BASE_URL, + COOKIE_SECURE: process.env.COOKIE_SECURE, + COOKIE_SAMESITE: process.env.COOKIE_SAMESITE, + CSP_ENABLED: process.env.CSP_ENABLED, + CSP_REPORT_ONLY: process.env.CSP_REPORT_ONLY, + CSP_VALUE: process.env.CSP_VALUE, + HIBP_ENABLED: process.env.HIBP_ENABLED, + HIBP_USER_AGENT: process.env.HIBP_USER_AGENT, + HIBP_TIMEOUT_MS: process.env.HIBP_TIMEOUT_MS, + HIBP_CACHE_TTL_MS: process.env.HIBP_CACHE_TTL_MS, + HIBP_FAIL_CLOSED: process.env.HIBP_FAIL_CLOSED, + AUDIT_LOG_ENABLED: process.env.AUDIT_LOG_ENABLED, + WEBAUTHN_ORIGIN: process.env.WEBAUTHN_ORIGIN, + WEBAUTHN_RP_ID: process.env.WEBAUTHN_RP_ID, + WEBAUTHN_RP_NAME: process.env.WEBAUTHN_RP_NAME, + WEBAUTHN_REQUIRE_UV: process.env.WEBAUTHN_REQUIRE_UV }, error_file: '/var/log/pm2/harheimertc-error.log', out_file: '/var/log/pm2/harheimertc-out.log', diff --git a/harheimertc.simple.cjs b/harheimertc.simple.cjs index 47b7663..c0f3c90 100644 --- a/harheimertc.simple.cjs +++ b/harheimertc.simple.cjs @@ -1,3 +1,11 @@ +// Load environment variables from .env (production secrets) +try { + // eslint-disable-next-line global-require + require('dotenv').config({ path: '/var/www/harheimertc/.env' }) +} catch (_e) { + // ignore +} + module.exports = { apps: [{ name: 'harheimertc', @@ -9,7 +17,9 @@ module.exports = { max_memory_restart: '1G', env: { NODE_ENV: 'production', - PORT: 3100 + PORT: 3100, + ENCRYPTION_KEY: process.env.ENCRYPTION_KEY, + JWT_SECRET: process.env.JWT_SECRET } }] } diff --git a/package-lock.json b/package-lock.json index afea33f..b9e429d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11374,9 +11374,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": {