diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..52a58cb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +node_modules +dist +dist-ssr +.git +.gitignore +README.md +.vscode +.idea +.DS_Store +*.local +.env +.env.* +.vite diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..53c1ed2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# ============================ +# Stage 1 — Build (Vite + TypeScript) +# ============================ +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm ci + +COPY . . +RUN npm run build + +# ============================ +# Stage 2 — Serve (nginx) +# ============================ +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..9f84ee7 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,30 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # SPA routing — 404s caem no index.html (i18next/react-router lidam) + location / { + try_files $uri $uri/ /index.html; + } + + # Cache agressivo para assets versionados (Vite gera hash no nome) + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|webp|avif)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # index.html nunca cacheia (pega versão nova após deploy) + location = /index.html { + add_header Cache-Control "no-store, no-cache, must-revalidate"; + } + + # Healthcheck + location = /healthz { + access_log off; + return 200 "ok\n"; + add_header Content-Type text/plain; + } +}