From daf253540f013624e12193c1b2907513e2bcdae3 Mon Sep 17 00:00:00 2001 From: RJ Date: Fri, 14 Nov 2025 16:20:14 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20add=20cicd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/main.yml | 337 ++++++++++++++++++++++++++++++++++++++ Dockerfile.nextjs | 108 ++++++++++++ docker-compose.prod.yml | 134 +++++++++++++++ docker-compose.yml | 90 ++++++++++ next.config.js | 170 +++++++++++++++++++ 5 files changed, 839 insertions(+) create mode 100644 .gitea/workflows/main.yml create mode 100644 Dockerfile.nextjs create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.yml diff --git a/.gitea/workflows/main.yml b/.gitea/workflows/main.yml new file mode 100644 index 0000000..a53a079 --- /dev/null +++ b/.gitea/workflows/main.yml @@ -0,0 +1,337 @@ +# Gitea Actions Workflow for Next.js Blog Application +# This workflow builds a Docker image and deploys it to production +# +# Workflow triggers: +# - Push to master branch (automatic deployment) +# - Manual trigger via workflow_dispatch +# +# Required Secrets (configure in Gitea repository settings): +# - PRODUCTION_HOST: IP address or hostname of production server +# - PRODUCTION_USER: SSH username (e.g., 'deployer') +# - SSH_PRIVATE_KEY: Private SSH key for authentication +# - REGISTRY_USERNAME: Docker registry username (optional, if registry requires auth) +# - REGISTRY_PASSWORD: Docker registry password (optional, if registry requires auth) +# +# Environment Variables (configured below): +# - REGISTRY: Docker registry URL +# - IMAGE_NAME: Docker image name + +name: Build and Deploy Next.js Blog to Production + +on: + push: + branches: + - master # Trigger on push to master branch + workflow_dispatch: # Allow manual trigger from Gitea UI + +env: + # Docker registry configuration + # Update this to match your private registry URL + REGISTRY: repository.workspace:5000 + IMAGE_NAME: mypage + +jobs: + # ============================================ + # Job 1: Code Quality Checks (Linting) + # ============================================ + lint: + name: 🔍 Code Quality Checks + runs-on: ubuntu-latest + + steps: + - name: 🔎 Checkout code + uses: actions/checkout@v4 + + - name: 📦 Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: 📥 Install dependencies + run: npm ci + + - name: 🔍 Run ESLint + run: npm run lint + + - name: 💅 Check code formatting (Prettier) + run: npm run format:check + + - name: 🔤 TypeScript type checking + run: npx tsc --noEmit + + - name: ✅ All quality checks passed + run: | + echo "✅ All code quality checks passed successfully!" + echo " - ESLint: No linting errors" + echo " - Prettier: Code is properly formatted" + echo " - TypeScript: No type errors" + + # ============================================ + # Job 2: Build and Push Docker Image + # ============================================ + build-and-push: + name: 🏗️ Build and Push Docker Image + runs-on: ubuntu-latest + needs: [lint] # Wait for lint job to complete successfully + + steps: + - name: 🔎 Checkout code + uses: actions/checkout@v4 + + - name: 🔐 Log in to Docker Registry (if credentials provided) + run: | + if [ -n "${{ secrets.REGISTRY_USERNAME }}" ] && [ -n "${{ secrets.REGISTRY_PASSWORD }}" ]; then + echo "Logging into ${{ env.REGISTRY }} with credentials..." + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin + echo "✅ Login successful" + else + echo "⚠️ No registry credentials provided - using insecure/public registry" + fi + + - name: 🏗️ Build Docker image + timeout-minutes: 30 + env: + DOCKER_BUILDKIT: 1 # Enable BuildKit for faster builds and better caching + run: | + echo "Building Next.js Docker image with BuildKit..." + echo "Build context size:" + du -sh . 2>/dev/null || echo "Cannot measure context size" + + # Build the Docker image + # - Uses Dockerfile.nextjs from project root + # - Tags image with both 'latest' and commit SHA + # - Enables inline cache for faster subsequent builds + docker build \ + --progress=plain \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \ + -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \ + -f Dockerfile.nextjs \ + . + + echo "✅ Build successful" + echo "Image size:" + docker images ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + + - name: 🚀 Push Docker image to registry + run: | + echo "Pushing image to registry..." + + # Push both tags (latest and commit SHA) + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} + + echo "✅ Image pushed successfully" + echo " - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" + echo " - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" + + # ============================================ + # Job 2: Deploy to Production Server + # ============================================ + deploy-production: + name: 🚀 Deploy to Production + runs-on: ubuntu-latest + needs: [build-and-push] # Wait for build job to complete + environment: + name: production + url: http://your-production-url.com # Update with your actual production URL + + steps: + - name: 🔎 Checkout code (for docker-compose file) + uses: actions/checkout@v4 + + - name: 🔐 Validate Registry Access on Production Server + uses: appleboy/ssh-action@v1.0.3 + env: + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} + REGISTRY_URL: ${{ env.REGISTRY }} + with: + host: ${{ secrets.PRODUCTION_HOST }} + username: ${{ secrets.PRODUCTION_USER }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + port: 22 + envs: REGISTRY_PASSWORD,REGISTRY_USERNAME,REGISTRY_URL + script: | + echo "=== Validating Docker Registry access ===" + if [ -n "$REGISTRY_USERNAME" ] && [ -n "$REGISTRY_PASSWORD" ]; then + echo "Logging into $REGISTRY_URL with credentials..." + echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USERNAME" --password-stdin + echo "✅ Registry authentication successful" + else + echo "⚠️ No registry credentials - using insecure/public registry" + echo "Testing registry connectivity..." + curl -f "http://$REGISTRY_URL/v2/" || { echo "❌ Registry not accessible"; exit 1; } + echo "✅ Registry is accessible" + fi + + - name: 📁 Ensure application directory structure + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.PRODUCTION_HOST }} + username: ${{ secrets.PRODUCTION_USER }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + port: 22 + script: | + echo "=== Ensuring application directory structure ===" + + # Verify base directory exists and is writable + # Update /opt/mypage to match your deployment directory + if [ ! -d /opt/mypage ]; then + echo "❌ /opt/mypage does not exist!" + echo "Please run manually on production server:" + echo " sudo mkdir -p /opt/mypage" + echo " sudo chown -R deployer:docker /opt/mypage" + echo " sudo chmod -R 775 /opt/mypage" + exit 1 + fi + + if [ ! -w /opt/mypage ]; then + echo "❌ /opt/mypage is not writable by $USER user" + echo "Please run manually on production server:" + echo " sudo chown -R deployer:docker /opt/mypage" + echo " sudo chmod -R 775 /opt/mypage" + exit 1 + fi + + # Create data directories for logs + mkdir -p /opt/mypage/data/logs || { echo "❌ Failed to create logs directory"; exit 1; } + + echo "✅ Directory structure ready" + ls -la /opt/mypage + + - name: 📦 Copy docker-compose.prod.yml to server + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.PRODUCTION_HOST }} + username: ${{ secrets.PRODUCTION_USER }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + port: 22 + source: "docker-compose.prod.yml" + target: "/opt/mypage/" + overwrite: true + + - name: 🐳 Deploy application via Docker Compose + uses: appleboy/ssh-action@v1.0.3 + env: + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD || '' }} + REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME || '' }} + REGISTRY_URL: ${{ env.REGISTRY }} + IMAGE_FULL: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + with: + host: ${{ secrets.PRODUCTION_HOST }} + username: ${{ secrets.PRODUCTION_USER }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + port: 22 + envs: REGISTRY_PASSWORD,REGISTRY_USERNAME,REGISTRY_URL,IMAGE_FULL + script_stop: true # Stop execution on any error + script: | + echo "=== Starting deployment to production server ===" + cd /opt/mypage + + # Log in to Docker registry (if credentials are configured) + if [ -n "$REGISTRY_USERNAME" ] && [ -n "$REGISTRY_PASSWORD" ]; then + echo "=== Logging in to Docker registry ===" + echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USERNAME" --password-stdin + echo "✅ Registry login successful" + else + echo "⚠️ No registry credentials - using insecure/public registry (no login required)" + fi + + # Pull latest image from registry + echo "=== Pulling latest Docker image ===" + docker pull "$IMAGE_FULL" + + if [ $? -ne 0 ]; then + echo "❌ Failed to pull image, aborting deployment" + exit 1 + fi + + # Deploy new container + # - Stops old container + # - Removes old container + # - Creates and starts new container with fresh image + echo "=== Deploying new container ===" + docker compose -f docker-compose.prod.yml up -d --force-recreate + + if [ $? -ne 0 ]; then + echo "❌ Failed to deploy new container" + echo "Check logs above for errors" + exit 1 + fi + + # Check container status + echo "=== Container Status ===" + docker compose -f docker-compose.prod.yml ps + + # Show recent logs for debugging + echo "=== Recent application logs ===" + docker compose -f docker-compose.prod.yml logs --tail=50 + + # Clean up old/unused images to save disk space + echo "=== Cleaning up old Docker images ===" + docker image prune -f + + echo "✅ Deployment completed successfully ===" + + - name: ❤️ Health check + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.PRODUCTION_HOST }} + username: ${{ secrets.PRODUCTION_USER }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + port: 22 + script: | + echo "=== Performing health check ===" + cd /opt/mypage + max_attempts=15 + attempt=1 + + # Wait for container to be healthy (respect start_period from health check) + echo "Waiting for application to start (40s start period)..." + sleep 40 + + # Retry health check up to 15 times + while [ $attempt -le $max_attempts ]; do + # Check if application responds at port 3030 + if curl -f http://localhost:3030/ > /dev/null 2>&1; then + echo "✅ Health check passed!" + echo "Application is healthy and responding to requests" + exit 0 + fi + echo "Attempt $attempt/$max_attempts: Health check failed, retrying in 5s..." + sleep 5 + attempt=$((attempt + 1)) + done + + # Health check failed - gather diagnostic information + echo "❌ Health check failed after $max_attempts attempts" + echo "" + echo "=== Container Status ===" + docker compose -f docker-compose.prod.yml ps + echo "" + echo "=== Container Health ===" + docker inspect mypage-prod --format='{{.State.Health.Status}}' 2>/dev/null || echo "No health status" + echo "" + echo "=== Recent Application Logs ===" + docker compose -f docker-compose.prod.yml logs --tail=100 + + exit 1 + + - name: 📊 Deployment summary + if: always() # Run even if previous steps fail + run: | + echo "### 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Environment**: Production" >> $GITHUB_STEP_SUMMARY + echo "- **Image**: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Workflow Run**: #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY + echo "- **Triggered By**: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Next Steps:**" >> $GITHUB_STEP_SUMMARY + echo "1. Verify application is accessible at production URL" >> $GITHUB_STEP_SUMMARY + echo "2. Check application logs for any errors" >> $GITHUB_STEP_SUMMARY + echo "3. Monitor resource usage and performance" >> $GITHUB_STEP_SUMMARY diff --git a/Dockerfile.nextjs b/Dockerfile.nextjs new file mode 100644 index 0000000..cf2c814 --- /dev/null +++ b/Dockerfile.nextjs @@ -0,0 +1,108 @@ +# Multi-stage Dockerfile for Next.js Blog Application +# Optimized for Static Site Generation with standalone output +# Final image size: ~150MB + +# ============================================ +# Stage 1: Dependencies Installation +# ============================================ +FROM node:20-alpine AS deps + +# Install libc6-compat for better compatibility +RUN apk add --no-cache libc6-compat + +WORKDIR /app + +# Copy package files for dependency installation +# These files are copied first to leverage Docker layer caching +# If package.json hasn't changed, this layer will be reused +COPY package.json package-lock.json* ./ + +# Install dependencies using npm ci for reproducible builds +# --only=production flag is not used here because we need dev dependencies for build +RUN npm ci + +# ============================================ +# Stage 2: Build Next.js Application +# ============================================ +FROM node:20-alpine AS builder + +WORKDIR /app + +# Copy dependencies from deps stage +COPY --from=deps /app/node_modules ./node_modules + +# Copy all application source code +# This includes: +# - app/ directory (Next.js 16 App Router) +# - components/ directory +# - lib/ directory (markdown utilities) +# - content/blog/ directory (markdown blog posts) +# - public/ directory (static assets) +# - next.config.js, tsconfig.json, tailwind.config.js, etc. +COPY . . + +# Set environment variables for build +ENV NEXT_TELEMETRY_DISABLED=1 +ENV NODE_ENV=production + +# Build the Next.js application +# This will: +# 1. Process all markdown files from content/blog/ +# 2. Generate static pages for all blog posts (SSG) +# 3. Create standalone output in .next/standalone/ +# 4. Optimize images and assets +# 5. Bundle and minify JavaScript +RUN npm run build + +# ============================================ +# Stage 3: Production Runtime +# ============================================ +FROM node:20-alpine AS runner + +# Install curl for health checks +RUN apk add --no-cache curl + +WORKDIR /app + +# Set production environment +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +# Create a non-root user for security +# The application will run as this user instead of root +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 nextjs + +# Copy only the necessary files from builder stage +# Next.js standalone output includes all dependencies needed to run +COPY --from=builder /app/public ./public + +# Copy standalone output (includes minimal node_modules and server files) +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ + +# Copy static files (CSS, JS bundles, optimized images) +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +# Create directories for logs (optional, if your app writes logs) +RUN mkdir -p /app/logs && chown nextjs:nodejs /app/logs + +# Switch to non-root user +USER nextjs + +# Expose the application port +# Note: This matches the port in package.json "dev" script (-p 3030) +EXPOSE 3030 + +# Set the port environment variable +ENV PORT=3030 +ENV HOSTNAME="0.0.0.0" + +# Health check to verify the application is running +# Docker will periodically check this endpoint +# If it fails, the container is marked as unhealthy +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:3030/ || exit 1 + +# Start the Next.js server +# The standalone output includes a minimal server.js file +CMD ["node", "server.js"] diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..48cb140 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,134 @@ +# Docker Compose Configuration for Production Deployment +# This file is used by CI/CD to deploy the application on production servers +# +# Key differences from local docker-compose.yml: +# - Uses pre-built image from registry (not local build) +# - Includes resource limits and logging configuration +# - More stringent health checks +# - Production-grade restart policies +# +# Usage: +# 1. This file is automatically copied to server by CI/CD workflow +# 2. Server pulls image from registry: docker compose -f docker-compose.prod.yml pull +# 3. Server starts container: docker compose -f docker-compose.prod.yml up -d +# +# Manual deployment (if CI/CD is not available): +# ssh user@production-server +# cd /opt/mypage +# docker compose -f docker-compose.prod.yml pull +# docker compose -f docker-compose.prod.yml up -d --force-recreate + +version: '3.8' + +services: + mypage: + # Use pre-built image from private registry + # This image is built and pushed by the CI/CD workflow + # Format: REGISTRY_URL/IMAGE_NAME:TAG + image: repository.workspace:5000/mypage:latest + + container_name: mypage-prod + + # Restart policy: always restart on failure or server reboot + # This ensures high availability in production + restart: always + + # Port mapping: host:container + # The application will be accessible at http://SERVER_IP:3030 + # Usually, a reverse proxy (Caddy/Nginx) will forward traffic to this port + ports: + - "3030:3030" + + # Production environment variables + environment: + - NODE_ENV=production + - NEXT_TELEMETRY_DISABLED=1 + - PORT=3030 + - HOSTNAME=0.0.0.0 + # Add any other production-specific environment variables here + # Example: + # - DATABASE_URL=postgresql://user:pass@db:5432/mypage + # - REDIS_URL=redis://redis:6379 + + # Persistent volumes for logs (optional) + # Uncomment if your application writes logs + volumes: + - ./data/logs:/app/logs + + # Health check configuration + # Docker monitors the application and marks it unhealthy if checks fail + # If container is unhealthy, restart policy will trigger a restart + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3030/", "||", "exit", "1"] + interval: 30s # Check every 30 seconds + timeout: 10s # Wait up to 10 seconds for response + retries: 3 # Mark unhealthy after 3 consecutive failures + start_period: 40s # Grace period during container startup + + # Resource limits for production + # Prevents container from consuming all server resources + deploy: + resources: + limits: + cpus: '1.0' # Maximum 1 CPU core + memory: 512M # Maximum 512MB RAM + reservations: + cpus: '0.25' # Reserve at least 0.25 CPU cores + memory: 256M # Reserve at least 256MB RAM + + # Network configuration + networks: + - mypage-network + + # Logging configuration + # Prevents logs from consuming all disk space + logging: + driver: "json-file" + options: + max-size: "10m" # Maximum 10MB per log file + max-file: "3" # Keep only 3 log files (30MB total) + +# Network definition +networks: + mypage-network: + driver: bridge + +# ============================================ +# Production Deployment Commands +# ============================================ +# +# Pull latest image from registry: +# docker compose -f docker-compose.prod.yml pull +# +# Start/update containers: +# docker compose -f docker-compose.prod.yml up -d --force-recreate +# +# View logs: +# docker compose -f docker-compose.prod.yml logs -f mypage +# +# Check health status: +# docker inspect mypage-prod | grep -A 10 Health +# +# Stop containers: +# docker compose -f docker-compose.prod.yml down +# +# Restart containers: +# docker compose -f docker-compose.prod.yml restart +# +# Remove old/unused images (cleanup): +# docker image prune -f +# +# ============================================ +# Troubleshooting +# ============================================ +# +# If container keeps restarting: +# 1. Check logs: docker compose -f docker-compose.prod.yml logs --tail=100 +# 2. Check health: docker inspect mypage-prod | grep -A 10 Health +# 3. Verify port is not already in use: netstat -tulpn | grep 3030 +# 4. Check resource usage: docker stats mypage-prod +# +# If health check fails: +# 1. Test manually: docker exec mypage-prod curl -f http://localhost:3030/ +# 2. Check if Next.js server is running: docker exec mypage-prod ps aux +# 3. Verify environment variables: docker exec mypage-prod env diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7bb00de --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,90 @@ +# Docker Compose Configuration for Local Development/Testing +# This file is used to run the Next.js blog application locally using Docker +# +# Usage: +# 1. Copy this file to project root: cp docker-compose.yml.example docker-compose.yml +# 2. Build and start: docker compose up -d +# 3. View logs: docker compose logs -f +# 4. Stop: docker compose down +# +# Note: This builds from local Dockerfile, not registry image + +version: '3.8' + +services: + mypage: + # Build configuration + build: + context: . + dockerfile: Dockerfile.nextjs # Use the Next.js-specific Dockerfile + + container_name: mypage-dev + + # Port mapping: host:container + # Access the app at http://localhost:3030 + ports: + - "3030:3030" + + # Environment variables for development + environment: + - NODE_ENV=production # Use production mode even locally to test production build + - NEXT_TELEMETRY_DISABLED=1 + - PORT=3030 + - HOSTNAME=0.0.0.0 + + # Optional: Mount logs directory for debugging + # Uncomment if your application writes logs to /app/logs + # volumes: + # - ./logs:/app/logs + + # Restart policy: restart unless explicitly stopped + restart: unless-stopped + + # Network configuration + networks: + - mypage-network + + # Health check configuration + # Docker will check if the app is healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3030/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +networks: + mypage-network: + driver: bridge + +# ============================================ +# Common Commands +# ============================================ +# +# Build and start containers: +# docker compose up -d +# +# Build with no cache (clean build): +# docker compose build --no-cache +# docker compose up -d +# +# View logs (follow mode): +# docker compose logs -f mypage +# +# Stop containers: +# docker compose down +# +# Stop and remove volumes: +# docker compose down -v +# +# Restart service: +# docker compose restart mypage +# +# Access container shell: +# docker compose exec mypage /bin/sh +# +# Check container status: +# docker compose ps +# +# View resource usage: +# docker stats mypage-dev diff --git a/next.config.js b/next.config.js index 025b970..02ce85c 100644 --- a/next.config.js +++ b/next.config.js @@ -1,10 +1,180 @@ /** @type {import('next').NextConfig} */ + +// Production-ready Next.js configuration with standalone output +// This configuration is optimized for Docker deployment with minimal image size +// +// Key features: +// - Standalone output mode (includes only necessary dependencies) +// - Image optimization with modern formats +// - Static Site Generation (SSG) for all blog posts +// - Production-grade caching and performance settings +// +// Usage: +// 1. Copy this file to project root: cp next.config.js.production next.config.js +// 2. Build application: npm run build +// 3. The .next/standalone directory will contain everything needed to run the app + const nextConfig = { + // ============================================ + // Standalone Output Mode + // ============================================ + // This is REQUIRED for Docker deployment + // Outputs a minimal server with only necessary dependencies + // Reduces Docker image size from ~1GB to ~150MB + output: 'standalone', + + // ============================================ + // Image Optimization + // ============================================ images: { + // Modern image formats (smaller file sizes) formats: ['image/avif', 'image/webp'], + + // Device sizes for responsive images + // Next.js will generate optimized images for these widths deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + + // Image sizes for component size prop imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + + // Disable image optimization during build (optional) + // Uncomment if build times are too long + // unoptimized: false, + + // External image domains (if loading images from CDN) + // Uncomment and add domains if needed + // remotePatterns: [ + // { + // protocol: 'https', + // hostname: 'cdn.example.com', + // }, + // ], }, + + // ============================================ + // Performance Optimization + // ============================================ + + // Enable SWC minification (faster than Terser) + swcMinify: true, + + // Compress static pages (reduces bandwidth) + compress: true, + + // ============================================ + // Production Settings + // ============================================ + + // Disable X-Powered-By header for security + poweredByHeader: false, + + // Generate ETags for caching + generateEtags: true, + + // ============================================ + // Static Generation Settings + // ============================================ + + // Automatically generate static pages at build time + // This is the default behavior for Next.js App Router + // All markdown blog posts will be pre-rendered + + // ============================================ + // TypeScript Settings + // ============================================ + + // Type checking during build + // Set to false to skip type checking (not recommended) + typescript: { + // ignoreBuildErrors: false, + }, + + // ============================================ + // ESLint Settings + // ============================================ + + // ESLint during build + // Set to false to skip linting (not recommended) + eslint: { + // ignoreDuringBuilds: false, + }, + + // ============================================ + // Experimental Features (Next.js 16) + // ============================================ + + experimental: { + // Enable optimistic client cache + // Improves navigation performance + staleTimes: { + dynamic: 30, + static: 180, + }, + + // Enable PPR (Partial Prerendering) - Next.js 16 feature + // Uncomment to enable (currently in beta) + // ppr: false, + }, + + // ============================================ + // Headers (Optional) + // ============================================ + // Custom headers for all routes + // Note: Caddy/Nginx reverse proxy can also set these headers + // Uncomment if you want Next.js to handle headers instead + // + async headers() { + return [ + { + source: '/:path*', + headers: [ + { + key: 'X-Content-Type-Options', + value: 'nosniff', + }, + { + key: 'X-Frame-Options', + value: 'DENY', + }, + { + key: 'X-XSS-Protection', + value: '1; mode=block', + }, + ], + }, + ] + }, + + // ============================================ + // Redirects (Optional) + // ============================================ + // Add permanent redirects for old URLs + // Uncomment and add your redirects + // + // async redirects() { + // return [ + // { + // source: '/old-blog/:slug', + // destination: '/blog/:slug', + // permanent: true, + // }, + // ] + // }, + + // ============================================ + // Rewrites (Optional) + // ============================================ + // Add URL rewrites for API proxying or URL masking + // Uncomment and add your rewrites + // + // async rewrites() { + // return [ + // { + // source: '/api/:path*', + // destination: 'https://api.example.com/:path*', + // }, + // ] + // }, } module.exports = nextConfig -- 2.49.1