Files
web-hosts/domains/coppertone.tech/ARCHITECTURE.md
2025-12-26 13:38:04 +01:00

321 lines
12 KiB
Markdown

# 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.