Ton Forfait, Ton Choix
Deal-Voyager est un comparateur de forfaits mobiles français, 100 % indépendant. Il scrape automatiquement les prix des opérateurs (MNO/MVNO), puis les classe au centime près pour vous aider à trouver l'offre la plus honnête : sans partenariat financier, sans publicité et sans bullshit.
- Scraping automatisé et contrôlé : Crawlee + Playwright/Cheerio pour 18 opérateurs, avec retries, sessions, limites de concurrence, détection des blocages et validation avant sauvegarde. Cron horaire, déclenchement manuel, détection 4G/5G et Data Europe/DOM.
- Classement transparent sur 1 an : tri basé sur le coût annuel total, avec prix mensuel x 12, carte SIM, frais d'activation et frais de résiliation. Score euro/Go calculé.
- Design néobrutaliste et dark mode : interface avec bordures épaisses, contrastes forts, ombres nettes et dark mode dynamique. Inclut une page 404 sur-mesure.
- Filtrage avancé : slider interactif de 0 à 500 Go, saisie directe au Go près et filtre exclusif par réseau (Orange, SFR, Bouygues, Free).
- Bot Discord dédié : alertes de nouveaux forfaits formatées en temps réel pour vos serveurs.
- Liens directs sans affiliation : accès en un clic aux pages opérateurs, zéro tracking.
- Control Room : panneau d'administration hybride, accessible uniquement via
/adminet protégé par Basic Auth, avec suivi du scraping en temps réel, gestion des frais par opérateur, label "Fairplay" et bouton de purge de la base. - Déploiement Docker sécurisé : stack complète (frontend, backend, BDD) avec credentials configurables via
.env, proxy interne Nitro et zéro port exposé inutilement.
| Opérateur | Type | Réseau | Détection 4G/5G |
|---|---|---|---|
| Sosh | MNO low-cost | Orange | Oui |
| RED by SFR | MNO low-cost | SFR | Oui |
| B&You | MNO low-cost | Bouygues Telecom | Oui |
| Free Mobile | MNO | Free | Oui |
| YouPrice | MVNO | Orange / SFR / Bouygues Telecom | Oui (multi-réseau) |
| Coriolis | MVNO | SFR | Oui |
| La Poste Mobile | MVNO | Bouygues Telecom | Oui |
| NRJ Mobile | MVNO | Bouygues Telecom | Oui |
| Auchan Telecom | MVNO | Bouygues Telecom | Oui |
| Cdiscount Mobile | MVNO | Bouygues Telecom | Oui |
| Syma Mobile | MVNO | SFR | Oui |
| Lebara | MVNO | SFR | Oui |
| Lycamobile | MVNO | Bouygues Telecom | Oui |
| Prixtel | MVNO | SFR | Oui |
| TeleCoop | MVNO | Orange | Oui |
| Akeo Telecom | MVNO | Orange / Bouygues Telecom | Oui |
| Nordnet | MVNO | Orange | Oui |
| France Téléphone (Bleutel) | MVNO | Orange / Bouygues Telecom | Oui |
┌─────────────────────────┐
│ Navigateur (port │
│ configurable) │
└────────────┬────────────┘
│
┌────────────▼────────────┐ ┌───────────────┐
│ Frontend Nuxt (SSR) │ │ API Discord │
│ Proxy /api/v1/** ─────┼──► Backend ──────► (Bot alerts & │
│ Auth HTTP Basic │ Express:3001 │ Webhooks) │
└─────────────────────────┘ │ └───────────────┘
▼
┌────────────────┐
│ PostgreSQL:5432│
└────────────────┘
Seul le port du frontend est exposé. Le backend et PostgreSQL communiquent exclusivement via le réseau Docker interne. Le Backend effectue lui-même ses requêtes sortantes vers l'API de Discord.
| Couche | Technologie | Rôle |
|---|---|---|
| Frontend | Nuxt 4 (Vue 3) + Tailwind CSS | Interface utilisateur, SSR, proxy API via Nitro routeRules |
| Backend | Node.js + Express 5 + discord.js | API REST, orchestration du scraping, alertes via Discord Bot, rate limiting |
| Scrapers | Crawlee + Playwright + Cheerio | HTTP prioritaire quand possible, navigateur pour les parcours interactifs, validation centralisée |
| BDD | PostgreSQL 15 + Prisma ORM | Stockage des forfaits (MobilePlan), paramétrage (OperatorSettings) et serveurs Discord cibles (DiscordSubscription) |
| Infra | Docker Compose | Conteneurisation, réseau interne, healthchecks |
- Docker (Engine + Compose)
- 4 Go de RAM minimum (instances Chromium headless pendant le scraping)
git clone https://github.com/votre-username/Deal-Voyager.git
cd Deal-Voyager
cp .env.example .envEditez le fichier .env avec vos propres credentials :
# Admin credentials (OBLIGATOIRE)
DEAL_VOYAGER_ADMIN_USERNAME=votre_identifiant
DEAL_VOYAGER_ADMIN_PASSWORD=votre_mot_de_passe
# Base de données PostgreSQL (OBLIGATOIRE)
DEAL_VOYAGER_POSTGRES_USER=dealvoyager
DEAL_VOYAGER_POSTGRES_PASSWORD=votre_mdp_postgres
DEAL_VOYAGER_POSTGRES_DB=deal_voyager
# Configuration Discord Bot (Requis pour /deal-setup et les alertes automatiques)
DISCORD_CLIENT_ID=votre_client_id_discord
DISCORD_BOT_TOKEN=votre_token_bot_discord
# Port public (optionnel, défaut: 3000)
DEAL_VOYAGER_APP_PORT=3000
DEAL_VOYAGER_CORS_ORIGIN=http://localhost:3000Important : Le déploiement refuse de démarrer si les variables obligatoires ne sont pas définies.
docker compose up -d --buildLe conteneur backend execute automatiquement prisma db push au demarrage pour creer/mettre a jour le schema.
Le compose démarre un proxy Squid interne nommé proxy. Par défaut, Crawlee et
Playwright font transiter les requêtes des scrapers par ce service grâce à :
SCRAPER_PROXY_URLS=http://proxy:3128Le proxy n'expose aucun port sur la machine hôte : seul le réseau Docker de Deal Voyager peut l'utiliser. Il centralise la sortie HTTP(S) des collecteurs, mais ne masque pas l'adresse IP publique de votre connexion Internet.
Pour utiliser un ou plusieurs proxies externes, indiquez leurs URL séparées par des virgules. Les identifiants peuvent être inclus dans chaque URL :
SCRAPER_PROXY_URLS=http://utilisateur:mot_de_passe@proxy.example:3128,https://proxy2.example:8443Pour désactiver complètement le routage par proxy, laissez la valeur vide dans
votre .env, puis recréez le backend :
SCRAPER_PROXY_URLS=docker compose up -d --force-recreate backendLes journaux sont consultables avec docker compose logs -f proxy backend.
| Service | URL |
|---|---|
| Comparateur | http://localhost:3000 |
| Control Room | http://localhost:3000/admin |
L'API n'est pas exposee directement - toutes les requetes /api/v1/* passent par le proxy Nuxt.
- Rendez-vous sur la Control Room (authentification HTTP Basic).
- Cliquez sur Lancer l'extraction maintenant.
- Patientez 1 a 2 minutes - la page suit la progression automatiquement.
- Les offres apparaissent sur l'accueil une fois le scraping termine.
Pour permettre à votre communauté de recevoir des alertes automatiques lors de changements de prix :
- Créez une application sur le Discord Developer Portal.
- Ajoutez un Bot à l'application et récupérez son Token.
- Renseignez les variables correspondantes dans votre
.env:DISCORD_CLIENT_ID=votre_client_id DISCORD_BOT_TOKEN=votre_token_app
- Redémarrez vos conteneurs (
docker compose up -d --build). Le bouton "Inviter le Bot" de l'accueil utilisera désormais votre Client ID. - Une fois le bot invité sur un serveur Discord, un administrateur doit taper la commande
/deal-setupdans le salon souhaité pour activer l'envoi des notifications automatiques (Format Embed Néo-Brutaliste).
| Mesure | Détail |
|---|---|
| Pas de credentials par défaut | Aucun fallback admin/secret. Variables d'environnement obligatoires. |
| Comparaison timing-safe | Mots de passe comparés via XOR constant-time (backend + frontend). |
| Sécurité par l'obscurité | Le bouton /admin n'est pas affiché publiquement dans l'interface. |
| Rate limiting | 10 tentatives max par IP sur 15 min (backend). |
| API interne | Le backend n'est pas exposé : les appels web passent par le proxy inverse Nitro du frontend. |
| Proxy de collecte | Squid accepte uniquement le trafic du réseau Docker et n'expose aucun port public. |
| PostgreSQL isolé | Port 5432 non exposé, accessible uniquement via le réseau Docker. |
| CORS restreint | Whitelist configurable au lieu de origin: *. |
| Auth header transmis | Le header HTTP Basic du navigateur est stocké côté serveur (useState) et réutilisé pour les appels API. |
Tous accessibles via le proxy frontend. Les routes marquees d'un cadenas necessitent l'authentification Basic.
| Methode | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/deals |
Toutes les offres triees par score. | |
GET |
/stats |
Etat du backend (nombre d'offres, dernier scraping, statut). | |
GET |
/operators |
Configuration par operateur (SIM, frais, fairplay). | |
POST |
/scrape |
Oui | Declenche le scraping global. |
PUT |
/operators/:name/simprice |
Oui | Met a jour les frais (SIM, activation, resiliation). |
PUT |
/operators/:name/fairplay |
Oui | Bascule le label Fairplay. |
DELETE |
/clear |
Oui | Purge la base de donnees. |
Deal-Voyager/
├── backend/
│ ├── src/
│ │ ├── controllers/ # Logique de reponse HTTP
│ │ ├── middlewares/ # Auth Basic + rate limiting
│ │ ├── services/
│ │ │ └── scrapers/ # Un fichier par operateur
│ │ │ ├── sosh.scraper.ts
│ │ │ ├── red.scraper.ts
│ │ │ ├── byou.scraper.ts
│ │ │ ├── free.scraper.ts
│ │ │ ├── youprice.scraper.ts
│ │ │ ├── coriolis.scraper.ts
│ │ │ ├── laposte.scraper.ts
│ │ │ ├── nrj.scraper.ts
│ │ │ ├── auchan.scraper.ts
│ │ │ ├── cdiscount.scraper.ts
│ │ │ ├── syma.scraper.ts
│ │ │ ├── lebara.scraper.ts
│ │ │ ├── lycamobile.scraper.ts
│ │ │ ├── prixtel.scraper.ts
│ │ │ ├── telecoop.scraper.ts
│ │ │ ├── akeo.scraper.ts
│ │ │ ├── nordnet.scraper.ts
│ │ │ ├── francetelephone.scraper.ts
│ │ │ └── types.ts
│ │ ├── routes/
│ │ └── lib/ # Prisma Client
│ ├── prisma/schema.prisma
│ ├── entrypoint.sh
│ └── Dockerfile
│
├── frontend/
│ ├── components/ # DealCard, DataSlider, OperatorBadge...
│ ├── layouts/
│ ├── middleware/ # Auth HTTP Basic (route middleware)
│ ├── pages/ # Accueil + Admin
│ ├── assets/css/
│ └── Dockerfile
│
├── compose.yml
├── .env.example # Template de configuration
├── CHANGELOG.md
└── README.md
Pour exposer Deal-Voyager sur un domaine avec HTTPS, ajoutez un reverse proxy (Caddy recommande) devant le frontend :
# Exemple Caddyfile
deal-voyager.mondomaine.fr {
reverse_proxy frontend:3000
}Pensez a :
- Rediriger les ports 80/443 sur votre routeur
- Pointer le DNS vers l'IP publique de votre serveur
- Mettre a jour
DEAL_VOYAGER_APP_PORTsi necessaire
GNU General Public License v3.0 - Voir LICENSE.