233 lines
9.8 KiB
Markdown
233 lines
9.8 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
|
|
|
|
## Architecture
|
|
|
|
### Hexagonal Architecture (Backend)
|
|
|
|
```
|
|
apps/backend/src/
|
|
├── domain/ # CORE - Pure TypeScript, NO framework imports
|
|
│ ├── entities/ # Booking, RateQuote, User, Carrier, Port, Container, CsvBooking, etc.
|
|
│ ├── value-objects/ # Money, Email, BookingNumber, BookingStatus, PortCode, ContainerType, Volume, LicenseStatus, SubscriptionPlan, etc.
|
|
│ ├── services/ # Pure domain services (rate-search, csv-rate-price-calculator, booking, port-search, etc.)
|
|
│ ├── 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
|
|
└── infrastructure/ # TypeORM entities/repos/mappers, Redis cache, carrier APIs, MinIO/S3, email (MJML+Nodemailer), Sentry
|
|
```
|
|
|
|
**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`)
|
|
|
|
### 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.
|
|
|
|
Swagger plugin enabled in `nest-cli.json` — DTOs auto-documented.
|
|
|
|
### Frontend (Next.js 14 App Router)
|
|
|
|
```
|
|
apps/frontend/
|
|
├── app/ # App Router pages
|
|
│ ├── dashboard/ # Protected routes (bookings, admin, settings)
|
|
│ └── carrier/ # Carrier portal (magic link auth)
|
|
└── src/
|
|
├── components/ # React components (shadcn/ui in ui/, layout/, bookings/, admin/)
|
|
├── hooks/ # useBookings, useNotifications, useCsvRateSearch, useCompanies, useFilterOptions
|
|
├── lib/
|
|
│ ├── api/ # Fetch-based API client with auto token refresh (client.ts + per-module files)
|
|
│ ├── context/ # Auth context
|
|
│ └── fonts.ts # Manrope (headings) + Montserrat (body)
|
|
├── types/ # TypeScript type definitions
|
|
└── utils/ # Export utilities (Excel, PDF)
|
|
```
|
|
|
|
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).
|
|
|
|
### 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. Per-module files (auth.ts, bookings.ts, rates.ts, etc.) import from client.
|
|
|
|
### 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
|
|
|
|
### 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
|
|
|
|
## 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. **Port Interface** → `domain/ports/out/*.repository.ts` (with token constant)
|
|
4. **ORM Entity** → `infrastructure/persistence/typeorm/entities/*.orm-entity.ts`
|
|
5. **Migration** → `npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/MigrationName`
|
|
6. **Repository Impl** → `infrastructure/persistence/typeorm/repositories/`
|
|
7. **Mapper** → `infrastructure/persistence/typeorm/mappers/` (static toOrm/toDomain/toDomainMany)
|
|
8. **DTOs** → `application/dto/` (with class-validator decorators)
|
|
9. **Controller** → `application/controllers/` (with Swagger decorators)
|
|
10. **Module** → Register and 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`
|