From 4c7b07a9114d3591698bc5659d2f650be7501dbb Mon Sep 17 00:00:00 2001 From: David Date: Tue, 27 Jan 2026 19:33:51 +0100 Subject: [PATCH] fix error login --- CLAUDE.md | 1035 +++------------------------ apps/frontend/app/login/page.tsx | 176 ++++- apps/frontend/src/lib/api/client.ts | 7 +- 3 files changed, 272 insertions(+), 946 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index e574a13..7444d4d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,1002 +4,167 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -**Xpeditis** is a B2B SaaS maritime freight booking and management platform (maritime equivalent of WebCargo). The platform allows freight forwarders to search and compare real-time shipping rates, book containers online, and manage shipments from a centralized dashboard. - -**Current Status**: Phase 4+ - Production-ready with security hardening, monitoring, comprehensive testing infrastructure, and active administration features development. - -**Active Branch**: `administration` - Currently working on admin features, notifications system, and dashboard enhancements. Check `git status` for current feature branch. - -**Recent Development**: Notifications system, dashboard improvements, pagination fixes, and admin user management features. - -## Repository Structure - -This is a **monorepo** containing both backend and frontend applications: - -``` -/Users/david/Documents/xpeditis/dev/xpeditis2.0/ -├── apps/ -│ ├── backend/ # NestJS API (Node.js 20+, TypeScript 5+) -│ │ ├── src/ -│ │ │ ├── domain/ # Pure business logic (no framework deps) -│ │ │ ├── application/ # Controllers, DTOs, Guards -│ │ │ └── infrastructure/ # ORM, Cache, External APIs -│ │ ├── test/ # Integration & E2E tests -│ │ ├── load-tests/ # K6 load testing scripts -│ │ └── package.json # Backend dependencies -│ └── frontend/ # Next.js 14 App Router (React 18) -│ ├── app/ # Next.js App Router pages -│ ├── src/ # Components, hooks, utilities -│ ├── e2e/ # Playwright E2E tests -│ └── package.json # Frontend dependencies -├── infra/ -│ └── postgres/ # PostgreSQL init scripts -├── docker/ # Docker build & deployment configs -├── docker-compose.yml # Local development infrastructure -├── package.json # Root monorepo package.json (workspace scripts) -├── .prettierrc # Prettier configuration (shared) -├── .github/workflows/ # GitHub Actions CI/CD pipelines -└── CLAUDE.md # This file (architecture guide) -``` - -**Workspace Management**: -- Root `package.json` contains monorepo-level scripts -- Each app has its own `package.json` with specific dependencies -- Use root-level commands (`npm run backend:dev`) for convenience -- Or navigate to specific app and run commands directly +**Xpeditis** is a B2B SaaS maritime freight booking platform. Freight forwarders search and compare real-time shipping rates, book containers, and manage shipments. Built as a monorepo with NestJS backend (Hexagonal Architecture) and Next.js 14 frontend. ## Development Commands -### Local Development Setup - ```bash -# Install all dependencies (monorepo) -npm install -cd apps/backend && npm install -cd ../frontend && npm install - # Start infrastructure (PostgreSQL + Redis + MinIO) docker-compose up -d -# Verify all services are running -docker-compose ps -# Expected: xpeditis-postgres, xpeditis-redis, xpeditis-minio +# Install dependencies +npm install && cd apps/backend && npm install && cd ../frontend && npm install # Run database migrations -cd apps/backend -npm run migration:run +cd apps/backend && npm run migration:run -# Start backend development server (with hot reload) -npm run backend:dev # From root, or: -cd apps/backend && npm run dev - -# Start frontend development server -npm run frontend:dev # From root, or: -cd apps/frontend && npm run dev +# Start development servers +npm run backend:dev # http://localhost:4000, Swagger: /api/docs +npm run frontend:dev # http://localhost:3000 ``` -**Access Points**: -- Frontend: http://localhost:3000 -- Backend API: http://localhost:4000 -- API Docs (Swagger): http://localhost:4000/api/docs -- MinIO Console (local S3): http://localhost:9001 (minioadmin/minioadmin) -- Admin Dashboard: http://localhost:3000/dashboard/admin (ADMIN role required) -- Admin CSV Rates: http://localhost:3000/dashboard/admin/csv-rates -- Admin User Management: http://localhost:3000/dashboard/settings/users -- Notifications: http://localhost:3000/dashboard/notifications - -### Monorepo Scripts (from root) +### Testing ```bash -# Development -npm run backend:dev # Start backend dev server -npm run frontend:dev # Start frontend dev server +# Backend (from apps/backend/) +npm test # Unit tests +npm test -- booking.entity.spec.ts # Single file +npm run test:cov # With coverage +npm run test:integration # Integration tests (needs DB/Redis) +npm run test:e2e # E2E tests -# Testing -npm run backend:test # Run backend unit tests -npm run frontend:test # Run frontend tests -npm run backend:lint # Lint backend code -npm run frontend:lint # Lint frontend code - -# Code Quality -npm run format # Format all code (Prettier) -npm run format:check # Check formatting - -# Build -npm run backend:build # Build backend for production -npm run frontend:build # Build frontend for production - -# Utilities -npm run install:all # Install deps for all workspaces -npm run clean # Clean all node_modules and build artifacts -``` - -### Testing Commands - -#### Backend Tests - -```bash -cd apps/backend - -# Unit tests (domain layer - no external dependencies) -npm test # Run all unit tests -npm run test:watch # Run in watch mode -npm run test:cov # With coverage report - -# Integration tests (infrastructure layer with real DB/Redis) -npm run test:integration # Run all integration tests -npm run test:integration:watch # Run in watch mode -npm run test:integration:cov # With coverage report - -# E2E tests (full API workflow) -npm run test:e2e # Run end-to-end tests - -# Run a single test file -npm test -- booking.service.spec.ts -npm run test:integration -- redis-cache.adapter.spec.ts -npm run test:e2e -- carrier-portal.e2e-spec.ts -``` - -#### Load Testing (K6) - -```bash -cd apps/backend - -# Install k6 (macOS) -brew install k6 - -# Run rate search load test (100 virtual users) -k6 run load-tests/rate-search.test.js - -# Run with custom parameters -k6 run --vus 50 --duration 60s load-tests/rate-search.test.js -``` - -#### E2E Testing (Playwright) - -```bash -cd apps/frontend - -# Install Playwright -npx playwright install - -# Run E2E tests (booking workflow) -npx playwright test e2e/booking-workflow.spec.ts - -# Run with UI mode -npx playwright test --ui - -# Run specific browser -npx playwright test --project=chromium -``` - -#### API Testing (Postman/Newman) - -```bash -# Install Newman globally -npm install -g newman - -# Run Postman collection -newman run postman/Xpeditis_API.postman_collection.json +# Frontend (from apps/frontend/) +npm test +npx playwright test # E2E tests ``` ### Database Migrations ```bash cd apps/backend - -# Generate new migration (after changing ORM entities) npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/MigrationName - -# Run pending migrations npm run migration:run - -# Revert last migration npm run migration:revert - -# Check applied migrations (query database directly) -# Note: TypeORM doesn't have a built-in 'show' command -# Check the migrations table in the database to see applied migrations ``` -**Important Migration Notes**: -- Migration files use Unix timestamp format: `1733185000000-DescriptiveName.ts` -- Always test migrations in development before running in production -- Migrations run automatically via TypeORM DataSource configuration -- Never modify existing migrations that have been applied to production - -### Build & Production +### Build ```bash -# Backend build (uses tsc-alias to resolve path aliases) -cd apps/backend -npm run build # Compiles TypeScript and resolves @domain, @application, @infrastructure aliases -npm run start:prod # Runs the production build - -# Frontend build -cd apps/frontend -npm run build # Next.js production build -npm start # Start production server +npm run backend:build # Compiles TS with path alias resolution (tsc-alias) +npm run frontend:build # Next.js production build ``` ## Architecture -### Hexagonal Architecture (Ports & Adapters) - -The backend follows strict hexagonal architecture with three isolated layers: +### Hexagonal Architecture (Backend) ``` apps/backend/src/ -├── domain/ # 🔵 CORE - Pure business logic (NO framework dependencies) -│ ├── entities/ # Business entities (Booking, RateQuote, User, CarrierProfile) -│ ├── value-objects/ # Immutable VOs (Money, Email, BookingNumber, Port) -│ ├── services/ # Domain services (pure TypeScript) +├── domain/ # CORE - Pure TypeScript, NO framework imports +│ ├── entities/ # Booking, RateQuote, User (with private props, static create()) +│ ├── value-objects/# Money, Email, BookingNumber (immutable, validated) +│ ├── services/ # Domain services │ ├── ports/ -│ │ ├── in/ # API Ports (use cases exposed by domain) -│ │ └── out/ # SPI Ports (interfaces required by domain) +│ │ ├── in/ # Use case interfaces +│ │ └── out/ # Repository interfaces (SPIs) │ └── exceptions/ # Domain exceptions -│ -├── application/ # 🔌 Controllers & DTOs (depends ONLY on domain) -│ ├── auth/ # JWT authentication module -│ ├── rates/ # Rate search endpoints -│ ├── bookings/ # Booking management -│ ├── csv-bookings.module.ts # CSV booking imports -│ ├── controllers/ # REST endpoints -│ │ ├── health.controller.ts -│ │ ├── gdpr.controller.ts -│ │ └── index.ts -│ ├── dto/ # Data transfer objects with validation -│ │ ├── booking-*.dto.ts -│ │ ├── rate-*.dto.ts -│ │ └── csv-*.dto.ts -│ ├── services/ # Application services -│ │ ├── fuzzy-search.service.ts -│ │ ├── brute-force-protection.service.ts -│ │ ├── file-validation.service.ts -│ │ └── gdpr.service.ts -│ ├── guards/ # Auth guards, rate limiting, RBAC -│ │ ├── jwt-auth.guard.ts -│ │ └── throttle.guard.ts -│ ├── decorators/ # Custom decorators -│ │ ├── current-user.decorator.ts -│ │ ├── public.decorator.ts -│ │ └── roles.decorator.ts -│ ├── interceptors/ # Request/response interceptors -│ │ └── performance-monitoring.interceptor.ts -│ └── gdpr/ # GDPR compliance module -│ └── gdpr.module.ts -│ -└── infrastructure/ # 🏗️ External integrations (depends ONLY on domain) - ├── persistence/typeorm/ # PostgreSQL repositories - │ ├── entities/ - │ │ ├── booking.orm-entity.ts - │ │ ├── carrier.orm-entity.ts - │ │ ├── csv-rate-config.orm-entity.ts - │ │ ├── notification.orm-entity.ts - │ │ ├── port.orm-entity.ts - │ │ ├── rate-quote.orm-entity.ts - │ │ └── audit-log.orm-entity.ts - │ ├── repositories/ - │ ├── mappers/ # Domain ↔ ORM entity mappers - │ └── migrations/ - ├── cache/ # Redis adapter - ├── carriers/ # Maersk, MSC, CMA CGM connectors - │ ├── carrier.module.ts - │ ├── csv-loader/ # CSV-based rate connector - │ │ └── csv-converter.service.ts - │ └── maersk/ - │ └── maersk.types.ts - ├── email/ # MJML email service (carrier notifications) - ├── storage/ # S3 storage adapter - │ └── csv-storage/ # CSV rate files storage - │ └── rates/ - ├── monitoring/ # Monitoring and observability - │ └── sentry.config.ts - ├── websocket/ # Real-time carrier updates - └── security/ # Helmet.js, rate limiting, CORS +├── application/ # Controllers, DTOs, Guards (depends ONLY on domain) +└── infrastructure/ # TypeORM, Redis, Carrier APIs (depends ONLY on domain) ``` **Critical Rules**: -1. **Domain layer**: No imports of NestJS, TypeORM, Redis, or any framework - pure TypeScript only -2. **Dependencies flow inward**: Infrastructure → Application → Domain (never the reverse) -3. **TypeScript path aliases**: Use `@domain/*`, `@application/*`, `@infrastructure/*` -4. **Testing**: Domain tests must run without NestJS TestingModule -5. **Mappers**: Use dedicated mapper classes for Domain ↔ ORM and Domain ↔ DTO conversions +- Domain layer: Zero imports from NestJS, TypeORM, Redis +- Dependencies flow inward: Infrastructure → Application → Domain +- Use path aliases: `@domain/*`, `@application/*`, `@infrastructure/*` +- Domain tests run without NestJS TestingModule -**Example - Domain Entity Structure**: +### Frontend (Next.js 14 App Router) + +``` +apps/frontend/ +├── app/ # App Router pages +│ ├── dashboard/ # Protected routes +│ │ ├── bookings/ # Booking management +│ │ ├── admin/ # Admin features (ADMIN role) +│ │ └── settings/ # User/org settings +│ └── carrier/ # Carrier portal (magic link auth) +└── src/ + ├── components/ # React components (shadcn/ui in ui/) + ├── hooks/ # useBookings, useNotifications + ├── lib/api/ # API client modules + └── types/ # TypeScript definitions +``` + +Path aliases: `@/*` → `./src/*`, `@/components/*`, `@/lib/*`, `@/hooks/*` + +## Key Patterns + +### Entity Pattern (Domain) ```typescript -// apps/backend/src/domain/entities/booking.entity.ts export class Booking { private readonly props: BookingProps; - - static create(props: Omit): Booking { - const bookingProps: BookingProps = { - ...props, - bookingNumber: BookingNumber.generate(), - status: BookingStatus.create('draft'), - }; - Booking.validate(bookingProps); - return new Booking(bookingProps); - } - - updateStatus(newStatus: BookingStatus): Booking { - if (!this.status.canTransitionTo(newStatus)) { - throw new InvalidStatusTransitionException(); - } + static create(props: Omit): Booking { ... } + updateStatus(newStatus: BookingStatus): Booking { // Returns new instance return new Booking({ ...this.props, status: newStatus }); } } ``` -### Frontend Architecture (Next.js 14 App Router) +### Repository Pattern +- Interface in `domain/ports/out/booking.repository.ts` +- Implementation in `infrastructure/persistence/typeorm/repositories/typeorm-booking.repository.ts` +- Separate mappers for Domain ↔ ORM entity conversions -``` -apps/frontend/ -├── app/ # Next.js 14 App Router (routing) -│ ├── page.tsx # Landing page -│ ├── layout.tsx # Root layout -│ ├── login/ # Auth pages -│ ├── register/ -│ ├── forgot-password/ -│ ├── reset-password/ -│ ├── verify-email/ -│ ├── dashboard/ # Protected dashboard routes -│ │ ├── page.tsx # Main dashboard -│ │ ├── layout.tsx # Dashboard layout with navigation -│ │ ├── search/ # Rate search -│ │ ├── search-advanced/ # Advanced search with results -│ │ ├── bookings/ # Booking management -│ │ │ ├── page.tsx # Bookings list -│ │ │ ├── [id]/page.tsx # Booking details -│ │ │ └── new/page.tsx # Create booking -│ │ ├── profile/ # User profile -│ │ ├── notifications/ # Notifications page -│ │ ├── settings/ # Settings pages -│ │ │ ├── users/page.tsx # User management (admin) -│ │ │ └── organization/page.tsx # Organization settings -│ │ └── admin/ # Admin features (ADMIN role only) -│ │ └── csv-rates/page.tsx # CSV rate management -│ ├── booking/ # Booking actions (public with token) -│ │ ├── confirm/[token]/page.tsx -│ │ └── reject/[token]/page.tsx -│ ├── carrier/ # Carrier portal routes -│ │ ├── accept/[token]/page.tsx -│ │ └── reject/[token]/page.tsx -│ ├── demo-carte/ # Map demo page -│ └── test-image/ # Image testing page -├── src/ -│ ├── components/ # React components -│ │ ├── ui/ # shadcn/ui components (Button, Dialog, etc.) -│ │ ├── bookings/ # Booking components -│ │ └── admin/ # Admin components -│ ├── hooks/ # Custom React hooks -│ │ ├── useBookings.ts -│ │ ├── useCompanies.ts -│ │ └── useNotifications.ts -│ ├── lib/ # Utilities and API client -│ │ ├── api/ # API client modules -│ │ │ ├── auth.ts -│ │ │ ├── bookings.ts -│ │ │ ├── csv-rates.ts -│ │ │ └── dashboard.ts -│ │ ├── context/ # React contexts -│ │ └── providers/ # React Query and other providers -│ ├── types/ # TypeScript type definitions -│ │ ├── booking.ts -│ │ ├── carrier.ts -│ │ └── rates.ts -│ ├── utils/ # Helper functions -│ │ └── export.ts # Excel/CSV/PDF export utilities -│ └── pages/ # Legacy page components -└── public/ # Static assets (logos, images) -``` +### Circuit Breaker (External APIs) +- Library: `opossum`, Timeout: 5s +- Used for carrier API calls (Maersk, MSC, CMA CGM) -**Frontend Patterns**: -- Server Components by default, Client Components when needed (`"use client"`) -- React Hook Form + Zod for form validation -- TanStack Query for server state management -- Zustand for client state management -- shadcn/ui for accessible UI components - -**TypeScript Path Aliases** (Frontend): -- `@/*` - Maps to `./src/*` -- `@/components/*` - Maps to `./src/components/*` -- `@/lib/*` - Maps to `./src/lib/*` -- `@/app/*` - Maps to `./app/*` -- `@/types/*` - Maps to `./src/types/*` -- `@/hooks/*` - Maps to `./src/hooks/*` -- `@/utils/*` - Maps to `./src/utils/*` -- `@/pages/*` - Maps to `./src/pages/*` - -### Tech Stack - -**Backend**: -- NestJS 10+ (framework) -- TypeScript 5+ (strict mode) -- PostgreSQL 15+ (database) -- TypeORM 0.3+ (ORM) -- Redis 7+ (cache, 15min TTL for rates) -- Passport + JWT (authentication) -- Argon2 (password hashing) -- Helmet.js (security headers) -- Pino (structured logging) -- Sentry (error tracking + APM) - -**Frontend**: -- Next.js 14+ App Router -- TypeScript 5+ -- React 18+ -- TanStack Table (data grids) -- TanStack Query (server state) -- React Hook Form + Zod (forms) -- Socket.IO (real-time updates) -- Tailwind CSS + shadcn/ui -- Framer Motion (animations) -- Leaflet + React Leaflet (maps) - -**Infrastructure**: -- Docker + Docker Compose -- GitHub Actions (CI/CD) -- AWS RDS (PostgreSQL) -- AWS ElastiCache (Redis) -- AWS S3 (document storage) -- MinIO (local S3-compatible storage for development) - -## Testing Strategy - -### Test Coverage Targets -- **Domain layer**: 90%+ (currently ~100% for value objects and entities) -- **Application layer**: 80%+ -- **Infrastructure layer**: 70%+ (currently ~82% for Phase 3 services) - -### Test File Locations - -``` -apps/backend/ -├── src/ -│ ├── application/ -│ │ └── services/ -│ │ ├── brute-force-protection.service.spec.ts -│ │ ├── file-validation.service.spec.ts -│ │ ├── fuzzy-search.service.spec.ts -│ │ └── gdpr.service.spec.ts -│ └── domain/ -│ ├── entities/ -│ │ ├── rate-quote.entity.spec.ts -│ │ ├── notification.entity.spec.ts -│ │ └── webhook.entity.spec.ts -│ └── value-objects/ -│ ├── email.vo.spec.ts -│ └── money.vo.spec.ts -├── test/ -│ ├── integration/ -│ │ ├── booking.repository.spec.ts -│ │ ├── redis-cache.adapter.spec.ts -│ │ └── maersk.connector.spec.ts -│ ├── carrier-portal.e2e-spec.ts -│ ├── app.e2e-spec.ts -│ ├── jest-integration.json -│ ├── jest-e2e.json -│ └── setup-integration.ts -└── load-tests/ - └── rate-search.test.js - -apps/frontend/ -└── e2e/ - └── booking-workflow.spec.ts -``` - -### Running Tests in CI - -Tests run automatically on GitHub Actions for all PRs: -- Linting & formatting check -- Backend unit tests -- Backend integration tests (with PostgreSQL + Redis services) -- Backend E2E tests -- Frontend tests -- Security scans - -See [.github/workflows/ci.yml](.github/workflows/ci.yml) for full pipeline. - -## Security Features (Phase 4) - -**OWASP Top 10 Compliance**: -- ✅ Helmet.js security headers (CSP, HSTS, X-Frame-Options) -- ✅ Rate limiting (global: 100/min, auth: 5/min, search: 30/min) -- ✅ Brute-force protection (exponential backoff after 3 failed attempts) -- ✅ File upload validation (MIME, magic number, size limits) -- ✅ Password policy (12+ chars, complexity requirements, Argon2 hashing) -- ✅ CORS with strict origin validation -- ✅ SQL injection prevention (TypeORM parameterized queries) -- ✅ XSS protection (CSP headers + input sanitization) - -**Monitoring & Observability**: -- Sentry error tracking + APM (10% trace sampling) -- Performance monitoring interceptor (slow request alerts) -- Structured JSON logging with Pino -- WebSocket real-time notifications (NotificationsGateway) -- WebSocket carrier status updates - -## Database Schema - -**Key Tables**: -- `organizations` - Freight forwarders and carriers (has `is_carrier` flag) -- `users` - User accounts with RBAC roles (Argon2 password hashing) -- `carriers` - Shipping line integrations (Maersk, MSC, CMA CGM, etc.) -- `carrier_profiles` - Carrier profile metadata and settings -- `carrier_activities` - Audit trail for carrier actions (accept/reject bookings, etc.) -- `ports` - 10k+ global ports (UN LOCODE) -- `rate_quotes` - Cached shipping rates (15min TTL) -- `bookings` - Container bookings (status workflow) -- `containers` - Container details (type, VGM, seal numbers) -- `shipments` - Real-time shipment tracking -- `audit_logs` - Compliance audit trail -- `csv_rates` - CSV-based rate data for offline/bulk rate loading -- `csv_bookings` - CSV-based booking imports (has `carrier_id` foreign key) -- `notifications` - User notifications (email, in-app) -- `webhooks` - Webhook configurations for external integrations - -**Migrations**: Located in `apps/backend/src/infrastructure/persistence/typeorm/migrations/` - -See [apps/backend/DATABASE-SCHEMA.md](apps/backend/DATABASE-SCHEMA.md) for complete schema documentation. - -## Environment Variables - -### Required Variables - -**Backend** (`apps/backend/.env`): -```bash -NODE_ENV=development -PORT=4000 -DATABASE_HOST=localhost -DATABASE_PORT=5432 -DATABASE_USER=xpeditis -DATABASE_PASSWORD=xpeditis_dev_password -DATABASE_NAME=xpeditis_dev -REDIS_HOST=localhost -REDIS_PORT=6379 -REDIS_PASSWORD=xpeditis_redis_password -JWT_SECRET=your-super-secret-jwt-key-change-this-in-production -JWT_ACCESS_EXPIRATION=15m -JWT_REFRESH_EXPIRATION=7d - -# Email configuration (for carrier notifications) -EMAIL_HOST=smtp.example.com -EMAIL_PORT=587 -EMAIL_USER=noreply@xpeditis.com -EMAIL_PASSWORD=your-email-password -EMAIL_FROM=noreply@xpeditis.com -``` - -**Frontend** (`apps/frontend/.env.local`): -```bash -NEXT_PUBLIC_API_URL=http://localhost:4000 -NEXT_PUBLIC_WS_URL=ws://localhost:4000 -``` - -See `apps/backend/.env.example` and `apps/frontend/.env.example` for all available variables. - -## API Documentation - -**OpenAPI/Swagger**: http://localhost:4000/api/docs (when backend running) - -**Key Endpoints**: - -### Client Portal -- `POST /api/v1/auth/login` - JWT authentication -- `POST /api/v1/auth/register` - User registration -- `POST /api/v1/rates/search` - Search shipping rates (cached 15min) -- `POST /api/v1/rates/csv-search` - Search rates from CSV data -- `POST /api/v1/bookings` - Create booking -- `GET /api/v1/bookings` - List bookings (paginated) -- `GET /api/v1/bookings/:id` - Get booking details -- `POST /api/v1/bookings/csv-import` - Bulk import bookings from CSV - -### Admin Features -- `GET /api/v1/admin/users` - List users (ADMIN role) -- `POST /api/v1/admin/users` - Create user (ADMIN role) -- `PATCH /api/v1/admin/users/:id` - Update user (ADMIN role) -- `DELETE /api/v1/admin/users/:id` - Delete user (ADMIN role) -- `GET /api/v1/admin/csv-rates` - List CSV rate configs (ADMIN role) -- `POST /api/v1/admin/csv-rates/upload` - Upload CSV rates (ADMIN role) - -### Notifications -- `GET /api/v1/notifications` - Get user notifications -- `PATCH /api/v1/notifications/:id/read` - Mark notification as read -- `DELETE /api/v1/notifications/:id` - Delete notification -- `WS /notifications` - WebSocket for real-time notifications - -### GDPR Compliance -- `GET /api/v1/gdpr/export` - Export user data (GDPR compliance) -- `DELETE /api/v1/gdpr/delete` - Delete user data (GDPR right to be forgotten) - -### Health Checks -- `GET /api/v1/health` - Health check endpoint - -### Carrier Portal -- `POST /api/v1/carrier/auth/auto-login` - Auto-login via magic link token -- `POST /api/v1/carrier/auth/login` - Standard carrier login -- `GET /api/v1/carrier/dashboard/stats` - Carrier dashboard statistics -- `GET /api/v1/carrier/bookings` - List bookings assigned to carrier -- `GET /api/v1/carrier/bookings/:id` - Get booking details -- `PATCH /api/v1/carrier/bookings/:id/accept` - Accept booking request -- `PATCH /api/v1/carrier/bookings/:id/reject` - Reject booking request -- `GET /api/v1/carrier/profile` - Get carrier profile -- `PATCH /api/v1/carrier/profile` - Update carrier profile - -### Common -- `GET /api/v1/carriers/:id/status` - Real-time carrier status -- `WS /carrier-status` - WebSocket for carrier status updates - -See [apps/backend/docs/CARRIER_PORTAL_API.md](apps/backend/docs/CARRIER_PORTAL_API.md) for complete carrier portal API documentation. +### Caching +- Redis with 15-min TTL for rate quotes +- Cache key format: `rate:{origin}:{destination}:{containerType}` ## Business Rules -**Critical Constraints**: -- Rate quotes expire after 15 minutes (Redis TTL) -- Carrier API timeout: 5 seconds per carrier -- Booking workflow: Maximum 4 steps -- Session timeout: 2 hours inactivity -- Rate search: <2s response time (90% with cache) -- Booking number format: `WCM-YYYY-XXXXXX` (6 alphanumeric chars) -- Cache hit target: >90% for common routes -- Multi-currency support: USD, EUR +- Booking number format: `WCM-YYYY-XXXXXX` +- Rate quotes expire after 15 minutes +- Multi-currency: USD, EUR +- RBAC Roles: ADMIN, MANAGER, USER, VIEWER, CARRIER -**RBAC Roles**: -- `ADMIN` - Full system access, user management, CSV rate uploads -- `MANAGER` - Manage organization bookings + users -- `USER` - Create and view own bookings -- `VIEWER` - Read-only access -- `CARRIER` - Carrier portal access (view assigned bookings, accept/reject) +### 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 -**Carrier Portal Workflow**: -1. Admin creates CSV booking and assigns carrier -2. Email sent to carrier with magic link (auto-login token, valid 1 hour) -3. Carrier clicks link → auto-login → redirected to dashboard -4. Carrier can accept/reject booking, download documents -5. Activity logged in `carrier_activities` table -6. Client notified of carrier decision +## Tech Stack -## Real-Time Features (WebSocket) +**Backend**: NestJS 10, TypeORM 0.3, PostgreSQL 15, Redis 7, Argon2, Pino, Sentry +**Frontend**: Next.js 14, React 18, TanStack Query/Table, React Hook Form + Zod, Tailwind + shadcn/ui, Socket.IO -The platform uses WebSocket connections for real-time updates: +## Common Pitfalls -**Notifications Gateway** (`application/gateways/notifications.gateway.ts`): -- Real-time user notifications (booking updates, system alerts) -- JWT-authenticated WebSocket connections -- Auto-reconnect with exponential backoff -- Connect to `ws://localhost:4000/notifications` +- Never import NestJS/TypeORM in domain layer +- Never use `any` type (strict mode enabled) +- Never use `DATABASE_SYNC=true` in production +- Never modify applied migrations - create new ones +- Always validate DTOs with `class-validator` +- Always create mappers for Domain ↔ ORM conversions -**Carrier Status Updates**: -- Real-time carrier API status monitoring -- Live shipment tracking updates -- Connection status: online/offline/degraded -- Connect to `ws://localhost:4000/carrier-status` +## Adding a New Feature -**Frontend Integration**: -- Socket.IO client for WebSocket connections -- Context providers in `lib/providers/` -- Real-time notification dropdown component -- Auto-refresh on status changes - -## CSV Import/Export Features - -The platform supports CSV-based operations for bulk data management: - -**CSV Rate Search**: -- Upload CSV files with rate data for offline/bulk rate loading -- CSV-based carrier connectors in `infrastructure/carriers/csv-loader/` -- Stored in `csv_rates` table -- Accessible via admin dashboard at `/dashboard/admin/csv-rates` -- CSV files stored in `apps/backend/src/infrastructure/storage/csv-storage/rates/` -- Supported carriers: MSC, ECU Worldwide, NVO Consolidation, SSC Consolidation, TCC Logistics, Test Maritime Express - -**CSV Booking Import**: -- Bulk import bookings from CSV files -- Validation and mapping to domain entities -- Stored in `csv_bookings` table -- CSV parsing with `csv-parse` library -- Automatic carrier assignment and email notification - -**Export Features**: -- Export bookings to Excel (`.xlsx`) using `exceljs` -- Export to CSV format -- Export to PDF documents using `pdfkit` -- File downloads using `file-saver` on frontend - -## Admin User Management - -The platform includes a dedicated admin interface for user management: - -**Admin Features** (Active on `administration` branch): -- User CRUD operations (Create, Read, Update, Delete) -- Organization management -- Role assignment and permissions -- Argon2 password hash generation for new users -- Accessible at `/dashboard/settings/users` (ADMIN role required) -- CSV rate management at `/dashboard/admin/csv-rates` -- Real-time notifications management - -**Password Hashing Utility**: -- Use `apps/backend/generate-hash.js` to generate Argon2 password hashes -- Example: `node apps/backend/generate-hash.js mypassword` - -## Deployment - -### Docker Build - -```bash -# Build backend image -docker build -t xpeditis-backend:latest -f apps/backend/Dockerfile . - -# Build frontend image -docker build -t xpeditis-frontend:latest -f apps/frontend/Dockerfile . - -# Run with Docker Compose (development) -docker-compose up -d -``` - -### Production Deployment (Portainer) - -See [docker/PORTAINER_DEPLOYMENT_GUIDE.md](docker/PORTAINER_DEPLOYMENT_GUIDE.md) for complete instructions: -- Scaleway Container Registry (rg.fr-par.scw.cloud/weworkstudio) -- Docker Swarm stack deployment -- Traefik reverse proxy configuration -- Environment-specific configs (staging/production) - -**CI/CD**: Automated via GitHub Actions -- Build and push Docker images to Scaleway Registry -- Deploy to staging/production via Portainer -- Run smoke tests post-deployment - -**Deployment Scripts**: -- `docker/build-images.sh` - Build and tag Docker images -- `deploy-to-portainer.sh` - Automated deployment script -- `docker/portainer-stack.yml` - Production stack configuration - -## Performance Targets - -- Rate search: <2s for 90% of requests (with cache) -- Rate search: <5s for 90% of requests (cache miss) -- Dashboard load: <1s for up to 5k bookings -- Email confirmation: Send within 3s of booking -- Carrier email notification: Send within 5s of booking assignment -- Cache hit ratio: >90% for top 100 trade lanes -- Carrier API timeout: 5s (with circuit breaker) - -## Naming Conventions - -**TypeScript**: -- Entities: `Booking`, `RateQuote`, `CarrierProfile` (PascalCase) -- Value Objects: `Email`, `Money`, `BookingNumber` -- Services: `BookingService`, `RateSearchService`, `CarrierAuthService` -- Repositories: `BookingRepository`, `CarrierProfileRepository` (interface in domain) -- Repository Implementations: `TypeOrmBookingRepository`, `TypeOrmCarrierProfileRepository` -- DTOs: `CreateBookingDto`, `RateSearchRequestDto`, `CarrierAutoLoginDto` -- Ports: `SearchRatesPort`, `CarrierConnectorPort` - -**Files**: -- Entities: `booking.entity.ts` -- Value Objects: `email.vo.ts` -- Services: `booking.service.ts`, `carrier-auth.service.ts` -- Tests: `booking.service.spec.ts`, `carrier-auth.service.spec.ts` -- ORM Entities: `booking.orm-entity.ts`, `carrier-profile.orm-entity.ts` -- Migrations: `1730000000001-CreateBookings.ts`, `1733185000000-CreateCarrierProfiles.ts` - -## Key Architectural Patterns Used - -### 1. Domain-Driven Design (DDD) -- **Entities**: Mutable objects with identity (e.g., `Booking`, `User`) -- **Value Objects**: Immutable, identity-less objects (e.g., `Money`, `Email`, `BookingNumber`) -- **Aggregates**: Cluster of entities/VOs treated as a unit (e.g., `Booking` with `Container` items) -- **Domain Services**: Stateless operations that don't belong to entities -- **Domain Events**: Not yet implemented (planned for Phase 5) - -### 2. Repository Pattern -- **Interface in Domain**: `apps/backend/src/domain/ports/out/booking.repository.ts` -- **Implementation in Infrastructure**: `apps/backend/src/infrastructure/persistence/typeorm/repositories/typeorm-booking.repository.ts` -- **Mapper Pattern**: Separate mappers for Domain ↔ ORM entity conversion - -### 3. DTO Pattern -- **Request DTOs**: Validate incoming API requests with `class-validator` -- **Response DTOs**: Control API response shape -- **Mappers**: Convert between DTOs and Domain entities in application layer - -### 4. Circuit Breaker Pattern -- Used for external carrier API calls (Maersk, MSC, CMA CGM) -- Library: `opossum` -- Timeout: 5 seconds per carrier -- Location: `apps/backend/src/infrastructure/carriers/*/` - -### 5. Caching Strategy -- **Redis for rate quotes**: 15-minute TTL -- **Cache-aside pattern**: Check cache first, fetch from carriers on miss -- **Cache key format**: `rate:{origin}:{destination}:{containerType}` - -## Common Pitfalls to Avoid - -❌ **DO NOT**: -- Import NestJS/TypeORM in domain layer -- Put business logic in controllers or repositories -- Use `any` type (strict mode enabled in backend) -- Skip writing tests (coverage targets enforced) -- Use `DATABASE_SYNC=true` in production (always use migrations) -- Commit `.env` files (use `.env.example` templates) -- Expose sensitive data in API responses (passwords, tokens, internal IDs) -- Skip rate limiting on public endpoints -- Use circular imports (leverage barrel exports with `index.ts`) -- Send emails without proper error handling -- Store plain text passwords (always use Argon2) -- Modify applied migrations (create new migration instead) -- Mix domain logic with framework code - -✅ **DO**: -- Follow hexagonal architecture strictly (Infrastructure → Application → Domain) -- Write tests for all new features (domain 90%+, application 80%+) -- Use TypeScript path aliases (`@domain/*`, `@application/*`, `@infrastructure/*`) -- Validate all DTOs with `class-validator` decorators -- Implement circuit breakers for external APIs (carrier connectors) -- Cache frequently accessed data (Redis with TTL) -- Use structured logging (Pino JSON format) -- Document APIs with Swagger decorators (`@ApiOperation`, `@ApiResponse`) -- Run migrations before deployment (`npm run migration:run`) -- Test email sending in development with test accounts -- Use MJML for responsive email templates -- Create dedicated mappers for Domain ↔ ORM conversions -- Use Value Objects for domain concepts (Money, Email, etc.) -- Implement proper error handling with domain exceptions -- Use immutability in domain entities (return new instances on updates) +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` +4. **ORM Entity** → `infrastructure/persistence/typeorm/entities/*.orm-entity.ts` +5. **Generate Migration** → `npm run migration:generate -- ...` +6. **Repository Impl** → `infrastructure/persistence/typeorm/repositories/` +7. **DTOs** → `application/dto/` (with class-validator decorators) +8. **Controller** → `application/controllers/` (with Swagger decorators) +9. **Module** → Register and import in `app.module.ts` ## Documentation -📚 **Toute la documentation est maintenant centralisée dans le dossier [docs/](docs/)** - -**Documentation Principale**: -- [docs/README.md](docs/README.md) - 📖 Index complet de la documentation -- [docs/architecture.md](docs/architecture.md) - Architecture globale du système -- [docs/AUDIT-FINAL-REPORT.md](docs/AUDIT-FINAL-REPORT.md) - Rapport d'audit complet -- [docs/decisions.md](docs/decisions.md) - Architecture Decision Records (ADRs) -- [PRD.md](PRD.md) - Product requirements -- [TODO.md](TODO.md) - 30-week development roadmap - -**Par Catégorie**: -- 🔧 **Installation**: [docs/installation/](docs/installation/) - Guides d'installation -- 🚀 **Déploiement**: [docs/deployment/](docs/deployment/) - Déploiement et infrastructure -- 📈 **Phases**: [docs/phases/](docs/phases/) - Historique du développement -- 🧪 **Tests**: [docs/testing/](docs/testing/) - Tests et qualité -- 🏗️ **Architecture**: [docs/architecture/](docs/architecture/) - Documentation technique -- 🚢 **Portail Transporteur**: [docs/carrier-portal/](docs/carrier-portal/) -- 📊 **Système CSV**: [docs/csv-system/](docs/csv-system/) -- 🐛 **Debug**: [docs/debug/](docs/debug/) - -**API Documentation**: -- [apps/backend/docs/CARRIER_PORTAL_API.md](apps/backend/docs/CARRIER_PORTAL_API.md) - Carrier portal API reference - -## Quick Reference - Common Tasks - -### Running a Single Test File -```bash -# Backend unit test -cd apps/backend -npm test -- booking.entity.spec.ts - -# Backend integration test -npm run test:integration -- booking.repository.spec.ts - -# Backend E2E test -npm run test:e2e -- carrier-portal.e2e-spec.ts - -# Frontend test -cd apps/frontend -npm test -- BookingForm.test.tsx -``` - -### Debugging TypeScript Path Aliases -If imports like `@domain/*` don't resolve: -1. Check `apps/backend/tsconfig.json` has correct `paths` configuration -2. Verify VS Code is using workspace TypeScript version -3. Restart TypeScript server in VS Code: `Cmd+Shift+P` → "TypeScript: Restart TS Server" - -### Common Environment Issues - -**PostgreSQL connection fails**: -```bash -# Verify PostgreSQL container is running -docker ps | grep xpeditis-postgres - -# Check PostgreSQL logs -docker logs xpeditis-postgres - -# Restart PostgreSQL -docker-compose restart postgres -``` - -**Redis connection fails**: -```bash -# Verify Redis container is running -docker ps | grep xpeditis-redis - -# Test Redis connection -docker exec -it xpeditis-redis redis-cli -a xpeditis_redis_password ping -# Expected: PONG - -# Restart Redis -docker-compose restart redis -``` - -**Migrations fail**: -```bash -# Check migration status (query the database) -# The migrations are tracked in the 'migrations' table - -# If stuck, revert and try again -npm run migration:revert -npm run migration:run - -# Or connect to database to check manually -docker exec -it xpeditis-postgres psql -U xpeditis -d xpeditis_dev -c "SELECT * FROM migrations ORDER BY id DESC LIMIT 5;" -``` - -### Adding a New Feature (Step-by-Step) - -1. **Create Domain Entity** (if needed): - - Location: `apps/backend/src/domain/entities/` - - Pure TypeScript, no framework imports - - Write unit tests: `*.entity.spec.ts` - -2. **Create Value Objects** (if needed): - - Location: `apps/backend/src/domain/value-objects/` - - Immutable, validated in constructor - - Write unit tests: `*.vo.spec.ts` - -3. **Define Domain Port Interface**: - - Location: `apps/backend/src/domain/ports/out/` - - Interface only, no implementation - -4. **Create ORM Entity**: - - Location: `apps/backend/src/infrastructure/persistence/typeorm/entities/` - - File naming: `*.orm-entity.ts` - - Add `@Entity()` decorator - -5. **Generate Migration**: - ```bash - npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/CreateFeatureName - ``` - -6. **Implement Repository**: - - Location: `apps/backend/src/infrastructure/persistence/typeorm/repositories/` - - Implements domain port interface - - Write integration tests - -7. **Create DTOs**: - - Location: `apps/backend/src/application/dto/` - - Add `class-validator` decorators - -8. **Create Controller**: - - Location: `apps/backend/src/application/controllers/` - - Add Swagger decorators - - Write E2E tests - -9. **Create Application Module**: - - Location: `apps/backend/src/application/modules/` - - Register controllers, services, repositories - -10. **Import Module in App.module.ts** - -## Code Review Checklist - -1. Hexagonal architecture principles followed -2. Domain layer has zero external dependencies -3. Unit tests written (90%+ coverage for domain) -4. Integration tests for infrastructure adapters -5. DTOs validated with class-validator -6. Swagger documentation updated -7. No secrets committed -8. TypeScript strict mode passes -9. Prettier formatting applied -10. ESLint passes with no warnings -11. Email templates tested in development -12. Carrier workflow tested end-to-end -13. Database migrations tested in development -14. ORM entities have corresponding domain entities -15. Mappers created for Domain ↔ ORM conversions +- API Docs: http://localhost:4000/api/docs (Swagger) +- Architecture: `docs/architecture.md` +- Carrier Portal API: `apps/backend/docs/CARRIER_PORTAL_API.md` +- Full docs index: `docs/README.md` diff --git a/apps/frontend/app/login/page.tsx b/apps/frontend/app/login/page.tsx index 6e01279..17ed57d 100644 --- a/apps/frontend/app/login/page.tsx +++ b/apps/frontend/app/login/page.tsx @@ -13,6 +13,66 @@ import Link from 'next/link'; import Image from 'next/image'; import { useAuth } from '@/lib/context/auth-context'; +interface FieldErrors { + email?: string; + password?: string; +} + +// Map backend error messages to French user-friendly messages +function getErrorMessage(error: any): { message: string; field?: 'email' | 'password' | 'general' } { + const errorMessage = error?.message || error?.response?.message || ''; + + // Network or server errors + if (error?.name === 'TypeError' || errorMessage.includes('fetch')) { + return { + message: 'Impossible de se connecter au serveur. Vérifiez votre connexion internet.', + field: 'general' + }; + } + + // Backend error messages + if (errorMessage.includes('Invalid credentials') || errorMessage.includes('Identifiants')) { + return { + message: 'Email ou mot de passe incorrect', + field: 'general' + }; + } + + if (errorMessage.includes('inactive') || errorMessage.includes('désactivé')) { + return { + message: 'Votre compte a été désactivé. Contactez le support pour plus d\'informations.', + field: 'general' + }; + } + + if (errorMessage.includes('not found') || errorMessage.includes('introuvable')) { + return { + message: 'Aucun compte trouvé avec cet email', + field: 'email' + }; + } + + if (errorMessage.includes('password') || errorMessage.includes('mot de passe')) { + return { + message: 'Mot de passe incorrect', + field: 'password' + }; + } + + if (errorMessage.includes('Too many') || errorMessage.includes('rate limit')) { + return { + message: 'Trop de tentatives de connexion. Veuillez réessayer dans quelques minutes.', + field: 'general' + }; + } + + // Default error + return { + message: errorMessage || 'Une erreur est survenue. Veuillez réessayer.', + field: 'general' + }; +} + export default function LoginPage() { const { login } = useAuth(); const [email, setEmail] = useState(''); @@ -20,17 +80,64 @@ export default function LoginPage() { const [rememberMe, setRememberMe] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); + const [fieldErrors, setFieldErrors] = useState({}); + + // Validate form fields + const validateForm = (): boolean => { + const errors: FieldErrors = {}; + + // Email validation + if (!email.trim()) { + errors.email = 'L\'adresse email est requise'; + } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + errors.email = 'L\'adresse email n\'est pas valide'; + } + + // Password validation + if (!password) { + errors.password = 'Le mot de passe est requis'; + } else if (password.length < 6) { + errors.password = 'Le mot de passe doit contenir au moins 6 caractères'; + } + + setFieldErrors(errors); + return Object.keys(errors).length === 0; + }; + + // Handle input changes - keep errors visible until successful login + const handleEmailChange = (e: React.ChangeEvent) => { + setEmail(e.target.value); + }; + + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value); + }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(''); + setFieldErrors({}); + + // Validate form before submission + if (!validateForm()) { + return; + } + setIsLoading(true); try { await login(email, password); // Navigation is handled by the login function in auth context } catch (err: any) { - setError(err.message || 'Identifiants incorrects'); + const { message, field } = getErrorMessage(err); + + if (field === 'email') { + setFieldErrors({ email: message }); + } else if (field === 'password') { + setFieldErrors({ password: message }); + } else { + setError(message); + } } finally { setIsLoading(false); } @@ -65,7 +172,20 @@ export default function LoginPage() { {/* Error Message */} {error && ( -
+
+ + +

{error}

)} @@ -74,38 +194,74 @@ export default function LoginPage() {
{/* Email */}
-
{/* Password */}
-
{/* Remember Me & Forgot Password */} diff --git a/apps/frontend/src/lib/api/client.ts b/apps/frontend/src/lib/api/client.ts index e1abed0..f752f6f 100644 --- a/apps/frontend/src/lib/api/client.ts +++ b/apps/frontend/src/lib/api/client.ts @@ -165,7 +165,12 @@ export async function apiRequest( }); // Handle 401 Unauthorized - token expired - if (response.status === 401 && !isRetry && !endpoint.includes('/auth/refresh')) { + // Skip auto-redirect for auth endpoints (login, register, refresh) - they handle their own errors + const isAuthEndpoint = endpoint.includes('/auth/login') || + endpoint.includes('/auth/register') || + endpoint.includes('/auth/refresh'); + + if (response.status === 401 && !isRetry && !isAuthEndpoint) { // Check if we have a refresh token const refreshToken = getRefreshToken(); if (!refreshToken) {