From 5319ed5d7af6a1f72154b7a02047598a3d9db92d Mon Sep 17 00:00:00 2001 From: donpat1to Date: Tue, 28 Oct 2025 23:07:40 +0100 Subject: [PATCH] added entrypoint for docker --- .env.example | 4 ---- .env.template | 16 ++++++++++++++ Dockerfile | 24 ++++++++++++++++---- backend/src/server.ts | 2 +- docker-compose.yml | 8 +++++++ docker-init.sh | 49 +++++++++++++++++++++++++++++++++++++++++ frontend/vite.config.ts | 2 +- 7 files changed, 95 insertions(+), 10 deletions(-) delete mode 100644 .env.example create mode 100644 .env.template create mode 100644 docker-init.sh diff --git a/.env.example b/.env.example deleted file mode 100644 index 3331251..0000000 --- a/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# .env.production example -NODE_ENV=production -JWT_SECRET=your-secret-key -DATABASE_PATH=/app/data/production.db \ No newline at end of file diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..bab13d6 --- /dev/null +++ b/.env.template @@ -0,0 +1,16 @@ +# === SCHICHTPLANER DOCKER COMPOSE ENVIRONMENT VARIABLES === +# Diese Datei wird von docker-compose automatisch geladen + +# Security +JWT_SECRET=${JWT_SECRET:-your-secret-key-please-change} +NODE_ENV=${NODE_ENV:-production} + +# Database +DB_PATH=${DB_PATH:-/app/data/database.db} + +# Server +PORT=${PORT:-3002} + +# App Configuration +APP_TITLE="Shift Planning App" +ENABLE_PRO=${ENABLE_PRO:-false} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f24e4a3..a2418cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,14 +35,19 @@ RUN npm run build --workspace=frontend # Verify Python and OR-Tools installation RUN python -c "from ortools.sat.python import cp_model; print('OR-Tools installed successfully')" -# Production stage (same as above) +# Production stage FROM node:20-bookworm WORKDIR /app +# Install system dependencies including gettext-base for envsubst +RUN apt-get update && apt-get install -y gettext-base && \ + rm -rf /var/lib/apt/lists/* + RUN npm install -g pm2 RUN mkdir -p /app/data +# Copy application files COPY --from=builder /app/backend/dist/ ./dist/ COPY --from=builder /app/backend/package*.json ./ @@ -54,6 +59,14 @@ COPY --from=builder /app/ecosystem.config.cjs ./ COPY --from=builder /app/backend/src/database/ ./dist/database/ COPY --from=builder /app/backend/src/database/ ./database/ +# Copy init script and env template +COPY docker-init.sh /usr/local/bin/ +COPY .env.template ./ + +# Set execute permissions for init script +RUN chmod +x /usr/local/bin/docker-init.sh + +# Create user and set permissions RUN groupadd -g 1001 nodejs && \ useradd -m -u 1001 -s /bin/bash -g nodejs schichtplan && \ chown -R schichtplan:nodejs /app && \ @@ -61,10 +74,13 @@ RUN groupadd -g 1001 nodejs && \ chmod 775 /app/data ENV PM2_HOME=/app/.pm2 + +# Set entrypoint to init script and keep existing cmd +ENTRYPOINT ["/usr/local/bin/docker-init.sh"] +CMD ["pm2-runtime", "ecosystem.config.cjs"] + USER schichtplan EXPOSE 3002 HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:3002/api/health || exit 1 - -CMD ["pm2-runtime", "ecosystem.config.cjs"] \ No newline at end of file + CMD wget --no-verbose --tries=1 --spider http://localhost:3002/api/health || exit 1 \ No newline at end of file diff --git a/backend/src/server.ts b/backend/src/server.ts index fe25eb6..4887026 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -26,7 +26,7 @@ const isDevelopment = process.env.NODE_ENV === 'development'; if (process.env.NODE_ENV === 'production') { console.info('Checking for JWT_SECRET'); const JWT_SECRET = process.env.JWT_SECRET; - if (!JWT_SECRET || JWT_SECRET === 'your-secret-key') { + if (!JWT_SECRET || JWT_SECRET === 'your-secret-key-please-change') { console.error('❌ Fatal: JWT_SECRET not set or using default value'); process.exit(1); } diff --git a/docker-compose.yml b/docker-compose.yml index ccf6cb5..4666d37 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,11 +4,19 @@ services: schichtplaner: container_name: schichtplaner image: ghcr.io/donpat1to/schichtenplaner:v1.0.0 + environment: + - NODE_ENV=production + - JWT_SECRET=${JWT_SECRET:-your-secret-key-please-change} ports: - "3002:3002" volumes: - app_data:/app/data restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3002/api/health"] + interval: 30s + timeout: 10s + retries: 3 volumes: app_data: \ No newline at end of file diff --git a/docker-init.sh b/docker-init.sh new file mode 100644 index 0000000..aea0b92 --- /dev/null +++ b/docker-init.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -e + +echo "🚀 Container Initialisierung gestartet..." + +# Funktion zum Generieren eines sicheren Secrets +generate_secret() { + length=$1 + tr -dc 'A-Za-z0-9!@#$%^&*()_+-=' < /dev/urandom | head -c $length +} + +# Prüfe ob .env existiert, falls nicht erstelle sie +if [ ! -f /app/.env ]; then + echo "📝 Erstelle .env Datei..." + + # Generiere automatisch ein sicheres JWT Secret falls nicht gesetzt + if [ -z "$JWT_SECRET" ] || [ "$JWT_SECRET" = "your-secret-key-please-change" ]; then + export JWT_SECRET=$(generate_secret 64) + echo "🔑 Automatisch generiertes JWT Secret wurde erstellt" + fi + + # Erstelle .env aus Template + envsubst < /app/.env.template > /app/.env + + # Logge die ersten Zeilen (ohne Secrets) + echo "✅ .env Datei erstellt mit folgenden Einstellungen:" + head -n 5 /app/.env +else + echo "ℹ️ .env Datei existiert bereits" + + # Validiere bestehende .env Datei + if ! grep -q "JWT_SECRET=" /app/.env; then + echo "❌ Fehler: JWT_SECRET nicht in .env gefunden" + exit 1 + fi +fi + +# Sicherheitsüberprüfungen +if grep -q "your-secret-key" /app/.env; then + echo "❌ FEHLER: Standard JWT Secret in .env gefunden - bitte ändern!" + exit 1 +fi + +# Setze sichere Berechtigungen +chmod 600 /app/.env +chown -R schichtplaner:nodejs /app + +echo "🔧 Starte Anwendung..." +exec "$@" \ No newline at end of file diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 87d6054..7e6a61e 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -14,7 +14,7 @@ export default defineConfig(({ mode }) => { const clientEnv = { NODE_ENV: mode, ENABLE_PRO: env.ENABLE_PRO || 'false', - VITE_APP_TITLE: env.VITE_APP_TITLE || 'Shift Planning App', + VITE_APP_TITLE: env.APP_TITLE || 'Shift Planning App', VITE_API_URL: isProduction ? '/api' : 'http://localhost:3002/api', }