archetecture security fix
This commit is contained in:
@@ -0,0 +1,396 @@
|
||||
# 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 marketplace 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 marketplace 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/marketplace` | Public, optional renter JWT | Marketplace 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 90-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.
|
||||
|
||||
### Marketplace and public site
|
||||
|
||||
The public surface is split in two modules on purpose.
|
||||
|
||||
`marketplace` is the cross-company discovery layer:
|
||||
|
||||
- featured/public offers
|
||||
- marketplace cities
|
||||
- listed companies
|
||||
- vehicle search
|
||||
- marketplace 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
|
||||
- marketplace 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.
|
||||
Reference in New Issue
Block a user