Itinera — Architecture Reference
Last updated: February 26, 2026 — Phase 2 complete; nuqs URL filters added
Developer reference for file structure, API endpoints, environment variables, domain setup, and deployment. Stable — changes infrequently.
File Structure
# Base path: /Users/cernenchii/Documents/Itinera
/src
├── proxy.ts # CSRF origin check + 2FA enforcement (renamed from middleware.ts in Next.js 16)
├── app/
│ ├── page.tsx # Landing page (unauthenticated)
│ ├── layout.tsx # Root layout
│ ├── not-found.tsx # Custom 404 page
│ ├── (auth)/
│ │ ├── layout.tsx # Auth layout wrapper
│ │ ├── login/ # Login page
│ │ ├── two-factor/ # 2FA verification page (shown during login)
│ │ ├── invite/[code]/ # Invite registration page
│ │ └── change-password/ # Forced password change page
│ ├── (legal)/
│ │ ├── layout.tsx # Shared legal layout
│ │ ├── privacy/ # Privacy Policy
│ │ ├── terms/ # Terms of Service
│ │ └── support/ # Support page (FAQ, contact)
│ ├── (dashboard)/
│ │ ├── layout.tsx # Sidebar + top-nav shell
│ │ ├── dashboard/ # Main dashboard
│ │ ├── routes/
│ │ │ ├── page.tsx # Routes list
│ │ │ ├── new/ # Create route
│ │ │ └── [id]/ # Route detail
│ │ ├── fleet/ # Fleet management + live map
│ │ ├── analytics/
│ │ │ ├── page.tsx # Fleet fuel analytics
│ │ │ └── [truckId]/ # Per-truck drill-down
│ │ ├── notifications/ # Notification center
│ │ ├── reports/ # Reports page (5 types)
│ │ ├── settings/ # App settings + team + invites
│ │ └── profile/ # User profile (includes 2FA setup)
│ └── api/
│ ├── auth/ # NextAuth + register + invite + 2FA validate
│ ├── routes/ # Route CRUD + waypoints + generate + deviations
│ ├── fleet/ # Samsara vehicles/drivers/locations/status
│ ├── users/ # User management (admin)
│ ├── profile/ # Profile + password + 2FA setup/verify/disable
│ ├── invites/ # Invite generation + listing
│ ├── settings/ # App settings + stats + test connections
│ ├── fuel-prices/ # Fuel price store + near-route query
│ ├── uploads/ # File uploads (Excel)
│ ├── geocode/ # Location search
│ ├── notifications/ # Notification list + read tracking + delete
│ ├── reports/ # Route/activity/deviation/fuel-stop exports
│ └── cron/ # Deviation + fuel alert cron endpoint
├── components/
│ ├── dashboard/
│ │ ├── fuel-price-upload.tsx # Excel fuel price uploader
│ │ └── low-fuel-alerts.tsx # Fuel alert widget
│ ├── routes/
│ │ ├── route-editor.tsx # Waypoint management sidebar
│ │ ├── route-detail-panel.tsx # Route info display
│ │ ├── assignment-card.tsx # Driver/vehicle assignment
│ │ ├── deviations-card.tsx # Deviation history display
│ │ ├── fuel-stops-card.tsx # Nearby fuel stops with prices
│ │ └── google-maps-import.tsx # Google Maps URL import
│ ├── maps/
│ │ ├── google-route-map.tsx # Interactive map with draggable polyline
│ │ └── index.ts # Map component exports
│ ├── profile/
│ │ └── TwoFactorSetupCard.tsx # 2FA enable/disable/setup UI
│ ├── layout/
│ │ ├── sidebar.tsx # Navigation sidebar
│ │ ├── top-nav.tsx # Header navigation
│ │ └── notification-bell.tsx # Notification indicator with per-user unread count
│ ├── providers/
│ │ └── auth-provider.tsx # NextAuth session provider
│ ├── theme-provider.tsx # Theme context (system/light/dark)
│ ├── theme-toggle.tsx # Theme toggle dropdown
│ └── ui/ # Radix-based UI primitives (shadcn)
├── lib/
│ ├── auth.ts # NextAuth config + login rate limiting + 2FA JWT flow
│ ├── prisma.ts # Prisma client singleton
│ ├── rate-limit.ts # In-memory rate limiter (→ rate-limiter-flexible in Phase 1)
│ ├── totp.ts # TOTP implementation (RFC 6238/4226)
│ ├── two-factor-store.ts # In-memory 2FA verification proof store
│ ├── validation.ts # Shared input validation (email, name, password, role)
│ ├── samsara.ts # Samsara API helpers
│ ├── report-utils.ts # Shared report helpers (CSV/XLSX export, CSV injection prevention)
│ ├── notifications.ts # Notification service (createFuelAlert, createDeviationAlert, etc.)
│ ├── telegram.ts # Telegram Bot API helper
│ ├── route-utils.ts # Polyline decode + haversine distance calculations
│ └── utils.ts # General utilities (cn, etc.)
├── types/
│ └── next-auth.d.ts # Auth type extensions (mustChangePassword, twoFactorPending)
└── prisma/
└── schema.prisma # Database schema
Related Files by System
Touch all files in a group when working on that system.
Deviation & Fuel Alert Monitoring (Cron)
src/app/api/cron/check-deviations/route.ts— cron entry pointsrc/lib/notifications.ts— single source of truth for FUEL_LOW and ROUTE_DEVIATIONsrc/lib/telegram.ts— Telegram Bot APIsrc/lib/route-utils.ts— polyline decoding, haversine distance, off-route detectionsrc/lib/samsara.ts— Samsara API helpersprisma/schema.prisma— Notification, RouteDeviation, AppSettings modelsdocs/CRON_SETUP.md— production cron configuration
Notification System (UI)
src/app/api/notifications/route.ts— list + mark all readsrc/app/api/notifications/[id]/route.ts— mark single read (PATCH), delete (DELETE admin)src/app/(dashboard)/notifications/page.tsx— notification center UIsrc/components/layout/notification-bell.tsx— bell icon with per-user unread countsrc/components/dashboard/low-fuel-alerts.tsx— fuel alert banner on dashboardprisma/schema.prisma— Notification, NotificationRead models
2FA Flow
src/proxy.ts— enforces 2FA pending state, CSRF on/api/auth/two-factor/src/lib/auth.ts— issues JWT withtwoFactorPending: truesrc/lib/totp.ts— TOTP implementationsrc/lib/two-factor-store.ts— in-memory verification proof storesrc/lib/rate-limit.ts— rate limiter (5 attempts/15 min on all 2FA endpoints)src/app/api/auth/two-factor/validate/route.ts— validates TOTP/recovery during loginsrc/app/api/profile/two-factor/setup/route.ts— generates secret + QR (password-gated)src/app/api/profile/two-factor/verify/route.ts— verifies code, enables 2FA, returns recovery codessrc/app/api/profile/two-factor/disable/route.ts— disables 2FA (password-gated)src/app/(auth)/two-factor/page.tsx— 2FA verification page shown mid-loginsrc/components/profile/TwoFactorSetupCard.tsx— step-based setup UI on profile pagesrc/types/next-auth.d.ts— JWT/session type extensions
Route Management
src/app/(dashboard)/routes/new/page.tsx— create route formsrc/app/(dashboard)/routes/[id]/page.tsx— route detail pagesrc/app/api/routes/route.ts— list + createsrc/app/api/routes/[id]/route.ts— get, update, deletesrc/app/api/routes/[id]/waypoints/route.ts— list + add waypointssrc/app/api/routes/[id]/waypoints/[waypointId]/route.ts— update/delete single waypointsrc/app/api/routes/[id]/waypoints/reorder/route.ts— reorder waypointssrc/app/api/routes/[id]/generate/route.ts— generate Google Maps URL + activatesrc/app/api/routes/[id]/fuel-alert/route.ts— manual fuel low alert triggersrc/components/routes/route-editor.tsx— waypoint management sidebarsrc/components/routes/assignment-card.tsx— driver/vehicle assignmentsrc/components/routes/fuel-stops-card.tsx— nearby fuel stops with pricessrc/components/maps/google-route-map.tsx— interactive mapsrc/lib/route-utils.ts— polyline decode + distance calculations
Fuel Prices
src/app/api/fuel-prices/route.ts— store (POST) + list (GET)src/app/api/fuel-prices/near-route/route.ts— find stations near a routesrc/components/dashboard/fuel-price-upload.tsx— Excel upload widgetsrc/components/routes/fuel-stops-card.tsx— displays stations on route detail
Authentication & Session
src/lib/auth.ts— NextAuth config, login rate limiting, session/JWT callbacks, 2FA pending flowsrc/proxy.ts— CSRF protection, 2FA enforcement, route protectionsrc/lib/validation.ts— shared input validationsrc/lib/rate-limit.ts— PostgreSQL-backed rate limiter (Prisma RateLimit model)src/types/next-auth.d.ts— session/JWT type extensions
Fleet (Samsara)
src/lib/samsara.ts— Samsara API helperssrc/app/api/fleet/vehicles/route.ts— vehicle listsrc/app/api/fleet/drivers/route.ts— driver listsrc/app/api/fleet/locations/route.ts— live GPS positionssrc/app/api/fleet/status/route.ts— connection statussrc/app/(dashboard)/fleet/page.tsx— fleet management + live map UI
Reports
src/app/(dashboard)/reports/page.tsx— reports UIsrc/app/api/reports/routes/route.ts— route summary + detail exportsrc/app/api/reports/activity/route.ts— activity log exportsrc/app/api/reports/deviations/route.ts— deviation report exportsrc/app/api/reports/fuel-stops/route.ts— fuel stop usage exportsrc/lib/report-utils.ts— date validation, CSV/XLSX generation, CSV injection prevention
Settings
src/app/api/settings/route.ts— get/update company name, Samsara key, Telegram chat IDsrc/app/api/settings/stats/route.ts— data overview countssrc/app/api/settings/test-samsara/route.ts— test Samsara connectionsrc/app/api/settings/test-telegram/route.ts— test Telegram botsrc/app/api/users/route.ts+[id]/route.ts— user management (admin)src/app/api/invites/route.ts— invite generation + listingsrc/app/(dashboard)/settings/page.tsx— settings UI
API Endpoints
Authentication
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/[...nextauth] | NextAuth handlers |
| POST | /api/auth/register | Register with invite code (rate limited) |
| GET | /api/auth/invite/[code] | Validate invite code |
| POST | /api/auth/two-factor/validate | Validate TOTP/recovery code during login |
Invites
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/invites | List all invites with status |
| POST | /api/invites | Generate new invite link |
Routes
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/routes | List all routes |
| POST | /api/routes | Create new route |
| GET | /api/routes/[id] | Get route details |
| PATCH | /api/routes/[id] | Update route |
| DELETE | /api/routes/[id] | Delete route (admin only) |
| POST | /api/routes/[id]/generate | Generate Google Maps URL + activate |
| GET/POST | /api/routes/[id]/waypoints | Manage waypoints |
| PATCH/DELETE | /api/routes/[id]/waypoints/[waypointId] | Update/delete waypoint |
| POST | /api/routes/[id]/waypoints/reorder | Reorder waypoints |
| GET | /api/routes/[id]/deviations | Route deviation history |
| POST | /api/routes/[id]/fuel-alert | Trigger fuel low alert |
Fleet (Samsara)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/fleet/status | Samsara connection status |
| GET | /api/fleet/vehicles | Vehicle list |
| GET | /api/fleet/drivers | Driver list |
| GET | /api/fleet/locations | Live GPS positions |
Users & Profile
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users | List users (admin) |
| POST | /api/users | Create user (admin) |
| PATCH | /api/users/[id] | Update user / reset password (admin) |
| DELETE | /api/users/[id] | Delete user (admin) |
| GET/PATCH | /api/profile | User profile |
| POST | /api/profile/password | Change password |
| POST | /api/profile/two-factor/setup | Generate 2FA secret + QR (password-gated) |
| POST | /api/profile/two-factor/verify | Verify TOTP, enable 2FA, return recovery codes |
| POST | /api/profile/two-factor/disable | Disable 2FA (password-gated) |
Reports
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/reports/routes | Route summary or detail (JSON/CSV/XLSX) |
| GET | /api/reports/activity | Activity log (JSON/CSV/XLSX) |
| GET | /api/reports/deviations | Deviation report (JSON/CSV/XLSX) |
| GET | /api/reports/fuel-stops | Fuel stop usage (JSON/CSV/XLSX) |
Other
| Method | Endpoint | Description |
|---|---|---|
| GET/POST | /api/settings | App settings |
| GET | /api/settings/stats | Data overview counts |
| POST | /api/settings/test-samsara | Test Samsara connection |
| POST | /api/settings/test-telegram | Test Telegram bot |
| GET/POST | /api/fuel-prices | Fuel price data |
| POST | /api/uploads | File uploads (Excel) |
| GET | /api/geocode/search | Location search |
| GET | /api/notifications | Notification list (per-user read status) |
| POST | /api/notifications | Mark all read (per-user) |
| PATCH | /api/notifications/[id] | Mark single notification read |
| DELETE | /api/notifications/[id] | Delete notification (admin only) |
| POST | /api/cron/check-deviations | Deviation + fuel alert cron job |
Environment Variables
# Database
DATABASE_URL="postgresql://..."
# NextAuth
NEXTAUTH_SECRET="..."
NEXTAUTH_URL="https://app.getitinera.com"
# Google Maps (Maps, Geocoding, Directions) — moving to HERE in Phase 4
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY="..."
# Samsara API key — stored in DB via AppSettings (not in env)
# Telegram Bot
TELEGRAM_BOT_TOKEN="..."
# Added in Phase 1:
# SENTRY_DSN="..." — Bugsink self-hosted DSN
# RESEND_API_KEY="..." — Transactional email
# Added in Phase 4:
# HERE_API_KEY="..." — HERE Routing, Geocoding, Fuel Prices APIs
# Added in Phase 6:
# MINIO_ENDPOINT="..."
# MINIO_ACCESS_KEY="..."
# MINIO_SECRET_KEY="..."
# MINIO_BUCKET="itinera"
Domain Architecture
| Domain | Purpose | Tunnel → | Traefik |
|---|---|---|---|
getitinera.com | Public landing page | Cloudflare Pages (no tunnel) | — |
www.getitinera.com | Redirect | Cloudflare Pages (no tunnel) | — |
app.getitinera.com | Itinera platform (Next.js) | https://localhost:443 | HTTPS router, Let's Encrypt cert |
coolify.getitinera.com | Coolify deployment UI | https://localhost:443 | HTTPS router, Let's Encrypt cert |
secrets.getitinera.com | Infisical secret management | https://localhost:443 | HTTPS router, Let's Encrypt cert |
status.getitinera.com | Uptime Kuma status page | http://localhost:80 | HTTP router only |
errors.getitinera.com | Bugsink error tracking | http://localhost:80 | HTTP router only |
storage.getitinera.com | Minio file storage | TBD (Phase 6) | — |
workflows.getitinera.com | n8n automation | TBD (Phase 7) | — |
DNS: All subdomains are CNAMEs → efa99bf0-1a82-43ed-8b32-06e454745179.cfargotunnel.com. Managed in Cloudflare DNS. Droplet has no inbound ports open (SSH only via DigitalOcean firewall).
TLS: Cloudflare handles TLS at the edge for all domains. Internally, traffic flows over HTTP or HTTPS depending on the Coolify domain setting:
Tunnel routing rules
There are two routing patterns and it's important to keep them consistent:
Pattern A — https://localhost:443 with noTLSVerify: true
Used for: app, coolify, secrets
- Coolify domain is set to
https://→ Traefik generates both HTTP and HTTPS routers - Traefik obtains a Let's Encrypt cert via HTTP-01 challenge
- The tunnel sends traffic to port 443;
noTLSVerify: trueskips cert validation since it's an internal cert - These domains had their certs issued when port 80 was still open; cert auto-renews via ACME
Pattern B — http://localhost:80
Used for: status, errors
- Coolify domain is set to
http://→ Traefik generates only an HTTP router on port 80 - No Let's Encrypt cert is issued (HTTP-01 challenge fails — port 80 inbound is blocked by firewall)
- Cloudflare provides HTTPS to the end user; internally traffic is plain HTTP
- This is safe because the Cloudflare Tunnel is an encrypted outbound connection
When adding a new subdomain, decide which pattern to use:
- Use Pattern A if the service was deployed before ports were closed (cert already exists)
- Use Pattern B for any new service deployed after the firewall was locked down
- Set the Coolify domain scheme (
http://vshttps://) and the tunnel ingress rule to match
Deployment & CI/CD
Current (Phase 1 + 2):
- Push to
master→ GitHub Actions builds Docker image on 7GB runner → pushes toghcr.io/ioncernenchii/itinera:latest - Coolify webhook triggered → pulls latest image from ghcr.io → redeploys (seconds, no build)
- GitHub Actions also runs ESLint + build check (linting step blocks on issues)
- Rollback: via Coolify UI → Docker Image Tag → select previous
:sha-<short>tag
Previous pipeline (pre-Phase 2):
- Coolify ran multi-stage Dockerfile build locally on droplet (time-consuming, disk overhead)
- Now: zero builds on droplet, all builds on GitHub Actions 7GB runner (faster, cleaner)
Stack:
- Hosting: DigitalOcean 4GB droplet, Ubuntu 24.04
- Orchestration: Coolify v4 with Docker Image build pack
- Reverse proxy: Cloudflare Tunnel (no exposed ports; Tunnel → Traefik on droplet)
- Database: PostgreSQL 18 with PostGIS (custom Docker image
itinera-postgres:18-postgis; Dockerfile at/data/coolify/databases/pg-postgis/Dockerfileon production droplet; PostGIS 3.6.1 baked in viaapk add --no-cache postgisand .so file copying; survives container rebuilds) - Registry: GitHub Container Registry (ghcr.io) with pre-built images
- All env vars: stored in Coolify directly (Infisical as backup/reference only)