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