diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..6da98e8 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,11 @@ +node_modules +dist +.git +.gitignore +.angular +.vscode +*.md +.editorconfig +.prettierrc +src/test.ts +**/*.spec.ts \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..4aa493c --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,28 @@ +# Stage 1: Build the Angular application +FROM node:22-alpine AS build + +WORKDIR /app + +# Copy package files first for better layer caching +COPY package.json package-lock.json ./ +RUN npm ci + +# Copy source and build +COPY . . +RUN npx ng build --configuration production + +# Stage 2: Serve static files with nginx +FROM nginx:alpine + +# Remove default nginx config +RUN rm /etc/nginx/conf.d/default.conf + +# Copy custom nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Copy built Angular artifacts from build stage +COPY --from=build /app/dist/frontend/browser /usr/share/nginx/html + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..64b7fa0 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,42 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript; + gzip_min_length 256; + + # Angular SPA — fallback to index.html for client-side routing + location / { + try_files $uri $uri/ /index.html; + } + + # Cache static assets aggressively + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Proxy API requests to backend + # Uses resolver so nginx doesn't crash if backend isn't available at startup + resolver 127.0.0.11 valid=30s ipv6=off; + set $backend "extrudex-api:8080"; + + location /api/ { + proxy_pass http://$backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "ok"; + add_header Content-Type text/plain; + } +} \ No newline at end of file