12 KiB
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
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
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:
-
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
-
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 -
nginx Serves:
GET /→/usr/share/nginx/html/index.htmlGET /about→/usr/share/nginx/html/index.html(SPA routing)GET /assets/index-[hash].js→ Static file from diskPOST /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) + individualgo 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
testingbranch → 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
mainbranch → Woodpecker CI → SSH deploy
Deployment Flow
CI/CD Pipeline (.woodpecker/develop.yml)
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)
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)
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:
# 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:
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;
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;
Alternative Architecture (Why We DON'T Do This)
❌ WRONG: Single Go Server Serving Everything
// 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
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
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
cd frontend/dist/assets
ls -lh *.js | awk '{print $5, $9}' # Check bundle sizes
# Good: Main bundle < 500KB, vendor < 1MB
4. nginx Config Validation
nginx -t -c frontend/nginx.conf # Validate syntax
5. API Proxy Test
# 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.