fix celan v2
This commit is contained in:
parent
ad761372f5
commit
4baffe0b7a
393
INDEX.md
393
INDEX.md
@ -1,348 +1,81 @@
|
||||
# 📑 Xpeditis Documentation Index
|
||||
|
||||
Complete guide to all documentation files in the Xpeditis project.
|
||||
# Index de documentation — Xpeditis
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Getting Started (Read First)
|
||||
## Démarrage
|
||||
|
||||
Start here if you're new to the project:
|
||||
|
||||
1. **[README.md](README.md)** - Project overview and quick start
|
||||
2. **[QUICK-START.md](QUICK-START.md)** ⚡ - Get running in 5 minutes
|
||||
3. **[INSTALLATION-STEPS.md](INSTALLATION-STEPS.md)** - Detailed installation guide
|
||||
4. **[NEXT-STEPS.md](NEXT-STEPS.md)** - What to do after setup
|
||||
| Fichier | Description |
|
||||
|---------|-------------|
|
||||
| [README.md](README.md) | Vue d'ensemble du projet |
|
||||
| [QUICK-START.md](QUICK-START.md) | Démarrage en 5 minutes |
|
||||
| [CLAUDE.md](CLAUDE.md) | Architecture hexagonale, conventions, règles |
|
||||
| [docs/README.md](docs/README.md) | Index complet de la documentation |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Project Status & Planning
|
||||
## Documentation complète
|
||||
|
||||
### Sprint 0 (Complete ✅)
|
||||
Toute la documentation est organisée dans [docs/](docs/) :
|
||||
|
||||
- **[SPRINT-0-FINAL.md](SPRINT-0-FINAL.md)** - Complete Sprint 0 report
|
||||
- All deliverables
|
||||
- Architecture details
|
||||
- How to use
|
||||
- Success criteria
|
||||
|
||||
- **[SPRINT-0-SUMMARY.md](SPRINT-0-SUMMARY.md)** - Executive summary
|
||||
- Objectives achieved
|
||||
- Metrics
|
||||
- Key features
|
||||
- Next steps
|
||||
|
||||
- **[SPRINT-0-COMPLETE.md](SPRINT-0-COMPLETE.md)** - Technical completion checklist
|
||||
- Week-by-week breakdown
|
||||
- Files created
|
||||
- Remaining tasks
|
||||
|
||||
### Project Roadmap
|
||||
|
||||
- **[TODO.md](TODO.md)** 📅 - 30-week MVP development roadmap
|
||||
- Sprint-by-sprint breakdown
|
||||
- Detailed tasks with checkboxes
|
||||
- Phase 1-4 planning
|
||||
- Go-to-market strategy
|
||||
|
||||
- **[PRD.md](PRD.md)** 📋 - Product Requirements Document
|
||||
- Business context
|
||||
- Functional specifications
|
||||
- Technical requirements
|
||||
- Success metrics
|
||||
```
|
||||
docs/
|
||||
├── README.md # Index principal
|
||||
├── getting-started/ # Installation et démarrage
|
||||
│ ├── quick-start.md # Guide rapide mis à jour
|
||||
│ ├── installation.md # Installation détaillée
|
||||
│ └── windows.md # Spécifique Windows
|
||||
│
|
||||
├── architecture/ # Documentation technique
|
||||
│ ├── overview.md # Vue d'ensemble système
|
||||
│ ├── database.md # Schéma BDD complet (21 tables)
|
||||
│ ├── backend.md # NestJS hexagonal, patterns
|
||||
│ └── frontend.md # Next.js 14, App Router, i18n
|
||||
│
|
||||
├── features/ # Documentation par fonctionnalité
|
||||
│ ├── auth.md # Auth JWT/OAuth/API Keys + RBAC
|
||||
│ ├── bookings.md # Réservations standard
|
||||
│ ├── csv-bookings.md # CSV bookings + portail carrier
|
||||
│ ├── rate-search.md # Recherche tarifs FCL + CSV
|
||||
│ ├── subscriptions.md # Stripe + abonnements
|
||||
│ ├── notifications.md # WebSocket + webhooks
|
||||
│ └── api-access.md # Clés API
|
||||
│
|
||||
├── deployment/ # Déploiement
|
||||
│ ├── portainer.md # Portainer / Docker Swarm (consolidé)
|
||||
│ ├── hetzner/ # Kubernetes Hetzner (15 fichiers numérotés)
|
||||
│ └── STRIPE_SETUP.md # Configuration Stripe
|
||||
│
|
||||
├── testing/ # Tests
|
||||
├── csv-system/ # Système CSV (format, calcul prix)
|
||||
├── carrier-portal/ # Portail carrier (recherche API)
|
||||
├── api-access/ # Documentation accès API
|
||||
├── backend/ # Notes backend (cleanup, MinIO)
|
||||
└── archive/ # Rapports de sprint archivés
|
||||
├── phases/ # Historique phases 1-4
|
||||
└── debug/ # Notes de debug résolues
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture & Development Guidelines
|
||||
## Commandes essentielles
|
||||
|
||||
### Core Architecture
|
||||
```bash
|
||||
# Démarrer
|
||||
docker-compose up -d
|
||||
npm run install:all
|
||||
cd apps/backend && npm run migration:run && cd ../..
|
||||
npm run backend:dev # http://localhost:4000
|
||||
npm run frontend:dev # http://localhost:3000
|
||||
|
||||
- **[CLAUDE.md](CLAUDE.md)** 🏗️ - **START HERE FOR ARCHITECTURE**
|
||||
- Complete hexagonal architecture guide
|
||||
- Domain/Application/Infrastructure layers
|
||||
- Ports & Adapters pattern
|
||||
- Naming conventions
|
||||
- Testing strategy
|
||||
- Common pitfalls
|
||||
- Complete examples (476 lines)
|
||||
# Tests
|
||||
npm run backend:test
|
||||
npm run frontend:test
|
||||
|
||||
### Component-Specific Documentation
|
||||
|
||||
- **[apps/backend/README.md](apps/backend/README.md)** - Backend (NestJS + Hexagonal)
|
||||
- Architecture details
|
||||
- Available scripts
|
||||
- API endpoints
|
||||
- Testing guide
|
||||
- Hexagonal architecture DOs and DON'Ts
|
||||
|
||||
- **[apps/frontend/README.md](apps/frontend/README.md)** - Frontend (Next.js 14)
|
||||
- Tech stack
|
||||
- Project structure
|
||||
- API integration
|
||||
- Forms & validation
|
||||
- Testing guide
|
||||
# Qualité
|
||||
npm run format
|
||||
npm run backend:lint && npm run frontend:lint
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Technical Documentation
|
||||
|
||||
### Configuration Files
|
||||
|
||||
**Root Level**:
|
||||
- `package.json` - Workspace configuration
|
||||
- `.gitignore` - Git ignore rules
|
||||
- `.prettierrc` - Code formatting
|
||||
- `docker-compose.yml` - PostgreSQL + Redis
|
||||
- `tsconfig.json` - TypeScript configuration (per app)
|
||||
|
||||
**Backend** (`apps/backend/`):
|
||||
- `package.json` - Backend dependencies
|
||||
- `tsconfig.json` - TypeScript strict mode + path aliases
|
||||
- `nest-cli.json` - NestJS CLI configuration
|
||||
- `.eslintrc.js` - ESLint rules
|
||||
- `.env.example` - Environment variables template
|
||||
|
||||
**Frontend** (`apps/frontend/`):
|
||||
- `package.json` - Frontend dependencies
|
||||
- `tsconfig.json` - TypeScript configuration
|
||||
- `next.config.js` - Next.js configuration
|
||||
- `tailwind.config.ts` - Tailwind CSS theme
|
||||
- `postcss.config.js` - PostCSS configuration
|
||||
- `.env.example` - Environment variables template
|
||||
|
||||
### CI/CD
|
||||
|
||||
**GitHub Actions** (`.github/workflows/`):
|
||||
- `ci.yml` - Continuous Integration
|
||||
- Lint & format check
|
||||
- Unit tests (backend + frontend)
|
||||
- E2E tests
|
||||
- Build verification
|
||||
|
||||
- `security.yml` - Security Audit
|
||||
- npm audit
|
||||
- Dependency review
|
||||
|
||||
**Templates**:
|
||||
- `.github/pull_request_template.md` - PR template with hexagonal architecture checklist
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation by Use Case
|
||||
|
||||
### I want to...
|
||||
|
||||
**...get started quickly**
|
||||
1. [QUICK-START.md](QUICK-START.md) - 5-minute setup
|
||||
2. [INSTALLATION-STEPS.md](INSTALLATION-STEPS.md) - Detailed steps
|
||||
3. [NEXT-STEPS.md](NEXT-STEPS.md) - Begin development
|
||||
|
||||
**...understand the architecture**
|
||||
1. [CLAUDE.md](CLAUDE.md) - Complete hexagonal architecture guide
|
||||
2. [apps/backend/README.md](apps/backend/README.md) - Backend specifics
|
||||
3. [SPRINT-0-FINAL.md](SPRINT-0-FINAL.md) - See what's implemented
|
||||
|
||||
**...know what to build next**
|
||||
1. [TODO.md](TODO.md) - Full roadmap
|
||||
2. [NEXT-STEPS.md](NEXT-STEPS.md) - Immediate next tasks
|
||||
3. [PRD.md](PRD.md) - Business requirements
|
||||
|
||||
**...understand the business context**
|
||||
1. [PRD.md](PRD.md) - Product requirements
|
||||
2. [README.md](README.md) - Project overview
|
||||
3. [SPRINT-0-SUMMARY.md](SPRINT-0-SUMMARY.md) - Executive summary
|
||||
|
||||
**...fix an installation issue**
|
||||
1. [INSTALLATION-STEPS.md](INSTALLATION-STEPS.md) - Troubleshooting section
|
||||
2. [QUICK-START.md](QUICK-START.md) - Common issues
|
||||
3. [README.md](README.md) - Basic setup
|
||||
|
||||
**...write code following best practices**
|
||||
1. [CLAUDE.md](CLAUDE.md) - Architecture guidelines (READ THIS FIRST)
|
||||
2. [apps/backend/README.md](apps/backend/README.md) - Backend DOs and DON'Ts
|
||||
3. [TODO.md](TODO.md) - Task specifications and acceptance criteria
|
||||
|
||||
**...run tests**
|
||||
1. [apps/backend/README.md](apps/backend/README.md) - Testing section
|
||||
2. [apps/frontend/README.md](apps/frontend/README.md) - Testing section
|
||||
3. [CLAUDE.md](CLAUDE.md) - Testing strategy
|
||||
|
||||
**...deploy to production**
|
||||
1. [SPRINT-0-FINAL.md](SPRINT-0-FINAL.md) - Security checklist
|
||||
2. [apps/backend/.env.example](apps/backend/.env.example) - All required variables
|
||||
3. `.github/workflows/ci.yml` - CI/CD pipeline
|
||||
|
||||
---
|
||||
|
||||
## 📖 Documentation by Role
|
||||
|
||||
### For Developers
|
||||
|
||||
**Must Read**:
|
||||
1. [CLAUDE.md](CLAUDE.md) - Architecture principles
|
||||
2. [apps/backend/README.md](apps/backend/README.md) OR [apps/frontend/README.md](apps/frontend/README.md)
|
||||
3. [TODO.md](TODO.md) - Current sprint tasks
|
||||
|
||||
**Reference**:
|
||||
- [INSTALLATION-STEPS.md](INSTALLATION-STEPS.md) - Setup issues
|
||||
- [PRD.md](PRD.md) - Business context
|
||||
|
||||
### For Architects
|
||||
|
||||
**Must Read**:
|
||||
1. [CLAUDE.md](CLAUDE.md) - Complete architecture
|
||||
2. [SPRINT-0-FINAL.md](SPRINT-0-FINAL.md) - Implementation details
|
||||
3. [PRD.md](PRD.md) - Technical requirements
|
||||
|
||||
**Reference**:
|
||||
- [TODO.md](TODO.md) - Technical roadmap
|
||||
- [apps/backend/README.md](apps/backend/README.md) - Backend architecture
|
||||
|
||||
### For Project Managers
|
||||
|
||||
**Must Read**:
|
||||
1. [SPRINT-0-SUMMARY.md](SPRINT-0-SUMMARY.md) - Status overview
|
||||
2. [TODO.md](TODO.md) - Complete roadmap
|
||||
3. [PRD.md](PRD.md) - Requirements & KPIs
|
||||
|
||||
**Reference**:
|
||||
- [SPRINT-0-FINAL.md](SPRINT-0-FINAL.md) - Detailed completion report
|
||||
- [README.md](README.md) - Project overview
|
||||
|
||||
### For DevOps
|
||||
|
||||
**Must Read**:
|
||||
1. [INSTALLATION-STEPS.md](INSTALLATION-STEPS.md) - Setup guide
|
||||
2. [docker-compose.yml](docker-compose.yml) - Infrastructure
|
||||
3. `.github/workflows/` - CI/CD pipelines
|
||||
|
||||
**Reference**:
|
||||
- [apps/backend/.env.example](apps/backend/.env.example) - Environment variables
|
||||
- [SPRINT-0-FINAL.md](SPRINT-0-FINAL.md) - Security checklist
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ Complete File List
|
||||
|
||||
### Documentation (11 files)
|
||||
|
||||
| File | Purpose | Length |
|
||||
|------|---------|--------|
|
||||
| [README.md](README.md) | Project overview | Medium |
|
||||
| [CLAUDE.md](CLAUDE.md) | Architecture guide | Long (476 lines) |
|
||||
| [PRD.md](PRD.md) | Product requirements | Long (352 lines) |
|
||||
| [TODO.md](TODO.md) | 30-week roadmap | Very Long (1000+ lines) |
|
||||
| [QUICK-START.md](QUICK-START.md) | 5-minute setup | Short |
|
||||
| [INSTALLATION-STEPS.md](INSTALLATION-STEPS.md) | Detailed setup | Medium |
|
||||
| [NEXT-STEPS.md](NEXT-STEPS.md) | What's next | Medium |
|
||||
| [SPRINT-0-FINAL.md](SPRINT-0-FINAL.md) | Sprint 0 report | Long |
|
||||
| [SPRINT-0-SUMMARY.md](SPRINT-0-SUMMARY.md) | Executive summary | Medium |
|
||||
| [SPRINT-0-COMPLETE.md](SPRINT-0-COMPLETE.md) | Technical checklist | Short |
|
||||
| [INDEX.md](INDEX.md) | This file | Medium |
|
||||
|
||||
### App-Specific (2 files)
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| [apps/backend/README.md](apps/backend/README.md) | Backend guide |
|
||||
| [apps/frontend/README.md](apps/frontend/README.md) | Frontend guide |
|
||||
|
||||
### Configuration (10+ files)
|
||||
|
||||
Root, backend, and frontend configuration files (package.json, tsconfig.json, etc.)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Documentation Statistics
|
||||
|
||||
- **Total Documentation Files**: 13
|
||||
- **Total Lines**: ~4,000+
|
||||
- **Coverage**: Setup, Architecture, Development, Testing, Deployment
|
||||
- **Last Updated**: October 7, 2025
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Recommended Reading Path
|
||||
|
||||
### For New Team Members (Day 1)
|
||||
|
||||
**Morning** (2 hours):
|
||||
1. [README.md](README.md) - 10 min
|
||||
2. [QUICK-START.md](QUICK-START.md) - 30 min (includes setup)
|
||||
3. [CLAUDE.md](CLAUDE.md) - 60 min (comprehensive architecture)
|
||||
4. [PRD.md](PRD.md) - 20 min (business context)
|
||||
|
||||
**Afternoon** (2 hours):
|
||||
5. [apps/backend/README.md](apps/backend/README.md) OR [apps/frontend/README.md](apps/frontend/README.md) - 30 min
|
||||
6. [TODO.md](TODO.md) - Current sprint section - 30 min
|
||||
7. [NEXT-STEPS.md](NEXT-STEPS.md) - 30 min
|
||||
8. Start coding! 🚀
|
||||
|
||||
### For Code Review (30 minutes)
|
||||
|
||||
1. [CLAUDE.md](CLAUDE.md) - Hexagonal architecture section
|
||||
2. [apps/backend/README.md](apps/backend/README.md) - DOs and DON'Ts
|
||||
3. [TODO.md](TODO.md) - Acceptance criteria for the feature
|
||||
|
||||
### For Sprint Planning (1 hour)
|
||||
|
||||
1. [TODO.md](TODO.md) - Next sprint tasks
|
||||
2. [PRD.md](PRD.md) - Requirements for the module
|
||||
3. [SPRINT-0-SUMMARY.md](SPRINT-0-SUMMARY.md) - Current status
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Quick Reference
|
||||
|
||||
### Common Questions
|
||||
|
||||
**Q: How do I get started?**
|
||||
A: [QUICK-START.md](QUICK-START.md)
|
||||
|
||||
**Q: What is hexagonal architecture?**
|
||||
A: [CLAUDE.md](CLAUDE.md) - Complete guide with examples
|
||||
|
||||
**Q: What should I build next?**
|
||||
A: [NEXT-STEPS.md](NEXT-STEPS.md) then [TODO.md](TODO.md)
|
||||
|
||||
**Q: How do I run tests?**
|
||||
A: [apps/backend/README.md](apps/backend/README.md) or [apps/frontend/README.md](apps/frontend/README.md)
|
||||
|
||||
**Q: Where are the business requirements?**
|
||||
A: [PRD.md](PRD.md)
|
||||
|
||||
**Q: What's the project status?**
|
||||
A: [SPRINT-0-SUMMARY.md](SPRINT-0-SUMMARY.md)
|
||||
|
||||
**Q: Installation failed, what do I do?**
|
||||
A: [INSTALLATION-STEPS.md](INSTALLATION-STEPS.md) - Troubleshooting section
|
||||
|
||||
**Q: Can I change the database/framework?**
|
||||
A: Yes! That's the point of hexagonal architecture. See [CLAUDE.md](CLAUDE.md)
|
||||
|
||||
---
|
||||
|
||||
## 📞 Getting Help
|
||||
|
||||
If you can't find what you need:
|
||||
|
||||
1. **Check this index** - Use Ctrl+F to search
|
||||
2. **Read CLAUDE.md** - Covers 90% of architecture questions
|
||||
3. **Check TODO.md** - Has detailed task specifications
|
||||
4. **Open an issue** - If documentation is unclear or missing
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Happy Reading!
|
||||
|
||||
All documentation is up-to-date as of Sprint 0 completion.
|
||||
|
||||
**Quick Links**:
|
||||
- 🚀 [Get Started](QUICK-START.md)
|
||||
- 🏗️ [Architecture](CLAUDE.md)
|
||||
- 📅 [Roadmap](TODO.md)
|
||||
- 📋 [Requirements](PRD.md)
|
||||
|
||||
---
|
||||
|
||||
*Xpeditis MVP - Maritime Freight Booking Platform*
|
||||
*Documentation Index - October 7, 2025*
|
||||
*Dernière mise à jour : Mai 2026*
|
||||
|
||||
307
README.md
307
README.md
@ -1,206 +1,151 @@
|
||||
# Xpeditis - Maritime Freight Booking Platform
|
||||
# Xpeditis — Maritime Freight Booking Platform
|
||||
|
||||
**Xpeditis** is a B2B SaaS platform for freight forwarders to search, compare, and book maritime freight in real-time.
|
||||
Plateforme B2B SaaS permettant aux transitaires de rechercher, comparer et réserver du fret maritime en temps réel.
|
||||
|
||||
---
|
||||
|
||||
## ⭐ **[START HERE](START-HERE.md)** ⭐
|
||||
|
||||
**New to the project?** Read **[START-HERE.md](START-HERE.md)** - Get running in 10 minutes!
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js >= 20.0.0
|
||||
- npm >= 10.0.0
|
||||
- Docker & Docker Compose
|
||||
- PostgreSQL 15+
|
||||
- Redis 7+
|
||||
|
||||
### Installation
|
||||
## Démarrage rapide
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
# 1. Installer les dépendances
|
||||
npm run install:all
|
||||
|
||||
# Start infrastructure (PostgreSQL + Redis)
|
||||
# 2. Démarrer l'infrastructure (PostgreSQL + Redis + MinIO)
|
||||
docker-compose up -d
|
||||
|
||||
# Setup environment variables
|
||||
# 3. Configurer l'environnement
|
||||
cp apps/backend/.env.example apps/backend/.env
|
||||
cp apps/frontend/.env.example apps/frontend/.env
|
||||
cp apps/frontend/.env.example apps/frontend/.env.local
|
||||
|
||||
# Run database migrations
|
||||
npm run backend:migrate
|
||||
# 4. Exécuter les migrations
|
||||
cd apps/backend && npm run migration:run && cd ../..
|
||||
|
||||
# Start backend (development)
|
||||
npm run backend:dev
|
||||
|
||||
# Start frontend (development)
|
||||
npm run frontend:dev
|
||||
# 5. Démarrer les serveurs
|
||||
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 Documentation**: http://localhost:4000/api/docs
|
||||
|
||||
## 📁 Project Structure
|
||||
## Structure du projet
|
||||
|
||||
```
|
||||
xpeditis/
|
||||
├── apps/
|
||||
│ ├── backend/ # NestJS API (Hexagonal Architecture)
|
||||
│ ├── backend/ # NestJS 10 — Architecture hexagonale
|
||||
│ │ └── src/
|
||||
│ │ ├── domain/ # Pure business logic
|
||||
│ │ ├── application/ # Controllers & DTOs
|
||||
│ │ └── infrastructure/ # External adapters
|
||||
│ │ ├── domain/ # Logique métier pure (TypeScript)
|
||||
│ │ ├── application/ # Controllers, DTOs, Guards
|
||||
│ │ └── infrastructure/ # TypeORM, Redis, S3, Email, Stripe
|
||||
│ └── frontend/ # Next.js 14 App Router
|
||||
├── packages/
|
||||
│ ├── shared-types/ # Shared TypeScript types
|
||||
│ └── domain/ # Shared domain logic
|
||||
└── infra/ # Infrastructure configs
|
||||
│ ├── app/[locale]/ # Routing i18n (fr, en)
|
||||
│ └── src/ # Components, hooks, lib/api
|
||||
├── docker-compose.yml # PostgreSQL 15 + Redis 7 + MinIO
|
||||
└── docs/ # Documentation complète
|
||||
```
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
This project follows **Hexagonal Architecture** (Ports & Adapters) principles:
|
||||
|
||||
- **Domain Layer**: Pure business logic, no external dependencies
|
||||
- **Application Layer**: Use cases, controllers, DTOs
|
||||
- **Infrastructure Layer**: Database, external APIs, cache, email, storage
|
||||
|
||||
See [CLAUDE.md](CLAUDE.md) for detailed architecture guidelines.
|
||||
|
||||
## 🛠️ Development
|
||||
|
||||
### Backend
|
||||
|
||||
```bash
|
||||
npm run backend:dev # Start dev server
|
||||
npm run backend:test # Run tests
|
||||
npm run backend:test:watch # Run tests in watch mode
|
||||
npm run backend:test:cov # Generate coverage report
|
||||
npm run backend:lint # Lint code
|
||||
npm run backend:build # Build for production
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```bash
|
||||
npm run frontend:dev # Start dev server
|
||||
npm run frontend:build # Build for production
|
||||
npm run frontend:test # Run tests
|
||||
npm run frontend:lint # Lint code
|
||||
```
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
### Getting Started
|
||||
- **[QUICK-START.md](QUICK-START.md)** ⚡ - Get running in 5 minutes
|
||||
- **[INSTALLATION-STEPS.md](INSTALLATION-STEPS.md)** 📦 - Detailed installation guide
|
||||
- **[NEXT-STEPS.md](NEXT-STEPS.md)** 🚀 - What to do after setup
|
||||
|
||||
### Architecture & Guidelines
|
||||
- **[CLAUDE.md](CLAUDE.md)** 🏗️ - Hexagonal architecture guidelines (complete)
|
||||
- **[apps/backend/README.md](apps/backend/README.md)** - Backend documentation
|
||||
- **[apps/frontend/README.md](apps/frontend/README.md)** - Frontend documentation
|
||||
|
||||
### Project Planning
|
||||
- **[PRD.md](PRD.md)** 📋 - Product Requirements Document
|
||||
- **[TODO.md](TODO.md)** 📅 - 30-week development roadmap
|
||||
- **[SPRINT-0-FINAL.md](SPRINT-0-FINAL.md)** ✅ - Sprint 0 completion report
|
||||
- **[SPRINT-0-SUMMARY.md](SPRINT-0-SUMMARY.md)** 📊 - Executive summary
|
||||
|
||||
### API Documentation
|
||||
- **[API Docs](http://localhost:4000/api/docs)** 📖 - OpenAPI/Swagger (when running)
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npm run test:all
|
||||
|
||||
# Run backend tests
|
||||
npm run backend:test
|
||||
|
||||
# Run frontend tests
|
||||
npm run frontend:test
|
||||
|
||||
# E2E tests (after implementation)
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
## 🔒 Security
|
||||
|
||||
- All passwords hashed with bcrypt (12 rounds minimum)
|
||||
- JWT tokens (access: 15min, refresh: 7 days)
|
||||
- HTTPS/TLS 1.2+ enforced
|
||||
- OWASP Top 10 protection
|
||||
- Rate limiting on all endpoints
|
||||
- CSRF protection
|
||||
|
||||
## 📊 Tech Stack
|
||||
|
||||
### Backend
|
||||
- **Framework**: NestJS 10+
|
||||
- **Language**: TypeScript 5+
|
||||
- **Database**: PostgreSQL 15+
|
||||
- **Cache**: Redis 7+
|
||||
- **ORM**: TypeORM
|
||||
- **Testing**: Jest, Supertest
|
||||
- **API Docs**: Swagger/OpenAPI
|
||||
|
||||
### Frontend
|
||||
- **Framework**: Next.js 14+ (App Router)
|
||||
- **Language**: TypeScript 5+
|
||||
- **Styling**: Tailwind CSS
|
||||
- **UI Components**: shadcn/ui
|
||||
- **State**: React Query (TanStack Query)
|
||||
- **Forms**: React Hook Form + Zod
|
||||
- **Testing**: Jest, React Testing Library, Playwright
|
||||
|
||||
## 🚢 Carrier Integrations
|
||||
|
||||
MVP supports the following maritime carriers:
|
||||
|
||||
- ✅ Maersk
|
||||
- ✅ MSC
|
||||
- ✅ CMA CGM
|
||||
- ✅ Hapag-Lloyd
|
||||
- ✅ ONE (Ocean Network Express)
|
||||
|
||||
## 📈 Monitoring & Logging
|
||||
|
||||
- **Logging**: Winston / Pino
|
||||
- **Error Tracking**: Sentry
|
||||
- **APM**: Application Performance Monitoring
|
||||
- **Metrics**: Prometheus (planned)
|
||||
|
||||
## 🔧 Environment Variables
|
||||
|
||||
See `.env.example` files in each app for required environment variables.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. Create a feature branch
|
||||
2. Make your changes
|
||||
3. Write tests
|
||||
4. Run linting and formatting
|
||||
5. Submit a pull request
|
||||
|
||||
## 📝 License
|
||||
|
||||
Proprietary - All rights reserved
|
||||
|
||||
## 👥 Team
|
||||
|
||||
Built with ❤️ by the Xpeditis team
|
||||
|
||||
---
|
||||
|
||||
For detailed implementation guidelines, see [CLAUDE.md](CLAUDE.md).
|
||||
## Documentation
|
||||
|
||||
| Sujet | Fichier |
|
||||
|-------|---------|
|
||||
| Index complet | [docs/README.md](docs/README.md) |
|
||||
| Architecture hexagonale + conventions | [CLAUDE.md](CLAUDE.md) |
|
||||
| Vue d'ensemble système | [docs/architecture/overview.md](docs/architecture/overview.md) |
|
||||
| Schéma BDD (21 tables) | [docs/architecture/database.md](docs/architecture/database.md) |
|
||||
| Démarrage rapide | [docs/getting-started/quick-start.md](docs/getting-started/quick-start.md) |
|
||||
|
||||
---
|
||||
|
||||
## Commandes de développement
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
npm run backend:dev # Serveur avec hot-reload
|
||||
npm run backend:test # Tests unitaires Jest
|
||||
npm run backend:lint # ESLint
|
||||
npm run backend:build # Build production
|
||||
|
||||
# Frontend
|
||||
npm run frontend:dev # Serveur avec hot-reload
|
||||
npm run frontend:test # Tests unitaires Jest
|
||||
npm run frontend:lint # ESLint
|
||||
cd apps/frontend && npm run test:e2e # Playwright E2E
|
||||
|
||||
# Qualité
|
||||
npm run format # Prettier (tous les fichiers)
|
||||
|
||||
# Base de données
|
||||
cd apps/backend
|
||||
npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/NomMigration
|
||||
npm run migration:run
|
||||
npm run migration:revert
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Stack technique
|
||||
|
||||
### Backend
|
||||
|
||||
| Composant | Technologie |
|
||||
|-----------|-------------|
|
||||
| Framework | NestJS 10 + TypeScript 5 (strict) |
|
||||
| Base de données | PostgreSQL 15 + TypeORM |
|
||||
| Cache | Redis 7 (ioredis) |
|
||||
| Auth | JWT (15min) + Refresh + OAuth2 + API Keys (Argon2) |
|
||||
| Temps réel | Socket.IO |
|
||||
| Email | Nodemailer + MJML |
|
||||
| Paiements | Stripe |
|
||||
| Stockage | S3/MinIO |
|
||||
| Logging | nestjs-pino |
|
||||
| Monitoring | Sentry |
|
||||
|
||||
### Frontend
|
||||
|
||||
| Composant | Technologie |
|
||||
|-----------|-------------|
|
||||
| Framework | Next.js 14 App Router + TypeScript |
|
||||
| Styling | Tailwind CSS + shadcn/ui (Radix UI) |
|
||||
| State serveur | TanStack Query v5 |
|
||||
| Tables | TanStack Table v8 + Virtual |
|
||||
| Formulaires | react-hook-form + zod |
|
||||
| Temps réel | Socket.IO client |
|
||||
| i18n | next-intl (fr, en) |
|
||||
| Graphiques | recharts |
|
||||
|
||||
---
|
||||
|
||||
## Carriers intégrés
|
||||
|
||||
| Carrier | Code | Statut |
|
||||
|---------|------|--------|
|
||||
| Maersk | MAEU | Connecteur API |
|
||||
| MSC | MSCU | Connecteur API |
|
||||
| CMA CGM | CMDU | Connecteur API |
|
||||
| Hapag-Lloyd | HLCU | Connecteur API |
|
||||
| ONE | ONEY | Connecteur API |
|
||||
| SSC Consolidation | — | CSV |
|
||||
| ECU Worldwide | — | CSV + API |
|
||||
| TCC Logistics | — | CSV |
|
||||
| NVO Consolidation | — | CSV |
|
||||
|
||||
---
|
||||
|
||||
## Fonctionnalités principales
|
||||
|
||||
- **Recherche tarifs** : FCL (carriers API + cache Redis 15min) + LCL CSV
|
||||
- **Réservation standard** : workflow 4 étapes, numéro WCM-YYYY-XXXXXX
|
||||
- **Réservation CSV + Portail Carrier** : magic link, accept/reject
|
||||
- **Dashboard** : KPI, graphiques, table interactive virtuelle
|
||||
- **Auth** : JWT, OAuth2 (Google/Microsoft), API Keys, RBAC (5 rôles)
|
||||
- **Abonnements** : Stripe (FREE/BRONZE/SILVER/GOLD/PLATINIUM)
|
||||
- **Notifications** : WebSocket temps réel + webhooks tiers
|
||||
- **GDPR** : export/suppression des données utilisateur
|
||||
- **Blog** : gestion de contenu bilingue (fr/en)
|
||||
- **Audit** : journal d'audit de toutes les actions
|
||||
|
||||
---
|
||||
|
||||
*Architecture hexagonale — NestJS 10 + Next.js 14 — PostgreSQL 15 + Redis 7*
|
||||
|
||||
105
docs/README.md
Normal file
105
docs/README.md
Normal file
@ -0,0 +1,105 @@
|
||||
# Xpeditis — Documentation
|
||||
|
||||
Documentation complète de la plateforme B2B SaaS de réservation de fret maritime.
|
||||
|
||||
---
|
||||
|
||||
## Démarrage rapide
|
||||
|
||||
| Objectif | Fichier |
|
||||
|---|---|
|
||||
| Démarrer en 5 minutes | [getting-started/quick-start.md](getting-started/quick-start.md) |
|
||||
| Installation détaillée | [getting-started/installation.md](getting-started/installation.md) |
|
||||
| Windows | [getting-started/windows.md](getting-started/windows.md) |
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
| Sujet | Fichier |
|
||||
|---|---|
|
||||
| Vue d'ensemble système | [architecture/overview.md](architecture/overview.md) |
|
||||
| Schéma base de données | [architecture/database.md](architecture/database.md) |
|
||||
| Backend (NestJS hexagonal) | [architecture/backend.md](architecture/backend.md) |
|
||||
| Frontend (Next.js 14) | [architecture/frontend.md](architecture/frontend.md) |
|
||||
|
||||
---
|
||||
|
||||
## Fonctionnalités
|
||||
|
||||
| Fonctionnalité | Fichier |
|
||||
|---|---|
|
||||
| Recherche de tarifs (FCL + CSV) | [features/rate-search.md](features/rate-search.md) |
|
||||
| Réservation standard | [features/bookings.md](features/bookings.md) |
|
||||
| Réservation CSV + Portail Carrier | [features/csv-bookings.md](features/csv-bookings.md) |
|
||||
| Authentification & RBAC | [features/auth.md](features/auth.md) |
|
||||
| Abonnements Stripe | [features/subscriptions.md](features/subscriptions.md) |
|
||||
| Notifications temps réel | [features/notifications.md](features/notifications.md) |
|
||||
| Accès API (clés API) | [features/api-access.md](features/api-access.md) |
|
||||
| Système CSV | [../csv-system/CSV_RATE_SYSTEM.md](../csv-system/CSV_RATE_SYSTEM.md) |
|
||||
|
||||
---
|
||||
|
||||
## Déploiement
|
||||
|
||||
| Sujet | Fichier |
|
||||
|---|---|
|
||||
| Portainer / Docker Swarm | [deployment/portainer.md](deployment/portainer.md) |
|
||||
| Hetzner / Kubernetes | [deployment/hetzner/README.md](deployment/hetzner/README.md) |
|
||||
| Stripe (paiements) | [deployment/STRIPE_SETUP.md](deployment/STRIPE_SETUP.md) |
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
| Sujet | Fichier |
|
||||
|---|---|
|
||||
| Guide tests (unité, intégration, E2E) | [testing/TEST_EXECUTION_GUIDE.md](testing/TEST_EXECUTION_GUIDE.md) |
|
||||
| Tests locaux | [testing/LOCAL_TESTING.md](testing/LOCAL_TESTING.md) |
|
||||
| Tests manuels | [testing/MANUAL_TEST_INSTRUCTIONS.md](testing/MANUAL_TEST_INSTRUCTIONS.md) |
|
||||
| Postman | [testing/GUIDE_TESTS_POSTMAN.md](testing/GUIDE_TESTS_POSTMAN.md) |
|
||||
| Couverture | [testing/TEST_COVERAGE_REPORT.md](testing/TEST_COVERAGE_REPORT.md) |
|
||||
|
||||
---
|
||||
|
||||
## Portail Carrier
|
||||
|
||||
| Sujet | Fichier |
|
||||
|---|---|
|
||||
| Recherche API carriers | [carrier-portal/CARRIER_API_RESEARCH.md](carrier-portal/CARRIER_API_RESEARCH.md) |
|
||||
| Plan d'implémentation | [carrier-portal/CARRIER_PORTAL_IMPLEMENTATION_PLAN.md](carrier-portal/CARRIER_PORTAL_IMPLEMENTATION_PLAN.md) |
|
||||
|
||||
---
|
||||
|
||||
## Archives
|
||||
|
||||
Les rapports de sprint et notes de debug sont archivés dans [archive/](archive/).
|
||||
|
||||
---
|
||||
|
||||
## Pour les développeurs
|
||||
|
||||
### Lire dans cet ordre
|
||||
|
||||
1. [getting-started/quick-start.md](getting-started/quick-start.md) — démarrer le projet
|
||||
2. `CLAUDE.md` à la racine — **architecture hexagonale, règles et conventions**
|
||||
3. [architecture/overview.md](architecture/overview.md) — vision système
|
||||
4. [architecture/database.md](architecture/database.md) — schéma BDD complet
|
||||
|
||||
### Commandes essentielles
|
||||
|
||||
```bash
|
||||
# Infrastructure
|
||||
docker-compose up -d
|
||||
|
||||
# Démarrer
|
||||
npm run backend:dev # http://localhost:4000 — Swagger: /api/docs
|
||||
npm run frontend:dev # http://localhost:3000
|
||||
|
||||
# Tests
|
||||
npm run backend:test
|
||||
npm run frontend:test
|
||||
|
||||
# Migrations
|
||||
cd apps/backend && npm run migration:run
|
||||
```
|
||||
180
docs/architecture/backend.md
Normal file
180
docs/architecture/backend.md
Normal file
@ -0,0 +1,180 @@
|
||||
# Architecture Backend — NestJS Hexagonale
|
||||
|
||||
---
|
||||
|
||||
## Structure des couches
|
||||
|
||||
```
|
||||
apps/backend/src/
|
||||
├── domain/ # Logique métier PURE — aucun import NestJS/TypeORM
|
||||
│ ├── entities/ # 17 entités métier (private constructor + static create())
|
||||
│ ├── value-objects/ # Objets valeur immuables (Email, Money, BookingNumber...)
|
||||
│ ├── services/ # Services métier purs (csv-rate-price-calculator)
|
||||
│ ├── exceptions/ # Exceptions domaine
|
||||
│ └── ports/
|
||||
│ ├── in/ # Interfaces use-case (execute())
|
||||
│ └── out/ # Interfaces repository/SPI (token constants)
|
||||
│
|
||||
├── application/ # Couche API — dépend UNIQUEMENT du domain
|
||||
│ ├── controllers/ # REST controllers (Swagger + class-validator)
|
||||
│ ├── dto/ # Data Transfer Objects
|
||||
│ ├── mappers/ # Domain ↔ DTO (méthodes statiques)
|
||||
│ ├── guards/ # JwtAuthGuard, RolesGuard, ApiKeyOrJwtGuard
|
||||
│ ├── decorators/ # @Public(), @Roles(), @CurrentUser()
|
||||
│ ├── filters/ # DomainExceptionFilter
|
||||
│ ├── interceptors/ # PerformanceMonitoringInterceptor
|
||||
│ ├── gateways/ # WebSocket Socket.IO (notifications)
|
||||
│ ├── services/ # Services applicatifs (audit, notification, export, webhooks)
|
||||
│ └── [feature]/ # Modules par feature (auth, bookings, rates, etc.)
|
||||
│
|
||||
└── infrastructure/ # Adaptateurs externes — dépend UNIQUEMENT du domain
|
||||
├── persistence/typeorm/
|
||||
│ ├── entities/ # ORM entities (*.orm-entity.ts)
|
||||
│ ├── repositories/# Implémentations TypeORM
|
||||
│ ├── mappers/ # ORM ↔ Domain (static toOrm/toDomain/toDomainMany)
|
||||
│ └── migrations/ # Migrations TypeORM
|
||||
├── carriers/ # 5 connecteurs carriers + CSV loader
|
||||
├── cache/ # Adaptateur Redis
|
||||
├── email/ # MJML + Nodemailer
|
||||
├── storage/ # S3/MinIO (+ csv-storage/)
|
||||
├── stripe/ # Paiements et abonnements
|
||||
├── external/ # Pappers (SIRET), ECU Worldwide
|
||||
├── pdf/ # Génération PDF pdfkit
|
||||
├── security/ # Utilitaires sécurité
|
||||
└── monitoring/ # Sentry + Pino
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entités domaine (17)
|
||||
|
||||
| Entité | Fichier |
|
||||
|--------|---------|
|
||||
| ApiKey | api-key.entity.ts |
|
||||
| AuditLog | audit-log.entity.ts |
|
||||
| BlogPost | blog-post.entity.ts |
|
||||
| Booking | booking.entity.ts |
|
||||
| Carrier | carrier.entity.ts |
|
||||
| Container | container.entity.ts |
|
||||
| CsvBooking | csv-booking.entity.ts |
|
||||
| CsvRate | csv-rate.entity.ts |
|
||||
| InvitationToken | invitation-token.entity.ts |
|
||||
| License | license.entity.ts |
|
||||
| Notification | notification.entity.ts |
|
||||
| Organization | organization.entity.ts |
|
||||
| Port | port.entity.ts |
|
||||
| RateQuote | rate-quote.entity.ts |
|
||||
| Subscription | subscription.entity.ts |
|
||||
| User | user.entity.ts |
|
||||
| Webhook | webhook.entity.ts |
|
||||
|
||||
---
|
||||
|
||||
## Pattern entité domaine
|
||||
|
||||
```typescript
|
||||
// Private constructor + static create() factory
|
||||
export class Booking {
|
||||
private constructor(private readonly props: BookingProps) {}
|
||||
|
||||
static create(props: Omit<BookingProps, 'bookingNumber' | 'status'>): Booking {
|
||||
return new Booking({
|
||||
...props,
|
||||
bookingNumber: BookingNumber.generate(),
|
||||
status: BookingStatus.DRAFT,
|
||||
});
|
||||
}
|
||||
|
||||
// Les mutations retournent une nouvelle instance (immuabilité)
|
||||
updateStatus(newStatus: BookingStatus): Booking {
|
||||
return new Booking({ ...this.props, status: newStatus });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern repository
|
||||
|
||||
```typescript
|
||||
// domain/ports/out/booking.repository.ts
|
||||
export const BOOKING_REPOSITORY = 'BookingRepository';
|
||||
|
||||
export interface BookingRepository {
|
||||
findById(id: string): Promise<Booking | null>;
|
||||
save(booking: Booking): Promise<Booking>;
|
||||
findByOrganization(orgId: string, filters: BookingFilters): Promise<Booking[]>;
|
||||
}
|
||||
|
||||
// infrastructure/persistence/typeorm/repositories/typeorm-booking.repository.ts
|
||||
@Injectable()
|
||||
export class TypeOrmBookingRepository implements BookingRepository {
|
||||
constructor(
|
||||
@InjectRepository(BookingOrmEntity)
|
||||
private readonly repo: Repository<BookingOrmEntity>,
|
||||
) {}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conventions de nommage
|
||||
|
||||
| Couche | Suffix | Exemple |
|
||||
|--------|--------|---------|
|
||||
| Domain entity | .entity.ts | booking.entity.ts |
|
||||
| Value object | .vo.ts | money.vo.ts |
|
||||
| In port | .use-case.ts | create-booking.use-case.ts |
|
||||
| Out port | .repository.ts | booking.repository.ts |
|
||||
| ORM entity | .orm-entity.ts | booking.orm-entity.ts |
|
||||
| ORM mapper | .mapper.ts | booking.mapper.ts |
|
||||
| Controller | .controller.ts | bookings.controller.ts |
|
||||
| DTO | .dto.ts | create-booking-request.dto.ts |
|
||||
| App mapper | .mapper.ts | booking.mapper.ts |
|
||||
|
||||
---
|
||||
|
||||
## Règles critiques
|
||||
|
||||
- **Jamais** d'import NestJS/TypeORM dans `domain/`
|
||||
- **Jamais** de type `any` dans le backend (strict mode)
|
||||
- **Jamais** modifier une migration déjà appliquée
|
||||
- **Toujours** valider les DTOs avec class-validator
|
||||
- **Toujours** créer un mapper séparé pour ORM ↔ Domain
|
||||
- `DATABASE_SYNC` est hard-codé à `false` — utiliser les migrations
|
||||
|
||||
---
|
||||
|
||||
## Modules NestJS (app.module.ts)
|
||||
|
||||
**Guards globaux** (toutes routes) :
|
||||
- `JwtAuthGuard` — JWT obligatoire sauf routes `@Public()`
|
||||
- `CustomThrottlerGuard` — rate limiting
|
||||
|
||||
**Feature modules** :
|
||||
Auth · Rates · Ports · Bookings · CsvBookings · Organizations · Users · Dashboard · Audit · Notifications · Webhooks · GDPR · Admin · Subscriptions · ApiKeys · Blog · Logs
|
||||
|
||||
**Infrastructure modules** :
|
||||
CacheModule · CarrierModule · SecurityModule · CsvRateModule · StripeModule · PdfModule · StorageModule · EmailModule
|
||||
|
||||
---
|
||||
|
||||
## Alias de chemins (tsconfig.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"@domain/*": ["src/domain/*"],
|
||||
"@application/*": ["src/application/*"],
|
||||
"@infrastructure/*": ["src/infrastructure/*"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration TypeScript
|
||||
|
||||
- `strict: true`
|
||||
- `strictNullChecks: true`
|
||||
- `strictPropertyInitialization: false` (exception pour ORM entities)
|
||||
- `noImplicitAny: true`
|
||||
462
docs/architecture/database.md
Normal file
462
docs/architecture/database.md
Normal file
@ -0,0 +1,462 @@
|
||||
# Schéma de base de données — Xpeditis
|
||||
|
||||
PostgreSQL 15 — 21 tables.
|
||||
|
||||
**Extensions requises** : `uuid-ossp`, `pg_trgm`
|
||||
|
||||
---
|
||||
|
||||
## Vue d'ensemble des tables
|
||||
|
||||
| Table | Description |
|
||||
|-------|-------------|
|
||||
| organizations | Organisations (transitaires, transporteurs) |
|
||||
| users | Comptes utilisateurs |
|
||||
| carriers | Transporteurs maritimes |
|
||||
| ports | Base de données des ports (UN/LOCODE) |
|
||||
| rate_quotes | Cotations de tarifs carriers |
|
||||
| bookings | Réservations standard |
|
||||
| containers | Conteneurs liés aux réservations |
|
||||
| csv_bookings | Réservations créées via CSV |
|
||||
| csv_rate_configs | Configuration des carriers CSV |
|
||||
| carrier_profiles | Profils portail carrier |
|
||||
| carrier_activities | Activités du portail carrier |
|
||||
| audit_logs | Journal d'audit des actions |
|
||||
| notifications | Notifications push (WebSocket) |
|
||||
| webhooks | Configuration des webhooks tiers |
|
||||
| subscriptions | Abonnements Stripe |
|
||||
| licenses | Licences d'utilisation |
|
||||
| api_keys | Clés API (auth alternative au JWT) |
|
||||
| invitation_tokens | Tokens d'invitation utilisateur |
|
||||
| password_reset_tokens | Tokens de réinitialisation mot de passe |
|
||||
| cookie_consents | Consentements RGPD cookies |
|
||||
| blog_posts | Articles de blog |
|
||||
|
||||
---
|
||||
|
||||
## Tables détaillées
|
||||
|
||||
### organizations
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| name | VARCHAR(255) | NOT NULL, UNIQUE | Nom de l'organisation |
|
||||
| type | VARCHAR(50) | NOT NULL | FREIGHT_FORWARDER, CARRIER, SHIPPER |
|
||||
| scac | CHAR(4) | UNIQUE, NULLABLE | Standard Carrier Alpha Code |
|
||||
| address_street | VARCHAR(255) | NOT NULL | Adresse |
|
||||
| address_city | VARCHAR(100) | NOT NULL | Ville |
|
||||
| address_state | VARCHAR(100) | NULLABLE | État/Province |
|
||||
| address_postal_code | VARCHAR(20) | NOT NULL | Code postal |
|
||||
| address_country | CHAR(2) | NOT NULL | Code pays ISO 3166-1 |
|
||||
| logo_url | TEXT | NULLABLE | URL du logo |
|
||||
| documents | JSONB | DEFAULT '[]' | Documents compliance |
|
||||
| siret | VARCHAR(20) | NULLABLE | SIRET (entreprises françaises) |
|
||||
| status_badge | VARCHAR(50) | NULLABLE | Badge de statut Pappers |
|
||||
| is_carrier | BOOLEAN | DEFAULT FALSE | Est un carrier (portail) |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Compte actif |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
| updated_at | TIMESTAMP | DEFAULT NOW() | Mise à jour |
|
||||
|
||||
---
|
||||
|
||||
### users
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| organization_id | UUID | FK organizations | Organisation |
|
||||
| email | VARCHAR(255) | NOT NULL, UNIQUE | Email (minuscules) |
|
||||
| password_hash | VARCHAR(255) | NOT NULL | Hash Argon2 |
|
||||
| role | VARCHAR(50) | NOT NULL | ADMIN, MANAGER, USER, VIEWER, CARRIER |
|
||||
| first_name | VARCHAR(100) | NOT NULL | Prénom |
|
||||
| last_name | VARCHAR(100) | NOT NULL | Nom |
|
||||
| phone_number | VARCHAR(20) | NULLABLE | Téléphone |
|
||||
| is_email_verified | BOOLEAN | DEFAULT FALSE | Email vérifié |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Compte actif |
|
||||
| preferred_language | VARCHAR(10) | DEFAULT 'fr' | Langue préférée (fr, en) |
|
||||
| last_login_at | TIMESTAMP | NULLABLE | Dernière connexion |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
| updated_at | TIMESTAMP | DEFAULT NOW() | Mise à jour |
|
||||
|
||||
Index : `idx_users_email`, `idx_users_organization`, `idx_users_role`
|
||||
|
||||
---
|
||||
|
||||
### carriers
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| name | VARCHAR(255) | NOT NULL | Nom (ex: "Maersk") |
|
||||
| code | VARCHAR(50) | NOT NULL, UNIQUE | Code (ex: "MAERSK") |
|
||||
| scac | CHAR(4) | NOT NULL, UNIQUE | Standard Carrier Alpha Code |
|
||||
| logo_url | TEXT | NULLABLE | URL du logo |
|
||||
| website | TEXT | NULLABLE | Site web |
|
||||
| api_config | JSONB | NULLABLE | Config API (credentials, timeout) |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Actif |
|
||||
| supports_api | BOOLEAN | DEFAULT FALSE | Intégration API disponible |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
| updated_at | TIMESTAMP | DEFAULT NOW() | Mise à jour |
|
||||
|
||||
Carriers seedés : Maersk (MAEU), MSC (MSCU), CMA CGM (CMDU), Hapag-Lloyd (HLCU), ONE (ONEY)
|
||||
|
||||
---
|
||||
|
||||
### ports
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| code | CHAR(5) | NOT NULL, UNIQUE | UN/LOCODE (ex: "NLRTM") |
|
||||
| name | VARCHAR(255) | NOT NULL | Nom du port |
|
||||
| city | VARCHAR(255) | NOT NULL | Ville |
|
||||
| country | CHAR(2) | NOT NULL | Code pays |
|
||||
| country_name | VARCHAR(100) | NOT NULL | Nom du pays |
|
||||
| latitude | DECIMAL(9,6) | NOT NULL | Latitude |
|
||||
| longitude | DECIMAL(9,6) | NOT NULL | Longitude |
|
||||
| timezone | VARCHAR(50) | NULLABLE | Fuseau horaire IANA |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Actif |
|
||||
|
||||
Index GIN pour recherche floue : `idx_ports_name_trgm`, `idx_ports_city_trgm`
|
||||
|
||||
---
|
||||
|
||||
### rate_quotes
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| carrier_id | UUID | FK carriers | Carrier |
|
||||
| carrier_name | VARCHAR(255) | NOT NULL | Nom carrier (dénormalisé) |
|
||||
| carrier_code | VARCHAR(50) | NOT NULL | Code carrier (dénormalisé) |
|
||||
| origin_code | CHAR(5) | NOT NULL | Port d'origine |
|
||||
| destination_code | CHAR(5) | NOT NULL | Port de destination |
|
||||
| base_freight | DECIMAL(10,2) | NOT NULL | Fret de base |
|
||||
| surcharges | JSONB | DEFAULT '[]' | Surcharges (BAF, CAF, etc.) |
|
||||
| total_amount | DECIMAL(10,2) | NOT NULL | Prix total |
|
||||
| currency | CHAR(3) | NOT NULL | Devise ISO 4217 |
|
||||
| container_type | VARCHAR(20) | NOT NULL | Type conteneur (20GP, 40HC, etc.) |
|
||||
| mode | VARCHAR(10) | NOT NULL | FCL ou LCL |
|
||||
| etd | TIMESTAMP | NOT NULL | Départ estimé |
|
||||
| eta | TIMESTAMP | NOT NULL | Arrivée estimée |
|
||||
| transit_days | INTEGER | NOT NULL | Jours de transit |
|
||||
| route | JSONB | NOT NULL | Escales |
|
||||
| availability | INTEGER | NOT NULL | Disponibilité conteneurs |
|
||||
| valid_until | TIMESTAMP | NOT NULL | Expiration (created_at + 15 min) |
|
||||
|
||||
---
|
||||
|
||||
### bookings
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| booking_number | VARCHAR(20) | NOT NULL, UNIQUE | Format WCM-YYYY-XXXXXX |
|
||||
| user_id | UUID | FK users | Utilisateur créateur |
|
||||
| organization_id | UUID | FK organizations | Organisation |
|
||||
| rate_quote_id | UUID | FK rate_quotes, NULLABLE | Cotation source |
|
||||
| carrier_id | UUID | FK carriers | Carrier |
|
||||
| status | VARCHAR(50) | NOT NULL | draft, confirmed, shipped, delivered, cancelled |
|
||||
| shipper | JSONB | NOT NULL | Infos expéditeur |
|
||||
| consignee | JSONB | NOT NULL | Infos destinataire |
|
||||
| cargo | JSONB | NULLABLE | Détails marchandise |
|
||||
| notes | TEXT | NULLABLE | Notes internes |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
| updated_at | TIMESTAMP | DEFAULT NOW() | Mise à jour |
|
||||
|
||||
---
|
||||
|
||||
### containers
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| booking_id | UUID | FK bookings, NULLABLE | Réservation |
|
||||
| type | VARCHAR(20) | NOT NULL | 20GP, 40HC, 40RF, etc. |
|
||||
| category | VARCHAR(20) | NOT NULL | DRY, REEFER, OPEN_TOP, FLAT_RACK, TANK |
|
||||
| container_number | VARCHAR(11) | NULLABLE, UNIQUE | ISO 6346 |
|
||||
| seal_number | VARCHAR(50) | NULLABLE | Numéro de plomb |
|
||||
| vgm | INTEGER | NULLABLE | Masse Brute Vérifiée (kg) |
|
||||
| is_hazmat | BOOLEAN | DEFAULT FALSE | Marchandise dangereuse |
|
||||
| imo_class | VARCHAR(10) | NULLABLE | Classe IMO |
|
||||
| cargo_description | TEXT | NULLABLE | Description de la marchandise |
|
||||
|
||||
---
|
||||
|
||||
### csv_bookings
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| organization_id | UUID | FK organizations | Organisation |
|
||||
| carrier_id | UUID | FK carriers, NULLABLE | Carrier assigné |
|
||||
| booking_number | VARCHAR(50) | NOT NULL, UNIQUE | Numéro de réservation |
|
||||
| shipper_name | VARCHAR(255) | NOT NULL | Expéditeur |
|
||||
| consignee_name | VARCHAR(255) | NOT NULL | Destinataire |
|
||||
| origin_port | CHAR(5) | NOT NULL | Port d'origine (UN/LOCODE) |
|
||||
| destination_port | CHAR(5) | NOT NULL | Port de destination |
|
||||
| container_type | VARCHAR(20) | NOT NULL | Type conteneur |
|
||||
| commodity | TEXT | NULLABLE | Marchandise |
|
||||
| weight_kg | DECIMAL(10,2) | NULLABLE | Poids |
|
||||
| status | VARCHAR(50) | NOT NULL | pending, accepted, rejected, in_transit, delivered |
|
||||
| carrier_magic_link_token | VARCHAR(255) | NULLABLE | Token lien magique carrier |
|
||||
| carrier_magic_link_expires_at | TIMESTAMP | NULLABLE | Expiration du token |
|
||||
| carrier_password_hash | VARCHAR(255) | NULLABLE | Hash mdp portail carrier |
|
||||
| notes | TEXT | NULLABLE | Notes |
|
||||
| documents | JSONB | DEFAULT '[]' | Documents joints |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
| updated_at | TIMESTAMP | DEFAULT NOW() | Mise à jour |
|
||||
|
||||
---
|
||||
|
||||
### csv_rate_configs
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| company_name | VARCHAR(255) | NOT NULL, UNIQUE | Nom du carrier CSV |
|
||||
| csv_file_path | VARCHAR(500) | NOT NULL | Chemin fichier CSV |
|
||||
| type | VARCHAR(50) | DEFAULT 'CSV_ONLY' | CSV_ONLY ou CSV_AND_API |
|
||||
| has_api | BOOLEAN | DEFAULT FALSE | A une API |
|
||||
| api_connector | VARCHAR(100) | NULLABLE | Identifiant du connecteur API |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Actif |
|
||||
| uploaded_at | TIMESTAMP | NOT NULL | Upload |
|
||||
| uploaded_by | UUID | FK users, NULLABLE | Uploadé par |
|
||||
| row_count | INTEGER | NULLABLE | Nombre de lignes |
|
||||
|
||||
---
|
||||
|
||||
### carrier_profiles
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| organization_id | UUID | FK organizations, UNIQUE | Organisation carrier |
|
||||
| carrier_code | VARCHAR(50) | NOT NULL | Code carrier |
|
||||
| contact_email | VARCHAR(255) | NOT NULL | Email de contact |
|
||||
| contact_name | VARCHAR(255) | NULLABLE | Nom du contact |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Actif |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
---
|
||||
|
||||
### carrier_activities
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| carrier_profile_id | UUID | FK carrier_profiles | Profil carrier |
|
||||
| csv_booking_id | UUID | FK csv_bookings | Réservation CSV |
|
||||
| action | VARCHAR(50) | NOT NULL | ACCEPTED, REJECTED, VIEWED, DOCUMENT_UPLOADED |
|
||||
| notes | TEXT | NULLABLE | Notes de l'action |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
---
|
||||
|
||||
### audit_logs
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| user_id | UUID | FK users, NULLABLE | Utilisateur |
|
||||
| organization_id | UUID | FK organizations, NULLABLE | Organisation |
|
||||
| action | VARCHAR(100) | NOT NULL | Type d'action (26 types) |
|
||||
| resource_type | VARCHAR(50) | NULLABLE | Type de ressource |
|
||||
| resource_id | VARCHAR(255) | NULLABLE | ID de la ressource |
|
||||
| status | VARCHAR(20) | NOT NULL | SUCCESS, FAILURE, WARNING |
|
||||
| metadata | JSONB | NULLABLE | Données supplémentaires |
|
||||
| ip_address | VARCHAR(45) | NULLABLE | Adresse IP |
|
||||
| user_agent | TEXT | NULLABLE | User agent |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
Index : `idx_audit_logs_user_id`, `idx_audit_logs_action`, `idx_audit_logs_created_at`
|
||||
|
||||
---
|
||||
|
||||
### notifications
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| user_id | UUID | FK users | Destinataire |
|
||||
| type | VARCHAR(50) | NOT NULL | BOOKING_CREATED, DOCUMENT_UPLOADED, etc. (9 types) |
|
||||
| title | VARCHAR(255) | NOT NULL | Titre |
|
||||
| message | TEXT | NOT NULL | Message |
|
||||
| data | JSONB | NULLABLE | Données associées |
|
||||
| priority | VARCHAR(20) | NOT NULL | LOW, MEDIUM, HIGH, URGENT |
|
||||
| read | BOOLEAN | DEFAULT FALSE | Lu |
|
||||
| read_at | TIMESTAMP | NULLABLE | Date de lecture |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
---
|
||||
|
||||
### webhooks
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| organization_id | UUID | FK organizations | Organisation |
|
||||
| url | TEXT | NOT NULL | URL cible |
|
||||
| secret | VARCHAR(255) | NOT NULL | Secret HMAC SHA-256 |
|
||||
| events | JSONB | NOT NULL | Événements souscrits |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Actif |
|
||||
| last_triggered_at | TIMESTAMP | NULLABLE | Dernier déclenchement |
|
||||
| failure_count | INTEGER | DEFAULT 0 | Compteur d'échecs |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
---
|
||||
|
||||
### subscriptions
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| organization_id | UUID | FK organizations, UNIQUE | Organisation |
|
||||
| stripe_customer_id | VARCHAR(255) | NULLABLE | ID client Stripe |
|
||||
| stripe_subscription_id | VARCHAR(255) | NULLABLE | ID abonnement Stripe |
|
||||
| plan | VARCHAR(50) | NOT NULL | FREE, BRONZE, SILVER, GOLD, PLATINIUM |
|
||||
| status | VARCHAR(50) | NOT NULL | active, past_due, cancelled, trialing, pending_payment, pending_bank_transfer |
|
||||
| current_period_start | TIMESTAMP | NULLABLE | Début de période |
|
||||
| current_period_end | TIMESTAMP | NULLABLE | Fin de période |
|
||||
| billing_interval | VARCHAR(20) | NULLABLE | monthly, yearly |
|
||||
| commission_rate | DECIMAL(5,4) | DEFAULT 0 | Taux de commission Xpeditis |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
| updated_at | TIMESTAMP | DEFAULT NOW() | Mise à jour |
|
||||
|
||||
---
|
||||
|
||||
### licenses
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| organization_id | UUID | FK organizations | Organisation |
|
||||
| feature | VARCHAR(100) | NOT NULL | Fonctionnalité activée |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Actif |
|
||||
| valid_until | TIMESTAMP | NULLABLE | Expiration |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
---
|
||||
|
||||
### api_keys
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| organization_id | UUID | FK organizations | Organisation |
|
||||
| user_id | UUID | FK users | Créateur |
|
||||
| name | VARCHAR(255) | NOT NULL | Nom de la clé |
|
||||
| key_hash | VARCHAR(255) | NOT NULL | Hash Argon2 de la clé |
|
||||
| key_prefix | VARCHAR(10) | NOT NULL | Préfixe visible (ex: xped_) |
|
||||
| last_used_at | TIMESTAMP | NULLABLE | Dernière utilisation |
|
||||
| expires_at | TIMESTAMP | NULLABLE | Expiration |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | Actif |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
---
|
||||
|
||||
### invitation_tokens
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| organization_id | UUID | FK organizations | Organisation |
|
||||
| email | VARCHAR(255) | NOT NULL | Email invité |
|
||||
| token | VARCHAR(255) | NOT NULL, UNIQUE | Token d'invitation |
|
||||
| role | VARCHAR(50) | NOT NULL | Rôle assigné |
|
||||
| expires_at | TIMESTAMP | NOT NULL | Expiration (24h) |
|
||||
| used_at | TIMESTAMP | NULLABLE | Utilisé le |
|
||||
| created_by | UUID | FK users | Créateur |
|
||||
|
||||
---
|
||||
|
||||
### password_reset_tokens
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| user_id | UUID | FK users | Utilisateur |
|
||||
| token | VARCHAR(255) | NOT NULL, UNIQUE | Token de réinitialisation |
|
||||
| expires_at | TIMESTAMP | NOT NULL | Expiration (1h) |
|
||||
| used_at | TIMESTAMP | NULLABLE | Utilisé le |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
---
|
||||
|
||||
### cookie_consents
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| user_id | UUID | FK users, NULLABLE | Utilisateur (si connecté) |
|
||||
| session_id | VARCHAR(255) | NULLABLE | Session anonyme |
|
||||
| analytics | BOOLEAN | NOT NULL | Consentement analytics |
|
||||
| marketing | BOOLEAN | NOT NULL | Consentement marketing |
|
||||
| ip_address | VARCHAR(45) | NULLABLE | IP |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
|
||||
---
|
||||
|
||||
### blog_posts
|
||||
|
||||
| Colonne | Type | Contraintes | Description |
|
||||
|---------|------|-------------|-------------|
|
||||
| id | UUID | PK | Identifiant |
|
||||
| slug | VARCHAR(255) | NOT NULL, UNIQUE | Slug URL |
|
||||
| title_fr | VARCHAR(500) | NOT NULL | Titre en français |
|
||||
| title_en | VARCHAR(500) | NULLABLE | Titre en anglais |
|
||||
| content_fr | TEXT | NOT NULL | Contenu en français |
|
||||
| content_en | TEXT | NULLABLE | Contenu en anglais |
|
||||
| excerpt_fr | TEXT | NULLABLE | Extrait français |
|
||||
| excerpt_en | TEXT | NULLABLE | Extrait anglais |
|
||||
| cover_image_url | TEXT | NULLABLE | Image de couverture |
|
||||
| author_id | UUID | FK users | Auteur |
|
||||
| is_published | BOOLEAN | DEFAULT FALSE | Publié |
|
||||
| published_at | TIMESTAMP | NULLABLE | Date de publication |
|
||||
| tags | JSONB | DEFAULT '[]' | Tags |
|
||||
| created_at | TIMESTAMP | DEFAULT NOW() | Création |
|
||||
| updated_at | TIMESTAMP | DEFAULT NOW() | Mise à jour |
|
||||
|
||||
---
|
||||
|
||||
## Relations principales
|
||||
|
||||
```
|
||||
organizations 1──* users
|
||||
organizations 1──1 subscriptions
|
||||
organizations 1──* licenses
|
||||
organizations 1──* api_keys
|
||||
organizations 1──* webhooks
|
||||
organizations 1──* bookings
|
||||
organizations 1──* csv_bookings
|
||||
organizations 1──1 carrier_profiles
|
||||
|
||||
users 1──* bookings
|
||||
users 1──* audit_logs
|
||||
users 1──* notifications
|
||||
users 1──* api_keys
|
||||
|
||||
bookings 1──* containers
|
||||
bookings 1──1 rate_quotes (optionnel)
|
||||
|
||||
csv_bookings 1──* carrier_activities (via carrier_profiles)
|
||||
carrier_profiles 1──* carrier_activities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Stratégie de migrations
|
||||
|
||||
Les migrations sont dans `apps/backend/src/infrastructure/persistence/typeorm/migrations/`.
|
||||
Ne jamais modifier une migration déjà appliquée — créer une nouvelle migration.
|
||||
|
||||
```bash
|
||||
cd apps/backend
|
||||
npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/NomMigration
|
||||
npm run migration:run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Dernière mise à jour : Mai 2026 — 21 tables*
|
||||
157
docs/architecture/frontend.md
Normal file
157
docs/architecture/frontend.md
Normal file
@ -0,0 +1,157 @@
|
||||
# Architecture Frontend — Next.js 14
|
||||
|
||||
---
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
apps/frontend/
|
||||
├── app/ # Next.js App Router
|
||||
│ ├── [locale]/ # i18n wrapper (fr, en) — next-intl
|
||||
│ │ ├── about/
|
||||
│ │ ├── blog/
|
||||
│ │ ├── booking/ # Flux de réservation
|
||||
│ │ ├── careers/
|
||||
│ │ ├── carrier/ # Portail carrier (magic link)
|
||||
│ │ ├── compliance/
|
||||
│ │ ├── contact/
|
||||
│ │ ├── dashboard/ # Application protégée
|
||||
│ │ │ ├── bookings/
|
||||
│ │ │ ├── csv-bookings/
|
||||
│ │ │ ├── search/
|
||||
│ │ │ ├── settings/
|
||||
│ │ │ ├── admin/
|
||||
│ │ │ └── wiki/
|
||||
│ │ ├── docs/
|
||||
│ │ ├── forgot-password/
|
||||
│ │ ├── login/
|
||||
│ │ ├── pricing/
|
||||
│ │ ├── register/
|
||||
│ │ ├── reset-password/
|
||||
│ │ └── verify-email/
|
||||
│ └── api/v1/ # API routes Next.js (proxy)
|
||||
│
|
||||
├── src/
|
||||
│ ├── components/ # Composants React
|
||||
│ │ ├── admin/
|
||||
│ │ ├── blog/
|
||||
│ │ ├── bookings/
|
||||
│ │ ├── docs/
|
||||
│ │ ├── layout/
|
||||
│ │ ├── organization/
|
||||
│ │ ├── rate-search/
|
||||
│ │ └── ui/ # Composants shadcn/ui (Radix UI)
|
||||
│ ├── hooks/ # Custom hooks (TanStack Query)
|
||||
│ ├── lib/
|
||||
│ │ ├── api/ # Client fetch avec auto-refresh JWT
|
||||
│ │ │ ├── client.ts # get/post/patch/del/upload/download
|
||||
│ │ │ ├── auth.ts
|
||||
│ │ │ ├── bookings.ts
|
||||
│ │ │ ├── rates.ts
|
||||
│ │ │ └── ...
|
||||
│ │ ├── context/ # AuthContext, CookieContext
|
||||
│ │ └── providers/ # QueryProvider (TanStack Query)
|
||||
│ ├── types/ # Types TypeScript
|
||||
│ └── utils/ # Export Excel, PDF
|
||||
│
|
||||
├── i18n/ # Config next-intl
|
||||
├── messages/
|
||||
│ ├── fr.json # Traductions français
|
||||
│ └── en.json # Traductions anglais
|
||||
└── middleware.ts # Auth + i18n routing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## i18n (Internationalisation)
|
||||
|
||||
Le frontend supporte **français** (par défaut) et **anglais** via `next-intl`.
|
||||
|
||||
Routes : `/fr/login`, `/en/login`, etc.
|
||||
|
||||
```typescript
|
||||
// i18n/routing.ts
|
||||
export const routing = defineRouting({
|
||||
locales: ['fr', 'en'],
|
||||
defaultLocale: 'fr',
|
||||
});
|
||||
```
|
||||
|
||||
Le middleware `middleware.ts` combine la protection d'auth **et** le routing i18n.
|
||||
|
||||
---
|
||||
|
||||
## Client API
|
||||
|
||||
`src/lib/api/client.ts` — wrapper fetch avec auto-refresh JWT :
|
||||
|
||||
```typescript
|
||||
// Toutes les requêtes passent par ces fonctions
|
||||
export const get = <T>(url: string): Promise<T>
|
||||
export const post = <T>(url: string, body: unknown): Promise<T>
|
||||
export const patch = <T>(url: string, body: unknown): Promise<T>
|
||||
export const del = <T>(url: string): Promise<T>
|
||||
export const upload = <T>(url: string, formData: FormData): Promise<T>
|
||||
export const download = (url: string): Promise<Blob>
|
||||
```
|
||||
|
||||
- Sur 401 : refresh automatique du token, retry de la requête
|
||||
- Tokens : localStorage ET cookie `accessToken` (pour le middleware Next.js)
|
||||
|
||||
---
|
||||
|
||||
## State management
|
||||
|
||||
- **Données serveur** : TanStack Query v5 (`@tanstack/react-query`)
|
||||
- **État global UI** : zustand (minimal)
|
||||
- **Formulaires** : react-hook-form + zod
|
||||
|
||||
```typescript
|
||||
// Pattern hook standard
|
||||
export function useBookings(filters?: BookingFilters) {
|
||||
return useQuery({
|
||||
queryKey: ['bookings', filters],
|
||||
queryFn: () => bookingsApi.list(filters),
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Protection des routes
|
||||
|
||||
`middleware.ts` vérifie le cookie `accessToken` avant chaque route.
|
||||
|
||||
**Routes publiques exactes** : `/fr`, `/en`, `/`
|
||||
|
||||
**Routes publiques par préfixe** :
|
||||
`/login`, `/register`, `/forgot-password`, `/reset-password`, `/verify-email`,
|
||||
`/about`, `/blog`, `/pricing`, `/contact`, `/careers`, `/press`, `/security`,
|
||||
`/privacy`, `/terms`, `/cookies`, `/compliance`, `/carrier`
|
||||
|
||||
Toutes les autres routes → redirection `/login?redirect=<pathname>`
|
||||
|
||||
---
|
||||
|
||||
## Design system
|
||||
|
||||
| Élément | Valeur |
|
||||
|---------|--------|
|
||||
| Couleur primaire | Navy `#10183A` |
|
||||
| Couleur accent | Turquoise `#34CCCD` |
|
||||
| Succès | Green `#067224` |
|
||||
| Fond neutre | Gray `#F2F2F2` |
|
||||
| Font headings | Manrope |
|
||||
| Font body | Montserrat |
|
||||
| UI components | shadcn/ui (Radix UI) |
|
||||
| Icônes | lucide-react |
|
||||
|
||||
---
|
||||
|
||||
## Conventions
|
||||
|
||||
- Pas de fetch direct — toujours via `src/lib/api/*.ts`
|
||||
- Les hooks wrappent TanStack Query, pas de `useEffect` pour les données
|
||||
- `strict: false` en frontend (contrairement au backend)
|
||||
- Alias `@/*` → `./src/*`
|
||||
- La landing page est en **français**
|
||||
215
docs/architecture/overview.md
Normal file
215
docs/architecture/overview.md
Normal file
@ -0,0 +1,215 @@
|
||||
# Architecture — Xpeditis
|
||||
|
||||
**Xpeditis** est une plateforme B2B SaaS de réservation de fret maritime, construite en monorepo avec une architecture hexagonale stricte côté backend.
|
||||
|
||||
---
|
||||
|
||||
## Vue d'ensemble système
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Frontend (Next.js 14 + App Router) │
|
||||
│ React 18 · TanStack Query · Socket.IO · next-intl (i18n) │
|
||||
└──────────────────────────┬──────────────────────────────────┘
|
||||
│ HTTPS / WSS
|
||||
┌──────────────────────────▼──────────────────────────────────┐
|
||||
│ Backend (NestJS 10) │
|
||||
│ JWT Auth · Rate Limiting · Swagger OpenAPI │
|
||||
└────┬──────────┬──────────┬──────────┬──────────┬────────────┘
|
||||
│ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
Rates Bookings Users Carriers CSV System
|
||||
Service Service Service Service Service
|
||||
│ │ │ │ │
|
||||
└──────────┴──────────┴──────────┴──────────┘
|
||||
│
|
||||
┌──────────────────┼──────────────────┐
|
||||
▼ ▼ ▼
|
||||
PostgreSQL 15 Redis 7 MinIO (S3)
|
||||
(données) (cache 15min) (documents)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture hexagonale (backend)
|
||||
|
||||
```
|
||||
apps/backend/src/
|
||||
├── domain/ # Logique métier pure — zéro dépendance framework
|
||||
│ ├── entities/ # 17 entités métier
|
||||
│ ├── value-objects/ # Objets valeur immuables
|
||||
│ ├── services/ # Services métier purs
|
||||
│ ├── ports/in/ # Interfaces use-case (execute())
|
||||
│ └── ports/out/ # Interfaces repository/SPI
|
||||
│
|
||||
├── application/ # Controllers, DTOs, Guards
|
||||
│ ├── controllers/ # REST controllers avec Swagger
|
||||
│ ├── dto/ # DTOs class-validator
|
||||
│ ├── mappers/ # Domain ↔ DTO
|
||||
│ ├── guards/ # JwtAuthGuard, RolesGuard, ApiKeyGuard
|
||||
│ ├── gateways/ # WebSocket (Socket.IO)
|
||||
│ └── services/ # Services applicatifs (audit, notification)
|
||||
│
|
||||
└── infrastructure/ # Adaptateurs externes
|
||||
├── persistence/ # TypeORM (entities, repositories, mappers, migrations)
|
||||
├── carriers/ # Connecteurs carriers + CSV loader
|
||||
├── cache/ # Adaptateur Redis
|
||||
├── email/ # MJML + Nodemailer
|
||||
├── storage/ # S3/MinIO + CSV storage
|
||||
├── stripe/ # Abonnements et paiements
|
||||
├── external/ # Pappers (SIRET)
|
||||
├── pdf/ # Génération PDF (pdfkit)
|
||||
└── monitoring/ # Sentry, logging Pino
|
||||
```
|
||||
|
||||
**Règles de dépendance** :
|
||||
- Domain : aucune dépendance externe (TypeScript pur)
|
||||
- Application : dépend uniquement du domain
|
||||
- Infrastructure : dépend uniquement du domain
|
||||
- Le flux de dépendances pointe toujours vers le centre
|
||||
|
||||
---
|
||||
|
||||
## Stack technique
|
||||
|
||||
### Backend
|
||||
|
||||
| Composant | Technologie |
|
||||
|-----------|-------------|
|
||||
| Framework | NestJS 10.x |
|
||||
| Langage | TypeScript 5.3+ strict |
|
||||
| ORM | TypeORM 0.3.x |
|
||||
| Base de données | PostgreSQL 15+ |
|
||||
| Cache | Redis 7+ (ioredis) |
|
||||
| Auth | JWT (15min) + Refresh tokens (7j) + OAuth2 |
|
||||
| Auth alternative | API Keys (Argon2 hash) |
|
||||
| WebSocket | Socket.IO |
|
||||
| Email | Nodemailer + MJML templates |
|
||||
| PDF | pdfkit |
|
||||
| Paiements | Stripe |
|
||||
| Stockage fichiers | S3 / MinIO |
|
||||
| Validation | class-validator + class-transformer |
|
||||
| Docs API | Swagger/OpenAPI |
|
||||
| Logging | nestjs-pino (pino-pretty dev) |
|
||||
| Monitoring | Sentry |
|
||||
| i18n | nestjs-i18n |
|
||||
| Circuit breaker | opossum (5s timeout carriers) |
|
||||
|
||||
### Frontend
|
||||
|
||||
| Composant | Technologie |
|
||||
|-----------|-------------|
|
||||
| Framework | Next.js 14 (App Router) |
|
||||
| Langage | TypeScript |
|
||||
| Styling | Tailwind CSS |
|
||||
| State serveur | TanStack Query v5 |
|
||||
| Tables | TanStack Table v8 + Virtual |
|
||||
| Formulaires | react-hook-form + zod |
|
||||
| Temps réel | Socket.IO client |
|
||||
| Graphiques | recharts |
|
||||
| Cartes | react-leaflet |
|
||||
| i18n | next-intl (fr, en) |
|
||||
| Éditeur riche | Tiptap |
|
||||
| État global | zustand |
|
||||
| Animations | framer-motion |
|
||||
|
||||
---
|
||||
|
||||
## Modules NestJS
|
||||
|
||||
**Guards globaux** : `JwtAuthGuard` (toutes les routes protégées par défaut), `CustomThrottlerGuard`
|
||||
|
||||
**Feature modules** :
|
||||
- Auth, Rates, Ports, Bookings, CsvBookings, Organizations, Users
|
||||
- Dashboard, Audit, Notifications, Webhooks, GDPR, Admin
|
||||
- Subscriptions, ApiKeys, Blog, Logs
|
||||
|
||||
**Infrastructure modules** :
|
||||
- CacheModule (Redis), CarrierModule, SecurityModule
|
||||
- CsvRateModule, StripeModule, PdfModule, StorageModule, EmailModule
|
||||
|
||||
---
|
||||
|
||||
## Flux clés
|
||||
|
||||
### Recherche de tarifs (FCL)
|
||||
|
||||
```
|
||||
POST /api/v1/rates/search
|
||||
→ Vérification cache Redis (TTL 15min)
|
||||
→ Si cache miss : appel parallel carriers API (5s timeout + circuit breaker)
|
||||
→ Normalisation résultats → stockage Redis
|
||||
→ Réponse JSON paginée
|
||||
```
|
||||
|
||||
### Réservation standard
|
||||
|
||||
```
|
||||
POST /api/v1/bookings
|
||||
→ Validation DTO → Génération WCM-YYYY-XXXXXX
|
||||
→ Persistance PostgreSQL
|
||||
→ Audit log
|
||||
→ Notification WebSocket
|
||||
→ Déclenchement webhooks
|
||||
→ Email confirmation (MJML)
|
||||
→ PDF booking
|
||||
```
|
||||
|
||||
### Portail Carrier (CSV Bookings)
|
||||
|
||||
```
|
||||
Admin crée CSV booking → assigne carrier
|
||||
→ Email magic link (1h expiry)
|
||||
→ Carrier s'authentifie via token
|
||||
→ Accept/Reject → Activité logguée
|
||||
```
|
||||
|
||||
### Abonnements
|
||||
|
||||
```
|
||||
Stripe webhook → /api/v1/subscriptions/webhook
|
||||
→ Vérification signature HMAC
|
||||
→ Mise à jour subscription + license
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sécurité
|
||||
|
||||
| Mesure | Implémentation |
|
||||
|--------|---------------|
|
||||
| Rate limiting | 100 req/min global, 5/min auth, 30/min search |
|
||||
| Password hashing | Argon2id |
|
||||
| JWT | Access 15min + Refresh 7j avec rotation |
|
||||
| API Keys | Argon2 hash, préfixe xped_ |
|
||||
| Brute force | Exponential backoff après 3 échecs |
|
||||
| Headers sécurité | Helmet.js (CSP, HSTS, XSS) |
|
||||
| Validation | class-validator sur tous les DTOs |
|
||||
| RBAC | 5 rôles : ADMIN, MANAGER, USER, VIEWER, CARRIER |
|
||||
| CORS | Origines strictes |
|
||||
| Upload fichiers | Validation MIME, max 10MB |
|
||||
| GDPR | Export/suppression données utilisateur |
|
||||
|
||||
---
|
||||
|
||||
## Performances
|
||||
|
||||
- Recherche tarifs (avec cache) : < 2s (p90) — ~500ms observé
|
||||
- Création booking : < 3s — ~1s observé
|
||||
- Dashboard (5k bookings) : < 1s
|
||||
- Cache hit ratio cible : > 90%
|
||||
|
||||
---
|
||||
|
||||
## Déploiement
|
||||
|
||||
L'application est containerisée (Dockerfile dans chaque app).
|
||||
- **Développement** : docker-compose (PostgreSQL + Redis + MinIO)
|
||||
- **Production** : Portainer / Docker Swarm ou Kubernetes (Hetzner)
|
||||
- **CI/CD** : GitHub Actions (`.github/workflows/`)
|
||||
|
||||
Voir [../deployment/portainer.md](../deployment/portainer.md) et [../deployment/hetzner/README.md](../deployment/hetzner/README.md).
|
||||
|
||||
---
|
||||
|
||||
*Dernière mise à jour : Mai 2026*
|
||||
16
docs/archive/README.md
Normal file
16
docs/archive/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Archives
|
||||
|
||||
Ce dossier contient les documents historiques qui ne sont plus actifs mais conservés pour référence.
|
||||
|
||||
## Contenu
|
||||
|
||||
| Dossier | Description |
|
||||
|---------|-------------|
|
||||
| phases/ | Rapports de sprint (Sprint 0, Phase 1-4) — projet terminé |
|
||||
| debug/ | Notes de debugging résolues |
|
||||
| installation/ | Anciens guides d'installation (remplacés par docs/getting-started/) |
|
||||
| portainer/ | 24 fichiers de debug/fix Portainer — consolidés dans docs/deployment/portainer.md |
|
||||
|
||||
## Note
|
||||
|
||||
La documentation active et à jour se trouve dans [../](../README.md).
|
||||
248
docs/deployment/portainer.md
Normal file
248
docs/deployment/portainer.md
Normal file
@ -0,0 +1,248 @@
|
||||
# Déploiement Portainer / Docker Swarm
|
||||
|
||||
Guide de déploiement de Xpeditis sur Portainer avec Docker Swarm.
|
||||
|
||||
---
|
||||
|
||||
## Infrastructure
|
||||
|
||||
| Composant | Image | Registry |
|
||||
|-----------|-------|----------|
|
||||
| Backend NestJS | xpeditis-backend | rg.fr-par.scw.cloud/weworkstudio |
|
||||
| Frontend Next.js | xpeditis-frontend | rg.fr-par.scw.cloud/weworkstudio |
|
||||
| PostgreSQL 15 | postgres:15-alpine | Docker Hub |
|
||||
| Redis 7 | redis:7-alpine | Docker Hub |
|
||||
| MinIO | minio/minio | Docker Hub |
|
||||
|
||||
**Registry** : Scaleway Container Registry (fr-par)
|
||||
**Portainer** : https://portainer.weworkstudio.com
|
||||
|
||||
---
|
||||
|
||||
## Prérequis
|
||||
|
||||
### 1. Configurer le registry Scaleway dans Portainer
|
||||
|
||||
**Portainer → Registries → Add registry** :
|
||||
- Registry URL : `rg.fr-par.scw.cloud/weworkstudio`
|
||||
- Username : `nologin`
|
||||
- Password : token Scaleway (Console → Registry → Push/Pull credentials)
|
||||
|
||||
### 2. Créer le réseau Traefik
|
||||
|
||||
```bash
|
||||
docker network create traefik_network
|
||||
```
|
||||
|
||||
### 3. Vérifier que les images existent
|
||||
|
||||
```bash
|
||||
docker manifest inspect rg.fr-par.scw.cloud/weworkstudio/xpeditis-backend:preprod
|
||||
docker manifest inspect rg.fr-par.scw.cloud/weworkstudio/xpeditis-frontend:preprod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build et push des images
|
||||
|
||||
### Script de déploiement complet
|
||||
|
||||
```bash
|
||||
# Build et push backend (supporte AMD64 + ARM64)
|
||||
cd apps/backend
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
-t rg.fr-par.scw.cloud/weworkstudio/xpeditis-backend:preprod \
|
||||
--push .
|
||||
|
||||
# Build et push frontend
|
||||
cd ../frontend
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
-t rg.fr-par.scw.cloud/weworkstudio/xpeditis-frontend:preprod \
|
||||
--push .
|
||||
```
|
||||
|
||||
> Pour ARM64 seul (serveur Hetzner CAX) : `--platform linux/arm64`
|
||||
|
||||
---
|
||||
|
||||
## Déploiement dans Portainer
|
||||
|
||||
1. **Portainer → Stacks → Add stack**
|
||||
2. **Name** : `xpeditis-preprod`
|
||||
3. **Build method** : Web editor
|
||||
4. Coller le contenu de `docker/portainer-stack.yml`
|
||||
5. Définir les variables d'environnement (voir section suivante)
|
||||
6. **Deploy the stack**
|
||||
|
||||
---
|
||||
|
||||
## Variables d'environnement production
|
||||
|
||||
### Backend
|
||||
|
||||
```bash
|
||||
NODE_ENV=production
|
||||
PORT=4000
|
||||
API_PREFIX=api/v1
|
||||
FRONTEND_URL=https://app.xpeditis.com
|
||||
|
||||
# Base de données
|
||||
DATABASE_HOST=xpeditis-db
|
||||
DATABASE_PORT=5432
|
||||
DATABASE_USER=xpeditis
|
||||
DATABASE_PASSWORD=CHANGER_EN_PROD
|
||||
DATABASE_NAME=xpeditis_prod
|
||||
DATABASE_SYNC=false
|
||||
DATABASE_LOGGING=false
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=xpeditis-redis
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=CHANGER_EN_PROD
|
||||
|
||||
# JWT
|
||||
JWT_SECRET=MINIMUM_32_CARACTERES_ALEATOIRES
|
||||
JWT_ACCESS_EXPIRATION=15m
|
||||
JWT_REFRESH_EXPIRATION=7d
|
||||
|
||||
# Email
|
||||
SMTP_HOST=smtp-relay.brevo.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=
|
||||
SMTP_PASS=
|
||||
SMTP_FROM=noreply@xpeditis.com
|
||||
|
||||
# Storage (MinIO en prod ou S3)
|
||||
AWS_ACCESS_KEY_ID=minioadmin
|
||||
AWS_SECRET_ACCESS_KEY=minioadmin
|
||||
AWS_REGION=us-east-1
|
||||
AWS_S3_ENDPOINT=http://xpeditis-minio:9000
|
||||
|
||||
# Swagger
|
||||
SWAGGER_USERNAME=admin
|
||||
SWAGGER_PASSWORD=CHANGER_EN_PROD
|
||||
|
||||
# Stripe
|
||||
STRIPE_SECRET_KEY=sk_live_xxxx
|
||||
STRIPE_WEBHOOK_SECRET=whsec_xxxx
|
||||
STRIPE_SILVER_MONTHLY_PRICE_ID=price_xxxx
|
||||
# ... autres price IDs
|
||||
|
||||
# Monitoring
|
||||
SENTRY_DSN=
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_API_URL=https://api.xpeditis.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migrations automatiques
|
||||
|
||||
Le backend exécute les migrations automatiquement au démarrage via le script `docker-entrypoint.sh` :
|
||||
|
||||
```bash
|
||||
# apps/backend/docker-entrypoint.sh attend PostgreSQL puis :
|
||||
npm run migration:run
|
||||
node dist/main.js
|
||||
```
|
||||
|
||||
Les logs Portainer affichent :
|
||||
```
|
||||
✅ PostgreSQL is ready
|
||||
✅ Successfully ran N migration(s)
|
||||
🚀 Xpeditis API Server Running
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vérifier le déploiement
|
||||
|
||||
```bash
|
||||
# Depuis Portainer → Containers → xpeditis-backend → Logs
|
||||
# Ou en SSH sur le serveur :
|
||||
docker logs xpeditis-backend --tail 50 -f
|
||||
|
||||
# Tester les endpoints
|
||||
curl https://api.xpeditis.com/api/v1/health
|
||||
# → {"status":"ok"}
|
||||
|
||||
curl https://app.xpeditis.com
|
||||
# → 200 OK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Problèmes courants
|
||||
|
||||
### CSS manquant en production (Next.js)
|
||||
|
||||
Vérifier que la variable `NEXT_PUBLIC_API_URL` est définie **au moment du build** (pas au runtime).
|
||||
Solution : passer la variable en ARG dans le Dockerfile frontend ou utiliser `--build-arg`.
|
||||
|
||||
### 404 sur routes Next.js
|
||||
|
||||
Le frontend Next.js est configuré en mode `standalone`. Vérifier que le serveur pointe bien sur `server.js` et non sur un fichier statique.
|
||||
|
||||
Si Traefik retourne 404 :
|
||||
```yaml
|
||||
# Dans portainer-stack.yml, vérifier le label Traefik :
|
||||
traefik.http.routers.frontend.rule=Host(`app.xpeditis.com`)
|
||||
traefik.http.services.frontend.loadbalancer.server.port=3000
|
||||
```
|
||||
|
||||
### Migrations échouent
|
||||
|
||||
```bash
|
||||
docker exec -it xpeditis-backend sh
|
||||
cd /app && npm run migration:run
|
||||
```
|
||||
|
||||
Si blocage : vérifier que PostgreSQL est accessible (`DATABASE_HOST` = nom du service Docker).
|
||||
|
||||
### Registry pull échoue (401)
|
||||
|
||||
1. Vérifier le token Scaleway (peut expirer)
|
||||
2. Portainer → Registries → rafraîchir les credentials
|
||||
3. `docker login rg.fr-par.scw.cloud/weworkstudio`
|
||||
|
||||
### ARM64 / multi-architecture
|
||||
|
||||
Les Dockerfiles sont compatibles AMD64 et ARM64. Si le build échoue sur ARM64 :
|
||||
- Vérifier que `buildx` est installé et activé
|
||||
- Utiliser le builder multi-platform : `docker buildx create --use`
|
||||
|
||||
---
|
||||
|
||||
## CI/CD GitHub Actions
|
||||
|
||||
Le workflow `.github/workflows/ci.yml` exécute en automatique :
|
||||
1. Lint + type-check
|
||||
2. Tests unitaires (backend + frontend)
|
||||
3. Build des images Docker
|
||||
4. Push vers registry Scaleway (sur merge vers `main` ou `preprod`)
|
||||
|
||||
Variables GitHub Actions requises (Settings → Secrets) :
|
||||
```
|
||||
SCALEWAY_REGISTRY_TOKEN
|
||||
PORTAINER_URL
|
||||
PORTAINER_API_KEY
|
||||
PORTAINER_STACK_ID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Coûts d'infrastructure estimés
|
||||
|
||||
| Option | Coût/mois | Notes |
|
||||
|--------|-----------|-------|
|
||||
| Hetzner CAX21 (ARM64) | ~8€ | Recommandé pour preprod |
|
||||
| Hetzner CX31 (AMD64) | ~12€ | Production |
|
||||
| Scaleway Registry | Gratuit | Jusqu'à 1GB |
|
||||
|
||||
Voir [../deployment/hetzner/README.md](hetzner/README.md) pour le déploiement Kubernetes sur Hetzner.
|
||||
71
docs/features/api-access.md
Normal file
71
docs/features/api-access.md
Normal file
@ -0,0 +1,71 @@
|
||||
# Accès API via clés API
|
||||
|
||||
---
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
En plus du JWT, Xpeditis supporte l'authentification par **clé API** pour les intégrations tierces.
|
||||
|
||||
---
|
||||
|
||||
## Format
|
||||
|
||||
```
|
||||
Header: x-api-key: xped_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
Le préfixe `xped_` est toujours visible. Le reste est une chaîne aléatoire sécurisée.
|
||||
La clé complète est hashée avec Argon2 — impossible de la retrouver après création.
|
||||
|
||||
---
|
||||
|
||||
## Gestion des clés
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | /api/v1/api-keys | Liste les clés de l'organisation |
|
||||
| POST | /api/v1/api-keys | Créer une nouvelle clé |
|
||||
| PATCH | /api/v1/api-keys/:id | Renommer / désactiver |
|
||||
| DELETE | /api/v1/api-keys/:id | Supprimer |
|
||||
|
||||
### Créer une clé
|
||||
|
||||
```
|
||||
POST /api/v1/api-keys
|
||||
Authorization: Bearer <jwt>
|
||||
|
||||
{ "name": "Integration ERP" }
|
||||
```
|
||||
|
||||
Réponse (unique — la clé complète n'est retournée qu'une seule fois) :
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"name": "Integration ERP",
|
||||
"key": "xped_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"keyPrefix": "xped_xxxx",
|
||||
"createdAt": "2026-05-13T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Permissions
|
||||
|
||||
Les clés API héritent des permissions de l'organisation. Elles ne peuvent pas avoir plus de droits que le compte utilisateur qui les a créées.
|
||||
|
||||
---
|
||||
|
||||
## Sécurité
|
||||
|
||||
- Stockage : hash Argon2 uniquement (pas de récupération possible)
|
||||
- Expiration : configurable (`expires_at`, null = pas d'expiration)
|
||||
- Révocation immédiate : `DELETE /api/v1/api-keys/:id`
|
||||
- Suivi : `last_used_at` mis à jour à chaque utilisation
|
||||
|
||||
---
|
||||
|
||||
## Endpoints supportant les clés API
|
||||
|
||||
Tous les endpoints protégés acceptent soit un JWT soit une clé API.
|
||||
Voir `apps/backend/src/application/guards/api-key-or-jwt.guard.ts` pour l'implémentation.
|
||||
140
docs/features/auth.md
Normal file
140
docs/features/auth.md
Normal file
@ -0,0 +1,140 @@
|
||||
# Authentification & Autorisation
|
||||
|
||||
---
|
||||
|
||||
## Mécanismes d'authentification
|
||||
|
||||
### 1. JWT (principal)
|
||||
|
||||
- **Access token** : 15 minutes, signé avec `JWT_SECRET`
|
||||
- **Refresh token** : 7 jours, rotation automatique
|
||||
- **Stockage frontend** : localStorage + cookie `accessToken` (pour le middleware Next.js)
|
||||
|
||||
#### Flow login
|
||||
|
||||
```
|
||||
POST /api/v1/auth/login
|
||||
{ email, password }
|
||||
→ Vérification hash Argon2
|
||||
→ Brute-force check (3 échecs → backoff exponentiel)
|
||||
→ Retourne { accessToken, refreshToken, user }
|
||||
```
|
||||
|
||||
#### Refresh
|
||||
|
||||
```
|
||||
POST /api/v1/auth/refresh
|
||||
{ refreshToken }
|
||||
→ Nouveau accessToken + refreshToken (rotation)
|
||||
```
|
||||
|
||||
### 2. OAuth2
|
||||
|
||||
- **Google** : `GET /api/v1/auth/google` → callback `GOOGLE_CALLBACK_URL`
|
||||
- **Microsoft** : `GET /api/v1/auth/microsoft` → callback `MICROSOFT_CALLBACK_URL`
|
||||
|
||||
Configuration dans `.env` :
|
||||
```bash
|
||||
GOOGLE_CLIENT_ID=...
|
||||
GOOGLE_CLIENT_SECRET=...
|
||||
MICROSOFT_CLIENT_ID=...
|
||||
MICROSOFT_CLIENT_SECRET=...
|
||||
```
|
||||
|
||||
### 3. API Keys
|
||||
|
||||
Authentification alternative au JWT pour intégrations tierces.
|
||||
|
||||
```
|
||||
Header: x-api-key: xped_xxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
Gestion via `GET/POST /api/v1/api-keys`. Les clés sont hashées avec Argon2 — seul le préfixe est stocké en clair.
|
||||
|
||||
---
|
||||
|
||||
## RBAC — Rôles et permissions
|
||||
|
||||
| Rôle | Description | Accès |
|
||||
|------|-------------|-------|
|
||||
| ADMIN | Administrateur de l'organisation | Tout |
|
||||
| MANAGER | Gestionnaire | Bookings, dashboard, utilisateurs (no admin) |
|
||||
| USER | Utilisateur standard | Recherche, création bookings |
|
||||
| VIEWER | Lecture seule | Dashboard, liste bookings |
|
||||
| CARRIER | Portail carrier | Accepter/rejeter CSV bookings assignés |
|
||||
|
||||
### Décorateurs
|
||||
|
||||
```typescript
|
||||
@Roles('ADMIN', 'MANAGER') // Restreindre à des rôles
|
||||
@Public() // Route publique (pas de JWT requis)
|
||||
@CurrentUser() // Injecter l'utilisateur courant
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vérification email
|
||||
|
||||
```
|
||||
POST /api/v1/auth/register
|
||||
→ Email de vérification envoyé (token 24h)
|
||||
POST /api/v1/auth/verify-email?token=xxx
|
||||
→ Marque l'email comme vérifié
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Réinitialisation de mot de passe
|
||||
|
||||
```
|
||||
POST /api/v1/auth/forgot-password { email }
|
||||
→ Email avec token (1h, table password_reset_tokens)
|
||||
POST /api/v1/auth/reset-password { token, newPassword }
|
||||
→ Nouveau mot de passe hashé Argon2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Invitations
|
||||
|
||||
Les admins peuvent inviter des utilisateurs dans leur organisation :
|
||||
|
||||
```
|
||||
POST /api/v1/invitations { email, role }
|
||||
→ Email d'invitation avec token (24h, table invitation_tokens)
|
||||
GET /api/v1/invitations/accept?token=xxx
|
||||
→ Crée l'utilisateur avec le rôle assigné
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Protection des routes (middleware Next.js)
|
||||
|
||||
`apps/frontend/middleware.ts` vérifie le cookie `accessToken` avant chaque requête.
|
||||
|
||||
**Routes publiques** (pas de redirection) :
|
||||
- Exactes : `/`, pages légales
|
||||
- Par préfixe : `/login`, `/register`, `/carrier`, `/about`, `/blog`, `/pricing`, etc.
|
||||
|
||||
Toutes les autres routes redirigent vers `/login?redirect=<pathname>`.
|
||||
|
||||
---
|
||||
|
||||
## Variables d'environnement
|
||||
|
||||
```bash
|
||||
JWT_SECRET=your-super-secret-jwt-key
|
||||
JWT_ACCESS_EXPIRATION=15m
|
||||
JWT_REFRESH_EXPIRATION=7d
|
||||
|
||||
GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
GOOGLE_CALLBACK_URL=http://localhost:4000/api/v1/auth/google/callback
|
||||
|
||||
MICROSOFT_CLIENT_ID=
|
||||
MICROSOFT_CLIENT_SECRET=
|
||||
MICROSOFT_CALLBACK_URL=http://localhost:4000/api/v1/auth/microsoft/callback
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
SESSION_TIMEOUT_MS=7200000
|
||||
```
|
||||
109
docs/features/bookings.md
Normal file
109
docs/features/bookings.md
Normal file
@ -0,0 +1,109 @@
|
||||
# Réservations standard
|
||||
|
||||
---
|
||||
|
||||
## Workflow de réservation (4 étapes max)
|
||||
|
||||
```
|
||||
1. Sélection de l'offre (vérification dispo temps réel)
|
||||
2. Informations expéditeur / destinataire
|
||||
3. Détails conteneur
|
||||
4. Confirmation + CGU → Génération booking number
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Numéro de réservation
|
||||
|
||||
Format : `WCM-YYYY-XXXXXX` (XXXXXX = 6 caractères alphanumériques uniques)
|
||||
|
||||
Exemple : `WCM-2026-A3F7K2`
|
||||
|
||||
---
|
||||
|
||||
## Statuts
|
||||
|
||||
| Statut | Description |
|
||||
|--------|-------------|
|
||||
| draft | Brouillon (en cours de création) |
|
||||
| confirmed | Confirmé |
|
||||
| shipped | En transit |
|
||||
| delivered | Livré |
|
||||
| cancelled | Annulé |
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | /api/v1/bookings | Liste (paginée, filtrable) |
|
||||
| POST | /api/v1/bookings | Créer une réservation |
|
||||
| GET | /api/v1/bookings/:id | Détail |
|
||||
| PATCH | /api/v1/bookings/:id | Mettre à jour |
|
||||
| DELETE | /api/v1/bookings/:id | Annuler |
|
||||
| GET | /api/v1/bookings/:id/pdf | Générer PDF |
|
||||
| POST | /api/v1/bookings/export | Export Excel/PDF |
|
||||
|
||||
### Exemple création
|
||||
|
||||
```
|
||||
POST /api/v1/bookings
|
||||
Authorization: Bearer <token>
|
||||
|
||||
{
|
||||
"rateQuoteId": "uuid-de-la-cotation",
|
||||
"shipper": {
|
||||
"name": "Acme Corp",
|
||||
"address": "123 Main St",
|
||||
"country": "FR"
|
||||
},
|
||||
"consignee": {
|
||||
"name": "Shanghai Trading",
|
||||
"address": "456 Nanjing Rd",
|
||||
"country": "CN"
|
||||
},
|
||||
"containers": [
|
||||
{
|
||||
"type": "40HC",
|
||||
"cargoDescription": "Electronic components",
|
||||
"isHazmat": false
|
||||
}
|
||||
],
|
||||
"notes": "Fragile cargo"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Post-booking automatique
|
||||
|
||||
Après création d'une réservation :
|
||||
|
||||
1. **Audit log** — action `BOOKING_CREATED`
|
||||
2. **Notification WebSocket** — push en temps réel
|
||||
3. **Webhooks** — événement `BOOKING_CREATED` envoyé aux abonnés
|
||||
4. **Email confirmation** — template MJML
|
||||
5. **PDF** — généré via pdfkit, stocké sur MinIO
|
||||
|
||||
---
|
||||
|
||||
## Filtres disponibles
|
||||
|
||||
```
|
||||
GET /api/v1/bookings?status=confirmed&carrierId=xxx&page=1&limit=20
|
||||
&startDate=2026-01-01&endDate=2026-12-31
|
||||
&search=WCM-2026 (recherche floue sur numéro, expéditeur, destinataire)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dashboard
|
||||
|
||||
Le dashboard agrège :
|
||||
- KPI cards : bookings/mois, TEUs, montant total
|
||||
- Graphiques 6 mois (recharts)
|
||||
- Alertes (retards, confirmations en attente)
|
||||
- Table interactive avec TanStack Table (tri, filtres, pagination, virtual scroll)
|
||||
|
||||
Endpoint : `GET /api/v1/dashboard/stats`
|
||||
116
docs/features/csv-bookings.md
Normal file
116
docs/features/csv-bookings.md
Normal file
@ -0,0 +1,116 @@
|
||||
# Réservations CSV & Portail Carrier
|
||||
|
||||
---
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le système de réservation CSV permet aux admins de créer des réservations en important des fichiers CSV et d'envoyer automatiquement un **lien magique** aux transporteurs pour qu'ils acceptent ou rejettent via un portail dédié.
|
||||
|
||||
---
|
||||
|
||||
## Workflow complet
|
||||
|
||||
```
|
||||
1. Admin upload CSV → création csv_bookings
|
||||
2. Admin assigne un carrier à la réservation
|
||||
3. Système envoie email avec magic link (expiry 1h)
|
||||
4. Carrier clique → authentification automatique (token URL)
|
||||
5. Carrier voit les détails → Accept ou Reject
|
||||
6. Activité logguée dans carrier_activities
|
||||
7. Admin voit le statut mis à jour en dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API — Réservations CSV (Admin)
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | /api/v1/csv-bookings | Liste des réservations CSV |
|
||||
| POST | /api/v1/csv-bookings | Créer une réservation CSV |
|
||||
| GET | /api/v1/csv-bookings/:id | Détail d'une réservation |
|
||||
| PATCH | /api/v1/csv-bookings/:id | Mettre à jour |
|
||||
| DELETE | /api/v1/csv-bookings/:id | Supprimer |
|
||||
| POST | /api/v1/csv-bookings/:id/assign-carrier | Assigner un carrier |
|
||||
| POST | /api/v1/csv-bookings/:id/send-magic-link | Envoyer le lien magique |
|
||||
|
||||
---
|
||||
|
||||
## API — Portail Carrier
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | /api/v1/carrier/auth | Auth via magic link token |
|
||||
| GET | /api/v1/carrier/booking | Voir la réservation assignée |
|
||||
| POST | /api/v1/carrier/booking/accept | Accepter |
|
||||
| POST | /api/v1/carrier/booking/reject | Rejeter |
|
||||
| POST | /api/v1/carrier/booking/documents | Uploader des documents |
|
||||
|
||||
### Authentification portail carrier
|
||||
|
||||
Le lien magique contient un token unique :
|
||||
```
|
||||
https://app.xpeditis.com/carrier/auth?token=xxxxxxxx
|
||||
```
|
||||
|
||||
Le token est stocké dans `csv_bookings.carrier_magic_link_token` (hashé).
|
||||
Expiry : 1 heure. Si expiré, l'admin doit renvoyer un nouveau lien.
|
||||
|
||||
---
|
||||
|
||||
## Statuts des réservations CSV
|
||||
|
||||
| Statut | Description |
|
||||
|--------|-------------|
|
||||
| pending | En attente d'assignation carrier |
|
||||
| sent | Lien magique envoyé au carrier |
|
||||
| accepted | Carrier a accepté |
|
||||
| rejected | Carrier a refusé |
|
||||
| in_transit | En cours de transport |
|
||||
| delivered | Livré |
|
||||
|
||||
---
|
||||
|
||||
## Pages frontend
|
||||
|
||||
| Route | Description |
|
||||
|-------|-------------|
|
||||
| /dashboard/csv-bookings | Liste admin des réservations CSV |
|
||||
| /carrier/auth | Page d'auth carrier (via magic link) |
|
||||
| /carrier/booking | Dashboard carrier (accept/reject) |
|
||||
| /carrier/documents | Upload documents carrier |
|
||||
|
||||
---
|
||||
|
||||
## Import CSV (upload admin)
|
||||
|
||||
Le fichier CSV peut être uploadé via `POST /api/v1/admin/csv-rates/upload`.
|
||||
|
||||
Format des colonnes requis : voir [../csv-system/CSV_RATE_SYSTEM.md](../csv-system/CSV_RATE_SYSTEM.md).
|
||||
|
||||
---
|
||||
|
||||
## Profils carrier
|
||||
|
||||
Les carriers qui utilisent le portail ont un `CarrierProfile` lié à leur `Organization`.
|
||||
Chaque action (accept, reject, document upload) est tracée dans `carrier_activities`.
|
||||
|
||||
```sql
|
||||
SELECT ca.action, ca.created_at, cb.booking_number
|
||||
FROM carrier_activities ca
|
||||
JOIN csv_bookings cb ON cb.id = ca.csv_booking_id
|
||||
WHERE ca.carrier_profile_id = 'xxx'
|
||||
ORDER BY ca.created_at DESC;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Email magic link
|
||||
|
||||
Template MJML dans `apps/backend/src/infrastructure/email/templates/`.
|
||||
|
||||
Variables disponibles :
|
||||
- `bookingNumber` — numéro de réservation
|
||||
- `magicLink` — URL avec token
|
||||
- `expiresIn` — durée de validité ("1 heure")
|
||||
- `carrierName` — nom du carrier
|
||||
112
docs/features/notifications.md
Normal file
112
docs/features/notifications.md
Normal file
@ -0,0 +1,112 @@
|
||||
# Notifications temps réel & Webhooks
|
||||
|
||||
---
|
||||
|
||||
## Notifications WebSocket
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
Événement serveur → NotificationService → Création en base (notifications)
|
||||
↓
|
||||
NotificationsGateway (Socket.IO)
|
||||
↓
|
||||
Émission vers la room userId
|
||||
↓
|
||||
Client reçoit l'événement 'notification'
|
||||
```
|
||||
|
||||
### Connexion depuis le frontend
|
||||
|
||||
Le hook `useNotifications` gère la connexion Socket.IO :
|
||||
|
||||
```typescript
|
||||
// Connexion avec JWT dans le handshake
|
||||
const socket = io(API_URL, {
|
||||
auth: { token: accessToken }
|
||||
});
|
||||
|
||||
socket.on('notification', (notification) => { /* ... */ });
|
||||
```
|
||||
|
||||
### Types de notifications
|
||||
|
||||
| Type | Déclencheur |
|
||||
|------|-------------|
|
||||
| BOOKING_CREATED | Nouvelle réservation |
|
||||
| BOOKING_STATUS_CHANGED | Changement de statut |
|
||||
| DOCUMENT_UPLOADED | Document uploadé |
|
||||
| CARRIER_ACCEPTED | Carrier a accepté une CSV booking |
|
||||
| CARRIER_REJECTED | Carrier a refusé |
|
||||
| PAYMENT_SUCCESS | Paiement Stripe réussi |
|
||||
| PAYMENT_FAILED | Paiement Stripe échoué |
|
||||
| SUBSCRIPTION_UPDATED | Abonnement modifié |
|
||||
| SYSTEM | Notification système |
|
||||
|
||||
### Niveaux de priorité
|
||||
|
||||
`LOW` · `MEDIUM` · `HIGH` · `URGENT`
|
||||
|
||||
### API notifications
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | /api/v1/notifications | Liste (paginée, filtrable) |
|
||||
| PATCH | /api/v1/notifications/:id/read | Marquer comme lu |
|
||||
| PATCH | /api/v1/notifications/read-all | Tout marquer comme lu |
|
||||
| DELETE | /api/v1/notifications/:id | Supprimer |
|
||||
|
||||
---
|
||||
|
||||
## Webhooks
|
||||
|
||||
### Pour qui
|
||||
|
||||
Les webhooks permettent à des systèmes tiers de recevoir des événements Xpeditis en temps réel.
|
||||
|
||||
### Configuration
|
||||
|
||||
```
|
||||
POST /api/v1/webhooks
|
||||
{
|
||||
"url": "https://your-system.com/webhook",
|
||||
"events": ["BOOKING_CREATED", "BOOKING_UPDATED"],
|
||||
"secret": "your-signing-secret"
|
||||
}
|
||||
```
|
||||
|
||||
### Sécurité
|
||||
|
||||
Chaque webhook est signé HMAC-SHA256 :
|
||||
```
|
||||
X-Webhook-Signature: sha256=xxxxxxxx
|
||||
```
|
||||
|
||||
Vérification côté récepteur :
|
||||
```typescript
|
||||
const signature = crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(rawBody)
|
||||
.digest('hex');
|
||||
const isValid = `sha256=${signature}` === headers['x-webhook-signature'];
|
||||
```
|
||||
|
||||
### Retry
|
||||
|
||||
- 3 tentatives avec backoff exponentiel
|
||||
- Après 3 échecs → statut `FAILED`, webhook désactivé
|
||||
- `failure_count` incrémenté à chaque échec
|
||||
|
||||
### Événements disponibles
|
||||
|
||||
`BOOKING_CREATED` · `BOOKING_UPDATED` · `BOOKING_CANCELLED` · `RATE_QUOTED` · `CARRIER_ACCEPTED` · `CARRIER_REJECTED` · `PAYMENT_SUCCESS` · `PAYMENT_FAILED`
|
||||
|
||||
### API webhooks
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | /api/v1/webhooks | Liste des webhooks |
|
||||
| POST | /api/v1/webhooks | Créer un webhook |
|
||||
| PATCH | /api/v1/webhooks/:id | Modifier |
|
||||
| DELETE | /api/v1/webhooks/:id | Supprimer |
|
||||
| POST | /api/v1/webhooks/:id/test | Tester le webhook |
|
||||
143
docs/features/rate-search.md
Normal file
143
docs/features/rate-search.md
Normal file
@ -0,0 +1,143 @@
|
||||
# Recherche de tarifs
|
||||
|
||||
---
|
||||
|
||||
## Deux types de recherche
|
||||
|
||||
| Type | Description | Endpoint |
|
||||
|------|-------------|----------|
|
||||
| FCL (Full Container Load) | Tarifs via connecteurs carriers (Maersk, MSC, etc.) | POST /api/v1/rates/search |
|
||||
| CSV / LCL | Tarifs chargés depuis des fichiers CSV | POST /api/v1/rates/csv-search |
|
||||
|
||||
---
|
||||
|
||||
## Recherche FCL — Carriers API
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
POST /api/v1/rates/search
|
||||
Authorization: Bearer <token>
|
||||
|
||||
{
|
||||
"origin": "NLRTM",
|
||||
"destination": "CNSHA",
|
||||
"containerType": "40HC",
|
||||
"mode": "FCL",
|
||||
"departureDate": "2026-06-01",
|
||||
"hazmat": false
|
||||
}
|
||||
```
|
||||
|
||||
### Réponse
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"carrier": { "name": "Maersk", "code": "MAERSK", "logoUrl": "..." },
|
||||
"origin": { "code": "NLRTM", "name": "Rotterdam" },
|
||||
"destination": { "code": "CNSHA", "name": "Shanghai" },
|
||||
"baseFreight": 1850.00,
|
||||
"surcharges": [
|
||||
{ "type": "BAF", "amount": 250.00, "currency": "USD" }
|
||||
],
|
||||
"totalAmount": 2100.00,
|
||||
"currency": "USD",
|
||||
"transitDays": 28,
|
||||
"etd": "2026-06-01T00:00:00Z",
|
||||
"eta": "2026-06-29T00:00:00Z",
|
||||
"availability": 50,
|
||||
"validUntil": "2026-05-13T10:15:00Z"
|
||||
}
|
||||
],
|
||||
"total": 5,
|
||||
"page": 1,
|
||||
"pageSize": 20
|
||||
}
|
||||
```
|
||||
|
||||
### Cache Redis
|
||||
|
||||
- Clé : `rate:{origin}:{destination}:{containerType}`
|
||||
- TTL : 15 minutes
|
||||
- Les cotations expirent automatiquement après 15min (`valid_until`)
|
||||
|
||||
### Carriers intégrés
|
||||
|
||||
| Carrier | Code | SCAC |
|
||||
|---------|------|------|
|
||||
| Maersk | MAERSK | MAEU |
|
||||
| MSC | MSC | MSCU |
|
||||
| CMA CGM | CMACGM | CMDU |
|
||||
| Hapag-Lloyd | HAPAG | HLCU |
|
||||
| ONE | ONE | ONEY |
|
||||
|
||||
Chaque carrier a son connecteur dans `apps/backend/src/infrastructure/carriers/`.
|
||||
Circuit breaker via `opossum` : timeout 5s, bascule sur fallback si indisponible.
|
||||
|
||||
---
|
||||
|
||||
## Recherche CSV
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
POST /api/v1/rates/csv-search
|
||||
Authorization: Bearer <token>
|
||||
|
||||
{
|
||||
"origin": "NLRTM",
|
||||
"destination": "USNYC",
|
||||
"volumeCBM": 25.5,
|
||||
"weightKG": 3500,
|
||||
"palletCount": 10,
|
||||
"filters": {
|
||||
"companies": ["SSC Consolidation"],
|
||||
"minPrice": 1000,
|
||||
"maxPrice": 3000,
|
||||
"currency": "USD"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Carriers CSV disponibles
|
||||
|
||||
| Company | Type | API |
|
||||
|---------|------|-----|
|
||||
| SSC Consolidation | CSV_ONLY | Non |
|
||||
| ECU Worldwide | CSV_AND_API | Oui |
|
||||
| TCC Logistics | CSV_ONLY | Non |
|
||||
| NVO Consolidation | CSV_ONLY | Non |
|
||||
|
||||
Les fichiers CSV sont dans `apps/backend/src/infrastructure/storage/csv-storage/rates/`.
|
||||
|
||||
### Calcul de prix CSV
|
||||
|
||||
```typescript
|
||||
// Freight class : prendre le max de volume-based ou weight-based
|
||||
const volumePrice = volumeCBM * pricePerCBM;
|
||||
const weightPrice = weightKG * pricePerKG;
|
||||
const freightPrice = Math.max(volumePrice, weightPrice);
|
||||
const totalPrice = freightPrice + surchargeBAF + surchargeCAF;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recherche de ports
|
||||
|
||||
Autocomplete avec recherche floue (pg_trgm) :
|
||||
|
||||
```
|
||||
GET /api/v1/ports/search?q=rotterdam&limit=10
|
||||
```
|
||||
|
||||
~10 000 ports UN/LOCODE seedés en base.
|
||||
|
||||
---
|
||||
|
||||
## Export des résultats
|
||||
|
||||
Les résultats peuvent être exportés en PDF ou Excel depuis le frontend.
|
||||
Voir `apps/frontend/src/utils/` pour les utilitaires d'export.
|
||||
110
docs/features/subscriptions.md
Normal file
110
docs/features/subscriptions.md
Normal file
@ -0,0 +1,110 @@
|
||||
# Abonnements & Paiements (Stripe)
|
||||
|
||||
---
|
||||
|
||||
## Plans disponibles
|
||||
|
||||
| Plan | Description |
|
||||
|------|-------------|
|
||||
| FREE | Accès limité (après inscription) |
|
||||
| BRONZE | Entrée de gamme |
|
||||
| SILVER | Standard |
|
||||
| GOLD | Avancé |
|
||||
| PLATINIUM | Entreprise |
|
||||
|
||||
Les plans BRONZE/SILVER/GOLD/PLATINIUM sont disponibles en facturation **mensuelle** ou **annuelle**.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
Le système utilise Stripe pour la gestion des paiements. Chaque organisation a exactement un enregistrement `subscriptions`.
|
||||
|
||||
```
|
||||
Organization 1──1 Subscription
|
||||
└── stripe_customer_id (Stripe Customer)
|
||||
└── stripe_subscription_id (Stripe Subscription)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | /api/v1/subscriptions/current | Abonnement courant de l'org |
|
||||
| POST | /api/v1/subscriptions/create-checkout | Créer session Stripe Checkout |
|
||||
| POST | /api/v1/subscriptions/cancel | Annuler l'abonnement |
|
||||
| POST | /api/v1/subscriptions/webhook | Webhook Stripe (signature HMAC) |
|
||||
| GET | /api/v1/subscriptions/plans | Liste des plans disponibles |
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# .env backend
|
||||
STRIPE_SECRET_KEY=sk_test_xxxx
|
||||
STRIPE_WEBHOOK_SECRET=whsec_xxxx
|
||||
|
||||
# Price IDs depuis le Dashboard Stripe
|
||||
STRIPE_SILVER_MONTHLY_PRICE_ID=price_xxxx
|
||||
STRIPE_SILVER_YEARLY_PRICE_ID=price_xxxx
|
||||
STRIPE_GOLD_MONTHLY_PRICE_ID=price_xxxx
|
||||
STRIPE_GOLD_YEARLY_PRICE_ID=price_xxxx
|
||||
STRIPE_PLATINIUM_MONTHLY_PRICE_ID=price_xxxx
|
||||
STRIPE_PLATINIUM_YEARLY_PRICE_ID=price_xxxx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Webhook Stripe
|
||||
|
||||
Le endpoint `/api/v1/subscriptions/webhook` reçoit les événements Stripe.
|
||||
|
||||
**Événements gérés** :
|
||||
- `checkout.session.completed` — activation abonnement
|
||||
- `customer.subscription.updated` — changement de plan
|
||||
- `customer.subscription.deleted` — annulation
|
||||
- `invoice.payment_succeeded` — paiement réussi
|
||||
- `invoice.payment_failed` — paiement échoué → statut `past_due`
|
||||
|
||||
Le secret `STRIPE_WEBHOOK_SECRET` est utilisé pour vérifier la signature de chaque événement.
|
||||
|
||||
### Configurer le webhook en local (test)
|
||||
|
||||
```bash
|
||||
# Installer Stripe CLI
|
||||
stripe listen --forward-to localhost:4000/api/v1/subscriptions/webhook
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Statuts d'abonnement
|
||||
|
||||
| Statut | Description |
|
||||
|--------|-------------|
|
||||
| active | Abonnement en cours, paiement OK |
|
||||
| trialing | Période d'essai |
|
||||
| past_due | Paiement en retard |
|
||||
| cancelled | Annulé |
|
||||
| pending_payment | En attente de paiement CB |
|
||||
| pending_bank_transfer | En attente de virement bancaire |
|
||||
|
||||
---
|
||||
|
||||
## Licenses
|
||||
|
||||
Le système de licenses (`licenses` table) permet d'activer des fonctionnalités spécifiques par organisation, indépendamment du plan Stripe.
|
||||
|
||||
---
|
||||
|
||||
## Taux de commission
|
||||
|
||||
La colonne `subscriptions.commission_rate` (DECIMAL 5,4) permet de stocker un taux de commission Xpeditis par organisation (pour tracking interne).
|
||||
|
||||
---
|
||||
|
||||
## Guide de configuration initial
|
||||
|
||||
Voir [../deployment/STRIPE_SETUP.md](../deployment/STRIPE_SETUP.md) pour la configuration complète de Stripe (création des products/prices, webhooks, etc.).
|
||||
160
docs/getting-started/quick-start.md
Normal file
160
docs/getting-started/quick-start.md
Normal file
@ -0,0 +1,160 @@
|
||||
# Démarrage rapide — Xpeditis
|
||||
|
||||
Guide de démarrage en 5 minutes.
|
||||
|
||||
---
|
||||
|
||||
## Prérequis
|
||||
|
||||
- **Node.js** ≥ 20.x
|
||||
- **npm** ≥ 10.x
|
||||
- **Docker** + Docker Compose (pour PostgreSQL, Redis, MinIO)
|
||||
|
||||
---
|
||||
|
||||
## 1. Installer les dépendances
|
||||
|
||||
```bash
|
||||
npm run install:all
|
||||
```
|
||||
|
||||
> Sur Windows si l'installation échoue, voir [windows.md](windows.md).
|
||||
|
||||
---
|
||||
|
||||
## 2. Démarrer l'infrastructure
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Vérifier que tout est actif :
|
||||
|
||||
```bash
|
||||
docker-compose ps
|
||||
# postgres Up (healthy)
|
||||
# redis Up (healthy)
|
||||
# minio Up (healthy)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurer l'environnement
|
||||
|
||||
```bash
|
||||
cp apps/backend/.env.example apps/backend/.env
|
||||
cp apps/frontend/.env.example apps/frontend/.env.local
|
||||
```
|
||||
|
||||
Les valeurs par défaut fonctionnent pour le développement local — aucune modification nécessaire.
|
||||
|
||||
---
|
||||
|
||||
## 4. Exécuter les migrations
|
||||
|
||||
```bash
|
||||
cd apps/backend && npm run migration:run && cd ../..
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Démarrer les serveurs
|
||||
|
||||
```bash
|
||||
# Terminal 1
|
||||
npm run backend:dev
|
||||
# → http://localhost:4000/api/v1
|
||||
# → Swagger: http://localhost:4000/api/docs
|
||||
|
||||
# Terminal 2
|
||||
npm run frontend:dev
|
||||
# → http://localhost:3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vérification
|
||||
|
||||
| URL | Attendu |
|
||||
|-----|---------|
|
||||
| http://localhost:4000/api/v1/health | `{"status":"ok"}` |
|
||||
| http://localhost:4000/api/docs | Swagger UI |
|
||||
| http://localhost:3000 | Page d'accueil Xpeditis |
|
||||
|
||||
---
|
||||
|
||||
## Commandes de référence
|
||||
|
||||
```bash
|
||||
# Développement
|
||||
npm run backend:dev # Backend avec hot-reload
|
||||
npm run frontend:dev # Frontend avec hot-reload
|
||||
|
||||
# Tests
|
||||
npm run backend:test # Tests unitaires backend
|
||||
npm run frontend:test # Tests unitaires frontend
|
||||
cd apps/backend && npm run test:integration # Tests d'intégration
|
||||
cd apps/frontend && npm run test:e2e # Tests E2E Playwright
|
||||
|
||||
# Qualité de code
|
||||
npm run backend:lint # ESLint backend
|
||||
npm run frontend:lint # ESLint frontend
|
||||
npm run format # Prettier (tous les fichiers)
|
||||
npm run format:check # Vérifier le formatage
|
||||
|
||||
# Base de données
|
||||
cd apps/backend
|
||||
npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/NomMigration
|
||||
npm run migration:run
|
||||
npm run migration:revert
|
||||
|
||||
# Build
|
||||
npm run backend:build # Build NestJS
|
||||
npm run frontend:build # Build Next.js
|
||||
npm run clean # Supprimer node_modules, dist, .next
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Infrastructure locale (Docker)
|
||||
|
||||
| Service | URL / Port | Credentials |
|
||||
|---------|-----------|-------------|
|
||||
| PostgreSQL | localhost:5432 | xpeditis / xpeditis_dev_password |
|
||||
| Redis | localhost:6379 | password: xpeditis_redis_password |
|
||||
| MinIO API | http://localhost:9000 | minioadmin / minioadmin |
|
||||
| MinIO Console | http://localhost:9001 | minioadmin / minioadmin |
|
||||
|
||||
---
|
||||
|
||||
## Comptes de test (après migration)
|
||||
|
||||
Les migrations seed créent automatiquement des comptes de test. Voir la migration `1730000000007-SeedTestUsers.ts` pour les credentials exacts.
|
||||
|
||||
---
|
||||
|
||||
## Résolution de problèmes courants
|
||||
|
||||
**Backend ne démarre pas :**
|
||||
```bash
|
||||
cd apps/backend && rm -rf node_modules && npm install && npm run dev
|
||||
```
|
||||
|
||||
**Erreur de migration :**
|
||||
```bash
|
||||
cd apps/backend
|
||||
npm run migration:revert # Annuler la dernière migration
|
||||
npm run migration:run # Relancer
|
||||
```
|
||||
|
||||
**Port déjà utilisé :**
|
||||
```bash
|
||||
# Modifier PORT dans apps/backend/.env
|
||||
PORT=4001
|
||||
```
|
||||
|
||||
**Docker ne démarre pas :**
|
||||
```bash
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
Loading…
Reference in New Issue
Block a user