138 lines
3.5 KiB
Docker
138 lines
3.5 KiB
Docker
# Frontend Dockerfile
|
|
FROM node:18-alpine AS builder
|
|
|
|
# Set working directory
|
|
WORKDIR /app
|
|
|
|
# Copy package files
|
|
COPY package*.json ./
|
|
|
|
# Install dependencies
|
|
RUN npm ci
|
|
|
|
# Copy source code
|
|
COPY . .
|
|
|
|
# Build arguments for environment variables
|
|
# For local network: VITE_API_URL will be set dynamically by the container hostname
|
|
ARG VITE_API_URL
|
|
ENV VITE_API_URL=$VITE_API_URL
|
|
|
|
# Build the application
|
|
RUN npm run build
|
|
|
|
# Production stage with Nginx
|
|
FROM nginx:alpine AS production
|
|
|
|
# Install curl for healthcheck
|
|
RUN apk add --no-cache curl
|
|
|
|
# Copy custom nginx configuration
|
|
COPY <<EOF /etc/nginx/nginx.conf
|
|
user nginx;
|
|
worker_processes auto;
|
|
error_log /var/log/nginx/error.log warn;
|
|
pid /var/run/nginx.pid;
|
|
|
|
events {
|
|
worker_connections 1024;
|
|
}
|
|
|
|
http {
|
|
include /etc/nginx/mime.types;
|
|
default_type application/octet-stream;
|
|
|
|
log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
|
|
'\$status \$body_bytes_sent "\$http_referer" '
|
|
'"\$http_user_agent" "\$http_x_forwarded_for"';
|
|
|
|
access_log /var/log/nginx/access.log main;
|
|
|
|
sendfile on;
|
|
tcp_nopush on;
|
|
tcp_nodelay on;
|
|
keepalive_timeout 65;
|
|
types_hash_max_size 2048;
|
|
|
|
# Gzip compression
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_min_length 1024;
|
|
gzip_types
|
|
text/plain
|
|
text/css
|
|
text/xml
|
|
text/javascript
|
|
application/javascript
|
|
application/xml+rss
|
|
application/json;
|
|
|
|
# Security headers
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
|
|
|
server {
|
|
listen 80;
|
|
server_name localhost;
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
|
|
# Handle client-side routing
|
|
location / {
|
|
try_files \$uri \$uri/ /index.html;
|
|
}
|
|
|
|
# Cache static assets
|
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
# Security headers for HTML files
|
|
location ~* \.html$ {
|
|
expires epoch;
|
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
}
|
|
|
|
# Health check endpoint
|
|
location /health {
|
|
access_log off;
|
|
return 200 "healthy\n";
|
|
add_header Content-Type text/plain;
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Copy built application from builder stage
|
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
|
|
|
# Create non-root user
|
|
RUN addgroup -g 1001 -S nginx_user && \
|
|
adduser -S nginx_user -u 1001 -G nginx_user
|
|
|
|
# Set proper permissions
|
|
RUN chown -R nginx_user:nginx_user /usr/share/nginx/html && \
|
|
chown -R nginx_user:nginx_user /var/cache/nginx && \
|
|
chown -R nginx_user:nginx_user /var/log/nginx && \
|
|
chown -R nginx_user:nginx_user /etc/nginx/conf.d
|
|
|
|
# Create nginx runtime directories
|
|
RUN touch /var/run/nginx.pid && \
|
|
chown -R nginx_user:nginx_user /var/run/nginx.pid
|
|
|
|
# Switch to non-root user
|
|
USER nginx_user
|
|
|
|
# Expose port
|
|
EXPOSE 80
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
CMD curl -f http://localhost:80/health || exit 1
|
|
|
|
# Start nginx
|
|
CMD ["nginx", "-g", "daemon off;"] |