1Month Portfolio CityFix
Technical Review

CityFix Platform Architecture

Civic issue reporting for municipalities - 2 NestJS microservices, AI moderation pipeline (NSFW + PII blur), offline-first mobile, PostGIS spatial queries, and Docker Compose deployment on GCP VM.

2 Microservices
6 Docker Containers
4 Domains
4 Languages
🏙️

Platform Overview

CityFix is a civic issue reporting platform for municipalities. Citizens report geolocated problems (potholes, broken streetlights, graffiti); city employees triage and resolve issues via dedicated admin portals. The platform features AI-powered content moderation, offline-first mobile reporting, and gamification with leaderboards.

"Report. Track. Fix. - Making cities better, one report at a time."
📸
Photo-Based Reporting

GPS-tagged photo reports with map pinning

🤖
AI Moderation Pipeline

Profanity filter, NSFW detection, PII blurring

📡
Offline-First

Background queue with auto-drain on reconnect

🗺️
Map-Based Interface

Interactive map for browsing and reporting issues

🏆
Gamification

Leaderboard, milestone confetti & upvoting

🔐
Multi-Tier Admin

Municipality portal + system admin portal

⚙️

Tech Stack

Layer Technology Details
Mobile App React Native + Expo Expo SDK, React Navigation
Municipality Admin Next.js + React admin.cityfix.co.il
System Admin Next.js + React app.cityfix.co.il
Marketing Website Next.js cityfix.co.il
Backend Services NestJS (TypeScript) × 2 auth-service, reports-service
ORM Prisma Multi-schema isolation
Database PostgreSQL 16 + PostGIS 3.4 postgis/postgis:16-3.4-alpine
AI - NSFW TensorFlow.js + NSFWJS Image content moderation
AI - OCR Tesseract.js PII text detection & blurring
Text Moderation leo-profanity Abusive language filtering
Image Processing Sharp Resize, WebP conversion, PII blur regions
Monitoring Sentry Error tracking & performance
Language TypeScript (100%) Full-stack typed
🏗️

Architecture

Monorepo Structure

CityFix/
├── apps/
│   ├── auth-service/       # NestJS - Auth, JWT, Users, Email
│   ├── reports-service/    # NestJS - Reports CRUD, AI Moderation
│   ├── admin-web/          # Next.js - Municipality Portal
│   ├── admin-sys/          # Next.js - System Admin Portal
│   ├── website/            # Next.js - Public Marketing Site
│   └── mobile/             # Expo React Native - Field Reporting
├── deploy/
│   ├── docker-compose.prod.yml
│   ├── nginx.prod.conf
│   ├── enable-tls.sh
│   └── backup-db.sh
└── .github/workflows/      # CI/CD pipeline definitions

System Architecture

Clients
📱 Mobile App
Expo React Native
🏛️ Municipality Admin
admin.cityfix.co.il
⚙️ System Admin
app.cityfix.co.il
🌐 Website
cityfix.co.il
Nginx Reverse Proxy + SSL (Certbot)
🔒 Nginx - 4 Domains
Docker Compose - GCP VM
🔐 auth-service
📋 reports-service
🏛️ admin-web
⚙️ admin-sys
🌐 website
Data Layer
🐘 PostgreSQL 16
PostGIS 3.4 (containerized)
📁 Docker Volumes
uploads, certs, db
📊

Architecture Diagrams

Software Architecture
CityFix Software Architecture - 2 NestJS microservices behind Nginx reverse proxy, with PostgreSQL, Docker Volumes, NSFWJS, and Tesseract.js
Clients → Nginx Proxy → NestJS Services → Data Layer + AI Pipeline
Deployment Architecture
CityFix Deployment Architecture - Users to Nginx SSL to Docker Compose containers, with persistent volumes and CI/CD pipeline
Users → Nginx (4 domains) → Docker Compose → PostgreSQL / Volumes
🔧

Service Breakdown

🔐 auth-service

User identity, JWT authentication, email-based auth (Nodemailer), user accounts and profile management.

📋 reports-service

Core business logic - civic issue reports CRUD, AI moderation pipeline, image processing, upvoting, and status management.

🤖

AI Moderation Pipeline

5-Stage Report Processing

Every incoming civic report passes through a multi-stage AI pipeline before being committed to the database.

Text Moderation

leo-profanity scans title and description for abusive language. Rejects immediately if detected.

Image Optimization

Sharp resizes to 1200px max width and converts to WebP. Original file is unlinked.

PII Text Detection & Blurring

Tesseract.js OCR scans the image for text (license plates, ID cards). Detected regions are blurred via Sharp.

NSFW Content Detection

NSFWJS + TensorFlow scans the blurred image. Rejects and deletes if pornographic or graphic content detected.

Database Commit

Verified, blurred, optimized WebP is saved to /uploads volume and the DB entry is created.

Memory Note: NSFWJS pulls heavy TensorFlow node-bindings. Container memory allocation must be at least 1–2 GB to prevent OOM during heavy report loads.
🗃️

Database Design

Schema Architecture

Both services share a single PostgreSQL 16 + PostGIS 3.4 containerized instance, separated by schemas managed via Prisma:

Prisma Config DB Schema Service
auth-service/prisma/schema.prisma auth auth-service
reports-service/prisma/schema.prisma reports reports-service
☁️

Infrastructure

GCP VM Deployment

Internet → Nginx Reverse Proxy
🔒 Nginx + Certbot SSL
cityfix.co.il admin.cityfix.co.il app.cityfix.co.il api.cityfix.co.il
Docker Compose - 6 Containers
auth-service
reports-service
admin-web
admin-sys
website
db (PostGIS)
Persistent Volumes
🐘 postgres_data
📁 uploads_data
Shared: Nginx + reports-service
🔑 certbot_data
🚀

DevOps & CI/CD

Pipeline Overview

CI
  • Backend: Lint & Test (auth, reports)
  • Web: Lint & Build (admin-web, admin-sys, website)
  • Mobile: Jest Tests
  • Docker Build Smoke Test
Trigger: push, Pull Request
Deploy to VM
  • GitHub Actions → SSH into GCP VM
  • git pull latest changes
  • docker compose -f docker-compose.prod.yml build
  • docker compose up -d
  • Health checks on all services
Trigger: CI passes on main
Mobile Build
  • EAS Build (iOS / Android)
  • TestFlight / Play Console submit
Trigger: Tag / Manual

Database Backups

Nightly cron job runs backup-db.sh - PostGIS dump with timestamped filenames, stored on the VM's persistent disk.
📡

Observability & Quality

System Admin Portal — Platform Observability

A dedicated Next.js System Admin portal (app.cityfix.co.il) provides platform-wide visibility. Separate from the municipality admin, this portal focuses on engineering operations:

💓
Health Dashboard

Auto-refreshing (30s) liveness checks across 5 Docker services (Admin Sys, Auth Service, Reports Service, Admin Web, Website). Per-service response time with color-coded latency bands: green (<200ms), orange (200ms–1s), red (>1s). Aggregate operational status banner.

🚀
CI/CD Dashboard

GitHub Actions integration showing CI success rate, deploys this week, latest CI and deploy run numbers. Two pipeline views: CI Pipeline (lint, test, build) and Production Deploy (SSH → Docker Compose). Each run shows pass/fail status, commit hash, author, and age. Direct link to GitHub Actions.

🔴
Sentry Dashboard

Integrated Sentry view tracking 4 projects: cityfix-auth-service, cityfix-reports-service, cityfix-admin, and cityfix-mobile. Shows unresolved and new error counts per project. Overview widget displays total Sentry errors (24h) alongside CI status and report counts.

📚
Taxonomy & Moderation

Taxonomy management for report categories across all municipalities. Moderation pipeline configuration and monitoring for the AI content filtering stages (profanity, NSFW, PII).

Error Tracking & Uptime

Tool Scope Details
Sentry Backend (×2) + Admin + Mobile @sentry/nestjs on auth-service and reports-service. @sentry/react-native on mobile. Admin dashboard Sentry tracking. Source maps uploaded in CI. Error counts surfaced in System Admin overview.
UptimeRobot All 4 public domains 60-second interval HTTP health checks on cityfix.co.il, admin.cityfix.co.il, app.cityfix.co.il, and api.cityfix.co.il. Instant Slack/email notifications on downtime. Public status page.
Docker Health Checks 6 containers Docker Compose healthcheck directives on all service containers. Post-deploy CI validation via SSH health check commands.

System Admin Overview

The System Overview dashboard aggregates key platform signals into a single pane of glass:

Signal Source Description
Active Cities Database Number of municipalities onboarded
Total Reports Database Aggregate civic reports across all cities
Reports Today Database 24h report volume
CI Status GitHub Actions API Latest CI run pass/fail status
Sentry Errors Sentry API New unresolved errors in the last 24h
Pipeline Summary GitHub Actions API Last 3 CI runs with commit messages and status
Error Monitoring Sentry API Per-project (auth, reports, admin, mobile) error counts
Service Health Health endpoints 5/5 services operational status with response times
💡

Key Architectural Decisions

VM over Serverless

Docker Compose on a single GCP VM keeps costs fixed and simplifies inter-container networking (bridge network with internal hostnames).

AI Pipeline Ordering

Stages run text → optimize → PII blur → NSFW. Cheap rejects first (profanity, oversized images), heavy ML last — minimizes wasted compute on rejected reports.

Containerized PostGIS

Self-hosted postgis/postgis:16-3.4-alpine in Docker Compose instead of Cloud SQL — keeps spatial queries co-located with the app, predictable cost, no egress fees.

Two Admin Portals

Municipality admin (admin.cityfix.co.il) and System admin (app.cityfix.co.il) are split — operational separation between municipal staff workflows and platform-wide engineering ops.

ML Memory Tradeoff

Sharp + Tesseract.js + NSFWJS share a single Node process. Container is sized at 1–2 GB to cover TensorFlow node-bindings without OOM under burst loads.

4-Language Support

Full i18n (en, he, ar, ru) with RTL support via I18nManager.forceRTL() - serving diverse municipal populations.