Architecture
Monorepo
A Turbo monorepo:
apps/
api/ Express + TypeScript REST API (taghvimam-backend)
calendar-web/ React 18 + Vite SPA (feature-sliced)
packages/
contracts/ @taghvimam/contracts — Zod schemas = single source of truth
Request flow
client ─▶ Caddy (TLS) ─▶ API (Express)
├─ auth middleware (JWT)
├─ route ─▶ service ─▶ Prisma ─▶ PostgreSQL
└─ response envelope { success, data?, error? }
Each event's origin is tracked via source and sourceId — source is one of
LOCAL, GOOGLE, ANDROID_PROVIDER, or SERVER.
Single source of truth
DTOs are defined once as Zod schemas in packages/contracts. The API imports
them for runtime validation, and the OpenAPI component schemas are generated from
them — so the docs and the validation layer can never drift. Regenerate the spec
with npm run openapi:generate; the CI docs workflow fails if the committed
apps/api/openapi.json is stale or a route lacks @swagger.
Data model (core)
| Model | Purpose |
|---|---|
User | Auth, FCM token, phone/email, sync tracking |
Calendar | Per-user calendars (Jalali/Gregorian), sharing |
Event | Full events with recurrence (RRule), iCal metadata, source tracking |
Task | Hierarchical tasks (self-referential subtasks) |
Reminder | Reminders linked to events or tasks |
Device | Device-aware auth (per-device refresh tokens, replay detection) |
SyncConflict | Banked losers from LWW conflict resolution |
See Database schema for relationships.