Files
carmanagement/docs/project-design/api-routes.md
T
root f22e0d45e1
Build & Deploy / Build & Push Docker Image (push) Successful in 7m57s
Test / Type Check (all packages) (push) Successful in 4m27s
Build & Deploy / Deploy to VPS (push) Successful in 7s
Test / API Unit Tests (push) Failing after 3m2s
Test / Homepage Unit Tests (push) Successful in 4m3s
Test / Storefront Unit Tests (push) Successful in 3m32s
Test / Admin Unit Tests (push) Successful in 3m27s
Test / Dashboard Unit Tests (push) Successful in 3m3s
Test / API Integration Tests (push) Failing after 3m53s
fix subscription page
2026-06-29 23:15:55 -04:00

397 lines
15 KiB
Markdown

# API Architecture and Route Design
This document explains how the API is structured today, how requests move through the stack, and what each route group is responsible for.
Source of truth for the runtime wiring:
- `apps/api/src/app.ts`
- `apps/api/src/modules/*`
- `apps/api/src/middleware/*`
- `apps/api/src/swagger/openapi.ts`
## Runtime Overview
The API is an Express application mounted under `/api/v1` with a few non-versioned utility endpoints:
- `GET /health` returns process health.
- `GET /docs` serves Swagger UI.
- `GET /api/v1/openapi.json` returns the generated OpenAPI document.
- `/storage/*` serves uploaded assets such as logos, hero images, vehicle photos, and reservation/customer documents.
The app boot sequence in `apps/api/src/app.ts` is intentionally ordered:
1. CORS is applied first.
2. storage guards are applied before static serving so private customer license files are never anonymously retrievable.
3. Swagger UI is mounted before Helmet so the UI assets are not blocked by CSP.
4. webhook routes that require raw payload handling are mounted before `express.json()`.
5. Helmet, request logging, JSON parsing, and the module routers are mounted.
6. the centralized error middleware converts validation, Prisma, and application errors into consistent JSON responses.
## Request Lifecycle
Most internal company routes follow the same pattern:
1. Express router receives the request.
2. Zod schemas validate `req.params`, `req.query`, and `req.body` through `parseParams`, `parseQuery`, and `parseBody`.
3. auth middleware resolves the caller identity.
4. tenant middleware resolves the company record.
5. subscription middleware blocks suspended or pending companies where required.
6. role middleware enforces employee or admin permissions.
7. the route handler calls a service function.
8. the service applies business rules and calls a repo or Prisma directly.
9. a presenter may normalize the payload for frontend consumption.
10. `ok()` or `created()` wraps successful responses in `{ "data": ... }`.
Common exceptions:
- `GET /health` and `GET /api/v1/docs` return raw JSON, not the `{ data }` envelope.
- webhook endpoints return minimal receipt payloads.
- some public endpoints return graceful fallback responses if the database is unavailable.
## Security and Access Model
There are four main access patterns.
### Employee JWT
`requireCompanyAuth` validates a Bearer token with `type === "employee"`, loads the employee, and attaches:
- `req.employee`
- `req.company`
- `req.companyId`
This is the default for dashboard/company management routes.
### Tenant Resolution
`requireTenant` reloads the company from `req.companyId` and guarantees the tenant context exists. Company-scoped modules then query by `companyId`.
### Subscription Guard
`requireSubscription` blocks company routes when the company status is `PENDING` or `SUSPENDED`. Public site and storefront routes are intentionally not behind this guard.
### Role-Based Access Control
Employee roles are hierarchical:
- `OWNER`
- `MANAGER`
- `AGENT`
Admin roles are hierarchical:
- `SUPER_ADMIN`
- `ADMIN`
- `SUPPORT`
- `FINANCE`
- `VIEWER`
### Renter JWT
`requireRenterAuth` validates a Bearer token with `type === "renter"` and attaches `req.renterId`.
`optionalRenterAuth` is used on storefront routes so public reads still work without a token.
### API Key
`requireApiKey` accepts a company public API key in `x-api-key`. This is the low-friction integration path for public company-site style calls.
## Implementation Pattern
The API codebase is organized per module. The most common pattern is:
- `*.routes.ts`: route definitions and middleware composition
- `*.schemas.ts`: Zod input validation
- `*.service.ts`: business rules and orchestration
- `*.repo.ts`: Prisma access helpers
- `*.presenter.ts`: response shaping
The `vehicles` module is representative:
- `vehicle.routes.ts` mounts auth, tenant, and subscription guards once for the router.
- route handlers validate input and call service functions.
- `vehicle.service.ts` contains business rules such as publish-state normalization and availability calculation.
- `vehicle.repo.ts` owns the Prisma queries and enforces tenant filters in reads.
- `vehicle.presenter.ts` shapes the response returned to clients.
Some modules split specialized workflows into additional services instead of one large file. The reservations module is the clearest example:
- `reservation.service.ts` handles CRUD and list behavior
- `reservation.lifecycle.service.ts` handles confirm/checkin/checkout/close/extend/cancel transitions
- `reservation.inspection.service.ts` handles pickup/dropoff inspections
- `reservation.additional-driver.service.ts` handles approval workflows
- `reservation.photo.service.ts` handles pickup/dropoff photo uploads
- `reservation.document.service.ts` generates contract and billing payloads
## Route Groups
The table below describes the current route groups mounted in `app.ts`.
| Base path | Access | Purpose | Notes |
| --- | --- | --- | --- |
| `/health` | Public | Process health | Non-versioned utility endpoint |
| `/docs` | Public | Swagger UI | Backed by `openapi.ts` |
| `/api/v1/openapi.json` | Public | OpenAPI JSON | Generated from Zod schemas and manual path entries |
| `/api/v1/auth/company` | Public | Company signup | `complete-signup` and `verify-email` now return disabled errors |
| `/api/v1/auth/employee` | Public plus employee JWT | Employee login, password reset, profile, language | Dashboard/staff authentication path |
| `/api/v1/auth/renter` | Renter JWT for profile routes | Renter profile and push token | `signup` and `login` are currently disabled in this codebase |
| `/api/v1/admin` | Admin JWT | Platform operations | Includes company admin, billing, subscriptions, pricing, audit, and admin-user management |
| `/api/v1/storefront` | Public, optional renter JWT | Storefront discovery and reservation intake | Designed for discovery and lead capture across companies |
| `/api/v1/site` | Public | White-label company site APIs | Drives each company-branded booking site |
| `/api/v1/subscriptions` | Mixed | Plan listing, webhooks, subscription lifecycle | Public, webhook, and authenticated sub-routers share the same prefix |
| `/api/v1/vehicles` | Employee JWT + tenant + subscription | Fleet management | Includes photos, status, calendar blocks, maintenance, and availability |
| `/api/v1/reservations` | Employee JWT + tenant + subscription | Reservation operations | Central booking lifecycle for internal staff |
| `/api/v1/team` | Employee JWT + tenant + subscription | Employee/team management | Owner-controlled invite, role, activation, removal |
| `/api/v1/customers` | Employee JWT + tenant + subscription | Company CRM and license handling | Includes protected license-image retrieval and approval flows |
| `/api/v1/offers` | Employee JWT + tenant + subscription | Promotional offers | Company marketing and promo codes |
| `/api/v1/analytics` | Employee JWT + tenant + subscription | Dashboard metrics and reports | Includes summary, dashboard, source, and report routes |
| `/api/v1/notifications` | Employee or renter JWT depending on route | Notification inbox and preferences | Separate company and renter surfaces |
| `/api/v1/companies` | Employee JWT + tenant + subscription | Company profile and settings | Brand, domains, contract settings, insurance policies, pricing rules, accounting, API key |
| `/api/v1/payments` | Mixed | Rental payment webhooks and company payment actions | Webhooks are public; operational routes are company-authenticated |
| `/api/v1/reviews` | Employee JWT + tenant + subscription | Review moderation | Company-side review visibility and replies |
| `/api/v1/complaints` | Employee JWT + tenant + subscription | Complaint handling | Internal complaint tracking |
| `/api/v1/webhooks` | Public | External webhook receivers | Currently includes `/clerk` placeholder |
## Functional Breakdown by Module
### Company and employee onboarding
- `POST /api/v1/auth/company/signup` creates the company tenant, owner employee, default subscription state, and related baseline records in one transaction.
- The signup flow generates a unique company slug, hashes the owner password, starts a 30-day trial window, and sends account-created notifications.
- Employee auth supports login, forgot-password, reset-password, profile read, and language updates.
### Company profile and business configuration
The `companies` module owns tenant-level settings:
- company identity
- brand settings and asset upload
- subdomain/custom-domain configuration
- contract settings used by rental documents
- insurance policies
- pricing rules
- accounting settings
- public API key generation/regeneration
This module is where most long-lived company configuration lives.
### Fleet management
The `vehicles` module handles:
- CRUD for vehicles
- publish/unpublish
- explicit status updates
- photo upload and deletion
- availability checks
- monthly vehicle calendar events
- manual calendar blocks
- maintenance logs
Uploads are persisted through the storage service, then surfaced under `/storage/*`.
### Reservations and rental workflow
The reservation aggregate is the center of the operational API.
Important transitions:
1. create draft reservation
2. confirm reservation after license checks
3. check in and move the reservation to `ACTIVE`
4. check out and mark the reservation `COMPLETED`
5. close the reservation operationally after post-rental tasks are done
The lifecycle service also handles:
- extensions with conflict checks
- cancellations
- vehicle status synchronization
- review token generation
- review request email dispatch on close
The reservations module also owns:
- contract and billing payload generation
- pickup/dropoff inspections
- additional-driver approval
- pickup/dropoff photo upload
### Customer CRM and license compliance
Customers are company-scoped CRM records, even when a renter identity also exists. The `customers` module supports:
- customer CRUD
- flag/unflag flows
- license image upload and protected retrieval
- license validation and manager approval
This is intentionally separate from renter identity because one renter may interact with multiple companies while each company still needs its own operational customer record.
### Storefront and public site
The public surface is split in two modules on purpose.
`storefront` is the cross-company discovery layer:
- featured/public offers
- storefront cities
- listed companies
- vehicle search
- storefront reservation intake
- review submission by token
- company public pages under `/:slug`
`site` is the white-label company booking API:
- company brand payload
- published vehicle catalog
- booking options
- date-range availability
- promo validation
- booking creation
- payment initialization
- booking lookup
- company contact form
Both modules deliberately include graceful degradation paths when the database is unavailable so public pages can fail softer than internal operations.
### Payments
Rental payments are company-side operational payments, distinct from SaaS subscription billing.
The `payments` module handles:
- AmanPay and PayPal rental webhooks
- listing payments by company or reservation
- initializing online charges
- capturing PayPal orders
- recording manual payments
- refunding successful online payments
Payment success updates both the payment record and the reservation paid amount.
### Subscriptions and SaaS billing
The `subscriptions` module owns the platform billing lifecycle:
- public plan/provider/feature listing
- company subscription read endpoints
- trial start
- checkout
- plan changes
- cancellation/resume/reactivation
- provider webhooks
- admin overrides
It also exposes background-job style functions for:
- trial expiration
- payment-pending timeout
- past-due timeout
- suspension timeout
- period-end cancellation
### Notifications
Notifications are multi-channel and multi-audience:
- company inbox
- renter inbox
- unread counts
- preferences per type and channel
- notification history
Templates and preference records live in the database; delivery orchestration lives in `services/notificationService.ts`.
### Admin surface
The admin router is broad because it covers platform operations across multiple domains:
- admin authentication and 2FA
- company listing, inspection, status changes, deletion, impersonation
- renter blocking/unblocking
- platform metrics
- notification inspection
- audit log access
- admin-user and permission management
- billing account and billing invoice operations
- pricing config, plan features, and promotions
- subscription overrides and extensions
- storefront homepage configuration
This is the only route group allowed to work across tenants.
## Important Workflow Examples
### 1. Company signup
`POST /api/v1/auth/company/signup`
- validates the payload with Zod
- checks for duplicate company and owner email addresses
- generates a unique slug
- hashes the owner password
- creates the tenant, owner employee, and initial subscription state in one transaction
- sends localized account-created notifications
### 2. Public booking from a company site
`POST /api/v1/site/:slug/book`
- resolves the company from the slug
- verifies vehicle availability for the requested date range
- upserts the company-scoped customer record
- computes total days and base rental price
- applies promo code discount if present
- applies pricing rules
- stores a draft reservation
- attaches reservation insurance snapshots and additional-driver snapshots
- triggers license validation flags
Payment is then started with `POST /api/v1/site/:slug/booking/:id/pay`.
### 3. Reservation lifecycle after booking
- reservation starts in `DRAFT`
- staff confirms it after license compliance checks
- vehicle status moves to `RESERVED`
- checkin moves reservation to `ACTIVE` and vehicle to `RENTED`
- checkout moves reservation to `COMPLETED`, records mileage, and generates a review token
- close marks the operational workflow complete and can send the review request email
## Error Handling and Response Shape
The API standardizes error handling in `http/errors/errorMiddleware.ts`.
Special handling exists for:
- Zod validation errors -> `400 validation_error`
- Prisma `P2025` -> `404 not_found`
- Prisma `P2002` -> `409 conflict`
- custom `AppError` subclasses -> explicit status and machine-readable code
Successful route handlers usually return:
```json
{
"data": {}
}
```
Common deviations:
- `/health`
- `/api/v1/docs`
- webhook receipt payloads
- file downloads such as admin invoice PDFs
## Documentation Artifacts
There are two documentation layers in the codebase:
1. runtime API docs from Swagger/OpenAPI at `/docs` and `/api/v1/openapi.json`
2. this markdown document, which explains design decisions, route grouping, and request flow
The OpenAPI file is useful for request/response contracts, but it is not yet a perfect mirror of every newer route. This document should be updated together with `app.ts`, route files, and the OpenAPI generator whenever route groups change.