-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathDockerfile
More file actions
153 lines (122 loc) · 5.27 KB
/
Dockerfile
File metadata and controls
153 lines (122 loc) · 5.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# =============================================================================
# Eagle-Admin Multi-Stage Dockerfile
# =============================================================================
# Simplified deployment: Single Dockerfile with embedded nginx configuration.
# App fetches runtime config from /api/config (configEndpoint=true).
#
# Build: docker build -t eagle-admin .
# Run: docker run -p 8080:8080 eagle-admin
# =============================================================================
# -----------------------------------------------------------------------------
# Stage 1: Build Angular Application
# -----------------------------------------------------------------------------
FROM node:24-alpine AS builder
WORKDIR /app
# Copy package files and yarn configuration
COPY package.json yarn.lock .yarnrc.yml ./
COPY .yarn ./.yarn
# Install dependencies (uses node-modules linker per .yarnrc.yml)
RUN corepack enable && yarn install --immutable
# Copy source code (node_modules already exists from previous layer)
COPY . .
# Configure env.js for deployed environment:
# - configEndpoint=true: App fetches config from /api/config at runtime
# - All other config (ENVIRONMENT, ANALYTICS_API_URL, etc.) comes from API
RUN sed -i 's/configEndpoint = false/configEndpoint = true/' src/env.js
# Build production bundle
RUN yarn build
# -----------------------------------------------------------------------------
# Stage 2: Production nginx Server
# -----------------------------------------------------------------------------
FROM nginx:1.27-alpine
# Update Alpine packages to latest security patches
RUN apk upgrade --no-cache
# Labels for OpenShift compatibility
LABEL io.openshift.expose-services="8080:http" \
io.openshift.tags="nginx,angular,eagle-admin"
# Create nginx config directory and set permissions for OpenShift
RUN mkdir -p /var/cache/nginx /var/run /tmp/app/dist/admin && \
chown -R nginx:0 /var/cache/nginx /var/run /var/log/nginx /tmp/app && \
chmod -R g+rwx /var/cache/nginx /var/run /var/log/nginx /tmp/app
# Copy nginx configuration
COPY <<'EOF' /etc/nginx/nginx.conf
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_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript
application/xml application/xml+rss text/javascript application/wasm;
include /etc/nginx/conf.d/*.conf;
}
EOF
# Copy server configuration
# Note: eagle-admin is served at /admin/* path via rproxy
# Files are in /tmp/app/dist/admin/ - alias maps requests correctly
COPY <<'EOF' /etc/nginx/conf.d/default.conf
server {
listen 8080 default_server;
server_name localhost;
# Security headers
# CSP allows: BC Gov resources, Google Fonts (Material Icons), Adobe Typekit, Keycloak
add_header Content-Security-Policy "default-src 'self' https://*.gov.bc.ca; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www2.gov.bc.ca https://use.typekit.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com https://use.typekit.net; connect-src 'self' https://*.gov.bc.ca https://*.loginproxy.gov.bc.ca; frame-ancestors 'none';" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Health check endpoint
location /health {
access_log off;
return 200 'healthy';
add_header Content-Type text/plain;
}
# Serve Angular app - alias maps /admin/* to /tmp/app/dist/admin/*
location / {
alias /tmp/app/dist/;
try_files $uri $uri/ /admin/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";
}
# Don't cache HTML or config files
location ~* \.(html|json)$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
# Error pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
EOF
# Copy built Angular application
COPY --from=builder /app/dist /tmp/app/dist/admin
# Use non-root user for OpenShift compatibility
USER nginx
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -q --spider http://localhost:8080/health || exit 1
CMD ["nginx", "-g", "daemon off;"]