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.
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.
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."
GPS-tagged photo reports with map pinning
Profanity filter, NSFW detection, PII blurring
Background queue with auto-drain on reconnect
Interactive map for browsing and reporting issues
Leaderboard, milestone confetti & upvoting
Municipality portal + system admin portal
| 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 |
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
User identity, JWT authentication, email-based auth (Nodemailer), user accounts and profile management.
Core business logic - civic issue reports CRUD, AI moderation pipeline, image processing, upvoting, and status management.
Every incoming civic report passes through a multi-stage AI pipeline before being committed to the database.
leo-profanity scans title and description for abusive language.
Rejects immediately if detected.
Sharp resizes to 1200px max width and converts to WebP. Original
file is unlinked.
Tesseract.js OCR scans the image for text (license plates, ID
cards). Detected regions are blurred via Sharp.
NSFWJS + TensorFlow scans the blurred image. Rejects and deletes
if pornographic or graphic content detected.
Verified, blurred, optimized WebP is saved to /uploads volume and
the DB entry is created.
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 |
git pull latest changesdocker compose -f docker-compose.prod.yml builddocker compose up -dbackup-db.sh - PostGIS dump with timestamped filenames,
stored on the VM's persistent disk.
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:
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.
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.
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 management for report categories across all municipalities. Moderation pipeline configuration and monitoring for the AI content filtering stages (profanity, NSFW, PII).
| 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. |
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 |
Docker Compose on a single GCP VM keeps costs fixed and simplifies inter-container networking (bridge network with internal hostnames).
Stages run text → optimize → PII blur → NSFW. Cheap rejects first (profanity, oversized images), heavy ML last — minimizes wasted compute on rejected reports.
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.
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.
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.
Full i18n (en, he, ar, ru) with RTL support via I18nManager.forceRTL() - serving diverse
municipal populations.
See the live platform and citizen portal