feat: add fullstack TypeScript template with Docker support
- Created package.json for managing workspaces (frontend and backend) - Added scripts for development, build, testing, and Docker operations - Implemented kill-dev-processes.sh script to terminate development processes gracefully
This commit is contained in:
230
Dockerfile
Normal file
230
Dockerfile
Normal file
@@ -0,0 +1,230 @@
|
||||
# Multi-Stage Dockerfile für Full Stack TypeScript Template
|
||||
# Optimiert für Produktion mit Nginx + Node.js Backend für Traefik Deployment
|
||||
|
||||
# =============================================================================
|
||||
# Build Stage für Frontend
|
||||
# =============================================================================
|
||||
FROM node:18-alpine AS frontend-builder
|
||||
|
||||
WORKDIR /app/frontend
|
||||
|
||||
# Package files kopieren für besseres Caching
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm ci && npm cache clean --force
|
||||
|
||||
# Source Code kopieren und builden
|
||||
COPY frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
# =============================================================================
|
||||
# Build Stage für Backend
|
||||
# =============================================================================
|
||||
FROM node:18-alpine AS backend-builder
|
||||
|
||||
WORKDIR /app/backend
|
||||
|
||||
# Package files kopieren
|
||||
COPY backend/package*.json ./
|
||||
RUN npm install
|
||||
|
||||
# Source Code kopieren und builden
|
||||
COPY backend/ ./
|
||||
RUN npm run build
|
||||
|
||||
# =============================================================================
|
||||
# Production Stage mit Nginx + Node.js + Supervisor
|
||||
# =============================================================================
|
||||
FROM node:18-alpine AS production
|
||||
|
||||
# Build Arguments
|
||||
ARG NODE_ENV=production
|
||||
ARG BUILD_VERSION=unknown
|
||||
|
||||
# Environment Variables setzen
|
||||
ENV NODE_ENV=${NODE_ENV}
|
||||
ENV BUILD_VERSION=${BUILD_VERSION}
|
||||
ENV TZ=Europe/Berlin
|
||||
|
||||
# System-Dependencies installieren
|
||||
RUN apk add --no-cache \
|
||||
nginx \
|
||||
supervisor \
|
||||
curl \
|
||||
tzdata \
|
||||
tini \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Arbeitsverzeichnisse erstellen
|
||||
RUN mkdir -p /var/www/html \
|
||||
&& mkdir -p /app/backend/dist \
|
||||
&& mkdir -p /etc/supervisor.d \
|
||||
&& mkdir -p /data
|
||||
|
||||
# Frontend Build von Builder Stage kopieren
|
||||
COPY --from=frontend-builder /app/frontend/build /var/www/html
|
||||
|
||||
# Backend Build und Dependencies kopieren
|
||||
COPY --from=backend-builder /app/backend/dist /app/backend/dist
|
||||
COPY --from=backend-builder /app/backend/node_modules /app/backend/node_modules
|
||||
|
||||
# Nginx Konfiguration erstellen
|
||||
RUN printf 'worker_processes auto;\n\
|
||||
error_log /var/log/nginx/error.log warn;\n\
|
||||
pid /var/run/nginx.pid;\n\
|
||||
\n\
|
||||
events {\n\
|
||||
worker_connections 1024;\n\
|
||||
use epoll;\n\
|
||||
multi_accept on;\n\
|
||||
}\n\
|
||||
\n\
|
||||
http {\n\
|
||||
include /etc/nginx/mime.types;\n\
|
||||
default_type application/octet-stream;\n\
|
||||
\n\
|
||||
# Logging\n\
|
||||
log_format main '\''$remote_addr - $remote_user [$time_local] "$request" '\''\n\
|
||||
'\''$status $body_bytes_sent "$http_referer" '\''\n\
|
||||
'\''"$http_user_agent" "$http_x_forwarded_for"'\'';\n\
|
||||
access_log /var/log/nginx/access.log main;\n\
|
||||
\n\
|
||||
# Performance\n\
|
||||
sendfile on;\n\
|
||||
tcp_nopush on;\n\
|
||||
tcp_nodelay on;\n\
|
||||
keepalive_timeout 65;\n\
|
||||
gzip on;\n\
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;\n\
|
||||
\n\
|
||||
# Security Headers\n\
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;\n\
|
||||
add_header X-Content-Type-Options "nosniff" always;\n\
|
||||
add_header X-XSS-Protection "1; mode=block" always;\n\
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;\n\
|
||||
\n\
|
||||
server {\n\
|
||||
listen 80;\n\
|
||||
server_name _;\n\
|
||||
\n\
|
||||
client_max_body_size 50M;\n\
|
||||
client_body_timeout 60s;\n\
|
||||
client_header_timeout 60s;\n\
|
||||
\n\
|
||||
# Health Check Endpoint\n\
|
||||
location /health {\n\
|
||||
access_log off;\n\
|
||||
return 200 "healthy";\n\
|
||||
add_header Content-Type text/plain;\n\
|
||||
}\n\
|
||||
\n\
|
||||
# Serve Frontend (SPA)\n\
|
||||
location / {\n\
|
||||
root /var/www/html;\n\
|
||||
index index.html;\n\
|
||||
try_files $uri $uri/ /index.html;\n\
|
||||
\n\
|
||||
# Cache static assets\n\
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {\n\
|
||||
expires 1y;\n\
|
||||
add_header Cache-Control "public, immutable";\n\
|
||||
}\n\
|
||||
}\n\
|
||||
\n\
|
||||
# Proxy API requests to backend\n\
|
||||
location /api/ {\n\
|
||||
proxy_pass http://127.0.0.1:3001/api/;\n\
|
||||
proxy_http_version 1.1;\n\
|
||||
proxy_set_header Upgrade $http_upgrade;\n\
|
||||
proxy_set_header Connection "upgrade";\n\
|
||||
proxy_set_header Host $host;\n\
|
||||
proxy_set_header X-Real-IP $remote_addr;\n\
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\
|
||||
proxy_set_header X-Forwarded-Proto $scheme;\n\
|
||||
proxy_set_header X-Forwarded-Host $host;\n\
|
||||
proxy_cache_bypass $http_upgrade;\n\
|
||||
\n\
|
||||
# Timeouts\n\
|
||||
proxy_connect_timeout 60s;\n\
|
||||
proxy_send_timeout 60s;\n\
|
||||
proxy_read_timeout 60s;\n\
|
||||
}\n\
|
||||
}\n\
|
||||
}' > /etc/nginx/nginx.conf
|
||||
|
||||
# Supervisor Konfiguration erstellen
|
||||
RUN printf '[supervisord]\n\
|
||||
nodaemon=true\n\
|
||||
loglevel=info\n\
|
||||
logfile=/dev/stdout\n\
|
||||
logfile_maxbytes=0\n\
|
||||
\n\
|
||||
[program:nginx]\n\
|
||||
command=/usr/sbin/nginx -g "daemon off;"\n\
|
||||
autorestart=true\n\
|
||||
startretries=3\n\
|
||||
stdout_logfile=/dev/stdout\n\
|
||||
stdout_logfile_maxbytes=0\n\
|
||||
stderr_logfile=/dev/stderr\n\
|
||||
stderr_logfile_maxbytes=0\n\
|
||||
priority=10\n\
|
||||
\n\
|
||||
[program:backend]\n\
|
||||
command=node /app/backend/dist/index.js\n\
|
||||
directory=/app/backend\n\
|
||||
environment=NODE_ENV="production",DATA_DIR="/data",PORT="3001"\n\
|
||||
autorestart=true\n\
|
||||
startretries=3\n\
|
||||
stdout_logfile=/dev/stdout\n\
|
||||
stdout_logfile_maxbytes=0\n\
|
||||
stderr_logfile=/dev/stderr\n\
|
||||
stderr_logfile_maxbytes=0\n\
|
||||
priority=20\n\
|
||||
user=node\n' > /etc/supervisor.d/supervisord.ini
|
||||
|
||||
# Health Check Script erstellen
|
||||
RUN printf '#!/bin/sh\n\
|
||||
# Health check for both nginx and backend\n\
|
||||
nginx_status=$(curl -f -s http://localhost/health > /dev/null && echo "ok" || echo "fail")\n\
|
||||
backend_status=$(curl -f -s http://localhost:3001/api/health > /dev/null && echo "ok" || echo "fail")\n\
|
||||
\n\
|
||||
if [ "$nginx_status" = "ok" ] && [ "$backend_status" = "ok" ]; then\n\
|
||||
exit 0\n\
|
||||
else\n\
|
||||
echo "Health check failed: nginx=$nginx_status, backend=$backend_status"\n\
|
||||
exit 1\n\
|
||||
fi' > /usr/local/bin/healthcheck.sh && chmod +x /usr/local/bin/healthcheck.sh
|
||||
|
||||
# Berechtigungen setzen
|
||||
RUN chown -R node:node /data /app/backend
|
||||
RUN chown nginx:nginx /var/www/html
|
||||
|
||||
# Health Check konfigurieren
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||
CMD /usr/local/bin/healthcheck.sh
|
||||
|
||||
# Arbeitsverzeichnis setzen
|
||||
WORKDIR /app
|
||||
|
||||
# Startup mit Tini für proper signal handling
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor.d/supervisord.ini"]
|
||||
|
||||
# =============================================================================
|
||||
# Traefik Labels für automatisches Service Discovery
|
||||
# =============================================================================
|
||||
LABEL traefik.enable=true
|
||||
LABEL traefik.http.routers.app.rule="Host(\`your-domain.com\`)"
|
||||
LABEL traefik.http.routers.app.entrypoints=websecure
|
||||
LABEL traefik.http.routers.app.tls.certresolver=letsencrypt
|
||||
LABEL traefik.http.services.app.loadbalancer.server.port=80
|
||||
|
||||
# =============================================================================
|
||||
# Metadata Labels
|
||||
# =============================================================================
|
||||
LABEL maintainer="Your Name <your.email@example.com>"
|
||||
LABEL version="${BUILD_VERSION}"
|
||||
LABEL description="Full Stack TypeScript Template - Traefik Ready"
|
||||
LABEL org.opencontainers.image.source="https://github.com/your-org/fullstack-typescript-template"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/your-org/fullstack-typescript-template#readme"
|
||||
LABEL org.opencontainers.image.vendor="Your Organization"
|
||||
LABEL org.opencontainers.image.licenses="MIT"
|
||||
Reference in New Issue
Block a user