270 lines
13 KiB
Markdown
270 lines
13 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
**Xpeditis** is a B2B SaaS maritime freight booking platform. Freight forwarders search and compare real-time shipping rates, book containers, and manage shipments. Monorepo with NestJS 10 backend (Hexagonal Architecture) and Next.js 14 frontend.
|
|
|
|
## Development Commands
|
|
|
|
All commands run from repo root unless noted otherwise.
|
|
|
|
```bash
|
|
# Infrastructure (PostgreSQL 15 + Redis 7 + MinIO)
|
|
docker-compose up -d
|
|
|
|
# Install all dependencies
|
|
npm run install:all
|
|
|
|
# Environment setup (required on first run)
|
|
cp apps/backend/.env.example apps/backend/.env
|
|
cp apps/frontend/.env.example apps/frontend/.env
|
|
|
|
# Database migrations (from apps/backend/)
|
|
cd apps/backend && npm run migration:run
|
|
|
|
# Development servers
|
|
npm run backend:dev # http://localhost:4000, Swagger: /api/docs
|
|
npm run frontend:dev # http://localhost:3000
|
|
```
|
|
|
|
### Testing
|
|
|
|
```bash
|
|
# Backend (from apps/backend/)
|
|
npm test # Unit tests (Jest)
|
|
npm test -- booking.entity.spec.ts # Single file
|
|
npm run test:cov # With coverage
|
|
npm run test:integration # Integration tests (needs DB/Redis, 30s timeout)
|
|
npm run test:e2e # E2E tests
|
|
|
|
# Frontend (from apps/frontend/)
|
|
npm test
|
|
npm run test:e2e # Playwright (chromium, firefox, webkit + mobile)
|
|
|
|
# From root
|
|
npm run backend:test
|
|
npm run frontend:test
|
|
```
|
|
|
|
Backend test config is in `apps/backend/package.json` (Jest). Integration test config: `apps/backend/jest-integration.json` (covers infrastructure layer, setup in `test/setup-integration.ts`). Frontend E2E config: `apps/frontend/playwright.config.ts`.
|
|
|
|
### Linting, Formatting & Type Checking
|
|
|
|
```bash
|
|
npm run backend:lint # ESLint backend
|
|
npm run frontend:lint # ESLint frontend
|
|
npm run format # Prettier (all files)
|
|
npm run format:check # Check formatting
|
|
# From apps/frontend/
|
|
npm run type-check # TypeScript checking (frontend only)
|
|
```
|
|
|
|
### Database Migrations
|
|
|
|
```bash
|
|
cd apps/backend
|
|
npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/MigrationName
|
|
npm run migration:run
|
|
npm run migration:revert
|
|
```
|
|
|
|
### Build
|
|
|
|
```bash
|
|
npm run backend:build # NestJS build with tsc-alias for path resolution
|
|
npm run frontend:build # Next.js production build (standalone output)
|
|
```
|
|
|
|
## Local Infrastructure
|
|
|
|
Docker-compose defaults (no `.env` changes needed for local dev):
|
|
- **PostgreSQL**: `xpeditis:xpeditis_dev_password@localhost:5432/xpeditis_dev`
|
|
- **Redis**: password `xpeditis_redis_password`, port 6379
|
|
- **MinIO** (S3-compatible storage): `minioadmin:minioadmin`, API port 9000, console port 9001
|
|
|
|
Frontend env var: `NEXT_PUBLIC_API_URL` (defaults to `http://localhost:4000`) — configured in `next.config.js`.
|
|
|
|
## Architecture
|
|
|
|
### Hexagonal Architecture (Backend)
|
|
|
|
```
|
|
apps/backend/src/
|
|
├── domain/ # CORE - Pure TypeScript, NO framework imports
|
|
│ ├── entities/ # Booking, RateQuote, Carrier, Port, Container, Notification, Webhook,
|
|
│ │ # AuditLog, User, Organization, Subscription, License, CsvBooking,
|
|
│ │ # CsvRate, InvitationToken
|
|
│ ├── value-objects/ # Money, Email, BookingNumber, BookingStatus, PortCode, ContainerType,
|
|
│ │ # Volume, DateRange, Surcharge
|
|
│ ├── services/ # Pure domain services (csv-rate-price-calculator)
|
|
│ ├── ports/
|
|
│ │ ├── in/ # Use case interfaces with execute() method
|
|
│ │ └── out/ # Repository/SPI interfaces (token constants like BOOKING_REPOSITORY = 'BookingRepository')
|
|
│ └── exceptions/ # Domain-specific exceptions
|
|
├── application/ # Controllers, DTOs (class-validator), Guards, Decorators, Mappers
|
|
│ ├── [feature]/ # Feature modules: auth/, bookings/, csv-bookings, rates/, ports/,
|
|
│ │ # organizations/, users/, dashboard/, audit/, notifications/, webhooks/,
|
|
│ │ # gdpr/, admin/, subscriptions/
|
|
│ ├── controllers/ # REST controllers (also nested under feature folders)
|
|
│ ├── services/ # Application services: audit, notification, webhook,
|
|
│ │ # booking-automation, export, fuzzy-search, brute-force-protection
|
|
│ ├── gateways/ # WebSocket gateways (notifications.gateway.ts via Socket.IO)
|
|
│ ├── guards/ # JwtAuthGuard, RolesGuard, CustomThrottlerGuard
|
|
│ ├── decorators/ # @Public(), @Roles(), @CurrentUser()
|
|
│ ├── dto/ # Request/response DTOs with class-validator
|
|
│ ├── mappers/ # Domain ↔ DTO mappers
|
|
│ └── interceptors/ # PerformanceMonitoringInterceptor
|
|
└── infrastructure/ # TypeORM entities/repos/mappers, Redis cache, carrier APIs,
|
|
# MinIO/S3, email (MJML+Nodemailer), Stripe, Sentry,
|
|
# Pappers (French SIRET registry), PDF generation
|
|
```
|
|
|
|
**Critical dependency rules**:
|
|
- Domain layer: zero imports from NestJS, TypeORM, Redis, or any framework
|
|
- Dependencies flow inward only: Infrastructure → Application → Domain
|
|
- Path aliases: `@domain/*`, `@application/*`, `@infrastructure/*` (defined in `apps/backend/tsconfig.json`)
|
|
- Domain tests run without NestJS TestingModule
|
|
- Backend has strict TypeScript: `strict: true`, `strictNullChecks: true` (but `strictPropertyInitialization: false`)
|
|
- Env vars validated at startup via Joi schema in `app.module.ts` — required vars include DATABASE_*, REDIS_*, JWT_SECRET, SMTP_*
|
|
|
|
### NestJS Modules (app.module.ts)
|
|
|
|
Global guards: JwtAuthGuard (all routes protected by default), CustomThrottlerGuard.
|
|
|
|
Feature modules: Auth, Rates, Ports, Bookings, CsvBookings, Organizations, Users, Dashboard, Audit, Notifications, Webhooks, GDPR, Admin, Subscriptions.
|
|
|
|
Infrastructure modules: CacheModule, CarrierModule, SecurityModule, CsvRateModule, StripeModule, PdfModule, StorageModule, EmailModule.
|
|
|
|
Swagger plugin enabled in `nest-cli.json` — DTOs auto-documented. Logging via `nestjs-pino` (pino-pretty in dev).
|
|
|
|
### Frontend (Next.js 14 App Router)
|
|
|
|
```
|
|
apps/frontend/
|
|
├── app/ # App Router pages (root-level)
|
|
│ ├── dashboard/ # Protected routes (bookings, admin, settings, wiki, search)
|
|
│ ├── carrier/ # Carrier portal (magic link auth — accept/reject/documents)
|
|
│ ├── booking/ # Booking confirmation/rejection flows
|
|
│ └── [auth pages] # login, register, forgot-password, verify-email
|
|
└── src/
|
|
├── app/ # Additional app pages (e.g. rates/csv-search)
|
|
├── components/ # React components (ui/, layout/, bookings/, admin/, rate-search/, organization/)
|
|
├── hooks/ # useBookings, useNotifications, useCsvRateSearch, useCompanies, useFilterOptions
|
|
├── lib/
|
|
│ ├── api/ # Fetch-based API client with auto token refresh (client.ts + per-module files)
|
|
│ ├── context/ # Auth context, cookie context
|
|
│ ├── providers/ # QueryProvider (TanStack Query / React Query)
|
|
│ └── fonts.ts # Manrope (headings) + Montserrat (body)
|
|
├── types/ # TypeScript type definitions
|
|
├── utils/ # Export utilities (Excel, PDF)
|
|
└── legacy-pages/ # Archived page components (BookingsManagement, CarrierManagement, CarrierMonitoring)
|
|
```
|
|
|
|
Path aliases: `@/*` → `./src/*`, `@/components/*`, `@/lib/*`, `@/app/*` → `./app/*`, `@/types/*`, `@/hooks/*`, `@/utils/*`
|
|
|
|
**Note**: Frontend tsconfig has `strict: false`, `noImplicitAny: false`, `strictNullChecks: false` (unlike backend which is strict). Uses TanStack Query (React Query) for server state — wrap new data fetching in hooks, not bare `fetch` calls.
|
|
|
|
### Brand Design
|
|
|
|
Colors: Navy `#10183A` (primary), Turquoise `#34CCCD` (accent), Green `#067224` (success), Gray `#F2F2F2`.
|
|
Fonts: Manrope (headings), Montserrat (body).
|
|
Landing page is in French.
|
|
|
|
## Key Patterns
|
|
|
|
### Entity Pattern (Domain)
|
|
Private constructor + static `create()` factory. Immutable — mutation methods return new instances. Some entities also have `fromPersistence()` for reconstitution and `toObject()` for serialization.
|
|
```typescript
|
|
export class Booking {
|
|
private readonly props: BookingProps;
|
|
static create(props: Omit<BookingProps, 'bookingNumber' | 'status'>): Booking { ... }
|
|
updateStatus(newStatus: BookingStatus): Booking { // Returns new instance
|
|
return new Booking({ ...this.props, status: newStatus });
|
|
}
|
|
}
|
|
```
|
|
|
|
### Value Object Pattern
|
|
Immutable, self-validating via static `create()`. E.g. `Money` supports USD, EUR, GBP, CNY, JPY with arithmetic and formatting methods.
|
|
|
|
### Repository Pattern
|
|
- Interface in `domain/ports/out/` with token constant (e.g. `BOOKING_REPOSITORY = 'BookingRepository'`)
|
|
- Implementation in `infrastructure/persistence/typeorm/repositories/`
|
|
- ORM entities: `infrastructure/persistence/typeorm/entities/*.orm-entity.ts`
|
|
- Separate mapper classes (`infrastructure/persistence/typeorm/mappers/`) with static `toOrm()`, `toDomain()`, `toDomainMany()` methods
|
|
|
|
### Frontend API Client
|
|
Custom Fetch wrapper in `src/lib/api/client.ts` — exports `get()`, `post()`, `patch()`, `del()`, `upload()`, `download()`. Auto-refreshes JWT on 401. Tokens stored in localStorage **and synced to cookies** (`accessToken` cookie) so Next.js middleware can read them server-side. Per-module files (auth.ts, bookings.ts, rates.ts, etc.) import from client.
|
|
|
|
### Route Protection (Middleware)
|
|
`apps/frontend/middleware.ts` checks the `accessToken` cookie to protect routes. Public paths are defined in two lists:
|
|
- `exactPublicPaths`: exact matches (e.g. `/`)
|
|
- `prefixPublicPaths`: prefix matches including sub-paths (e.g. `/login`, `/carrier`, `/about`, etc.)
|
|
|
|
All other routes redirect to `/login?redirect=<pathname>` when the cookie is absent.
|
|
|
|
### Application Decorators
|
|
- `@Public()` — skip JWT auth
|
|
- `@Roles()` — role-based access control
|
|
- `@CurrentUser()` — inject authenticated user
|
|
|
|
### Carrier Connectors
|
|
Five carrier connectors (Maersk, MSC, CMA CGM, Hapag-Lloyd, ONE) extending `base-carrier.connector.ts`, each with request/response mappers. Circuit breaker via `opossum` (5s timeout).
|
|
|
|
### Caching
|
|
Redis with 15-min TTL for rate quotes. Key format: `rate:{origin}:{destination}:{containerType}`.
|
|
|
|
## Business Rules
|
|
|
|
- Booking number format: `WCM-YYYY-XXXXXX`
|
|
- Booking status flow: draft → confirmed → shipped → delivered
|
|
- Rate quotes expire after 15 minutes
|
|
- Multi-currency: USD, EUR, GBP, CNY, JPY
|
|
- RBAC Roles: ADMIN, MANAGER, USER, VIEWER, CARRIER
|
|
- JWT: access token 15min, refresh token 7d
|
|
- Password hashing: Argon2
|
|
- OAuth providers: Google, Microsoft (configured via passport strategies)
|
|
- Organizations can be validated via Pappers API (French SIRET/company registry) at `infrastructure/external/pappers-siret.adapter.ts`
|
|
|
|
### Carrier Portal Workflow
|
|
1. Admin creates CSV booking → assigns carrier
|
|
2. Email with magic link sent (1-hour expiry)
|
|
3. Carrier auto-login → accept/reject booking
|
|
4. Activity logged in `carrier_activities` table (via `CarrierProfile` + `CarrierActivity` ORM entities)
|
|
|
|
## Common Pitfalls
|
|
|
|
- Never import NestJS/TypeORM in domain layer
|
|
- Never use `any` type in backend (strict mode enabled)
|
|
- Never modify applied migrations — create new ones
|
|
- Always validate DTOs with `class-validator` decorators
|
|
- Always create separate mappers for Domain ↔ ORM conversions
|
|
- ORM entity files must match pattern `*.orm-entity.{ts,js}` (auto-discovered by data-source)
|
|
- Migration files must be in `infrastructure/persistence/typeorm/migrations/`
|
|
- Database synchronize is hard-coded to `false` — always use migrations
|
|
|
|
## Adding a New Feature
|
|
|
|
1. **Domain Entity** → `domain/entities/*.entity.ts` (pure TS, unit tests)
|
|
2. **Value Objects** → `domain/value-objects/*.vo.ts` (immutable)
|
|
3. **In Port (Use Case)** → `domain/ports/in/*.use-case.ts` (interface with `execute()`)
|
|
4. **Out Port (Repository)** → `domain/ports/out/*.repository.ts` (with token constant)
|
|
5. **ORM Entity** → `infrastructure/persistence/typeorm/entities/*.orm-entity.ts`
|
|
6. **Migration** → `npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/MigrationName`
|
|
7. **Repository Impl** → `infrastructure/persistence/typeorm/repositories/`
|
|
8. **Mapper** → `infrastructure/persistence/typeorm/mappers/` (static toOrm/toDomain/toDomainMany)
|
|
9. **DTOs** → `application/dto/` (with class-validator decorators)
|
|
10. **Controller** → `application/controllers/` (with Swagger decorators)
|
|
11. **Module** → Register repository + use-case providers, import in `app.module.ts`
|
|
|
|
## Documentation
|
|
|
|
- API Docs: http://localhost:4000/api/docs (Swagger, when running)
|
|
- Setup guide: `docs/installation/START-HERE.md`
|
|
- Carrier Portal API: `apps/backend/docs/CARRIER_PORTAL_API.md`
|
|
- Full docs index: `docs/README.md`
|
|
- Development roadmap: `TODO.md`
|
|
- Infrastructure configs (CI/CD, Docker): `infra/`
|