321 lines
12 KiB
Markdown
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.
|