# Copper Tone Technologies - Architecture Documentation ## Production Frontend Serving ✅ CORRECT ### Current Architecture The frontend is **correctly configured** to build and serve statically via nginx (not Go). This is the recommended approach for production Vue.js + Go microservices applications. ``` ┌─────────────────────────────────────────────────────────────┐ │ Client Browser │ └──────────────────────┬──────────────────────────────────────┘ │ HTTPS ▼ ┌─────────────────────────────────────────────────────────────┐ │ nginx (Frontend Container:8080) │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Static Assets: HTML, CSS, JS (Vue.js built bundle) │ │ │ │ Routes: /, /about, /services, /blog, etc. │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Reverse Proxy Rules (nginx.conf): │ │ │ │ /api/auth/* → auth-service:8080 │ │ │ │ /api/work/* → work-management-service:8080 │ │ │ │ /api/contact/* → contact-service:8080 │ │ │ │ /api/llm/* → llm-service:8080 │ │ │ │ /api/payment/* → payment-service:8080 │ │ │ │ /api/blog/* → blog-service:8080 │ │ │ │ /api/ipfs/* → ipfs-service:8080 │ │ │ │ /api/forum/* → forum-service:8080 │ │ │ └────────────────────────────────────────────────────────┘ │ └──────────────────────┬──────────────────────────────────────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ auth-service│ │ work-mgmt │ │ contact │ ... (8+ Go services) │ :8082 │ │ :8083 │ │ :8088 │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ └───────────────┼───────────────┘ ▼ ┌─────────────────┐ │ PostgreSQL DB │ │ :5432 │ └─────────────────┘ ``` ## Why nginx (not Go) is Correct ✅ ### 1. **Separation of Concerns** - **Frontend Container**: Serves pre-built static assets (HTML, CSS, JS) - **Backend Go Services**: Handle business logic, API endpoints, database operations - Each service has a single responsibility ### 2. **Performance Optimization** - **nginx**: Highly optimized C-based web server for static file serving - Zero-copy file serving - Efficient caching (HTTP cache headers, ETag support) - Built-in gzip/brotli compression - **Go services**: Focus CPU on business logic, not file I/O ### 3. **nginx Production Features** - Rate limiting (per IP, per endpoint) - Request buffering and queuing - SSL/TLS termination - HTTP/2 and HTTP/3 support - Load balancing (if scaling horizontally) - Security headers injection ### 4. **Microservices Best Practices** - API Gateway pattern (nginx = gateway) - Service independence (frontend can be updated without touching Go services) - Horizontal scaling (can run multiple instances of each service) ## Build Process ✅ CORRECT ### Frontend Containerfile (Multi-stage Build) **Stage 1: Build** ```dockerfile FROM node:lts AS build WORKDIR /app COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile COPY . . RUN pnpm run build # Creates optimized production bundle in /app/dist ``` **Stage 2: Production** ```dockerfile FROM nginx:stable-alpine AS production COPY nginx.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/dist /usr/share/nginx/html # Static files EXPOSE 8080 CMD ["nginx", "-g", "daemon off;"] ``` ### What Happens During Build: 1. **Vite Build** (`pnpm run build`): - Bundles Vue.js components into optimized JS - Tree-shakes unused code - Minifies HTML, CSS, JS - Generates content-hashed filenames (`index-CG-0GT5f.js`) - Creates source maps (for debugging) - Outputs to `/app/dist` 2. **Production Assets**: ``` dist/ ├── index.html # Entry point ├── assets/ │ ├── index-[hash].js # Main bundle │ ├── index-[hash].css # Styles │ ├── vendor-[hash].js # Dependencies │ └── [component]-[hash].js # Code-split chunks ├── favicon.ico └── manifest.json # PWA manifest ``` 3. **nginx Serves**: - `GET /` → `/usr/share/nginx/html/index.html` - `GET /about` → `/usr/share/nginx/html/index.html` (SPA routing) - `GET /assets/index-[hash].js` → Static file from disk - `POST /api/auth/login` → Proxy to auth-service:8080 ## Environment-Specific Configurations ### Development (Local) - **Frontend**: Vite dev server on port 5173 (Hot Module Replacement) - **Backend**: Individual Go services running locally - **Database**: PostgreSQL with `DB_SCHEMA=dev` - **Command**: `pnpm run dev` (frontend) + individual `go run main.go` (backend) ### Testing (test.coppertone.tech) - **Frontend**: nginx container serving production build (port 8090) - **Backend**: Go services in containers - **Database**: PostgreSQL with `DB_SCHEMA=testing` - **Deploy**: Push to `testing` branch → Woodpecker CI → SSH deploy ### Production (coppertone.tech) - **Frontend**: nginx container serving production build - **Backend**: Go services in containers - **Database**: PostgreSQL with `DB_SCHEMA=prod` - **Deploy**: Push to `main` branch → Woodpecker CI → SSH deploy ## Deployment Flow ### CI/CD Pipeline (.woodpecker/develop.yml) ```yaml when: - event: push branch: testing # Triggers on push to testing branch steps: 1. test-frontend: # Lint, type-check, unit tests 2. test-backend: # Go vet, go test for all services 3. build-frontend: # pnpm run build (verify build works) 4. deploy-testing: # SSH to server, git pull, podman-compose build, restart ``` ### Deployment Script (Executed on Server) ```bash cd /docker/web-hosts/domains/test.coppertone.tech/repo git fetch origin testing git checkout testing git pull origin testing cd .. podman-compose build --no-cache # Rebuilds all containers including frontend podman-compose down --timeout 30 podman-compose up -d # Starts all containers ``` ## Container Orchestration (podman-compose.yml) ```yaml services: frontend: build: context: ./frontend dockerfile: Containerfile # Multi-stage: build → nginx ports: - "8090:8080" # Host:Container restart: unless-stopped auth-service: build: context: ./backend/functions/auth-service ports: - "8082:8080" environment: JWT_SECRET: ${JWT_SECRET} DB_HOST: db depends_on: - db # ... 8+ other Go services ... db: image: docker.io/library/postgres:16-alpine volumes: - postgres_data:/var/lib/postgresql/data environment: POSTGRES_DB: coppertone_db POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} ``` ## Static Asset Serving Performance ### nginx Configuration (frontend/nginx.conf) **Cache Headers**: ```nginx # Never cache index.html (ensures fresh JS/CSS references) location = /index.html { add_header Cache-Control "no-cache, no-store, must-revalidate"; expires -1; } # Aggressively cache hashed assets (immutable) location /assets/ { add_header Cache-Control "public, max-age=31536000, immutable"; expires 1y; } ``` **Compression**: ```nginx gzip on; gzip_types text/plain text/css application/json application/javascript text/xml; gzip_min_length 1000; ``` **Security Headers**: ```nginx 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; ``` ## Alternative Architecture (Why We DON'T Do This) ### ❌ WRONG: Single Go Server Serving Everything ```go // DON'T DO THIS http.Handle("/", http.FileServer(http.Dir("./dist"))) http.HandleFunc("/api/auth/login", handleLogin) ``` **Problems**: - Go binary size increases (includes all static assets) - Slower static file serving vs nginx - No built-in caching/compression optimization - Single point of failure (one crash = entire site down) - Can't scale frontend and backend independently - Violates microservices principles ## Verification Checklist To verify production mode is working correctly: ### 1. Frontend Build Artifacts ```bash cd frontend pnpm run build ls -lh dist/ # Should see index.html, assets/ ls -lh dist/assets/*.js # Should see hashed filenames ``` ### 2. Container Build ```bash podman build -t frontend-test -f frontend/Containerfile frontend/ podman run -p 8080:8080 frontend-test curl http://localhost:8080 # Should return index.html ``` ### 3. Production Bundle Size ```bash cd frontend/dist/assets ls -lh *.js | awk '{print $5, $9}' # Check bundle sizes # Good: Main bundle < 500KB, vendor < 1MB ``` ### 4. nginx Config Validation ```bash nginx -t -c frontend/nginx.conf # Validate syntax ``` ### 5. API Proxy Test ```bash # After deployment curl https://test.coppertone.tech/ # Should return HTML curl https://test.coppertone.tech/api/auth/health # Should proxy to auth-service ``` ## Current Status ### ✅ CORRECT Implementation - Frontend builds to static files via Vite - nginx serves static files and proxies API requests - Multi-stage Docker build keeps images small - Proper separation between frontend and backend - Microservices architecture maintained ### ⚠️ Deployment Issue (502 Errors) - Architecture is correct - Build process is correct - Issue is with CI/CD pipeline execution or server-side configuration - Requires server access to diagnose container startup issues ## Conclusion **The current setup is architecturally correct**. The frontend is properly built and served statically via nginx in production mode for both `testing` and `main` branches. The 502 errors on test.coppertone.tech are not due to architectural issues, but rather deployment/infrastructure issues that need server-side investigation. **Do NOT change to serving frontend via Go** - the current nginx approach is the industry standard and correct practice.