Serveur d'emails jetables avec Next.js 15, TypeScript, serveur SMTP local et Redis.
- ✉️ Serveur SMTP intégré (port 25) pour réception d'emails
- 🔄 Stockage temporaire avec Redis (TTL configurable via env)
- 💾 Historique persistant avec SQLite (adresses créées, compteurs emails)
- 🎨 Interface moderne Next.js 15 + TypeScript + SCSS
- 📱 Mobile-first design responsive
- 📧 Adresses permanentes : création aléatoire ou personnalisée (a-z, 0-9, .-_)
- 🗑️ Suppression manuelle : gestion individuelle des adresses via interface
- 📜 Historique adresses : sidebar avec adresses récentes
- 🔗 Liens directs inbox : accès direct via URL partageable
/inbox/[address] - 📎 Pièces jointes : téléchargement et affichage inline des images
- 🖼️ HTML sécurisé : affichage emails HTML avec sanitization renforcée
- 🔒 Protection par mot de passe : authentification cookie HTTP-only
- 🐳 Docker Compose pour déploiement simplifié
- 🔒 Sécurité : sanitization HTML stricte, validation, protection XSS
├── src/
│ ├── app/ # Next.js App Router
│ │ └── api/ # API routes
│ ├── components/ # React components (Atomic Design)
│ ├── lib/ # Business logic
│ │ ├── redis.ts # Redis client (emails temporaires)
│ │ ├── database.ts # SQLite (historique persistant)
│ │ └── emailStorage.ts
│ ├── smtp/ # SMTP server
│ ├── styles/ # SCSS global + modules
│ └── types/ # TypeScript types
├── data/ # SQLite database (auto-créé)
├── Dockerfile # Next.js app
├── Dockerfile.smtp # SMTP server
└── docker-compose.yml # Orchestration
- Redis : Emails temporaires (TTL via
EMAIL_RETENTION_DAYS), adresses actives - SQLite :
- Historique des adresses créées (permanentes)
- Compteurs emails reçus, statistiques
- Adresses supprimables manuellement via interface
- Node.js 22+
- Redis (ou Docker)
- Port 25 disponible (SMTP)
- Domaine configuré avec MX record sur Cloudflare
# 1. Installer dépendances
npm install
# 2. Configurer environnement
cp .env.example .env
# Éditer .env avec votre domaine
# 3. Lancer Redis (ou via Docker)
docker run -d -p 6379:6379 redis:7-alpine
# 4. Lancer serveur SMTP (terminal 1)
npm run smtp:dev
# 5. Lancer app Next.js (terminal 2)
npm run devL'application sera accessible sur http://localhost:3000
# 1. Créer fichier de configuration
cp env.prod.example .env.prod
# 2. Éditer .env.prod avec vos valeurs
nano .env.prod
# Modifier au minimum : SMTP_DOMAIN, APP_PASSWORD
# 3. Build et lancer
docker-compose up -d
# 4. Vérifier logs
docker-compose logs -f
# 5. Arrêter
docker-compose downFichier .env.prod (production Docker) ou .env (développement) :
| Variable | Défaut | Description |
|---|---|---|
NODE_ENV |
production | Environment (production/development) |
WEB_PORT |
3000 | Port externe Next.js |
APP_PASSWORD |
- | Mot de passe de protection (requis) |
EMAIL_RETENTION_DAYS |
365 | Durée de rétention des emails (jours) |
SMTP_PORT |
25 | Port SMTP |
SMTP_HOST |
0.0.0.0 | Bind SMTP |
SMTP_DOMAIN |
localhost | Domaine mail |
REDIS_HOST |
redis | Hôte Redis (nom service Docker) |
REDIS_PORT |
6379 | Port Redis |
DB_PATH |
/app/data | Chemin base SQLite (Docker) |
Note :
- Les adresses sont permanentes (plus d'expiration automatique). Vous pouvez supprimer une adresse manuellement via le bouton de suppression dans l'historique.
- La durée de rétention des emails est configurable via
EMAIL_RETENTION_DAYS(défaut: 365 jours). Les emails expirent automatiquement après ce délai.
L'application est protégée par authentification simple avec cookie HTTP-only :
Configuration requise :
# Dans .env.prod
APP_PASSWORD=votre_mot_de_passe_securiseFonctionnement :
- Accès à l'app → Redirection automatique vers
/login(middleware) - Connexion avec
APP_PASSWORD→ Cookie HTTP-only défini (validité 7 jours) - Middleware vérifie le cookie sur chaque requête
- Logout via
/api/auth/logout→ Suppression du cookie
Sécurité :
- Cookie HTTP-only (non accessible JavaScript, protection XSS)
- Secure en production (HTTPS uniquement)
- SameSite=lax (protection CSRF)
- Expiration automatique après 7 jours
- Validation côté serveur via middleware Next.js
Page d'accueil simplifiée :
- Input direct pour créer une adresse personnalisée + bouton "Créer"
- Bouton "Générer une adresse aléatoire" pour création automatique
- Sidebar avec historique des adresses récentes
- Si vous créez une adresse existante, vous êtes automatiquement redirigé vers son inbox (pas d'erreur)
- Accès direct via URL : Si vous accédez à
/inbox/monAdresseet que l'adresse n'existe pas, elle est créée automatiquement
Gestion des adresses :
- Les adresses sont permanentes (pas d'expiration automatique)
- Suppression manuelle via bouton 🗑️ dans l'historique
- Une adresse supprimée peut être recréée par la suite
- Les emails sont conservés selon
EMAIL_RETENTION_DAYS(défaut: 365 jours)
Gestion des emails :
- Suppression individuelle : Bouton 🗑️ sur chaque email dans la liste
- Vider l'inbox : Bouton pour supprimer tous les emails d'une inbox (conserve l'adresse)
- Confirmations avant suppression pour éviter les erreurs
Adresses :
POST /api/address— Créer une adresse (personnalisée ou aléatoire)GET /api/addresses— Liste des adresses créées avec paginationDELETE /api/address/[address]— Supprimer une adresse et tous ses emails
Inbox & Emails :
GET /api/inbox/[address]— Récupérer une inbox et ses emails (auto-création si inexistante)DELETE /api/inbox/[address]— Vider tous les emails d'une inbox (conserve l'adresse)GET /api/email/[id]— Récupérer un email spécifiqueDELETE /api/email/[id]— Supprimer un email spécifique
Authentification :
POST /api/auth/login— Connexion (cookie HTTP-only)POST /api/auth/logout— Déconnexion
Connectez-vous à Cloudflare et accédez à la zone DNS de votre domaine.
Option A : Domaine principal (ex: exemple.com)
| Type | Nom | Contenu/Valeur | Priorité | Proxy | TTL |
|---|---|---|---|---|---|
| A | mail |
VOTRE_IP_SERVEUR |
- | Auto | |
| MX | @ |
mail.exemple.com |
10 |
- | Auto |
Option B : Sous-domaine dédié (ex: mail.exemple.com)
| Type | Nom | Contenu/Valeur | Priorité | Proxy | TTL |
|---|---|---|---|---|---|
| A | mail |
VOTRE_IP_SERVEUR |
- | Auto | |
| MX | mail |
mail.exemple.com |
10 |
- | Auto |
# Ouvrir port SMTP (25)
sudo ufw allow 25/tcp
# Ouvrir port web (3000 ou 80/443 si reverse proxy)
sudo ufw allow 3000/tcp
# Vérifier règles
sudo ufw status# Sur votre serveur
cd /opt/junk-mail # ou votre chemin
# Configurer domaine
export SMTP_DOMAIN=mail.exemple.com
# Lancer services
docker-compose up -d
# Vérifier que SMTP écoute
docker-compose logs smtp
# Devrait afficher: [SMTP] Server listening on 0.0.0.0:25Attendre 5-15 minutes pour propagation DNS, puis tester :
# Vérifier MX record
dig MX exemple.com +short
# Résultat attendu: 10 mail.exemple.com.
# Vérifier A record
dig A mail.exemple.com +short
# Résultat attendu: VOTRE_IP_SERVEUR
# Test connexion SMTP
telnet mail.exemple.com 25
# Si connecté, taper: QUIT# Depuis un autre serveur ou Gmail/Outlook
# Envoyer email à : [email protected]
# Vérifier réception (sur le serveur)
curl http://localhost:3000/api/inbox/[email protected]
# Ou depuis navigateur
# https://mail.exemple.com (si reverse proxy HTTPS configuré)SPF (Sender Policy Framework) :
| Type | Nom | Contenu |
|---|---|---|
| TXT | @ |
v=spf1 ip4:VOTRE_IP_SERVEUR -all |
Note : SPF/DKIM utiles si vous implémentez l'envoi d'emails plus tard. Ce serveur est actuellement réception uniquement.
MX record pointe vers mauvaise cible :
# Vérifier configuration
dig MX exemple.com
# Doit pointer vers mail.exemple.com, pas directement vers IPPort 25 inaccessible depuis extérieur :
# Test depuis machine externe
nc -zv mail.exemple.com 25
# Si bloqué par hébergeur, utiliser port alternatif (587)
# Dans docker-compose.yml et .env: SMTP_PORT=587Cloudflare proxy activé par erreur :
- Le proxy (nuage orange) casse le SMTP (uniquement HTTP/HTTPS)
- Cliquer sur le nuage pour le rendre gris
- Attendre 5 min pour propagation
- Caractères autorisés : lettres minuscules (a-z) et chiffres (0-9)
- Longueur : 10 caractères
- Exemple :
[email protected]
- Caractères autorisés :
- Lettres (a-z, A-Z) - case insensitive
- Chiffres (0-9)
- Point (.)
- Tiret (-)
- Underscore (_)
- Restrictions :
- Ne peut pas commencer ou finir par un point
- Au moins 1 caractère
- Exemples valides :
john.doeuser_123test-email
- Exemples invalides :
.john(commence par un point)user.(finit par un point)user@domain(@ non autorisé dans partie locale)user space(espaces non autorisés)
Accès direct à une inbox spécifique via URL simplifiée (username uniquement).
Exemples :
https://mail.votredomaine.com/inbox/test123https://mail.votredomaine.com/inbox/abc-xyz- Permet de partager un lien direct vers une inbox
- Bouton "Share Link" disponible dans l'interface pour copier l'URL
- Le domaine (@mail.votredomaine.com) est automatiquement ajouté côté serveur
Génère une nouvelle adresse email (permanente)
Response:
{
"success": true,
"data": {
"address": "[email protected]",
"createdAt": 1234567890,
"expiresAt": 0
}
}Note: expiresAt: 0 signifie adresse permanente.
Récupère emails pour une adresse
Response:
{
"success": true,
"data": {
"inbox": { ... },
"emails": [...]
}
}Récupère un email par ID
Supprime un email
Télécharge une pièce jointe
Exemple:
/api/email/abc123xyz/attachment/document.pdf/api/email/abc123xyz/attachment/image.png
Headers:
Content-Type: Type MIME de la pièce jointeContent-Disposition:attachment; filename="..."Content-Length: Taille du fichierCache-Control:private, max-age=3600
Récupère l'historique des adresses créées
Query params:
limit(default: 50) : Nombre d'adressesoffset(default: 0) : Pagination
Response:
{
"success": true,
"data": {
"addresses": [
{
"id": 1,
"address": "[email protected]",
"created_at": 1234567890,
"email_count": 5,
"last_email_at": 1234570000
}
],
"stats": {
"total_addresses": 150,
"total_emails": 423
}
}
}Récupère statistiques globales
Response:
{
"success": true,
"data": {
"total_addresses": 150,
"total_emails": 423,
"active_addresses": 12
}
}npm run dev: Lancer Next.js en devnpm run build: Build productionnpm run start: Lancer Next.js productionnpm run smtp: Lancer serveur SMTPnpm run smtp:dev: SMTP avec hot-reloadnpm run lint: ESLint
- Disponibilité : Redis persistence, healthchecks Docker, volumes persistants SQLite
- Intégrité : Validation email, sanitization HTML, transactions SQLite
- Confidentialité : TTL auto, pas de logs sensibles, base locale
- Preuve : Timestamps, logs structurés, historique persistant
Fichier : ./data/addresses.db (ou DB_PATH)
Table addresses :
id: Primary key auto-incrementaddress: Email address (unique)created_at: Timestamp créationemail_count: Nombre emails reçuslast_email_at: Dernier email reçu
Note: Les adresses sont maintenant permanentes (plus de colonne expires_at).
Backup :
# Copier base de données
cp ./data/addresses.db ./backup-$(date +%Y%m%d).db
# Avec Docker
docker cp junk-mail-web-1:/app/data/addresses.db ./backup.dbSuppression manuelle d'une adresse :
# Via l'interface web : bouton supprimer dans l'historique
# Ou via API
curl -X DELETE http://localhost:3000/api/address/[email protected]
# Ou directement en base
sqlite3 ./data/addresses.db "DELETE FROM addresses WHERE address = '[email protected]';"Mode aléatoire :
- Génère automatiquement une adresse unique
- Format :
[email protected]
Mode personnalisé :
- Saisir votre propre username
- Validation : caractères autorisés
a-z 0-9 . _ - - Détection automatique si adresse déjà existante
- Affichage des 10 dernières adresses créées (permanentes)
- Compteur d'emails reçus par adresse
- Bouton de suppression pour chaque adresse (🗑️)
- Clic sur adresse pour charger l'inbox
- Adresse supprimée peut être recréée ultérieurement
- Affichage HTML : rendu sécurisé des emails HTML avec styles
- Pièces jointes :
- Liste interactive avec icônes (📎 fichiers, 🖼️ images)
- Taille affichée (B, KB, MB)
- Téléchargement au clic
- Images : affichage automatique en grille responsive en bas de l'email
- Lazy loading pour performance
- Sanitization HTML :
- Tags autorisés : texte, formatage, images, styles inline
- Protection XSS : scripts, iframes, objets bloqués
- Styles CSS filtrés (colors, sizing, spacing uniquement)
- Links sécurisés
# Envoyer email test (depuis un autre serveur avec sendmail ou similaire)
echo "Test email body" | mail -s "Test Subject" [email protected]
# Vérifier réception
curl http://localhost:3000/api/inbox/[email protected]Port 25 déjà utilisé:
# Linux: vérifier processus
sudo lsof -i :25
# Changer port dans .env (SMTP_PORT=2525)Redis connexion error:
# Vérifier Redis
redis-cli ping
# Doit retourner: PONGEmails non reçus:
- Vérifier MX record DNS propagé :
dig MX votredomaine.com - Vérifier port 25 ouvert :
telnet votredomaine.com 25 - Vérifier logs SMTP :
docker-compose logs smtp
MIT
Eden Solutions [email protected]