ft_transcendance-42

This project has been created as part of the 42 curriculum by iouhssei, mel-rhay, aessadik,achahbal, aait-bou

Rihla — Student Travel Platform for Morocco

Description:

Rihla is a community-driven travel platform built for students exploring Morocco. It combines location-based discovery, social interaction, and AI-powered support to make travel easier, safer, and more social.

MVP Features

Area Description
Authentication Email register/login, logout, Google + 42 OAuth, cookie JWT + CSRF, change password (local accounts), link providers in Settings
Users & profiles Profiles (username, avatar, bio), edit profile, change-password flow
File uploads Avatars via profiles service
Location & places Google Places discovery, city & saved places, planner integration
Activity-based discovery Places categorized by activity type
Ratings & reviews Community reviews + aggregates (review-places)
Real-time friends-service: REST + WebSockets (chat / notifications; polish ongoing)
AI Trip planner (Gemini) + places suggestions (places-service)

Technical stack

Frontend technologies and frameworks

Backend technologies and frameworks

Database system and why it was chosen

Other significant technologies and libraries

Justification for major technical choices

Features list

Public and marketing

Authentication and account security

Profile and preferences

Places, discovery, and content

Trip planning (AI)

Social and realtime

Experience and resilience

Instructions

Prerequisites

Configuration

1. Repository root .env

docker-compose.yml loads ./.env at the repo root for multiple services (including frontend). Create it (not committed) before starting the stack. At minimum, define ports and shared settings your deployment expects, for example:

# Ports
PORT_FRONT=5173
PORT_POSTGRES=5432
PORT_VAULT=8200

# CORS / redirects
FRONTEND_URL=http://localhost:5173

# JWT (shared across backend services)
JWT_ACCESS_SECRET=change_me
JWT_ACCESS_EXPIRES_IN=15m

# Postgres — Prisma / services (use a URL that matches your setup; Docker services usually reach Postgres on the compose network)
DATABASE_URL=postgresql://postgres:password@database:5432/rihla

# External APIs (also mirrored for Vault where applicable)
GEMINI_API_KEY=your_gemini_api_key
GOOGLE_PLACES_API_KEY=your_google_places_api_key

# Redis
REDIS_URL=redis://redis:6379

2. Vault secret files

The vault service mounts Docker secrets: from backend/devops/secrets/. Create these files (typically one line each, gitignored) before the first full start:

Run the project

  1. Clone the repository and cd into the project root.
  2. Add ./.env and populate backend/devops/secrets/ as above.
  3. Ensure Docker is running.
  4. From the project root, build and start the full stack:
    make all
    

    This runs docker compose -f ./docker-compose.yml build … then up -d (see Makefile targets below). The first run can take several minutes.

  5. Wait until healthchecks settle (especially Vault, database, then app services).
  6. Open the app at https://localhost (port 443). The gateway uses a self-signed certificate; your browser will warn you—continue for local development.
  7. Logs: make logs or docker compose -f ./docker-compose.yml logs -f [service].
  8. Stop: make stop. Tear down and remove volumes/images: make clean or make fclean (destructive; see Makefile).

Hostnames behind the gateway (HTTPS on port 443): the ModSecurity/nginx config uses names such as auth.localhost, netdata.localhost, and logbull.localhost for some tools; the default server handles localhost for the main UI and API paths.

Makefile targets

From the repo root, with DOCKER=docker and COMPOSE=./docker-compose.yml:

Target What it does
make all Full docker compose build (--no-cache, --parallel, --force-rm, --pull) then up -d --remove-orphans.
make front Build and start only the frontend service.
make logs docker compose … logs -f (all services), or logs -f $1 if shell $1 is set.
make start / make stop docker compose … start / stop.
make clean Stop stack, down --remove-orphans --rmi all -v, then prune listed containers, images, volumes, networks.
make fclean clean plus docker system prune -a --volumes -f.
make re fclean then all.

Note: The Makefile defines make backend, but docker-compose.yml has no backend service, so that target will fail until the compose file and Makefile are aligned. Use make all for the full stack.

Team Information

Ilyass Ouhsseine (iouhssei)

Roles: Product Owner, Project Manager, Frontend Developer
Description:
Defined and led the product vision of Rihla, a travel discovery platform built on a microservices architecture (React / Node.js). Managed the full product lifecycle — from ideation and feature prioritization to delivery — while contributing to frontend development. Focused on AI-powered trip planning, personalized recommendations, and seamless user experience.


Amine Essadiki (aessadik)

Roles: Tech Lead, DevOps Engineer
Description:
Led the infrastructure and DevOps strategy, ensuring scalability, reliability, and security across services. Designed and maintained CI/CD pipelines, containerized services (Docker), and managed deployment workflows. Oversaw system architecture decisions and performance optimization.


Mohammed El Rhayour (mel-rhay)

Roles: Tech Lead, Backend Developer
Description:
Architected and developed core backend services using a microservices approach. Designed APIs, handled business logic, and ensured system consistency and performance. Collaborated closely on authentication, data models, and service communication.


Anas Chahlabani (achahbal)

Roles: Mobile Developer
Description:
Developed the mobile application experience for Rihla, focusing on performance, usability, and consistency with the web platform. Integrated APIs and contributed to delivering a smooth cross-platform user experience.


Ali Ait Bouih (aait-bou)

Roles: Backend Developer
Description:
Contributed to backend service development, including API implementation, database interactions, and feature integration. Worked on ensuring reliability and maintainability of services.

Project Management

Team Organization & Task Distribution

The team of five was organized around complementary roles, with each member owning specific domains while collaborating across boundaries:

Member Role Responsibilities
Ilyass Ouhsseine (iouhssei) Product Owner, Project Manager, Frontend Dev Product vision, feature prioritization, sprint planning, AI features, frontend development
Amine Essadiki (aessadik) Tech Lead, DevOps Engineer Infrastructure, CI/CD, Docker orchestration, Nginx gateway, Vault, security, deployment
Mohammed El Rhayour (mel-rhay) Tech Lead, Backend Developer Core backend architecture, API design, auth flows, data models, service communication
Anas Chahlabani (achahbal) Mobile Developer Mobile application, API integration, cross-platform UX consistency
Ali Ait Bouih (aait-bou) Backend Developer Backend service development, API implementation, database interactions, feature integration

Agile Workflow

The team followed an Agile methodology with iterative development cycles:

Project Management Tools

Communication Channels


Tech Stack

Frontend

Tool Version Purpose
React 19.2 Modern UI framework with hooks
Vite 7.2 Lightning-fast build tool and dev server
TypeScript 5.9 Type-safe JavaScript
Tailwind CSS 4.1 Utility-first styling framework
Radix UI 1.4 Unstyled, accessible component primitives
React Router 7.13 Client-side routing with protected routes
Lucide React 0.563 Icon library
GSAP 3.14 Animation library
class-variance-authority + clsx + tailwind-merge Consistent, conditional class composition
ESLint 9 Code quality & consistency

Justification: React 19 + Vite offers the best developer experience for a fast-moving SPA — near-instant HMR, ESM output, and a component model that scales well across many pages. Tailwind + Radix keeps styling consistent and accessible without a heavy component library. GSAP handles richer animations that CSS transitions can’t easily express.

Backend

Tool Version Purpose
NestJS 10 Auth + Profiles microservices (structured modules, DI, guards)
Express 4/5 Lightweight HTTP services (places, planner, reviews, favorites, friends)
TypeScript 5.x Type safety across all services
Prisma 6.x Schema-first ORM with migrations
Passport.js 0.7 OAuth 2.0 & JWT middleware
Argon2 Industry-standard password hashing
ws WebSocket server for real-time chat and notifications
ioredis Redis client for caching and rate limiting
@google/generative-ai Gemini API integration for trip planning

Justification: NestJS is used for the two highest-security services (auth and profiles) where its module system, guards, and dependency injection provide the strongest safety guarantees. Express is used for the remaining, more focused services where a lighter footprint fits better. Separating services this way means a crash or vulnerability in one domain (e.g. AI places) cannot directly bring down authentication.

Database System

PostgreSQL 15+ is the primary relational store, accessed through Prisma in every service that requires persistence.

Why PostgreSQL:

Redis 7+ complements PostgreSQL as a caching and rate-limiting layer, reducing load on the database and external APIs on high-churn routes.

Other Significant Technologies

Technology Purpose
Docker + Docker Compose Reproducible environments and full-stack orchestration
Nginx + ModSecurity TLS-terminating reverse proxy, WAF, and API gateway
HashiCorp Vault Centralized secrets management — credentials never baked into images
Elasticsearch + Kibana Centralized log aggregation and visualization
Netdata Real-time system and container metrics

Justification for Major Technical Choices

Microservices architecture: Each service has independent deployment boundaries and failure domains. A bug in the AI planner cannot take down authentication. It also lets each service use the smallest stack that fits its needs.

Cookie-based JWT + CSRF: More secure than storing tokens in localStorage for a web SPA. The double-submit CSRF pattern protects mutating requests, and HTTP-only cookies prevent XSS exfiltration.

Nginx + ModSecurity as gateway: Traffic inspection, WAF rules, and TLS termination happen in one place, so internal services can communicate over plain HTTP without worrying about encryption and filtering individually.

Vault for secrets: API keys and OAuth credentials are injected at runtime rather than committed to the repo or baked into images, aligning with security-by-design principles.


Database Schema

Persistence is PostgreSQL, accessed through Prisma in each service. The stack uses one database instance; each service ships its own schema.prisma and migrations for the tables it owns. userId values align with User.id from the auth/profiles domain (string CUIDs), so reviews, favorites, chat, and trips all refer to the same identity without cross-schema foreign keys in every service.

Visual Overview (ER Diagram)

erDiagram
  User ||--o{ Account : "1:N"
  User ||--o| Profile : "0..1"
  Conversation ||--o{ ConversationParticipant : "N:M bridge"
  Conversation ||--o{ Message : "1:N"

  User {
    string id PK
    string email UK
    string hashPassword
    boolean isEmailVerified
    datetime createdAt
    datetime updatedAt
  }

  Account {
    string id PK
    string userId FK
    string provider
    string providerAccountId
    string accessToken
    string refreshToken
    int expiresAt
    datetime createdAt
  }

  Profile {
    string id PK
    string userId FK_UK
    string username UK
    string displayName
    string avatar
    string bio
    string status
    json interests
    datetime createdAt
    datetime updatedAt
  }

  FriendRequest {
    string id PK
    string fromUserId
    string toUserId
    datetime createdAt
    datetime updatedAt
  }

  Friendship {
    string id PK
    string userLowId
    string userHighId
    datetime createdAt
  }

  Conversation {
    string id PK
    datetime createdAt
  }

  ConversationParticipant {
    string conversationId FK
    string userId
  }

  Message {
    string id PK
    string conversationId FK
    string senderId
    string content
    datetime createdAt
  }

  Notification {
    string id PK
    string userId
    string type
    string title
    string body
    json data
    boolean read
    datetime readAt
    boolean archived
    datetime archivedAt
    datetime createdAt
  }

  Review {
    string id PK
    string placeName
    string city
    string userId
    int rating
    string comment
    datetime createdAt
    datetime updatedAt
  }

  SavedPlace {
    string id PK
    string userId
    string placeName
    string city
    string category
    string address
    string image
    string placeId
    float rating
    string status
    datetime savedAt
  }

  TripPlan {
    string id PK
    string userId
    string city
    int days
    json plan
    date tripStartDate
    date tripEndDate
    datetime createdAt
    datetime updatedAt
  }

Tables and Relationships (by Service)

auth-service / profiles-service: User is the root identity. Account rows are many-to-one with User (unique on [provider, providerAccountId]). Profile is an optional one-to-one with User. Deleting a user cascades to both accounts and profile.

friends-service: FriendRequest stores directed requests (unique pair). Friendship stores an undirected pair created after acceptance. Conversation has many Message rows and many users via ConversationParticipant (composite PK: conversationId + userId). Notification is per userId, indexed for unread and archive queries.

review-places: Review — one review per user per place name + city (@@unique([userId, placeName, city])).

fav-places: SavedPlace — one saved row per user per place name + city, with optional placeId, rating, image, and status.

planner-service: TripPlan — per userId and city, with days, preferences (Postgres text array), structured plan JSON, and optional trip date bounds.

Key Fields and Data Types

Category Fields Type
Identifiers User.id, Account.id, Profile.id, FriendRequest.id, Friendship.id, Notification.id Stringcuid()
Identifiers Conversation.id, Message.id, Review.id, SavedPlace.id, TripPlan.id Stringuuid()
Text emails, usernames, provider keys, message content, review comments, place names, addresses String
Booleans User.isEmailVerified, Notification.read, Notification.archived Boolean
Numbers Account.expiresAt (epoch), Review.rating (1–5), TripPlan.days, SavedPlace.rating Int / Float
JSON Profile.interests, Notification.data, TripPlan.plan (serialized AI itinerary) Json
Arrays TripPlan.preferences (native Postgres text array) String[]
Timestamps createdAt, updatedAt across all models DateTime
Date-only TripPlan.tripStartDate, TripPlan.tripEndDate Date (@db.Date)

ft_transcendence Plan (Total Score: 23/14)


iouhssei: [Minor] Custom-made design system: Reusable components, color palette, typography. [Major] Recommendation system: Machine learning based on user behavior.[Minor] Use a frontend framework: React, Vue, Angular, Svelte, etc. mel-rhay: [Minor] Notification system: For creation, update, and deletion actions. [Major] Implement real-time features: WebSockets, real-time updates, efficient broadcasting.[Minor] Use a backend framework: Express, Fastify, NestJS, Django, etc.[Major] Allow users to interact with other users: Chat, Profile, Friends system. aait-bou: [Minor] Use an ORM: Database Object-Relational Mapping. [Minor] Remote authentication: OAuth 2.0 (Google, GitHub, 42, etc.). achahbal: [Minor] Server-Side Rendering (SSR): Improved performance and SEO. [Minor] Support for additional browsers: Min 2 additional browsers (Firefox, Safari, etc.).[Minor] Health check & status page. [Major] Custom Major Module: Substantial technical complexity. Requires justification. aessadik: [Major] WAF/ModSecurity + HashiCorp Vault: Strict WAF config and secure secrets management. [Major] Backend as microservices: Loosely-coupled services.[Major] Prometheus & Grafana: Monitoring and alerting.

refactor this in markkdown and improve wrinting 3:12 PM Identified request to restructure and enhance project documentation formatting

The user wants me to reformat and improve the writing of their ft_transcendence plan in markdown. Done ft_transcendence — Project Plan

Total Score: 23/14 iouhssei Type Module Minor Custom Design System — Reusable components, color palette, and typography. Minor Frontend Framework — React, Vue, Angular, Svelte, or equivalent. Major Recommendation System — Machine learning model based on user behavior. mel-rhay Type Module Minor Notification System — Alerts for creation, update, and deletion actions. Minor Backend Framework — Express, Fastify, NestJS, Django, or equivalent. Major Real-Time Features — WebSocket integration with efficient event broadcasting. Major User Interactions — Chat, user profiles, and friends system. aait-bou Type Module Minor ORM Integration — Database access via Object-Relational Mapping. Minor Remote Authentication — OAuth 2.0 support (Google, GitHub, 42, etc.). achahbal Type Module Minor Server-Side Rendering (SSR) — Improved performance and SEO. Minor Cross-Browser Support — Compatibility with at least 2 additional browsers (Firefox, Safari, etc.). Minor Health Check & Status Page — Service availability monitoring endpoint. Major Custom Major Module — Substantial technical complexity; requires written justification. aessadik Type Module Major WAF / ModSecurity + HashiCorp Vault — Strict WAF configuration with secure secrets management. Major Microservices Architecture — Backend decomposed into loosely-coupled, independently deployable services. Major Prometheus & Grafana — Full monitoring stack with metrics collection and alerting.

in markdown 3:12 PM Deduced user’s markdown export preference

Here’s your .md file, ready to download or share with the team. Ft transcendence plan Document · MD Want to be notified when Claude responds?

Claude is AI and can make mistakes. Please double-check responses. Ft transcendence plan · MD

ft_transcendence — Project Plan

Total Score: 23/14


iouhssei

| Type | Module | |——|——–| | Minor | Custom Design System — Reusable components, color palette, and typography. | | Minor | Frontend Framework — React, Vue, Angular, Svelte, or equivalent. | | Major | Recommendation System — Machine learning model based on user behavior. | | Minor | Cross-Browser Support — Compatibility with at least 2 additional browsers (Firefox, Safari, etc.). |


mel-rhay

| Type | Module | |——|——–| | Minor | Notification System — Alerts for creation, update, and deletion actions. | | Minor | Backend Framework — Express, Fastify, NestJS, Django, or equivalent. | | Major | Real-Time Features — WebSocket integration with efficient event broadcasting. | | Major | User Interactions — Chat, user profiles, and friends system. | | Minor | Health Check & Status Page — Service availability monitoring endpoint. |


aait-bou

| Type | Module | |——|——–| | Minor | ORM Integration — Database access via Object-Relational Mapping. | | Minor | Remote Authentication — OAuth 2.0 support (Google, GitHub, 42, etc.). |


achahbal

| Type | Module | |——|——–| | Minor | Server-Side Rendering (SSR) — Improved performance and SEO. | | Major | Custom Major Module — Substantial technical complexity; requires written justification. |


aessadik

| Type | Module | |——|——–| | Major | WAF / ModSecurity + HashiCorp Vault — Strict WAF configuration with secure secrets management. | | Major | Microservices Architecture — Backend decomposed into loosely-coupled, independently deployable services. | | Major | Prometheus & Grafana — Full monitoring stack with metrics collection and alerting. |

Custom Module (Bonus)

RIHLA-MOBILE - The AI Travel Explorer


🎖️ Major Module of Choice: AI Travel Orchestration Engine

Grade: Major Module (2 Points)

∗ Why I chose this module

Traditional travel applications often suffer from a disconnect between data discovery and logistical planning. Furthermore, modern users overwhelmingly prefer to plan and navigate their journeys using mobile devices while on the go. I chose to implement a custom AI Travel Orchestration Engine within a native mobile environment because it meets the user where they are: in the palm of their hand. This project bridges the gap, creating a frictionless transition from raw geospatial discovery to a mathematically logical multi-day itinerary, optimized specifically for the mobile-first traveler.

∗ What technical challenges it addresses

  1. The Hallucination Problem: Large Language Models (LLMs) often generate non-existent locations or outdated addresses. My engine addresses this by implementing a Custom Prompt Bridge. Instead of allowing the AI to guess, the engine programmatically injects real-time, verified data from the Google Places API into the LLM context, forcing the AI to work strictly with physical, verifiable landmarks and coordinates.
  2. Geospatial Logic: Calculating a logical travel sequence for a human (avoiding diagonal zig-zags across a city) requires complex heuristics. The orchestration engine solves this by using AI to group activities based on neighborhood density and logical time-of-day constraints (e.g., matching museum hours with morning slots and restaurants with evening slots).
  3. Structured Data Parsing: Converting natural language AI thought processes into a strictly-typed JSON schema that a mobile UI can render without crashing is a significant technical hurdle. I implemented a robust parsing layer that enforces a specific schema on every generation.

∗ How it adds value to your project

It transforms RIHLA from a simple search directory into a dynamic personal travel assistant. By automating the planning phase, it saves users hours of research and ensures that their trips are geographically optimized. It takes the “guesswork” out of tourism, providing value that standard map applications currently lack.

∗ Why it deserves Major module status (2 points)

This module is substantial because it involves a complex multi-stage pipeline:


⚙️ Tech Stack

npm run ios