Client Deployment

Build Process

Production Build

# Install dependencies
npm install

# Create production build
npm run build

# Output directory: dist/
ls dist/
# index.html
# assets/index-[hash].js
# assets/index-[hash].css
# assets/[other-assets]

Build Configuration

// vite.config.js
export default {
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    sourcemap: false,        // Disable for production
    minify: 'terser',        // Aggressive minification
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom', 'dexie']
        }
      }
    }
  }
}

Environment Variables

# .env.production
VITE_SYNC_SERVER_URL=https://api.yourapp.com
VITE_APP_NAME=Polyglot
VITE_APP_VERSION=1.0.0
VITE_ENABLE_SYNC=true

Static Hosting Deployment

Automatic Deployment

# Install Vercel CLI
npm install -g vercel

# Deploy to production
vercel --prod

Manual Configuration

// vercel.json
{
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/static-build",
      "config": {
        "buildCommand": "npm run build",
        "outputDirectory": "dist"
      }
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "/index.html"
    }
  ],
  "env": {
    "VITE_SYNC_SERVER_URL": "@sync_server_url"
  }
}

Environment Setup

# Set production environment variables
vercel env add VITE_SYNC_SERVER_URL production
# Enter: https://api.yourapp.com

vercel env add VITE_ENABLE_SYNC production
# Enter: true

Netlify

Git-Based Deployment

  1. Connect GitHub repository to Netlify

  2. Configure build settings:

    • Build command: npm run build

    • Publish directory: dist

    • Node version: 18

Netlify Configuration

# netlify.toml
[build]
  publish = "dist"
  command = "npm run build"

[build.environment]
  NODE_VERSION = "18"
  VITE_SYNC_SERVER_URL = "https://api.yourapp.com"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

Manual Deployment

# Install Netlify CLI
npm install -g netlify-cli

# Build and deploy
npm run build
netlify deploy --prod --dir=dist

AWS S3 + CloudFront

S3 Bucket Setup

# Create S3 bucket
aws s3 mb s3://polyglot-app

# Configure bucket for static hosting
aws s3 website s3://polyglot-app \
  --index-document index.html \
  --error-document index.html

Upload Build

# Build and upload
npm run build
aws s3 sync dist/ s3://polyglot-app --delete

# Set proper MIME types
aws s3 cp dist/ s3://polyglot-app \
  --recursive \
  --metadata-directive REPLACE \
  --cache-control max-age=31536000

CloudFront Distribution

{
  "DistributionConfig": {
    "Origins": [{
      "Id": "S3Origin",
      "DomainName": "polyglot-app.s3.amazonaws.com",
      "S3OriginConfig": {
        "OriginAccessIdentity": ""
      }
    }],
    "DefaultCacheBehavior": {
      "TargetOriginId": "S3Origin",
      "ViewerProtocolPolicy": "redirect-to-https",
      "Compress": true
    },
    "CustomErrorResponses": [{
      "ErrorCode": 404,
      "ResponseCode": 200,
      "ResponsePagePath": "/index.html"
    }]
  }
}

GitHub Pages

Workflow Setup

# .github/workflows/deploy.yml
name: Deploy to GitHub Pages

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build
        env:
          VITE_SYNC_SERVER_URL: ${{ secrets.SYNC_SERVER_URL }}

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist

Container Deployment

Docker Setup

Dockerfile

# Build stage
FROM node:18-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build

# Production stage
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Nginx Configuration

# nginx.conf
events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;

    server {
        listen 80;
        server_name _;
        root /usr/share/nginx/html;
        index index.html;

        # Handle client-side routing
        location / {
            try_files $uri $uri/ /index.html;
        }

        # Cache static assets
        location /assets/ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }

        # Security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
    }
}

Build and Run

# Build Docker image
docker build -t polyglot-client .

# Run container
docker run -p 8080:80 polyglot-client

# Access at http://localhost:8080

Docker Compose

# docker-compose.yml
version: '3.8'
services:
  client:
    build: .
    ports:
      - "8080:80"
    environment:
      - VITE_SYNC_SERVER_URL=http://localhost:4001
    restart: unless-stopped

Performance Optimization

Build Optimization

// vite.config.js optimizations
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          storage: ['dexie'],
          utils: ['lodash', 'date-fns']
        }
      }
    },
    chunkSizeWarningLimit: 1000
  },

  // Enable tree shaking
  esbuild: {
    drop: ['console', 'debugger']  // Remove in production
  }
}

Asset Optimization

# Image optimization (if using images)
npm install --save-dev @squoosh/cli

# Optimize images in build process
npx @squoosh/cli --resize {width:800} --webp auto src/assets/*.{jpg,png}

CDN Configuration

<!-- Preload critical resources -->
<link rel="preload" href="/assets/index-[hash].js" as="script">
<link rel="preload" href="/assets/index-[hash].css" as="style">

<!-- DNS prefetch for external resources -->
<link rel="dns-prefetch" href="//api.yourapp.com">

Security Configuration

Content Security Policy

<!-- index.html -->
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self' 'unsafe-inline';
  style-src 'self' 'unsafe-inline';
  connect-src 'self' https://api.yourapp.com;
  img-src 'self' data: https:;
">

HTTPS Configuration

// Force HTTPS in production
if (import.meta.env.PROD && location.protocol !== 'https:') {
  location.replace(`https:${location.href.substring(location.protocol.length)}`);
}

Environment-Specific Security

// src/config/security.js
export const securityConfig = {
  development: {
    csp: false,
    httpsRedirect: false,
    syncUrl: 'http://localhost:4001'
  },
  production: {
    csp: true,
    httpsRedirect: true,
    syncUrl: 'https://api.yourapp.com'
  }
};

Monitoring and Analytics

Error Tracking Setup

// src/utils/errorTracking.js
export function setupErrorTracking() {
  if (import.meta.env.PROD) {
    window.addEventListener('error', (event) => {
      console.error('Global error:', event.error);
      // Send to error tracking service
      fetch('/api/errors', {
        method: 'POST',
        body: JSON.stringify({
          message: event.error.message,
          stack: event.error.stack,
          url: window.location.href,
          timestamp: new Date().toISOString()
        })
      });
    });
  }
}

Performance Monitoring

// src/utils/performance.js
export function trackPerformance() {
  if ('performance' in window) {
    window.addEventListener('load', () => {
      const perfData = performance.getEntriesByType('navigation')[0];

      console.log('Performance metrics:', {
        loadTime: perfData.loadEventEnd - perfData.loadEventStart,
        domReady: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart,
        firstPaint: performance.getEntriesByType('paint')[0]?.startTime
      });
    });
  }
}

Deployment Checklist

Pre-Deployment

Post-Deployment

Rollback Plan

# Vercel rollback
vercel --prod --force

# Netlify rollback (via dashboard or CLI)
netlify sites:list
netlify api updateSiteDeploy --site-id=SITE_ID --deploy-id=PREVIOUS_DEPLOY_ID

# AWS S3 rollback
aws s3 sync s3://polyglot-app-backup/ s3://polyglot-app --delete

Troubleshooting

Common Deployment Issues

Build Failures

# Clear cache and retry
npm run clean  # if available
rm -rf node_modules package-lock.json
npm install
npm run build

Environment Variable Issues

# Debug environment variables
npm run build -- --debug

# Check variable availability
console.log('Env vars:', import.meta.env);

Routing Issues (SPA)

# Ensure all routes serve index.html
location / {
    try_files $uri $uri/ /index.html;
}

CORS Issues

// Check sync server URL in production
const syncUrl = import.meta.env.VITE_SYNC_SERVER_URL;
console.log('Sync URL:', syncUrl);

Performance Issues

// Bundle analyzer
npm install --save-dev rollup-plugin-visualizer

// Add to vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';

export default {
  plugins: [
    visualizer({ filename: 'dist/stats.html' })
  ]
}

Last updated