diff --git a/.dockerignore b/.dockerignore index 8df7d92..2fb0736 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,37 @@ -.DS_Store node_modules +npm-debug.log +.astro +.git +.gitignore +README.md +.env +.nyc_output +coverage +.vscode +.env.local +.env.production +.env.staging dist -README:md -LICENSE \ No newline at end of file +.DS_Store +*.log +logs +*.tgz +.pnpm-debug.log* +.pnpm-store/ +.next +out +.cache +.parcel-cache +build +.next +.vercel +netlify.toml +fly.toml +render.yaml +Dockerfile* +docker-compose* +.dockerignore +kubernetes/ +k8s/ +helm/ +charts/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index cecd50a..2eee3a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,41 +1,68 @@ -FROM node:lts-alpine as builder +# syntax=docker/dockerfile:1 +ARG NODE_VERSION=20-alpine +ARG NGINX_VERSION=alpine -# Enable and configure pnpm +FROM node:${NODE_VERSION} AS builder + +# Install build dependencies +RUN apk add --no-cache libc6-compat + +# Configure pnpm ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable +RUN corepack enable WORKDIR /app -COPY . /app +# Copy dependency files first for better caching +COPY package.json pnpm-lock.yaml ./ +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile -# -------------- Project build Stage ----------------- +# Copy source code +COPY . . -# Install dependencies and enable cache -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +# Build application with optimizations +RUN pnpm run build && \ + # Remove source maps and unnecessary files + find /app/dist -name "*.map" -delete && \ + # Remove dev dependencies + pnpm prune --prod + +# Production stage +FROM nginx:${NGINX_VERSION} AS runtime -# Build app -RUN pnpm run build +# Install security updates and curl for health check +RUN apk --no-cache upgrade && \ + apk --no-cache add curl -# ------------------ NGINX Stage -------------------- -FROM nginx:alpine as runtime +# Fix permissions for nginx user +RUN chown -R nginx:nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/log/nginx && \ + chown -R nginx:nginx /etc/nginx/conf.d && \ + touch /var/run/nginx.pid && \ + chown -R nginx:nginx /var/run/nginx.pid -# Remove default nginx static assets -RUN rm -rf /usr/share/nginx/html +# Remove default nginx assets +RUN rm -rf /usr/share/nginx/html/* -# Copy built site -COPY --from=builder /app/dist /usr/share/nginx/html +# Copy built application with proper ownership +COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html # 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 custom nginx config with proper ownership +COPY --chown=nginx:nginx nginx.conf /etc/nginx/conf.d/default.conf -# Expose default Nginx port -EXPOSE 80 +# Switch to non-root user +USER nginx -# Start Nginx with global directives and daemon turned off -ENTRYPOINT [ "nginx", "-g", "daemon off;" ] +# Expose port +EXPOSE 8080 +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:8080/ || exit 1 +# Start nginx +ENTRYPOINT ["nginx", "-g", "daemon off;"] diff --git a/README.md b/README.md index 5e3574a..73a9653 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,5 @@ La página web usa la herramienta [Astro](https://astro.build/). Para poder usar 2. El contenedor generado es completamente autocontenido y usa un servidor de Nginx para servir los archivos, por tanto sólo queda ejecutar el contenedor: ```bash - docker run -d -p 8080:80 hackiit-website:latest + docker run -d -p 8080:8080 hackiit-website:latest ``` - - diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..9261dd9 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,44 @@ +server { + listen 8080; + root /usr/share/nginx/html; + index index.html; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Handle client-side routing + location / { + try_files $uri $uri/ /index.html; + } + + # Security: deny access to hidden files + location ~ /\. { + deny all; + } +}