Compare commits

..

3 Commits

Author SHA1 Message Date
663eb61352 added basic route for production 2025-10-28 23:54:28 +01:00
23f1dd7aa0 updated docker entry point 2025-10-28 23:33:00 +01:00
5319ed5d7a added entrypoint for docker 2025-10-28 23:07:40 +01:00
9 changed files with 100 additions and 13 deletions

View File

@@ -1,4 +0,0 @@
# .env.production example
NODE_ENV=production
JWT_SECRET=your-secret-key
DATABASE_PATH=/app/data/production.db

16
.env.template Normal file
View File

@@ -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}

View File

@@ -35,14 +35,19 @@ RUN npm run build --workspace=frontend
# Verify Python and OR-Tools installation # Verify Python and OR-Tools installation
RUN python -c "from ortools.sat.python import cp_model; print('OR-Tools installed successfully')" 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 FROM node:20-bookworm
WORKDIR /app 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 npm install -g pm2
RUN mkdir -p /app/data RUN mkdir -p /app/data
# Copy application files
COPY --from=builder /app/backend/dist/ ./dist/ COPY --from=builder /app/backend/dist/ ./dist/
COPY --from=builder /app/backend/package*.json ./ 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/ ./dist/database/
COPY --from=builder /app/backend/src/database/ ./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 && \ RUN groupadd -g 1001 nodejs && \
useradd -m -u 1001 -s /bin/bash -g nodejs schichtplan && \ useradd -m -u 1001 -s /bin/bash -g nodejs schichtplan && \
chown -R schichtplan:nodejs /app && \ chown -R schichtplan:nodejs /app && \
@@ -61,10 +74,13 @@ RUN groupadd -g 1001 nodejs && \
chmod 775 /app/data chmod 775 /app/data
ENV PM2_HOME=/app/.pm2 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 USER schichtplan
EXPOSE 3002 EXPOSE 3002
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ 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 wget --no-verbose --tries=1 --spider http://localhost:3002/api/health || exit 1
CMD ["pm2-runtime", "ecosystem.config.cjs"]

View File

@@ -26,7 +26,7 @@ const isDevelopment = process.env.NODE_ENV === 'development';
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
console.info('Checking for JWT_SECRET'); console.info('Checking for JWT_SECRET');
const JWT_SECRET = process.env.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'); console.error('❌ Fatal: JWT_SECRET not set or using default value');
process.exit(1); process.exit(1);
} }

View File

@@ -4,11 +4,19 @@ services:
schichtplaner: schichtplaner:
container_name: schichtplaner container_name: schichtplaner
image: ghcr.io/donpat1to/schichtenplaner:v1.0.0 image: ghcr.io/donpat1to/schichtenplaner:v1.0.0
environment:
- NODE_ENV=production
- JWT_SECRET=${JWT_SECRET:-your-secret-key-please-change}
ports: ports:
- "3002:3002" - "3002:3002"
volumes: volumes:
- app_data:/app/data - app_data:/app/data
restart: unless-stopped restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3002/api/health"]
interval: 30s
timeout: 10s
retries: 3
volumes: volumes:
app_data: app_data:

50
docker-init.sh Normal file
View File

@@ -0,0 +1,50 @@
#!/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
if [ ! -f /app/.env ]; then
echo "📝 Erstelle .env Datei..."
# Verwende vorhandenes JWT_SECRET oder generiere ein neues
if [ -z "$JWT_SECRET" ] || [ "$JWT_SECRET" = "your-secret-key-please-change" ]; then
export JWT_SECRET=$(generate_secret 64)
echo "🔑 Automatisch sicheres JWT Secret generiert"
else
echo "🔑 Verwende vorhandenes JWT Secret aus Umgebungsvariable"
fi
# Erstelle .env aus Template mit envsubst
envsubst < /app/.env.template > /app/.env
echo "✅ .env Datei erstellt"
else
echo " .env Datei existiert bereits"
# Wenn .env existiert, aber JWT_SECRET Umgebungsvariable gesetzt ist, aktualisiere sie
if [ -n "$JWT_SECRET" ] && [ "$JWT_SECRET" != "your-secret-key-please-change" ]; then
echo "🔑 Aktualisiere JWT Secret in .env Datei"
# Aktualisiere nur das JWT_SECRET in der .env Datei
sed -i "s/^JWT_SECRET=.*/JWT_SECRET=$JWT_SECRET/" /app/.env
fi
fi
# Validiere dass JWT_SECERT nicht der Standardwert ist
if grep -q "JWT_SECRET=your-secret-key-please-change" /app/.env; then
echo "❌ FEHLER: Standard JWT Secret in .env gefunden!"
echo "❌ Bitte setzen Sie JWT_SECRET Umgebungsvariable"
exit 1
fi
# Setze sichere Berechtigungen
chmod 600 /app/.env
echo "🔧 Starte Anwendung..."
exec "$@"

View File

@@ -1,4 +1,4 @@
// frontend/src/pages/Features/Features.tsx // frontend/src/components/Layou/FooterLinks/Features/Features.tsx
import React from 'react'; import React from 'react';
const Features: React.FC = () => { const Features: React.FC = () => {

View File

@@ -107,7 +107,7 @@
.createButton { .createButton {
padding: 10px 20px; padding: 10px 20px;
background-color: #2ecc71; background-color: #51258f;
color: white; color: white;
border: none; border: none;
border-radius: 4px; border-radius: 4px;
@@ -116,7 +116,7 @@
} }
.createButton:hover { .createButton:hover {
background-color: #27ae60; background-color: #51258f;
} }
.createButton:disabled { .createButton:disabled {

View File

@@ -14,7 +14,7 @@ export default defineConfig(({ mode }) => {
const clientEnv = { const clientEnv = {
NODE_ENV: mode, NODE_ENV: mode,
ENABLE_PRO: env.ENABLE_PRO || 'false', 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', VITE_API_URL: isProduction ? '/api' : 'http://localhost:3002/api',
} }
@@ -61,6 +61,7 @@ export default defineConfig(({ mode }) => {
sourcemap: isDevelopment ? 'inline' : false, sourcemap: isDevelopment ? 'inline' : false,
// Generate deterministic hashes for better caching and security // Generate deterministic hashes for better caching and security
assetsDir: 'assets', assetsDir: 'assets',
base: mode === 'production' ? '/' : '/',
rollupOptions: { rollupOptions: {
output: { output: {
// Security: Use content hashes for cache busting and integrity // Security: Use content hashes for cache busting and integrity