flowchart TB
%% ============================================
%% Internet & Client Layer
%% ============================================
CLIENT["๐ Client Browser"]
DOMAIN["๐ city-pulse.app
SSL/TLS (Let's Encrypt)"]
%% ============================================
%% CI/CD Pipeline (GitHub Actions)
%% ============================================
subgraph CICD["โก GitHub Actions CI/CD Pipeline"]
direction TB
TRIGGER["๐ Triggers
โข Push: main, production
โข PR: main"]
subgraph JOB1["๐งช Job 1: Test & Quality"]
direction LR
T1["๐ฆ Checkout
actions/checkout@v4"]
T2["๐ง Setup Node 20
pnpm 10.10.0"]
T3["๐ฅ Install deps
pnpm install"]
T4["๐ Lint
ESLint + Prettier"]
T5["๐๏ธ Build
TypeScript + Vite"]
end
subgraph JOB2["๐ณ Job 2: Build Images"]
direction LR
B1["๐ก Prepare names
REPO_LOWER"]
B2["๐๏ธ Docker Buildx
linux/amd64"]
B3["๐ Login GHCR
ghcr.io"]
B4["๐ฆ Build Backend
backend/Dockerfile.prod"]
B5["๐ฆ Build Frontend
frontend/Dockerfile.prod"]
B6["โฌ๏ธ Push to GHCR
ghcr.io/citypulse-backend:v1.2.3
ghcr.io/citypulse-frontend:v1.2.3"]
end
subgraph JOB3["๐ Job 3: Deploy"]
direction LR
D1["๐ Create package
.env.prod + docker-compose.prod.yml"]
D2["๐ฆ Tar archive
deploy-v1.2.3.tar.gz"]
D3["๐ค SCP Upload
โ /opt/citypulse"]
D4["๐ SSH Deploy
root@[Production Droplet]"]
D5["๐ณ Docker compose up
--env-file .env.prod"]
D6["โ
Health check
timeout 300s"]
end
JOB4["๐ข Job 4: Notify
โ
city-pulse.app/health"]
TRIGGER --> T1
T1 --> T2 --> T3 --> T4 --> T5
T5 -.-> B1
B1 --> B2 --> B3 --> B4 --> B5 --> B6
B6 -.-> D1
D1 --> D2 --> D3 --> D4 --> D5 --> D6
D6 -.-> JOB4
end
%% ============================================
%% DigitalOcean Droplet Infrastructure
%% ============================================
subgraph DROPLET["๐ฆ DigitalOcean Droplet
Production Server โข /opt/citypulse"]
direction TB
subgraph NGINX_LAYER["๐ Nginx Reverse Proxy"]
NGINX["citypulse-nginx-prod
nginx:1.25-alpine
Ports: 80, 443
nginx-domain.conf
Rate Limit: 10r/s"]
end
subgraph DOCKER_NET["๐ Docker Network: citypulse-network"]
direction TB
subgraph APP_SERVICES["๐ฏ Application Services"]
direction LR
BACKEND["๐ง citypulse-backend-prod
ghcr.io/citypulse-backend:v1.2.3
Port: 5000
JWT + WebSocket
Health: /health"]
FRONTEND["โ๏ธ citypulse-frontend-prod
ghcr.io/citypulse-frontend:v1.2.3
Port: 3001โ80
Vite + Tailwind
Health: localhost:80"]
end
POSTGRES["๐๏ธ citypulse-postgres-prod
postgres:15-alpine
Port: 5432
21+ Tables
Health: pg_isready"]
end
subgraph VOLUMES["๐พ Persistent Volumes"]
VOL1["postgres_data"]
VOL2["uploads_data"]
VOL3["letsencrypt"]
end
NGINX --> BACKEND
NGINX --> FRONTEND
BACKEND <--> POSTGRES
FRONTEND -.depends.-> BACKEND
POSTGRES -.mount.-> VOL1
BACKEND -.mount.-> VOL2
NGINX -.mount.-> VOL3
end
%% ============================================
%% External Services
%% ============================================
subgraph EXTERNAL["๐ External Services"]
direction TB
GOOGLE["๐ Google OAuth"]
EMAIL["๐ง SendGrid API"]
GHCR["๐ฆ GitHub Container Registry
ghcr.io/citypulse-*"]
end
%% ============================================
%% Main Flow Connections
%% ============================================
CLIENT -->|HTTPS| DOMAIN
DOMAIN -->|443| NGINX
BACKEND -->|OAuth| GOOGLE
BACKEND -->|Emails| EMAIL
D4 -.SSH.-> DROPLET
GHCR -.docker-pull.-> DOCKER_NET
JOB2 -.images.-> GHCR
%% ============================================
%% Environment & Health
%% ============================================
ENVVARS["โ๏ธ .env.prod
JWT_SECRET โข POSTGRES_*
GOOGLE_* โข EMAIL_*"]
HC["โค๏ธ Health Dashboard
pg_isready โข /health
wget localhost โข nginx"]
ENVVARS -.inject.-> BACKEND
ENVVARS -.inject.-> FRONTEND
ENVVARS -.inject.-> POSTGRES
HC -.monitor.-> POSTGRES
HC -.monitor.-> BACKEND
HC -.monitor.-> FRONTEND
HC -.monitor.-> NGINX
%% ============================================
%% Production-Grade Color Scheme (High Contrast)
%% ============================================
style CLIENT fill:#dbeafe,stroke:#1e40af,stroke-width:3px
style DOMAIN fill:#fef3c7,stroke:#b45309,stroke-width:3px
style CICD fill:#f8fafc,stroke:#1e293b,stroke-width:3px
style DROPLET fill:#fefce8,stroke:#a16207,stroke-width:4px
style NGINX_LAYER fill:#dcfce7,stroke:#166534,stroke-width:2px
style DOCKER_NET fill:#eff6ff,stroke:#1d4ed8,stroke-width:2px
style APP_SERVICES fill:#fef3c7,stroke:#b45309,stroke-width:2px
style VOLUMES fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px
style EXTERNAL fill:#fecaca,stroke:#b91c1c,stroke-width:2px
style NGINX fill:#10b981,stroke:#047857,stroke-width:3px,color:#000
style BACKEND fill:#f97316,stroke:#c2410c,stroke-width:3px,color:#fff
style FRONTEND fill:#3b82f6,stroke:#1d4ed8,stroke-width:3px,color:#fff
style POSTGRES fill:#8b5cf6,stroke:#6d28d9,stroke-width:3px,color:#fff
style GOOGLE fill:#ef4444,stroke:#dc2626,stroke-width:2px,color:#fff
style EMAIL fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff
style GHCR fill:#1f2937,stroke:#111827,stroke-width:2px,color:#fff
style JOB1 fill:#dcfce7,stroke:#166534,stroke-width:2px
style JOB2 fill:#eff6ff,stroke:#1d4ed8,stroke-width:2px
style JOB3 fill:#fef3c7,stroke:#b45309,stroke-width:2px
style JOB4 fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff
style ENVVARS fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px
style HC fill:#fecaca,stroke:#b91c1c,stroke-width:2px