diff --git a/TEST_LOCAL.md b/TEST_LOCAL.md new file mode 100644 index 0000000..0127a5f --- /dev/null +++ b/TEST_LOCAL.md @@ -0,0 +1,297 @@ +# Test Local du Système de Sécurité + +Ce guide te permet de tester la sécurité de l'API localement avant de déployer sur le serveur. + +## Étape 1 : Générer un token de test + +**Windows (PowerShell) :** +```powershell +-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 64 | % {[char]$_}) +``` + +**Linux/Mac :** +```bash +openssl rand -hex 32 +``` + +**Copie le token généré**. Exemple : `a1b2c3d4e5f6...` + +--- + +## Étape 2 : Configurer le .env + +Édite ton fichier `.env` et ajoute (ou modifie) ces lignes : + +```env +# Token API (colle ton token généré) +API_TOKEN=ton_token_genere_ici + +# CORS en développement (tout autoriser) +ALLOWED_ORIGINS=* + +# Port +PORT=8888 + +# Ta clé OpenAI (tu l'as déjà normalement) +OPENAI_API_KEY=sk-... +``` + +--- + +## Étape 3 : Démarrer le serveur + +```bash +npm run server +``` + +**Résultat attendu :** +``` +Server running on http://localhost:8888 + +Endpoints: + GET /health - Health check + GET /info?url= - Get video/playlist info + POST /download - Download as MP3 + ... +``` + +--- + +## Étape 4 : Tester l'authentification + +### Test 1 : Endpoint public (sans token) ✅ + +```bash +curl http://localhost:8888/health +``` + +**Résultat attendu :** +```json +{"status":"ok","timestamp":"2025-..."} +``` + +--- + +### Test 2 : Endpoint protégé SANS token ❌ + +```bash +curl http://localhost:8888/info?url=https://youtube.com/watch?v=test +``` + +**Résultat attendu (ERREUR 401) :** +```json +{ + "error": "Unauthorized", + "message": "API key required. Provide X-API-Key header or Authorization: Bearer " +} +``` + +✅ **Si tu vois cette erreur, c'est PARFAIT !** Ça veut dire que la sécurité fonctionne. + +--- + +### Test 3 : Endpoint protégé AVEC token ✅ + +**Remplace `ton_token_ici` par le token que tu as généré :** + +```bash +curl -H "X-API-Key: ton_token_ici" \ + "http://localhost:8888/info?url=https://youtube.com/watch?v=dQw4w9WgXcQ" +``` + +**Résultat attendu (informations sur la vidéo) :** +```json +{ + "success": true, + "title": "Rick Astley - Never Gonna Give You Up", + "type": "video", + "duration": 212, + ... +} +``` + +✅ **Si tu vois les infos de la vidéo, parfait !** + +--- + +### Test 4 : Avec un MAUVAIS token ❌ + +```bash +curl -H "X-API-Key: mauvais_token_123" \ + "http://localhost:8888/info?url=https://youtube.com/watch?v=test" +``` + +**Résultat attendu (ERREUR 403) :** +```json +{ + "error": "Forbidden", + "message": "Invalid API key" +} +``` + +✅ **Si tu vois cette erreur, c'est bon !** + +--- + +## Étape 5 : Tester l'interface web + +### 1. Ouvre ton navigateur + +Va sur : **http://localhost:8888** + +--- + +### 2. Configure le token + +1. **Clique sur le panneau "🔐 API Configuration"** +2. **Colle ton token** dans le champ "API Token" +3. **Clique sur "Save & Test"** + +**Résultat attendu :** +- Le statut passe en vert : **"Connected ✓"** +- Notification verte : **"✓ API token saved successfully!"** +- Le panneau se referme automatiquement + +![Configuration réussie] + +--- + +### 3. Teste un téléchargement + +1. Va dans l'onglet **"Download"** +2. Entre cette URL de test : `https://www.youtube.com/watch?v=dQw4w9WgXcQ` +3. Clique sur **"Download MP3"** + +**Résultat attendu :** +- La barre de progression apparaît +- Le téléchargement démarre +- Un fichier MP3 est créé dans `./output/` + +--- + +### 4. Teste sans token (localStorage) + +1. **Ouvre la console du navigateur** (F12) +2. **Efface le localStorage** : + ```javascript + localStorage.clear() + ``` +3. **Rafraîchis la page** (F5) +4. **Essaie de télécharger à nouveau** la même vidéo + +**Résultat attendu :** +- Une erreur apparaît : **"⚠️ Authentication Error"** +- Message : **"API token required"** + +✅ **C'est parfait !** Sans token dans localStorage, l'API refuse la requête. + +--- + +### 5. Re-configure le token + +1. **Rouvre le panneau de configuration** +2. **Entre ton token à nouveau** +3. **Clique "Save & Test"** +4. **Le téléchargement devrait maintenant fonctionner** + +--- + +## Étape 6 : Vérifier le localStorage + +**Dans la console du navigateur (F12) :** + +```javascript +localStorage.getItem('video_transcriptor_api_token') +``` + +**Résultat attendu :** +``` +"ton_token_genere_ici" +``` + +✅ **Le token est bien sauvegardé !** + +--- + +## Récapitulatif des Tests + +| Test | Attendu | Statut | +|------|---------|--------| +| `/health` sans token | ✅ 200 OK | ☐ | +| `/info` sans token | ❌ 401 Unauthorized | ☐ | +| `/info` avec bon token | ✅ 200 OK + données | ☐ | +| `/info` avec mauvais token | ❌ 403 Forbidden | ☐ | +| Interface web - config token | ✅ "Connected ✓" | ☐ | +| Interface web - download avec token | ✅ Fonctionne | ☐ | +| Interface web - download sans token | ❌ Erreur auth | ☐ | +| localStorage sauvegarde token | ✅ Token présent | ☐ | + +--- + +## Dépannage + +### ❌ Le serveur ne démarre pas + +**Erreur possible :** `API_TOKEN not configured in .env` + +**Solution :** Vérifie que `.env` contient bien `API_TOKEN=...` + +--- + +### ❌ "Authentication Error" même avec token + +**Vérifications :** + +1. **Le token dans `.env` est identique à celui dans l'interface ?** + ```bash + cat .env | grep API_TOKEN + ``` + +2. **Le serveur a bien redémarré après modification de `.env` ?** + - Arrête le serveur (Ctrl+C) + - Relance : `npm run server` + +3. **Le token ne contient pas d'espaces ?** + ```env + # ❌ MAUVAIS + API_TOKEN= mon_token + + # ✅ BON + API_TOKEN=mon_token + ``` + +--- + +### ❌ "CORS error" dans la console + +**Problème :** La requête est bloquée par CORS + +**Solution :** Vérifie dans `.env` : +```env +ALLOWED_ORIGINS=* +``` + +Redémarre le serveur. + +--- + +### ❌ Le localStorage ne sauvegarde pas + +**Problème :** Le navigateur bloque le localStorage + +**Solutions :** +1. Désactive les extensions qui bloquent (uBlock, Privacy Badger) +2. Vérifie que tu n'es pas en mode navigation privée +3. Autorise le stockage local pour `localhost` + +--- + +## Prochaine Étape + +Si tous les tests sont ✅ **OK**, tu peux déployer sur le serveur OVH ! + +📄 **Suis le guide** : [`docs/DEPLOIEMENT_OVH.md`](./docs/DEPLOIEMENT_OVH.md) + +--- + +**Bonne chance ! 🚀** diff --git a/docs/API.md b/docs/API.md index c54bd38..aaa6df1 100644 --- a/docs/API.md +++ b/docs/API.md @@ -5,13 +5,75 @@ http://localhost:8888 ``` +## 🔐 Authentication + +**⚠️ IMPORTANT**: All API endpoints (except `/health` and `/api`) require authentication using an API token. + +### How to Authenticate + +Include your API token in **one** of these ways: + +**Option 1: X-API-Key header (Recommended)** +```bash +curl -H "X-API-Key: your_api_token_here" http://localhost:8888/endpoint +``` + +**Option 2: Authorization Bearer header** +```bash +curl -H "Authorization: Bearer your_api_token_here" http://localhost:8888/endpoint +``` + +### Configuration + +1. Set your API token in `.env`: + ```env + API_TOKEN=your_secure_token_here + ``` + +2. Generate a secure token for production: + ```bash + # Linux/Mac + openssl rand -hex 32 + + # Or use any secure random string generator + ``` + +### Security Notes + +- **Public endpoints** (no auth required): `/health`, `/api` +- **Protected endpoints**: All other endpoints require authentication +- In **development**: If `API_TOKEN` is not set, the API will work without authentication (with a warning) +- In **production**: Always set a strong `API_TOKEN` + +### Error Responses + +**401 Unauthorized** - No API key provided: +```json +{ + "error": "Unauthorized", + "message": "API key required. Provide X-API-Key header or Authorization: Bearer " +} +``` + +**403 Forbidden** - Invalid API key: +```json +{ + "error": "Forbidden", + "message": "Invalid API key" +} +``` + +--- + ## Table of Contents +- [Authentication](#-authentication) - [Health & Info](#health--info) - [Download Endpoints](#download-endpoints) - [Transcription Endpoints](#transcription-endpoints) - [Translation Endpoints](#translation-endpoints) - [Summarization Endpoints](#summarization-endpoints) - [File Management](#file-management) +- [Security Configuration](#security-configuration) --- @@ -48,7 +110,8 @@ Get information about a YouTube video or playlist. **Example:** ```bash -curl "http://localhost:8888/info?url=https://www.youtube.com/watch?v=VIDEO_ID" +curl -H "X-API-Key: your_token" \ + "http://localhost:8888/info?url=https://www.youtube.com/watch?v=VIDEO_ID" ``` **Response:** @@ -76,7 +139,8 @@ Download YouTube video(s) to MP3 with Server-Sent Events (SSE) progress updates. **Example:** ```bash -curl "http://localhost:8888/download-stream?url=https://www.youtube.com/watch?v=VIDEO_ID" +curl -H "X-API-Key: your_token" \ + "http://localhost:8888/download-stream?url=https://www.youtube.com/watch?v=VIDEO_ID" ``` **SSE Events:** @@ -99,7 +163,8 @@ Download YouTube video(s) to MP3 (non-streaming). **Example:** ```bash -curl -X POST http://localhost:8888/download \ +curl -H "X-API-Key: your_token" \ + -X POST http://localhost:8888/download \ -H "Content-Type: application/json" \ -d '{"url":"https://www.youtube.com/watch?v=VIDEO_ID"}' ``` @@ -148,7 +213,8 @@ Transcribe an existing audio file. **Example:** ```bash -curl -X POST http://localhost:8888/transcribe \ +curl -H "X-API-Key: your_token" \ + -X POST http://localhost:8888/transcribe \ -H "Content-Type: application/json" \ -d '{ "filePath": "./output/audio.mp3", @@ -559,3 +625,91 @@ All endpoints that support `outputPath` parameter: ### API Key Ensure `OPENAI_API_KEY` is set in your `.env` file for transcription, translation, and summarization features to work. + +--- + +## Security Configuration + +### Environment Variables + +Required security variables in `.env`: + +```env +# API Authentication Token +API_TOKEN=your_secure_random_token_here + +# CORS - Allowed Origins (comma-separated) +# Development: * (all origins) +# Production: https://yourdomain.com,https://app.yourdomain.com +ALLOWED_ORIGINS=* + +# Server Port +PORT=8888 + +# Output Directory +OUTPUT_DIR=./output + +# OpenAI API Key (required for AI features) +OPENAI_API_KEY=sk-... +``` + +### Security Features + +The API implements the following security measures: + +1. **API Token Authentication** + - All endpoints (except `/health` and `/api`) require authentication + - Supports both `X-API-Key` and `Authorization: Bearer` headers + +2. **CORS Protection** + - Configurable allowed origins via `ALLOWED_ORIGINS` + - Restricts cross-origin requests to trusted domains + +3. **HTTP Security Headers** + - `X-Content-Type-Options: nosniff` + - `X-Frame-Options: DENY` + - `X-XSS-Protection: 1; mode=block` + - `Strict-Transport-Security: max-age=31536000; includeSubDomains` + - `Content-Security-Policy` with strict policies + +4. **Input Validation** + - File type validation for uploads + - Parameter validation on all endpoints + +### Production Deployment Checklist + +Before deploying to production: + +- [ ] Generate a strong, unique `API_TOKEN` (min 32 characters) +- [ ] Set `ALLOWED_ORIGINS` to your specific domains (remove `*`) +- [ ] Ensure `OPENAI_API_KEY` is properly set +- [ ] Use HTTPS (not HTTP) for all connections +- [ ] Set up rate limiting (recommended via reverse proxy) +- [ ] Configure firewall rules +- [ ] Set up monitoring and logging +- [ ] Review and secure file upload limits + +### Example Authenticated Requests + +**Using X-API-Key header:** +```bash +# Download endpoint +curl -H "X-API-Key: your_token" \ + -X POST http://localhost:8888/download \ + -H "Content-Type: application/json" \ + -d '{"url":"https://www.youtube.com/watch?v=VIDEO_ID"}' + +# Transcribe endpoint +curl -H "X-API-Key: your_token" \ + -X POST http://localhost:8888/transcribe \ + -H "Content-Type: application/json" \ + -d '{"filePath":"./output/audio.mp3"}' +``` + +**Using Authorization Bearer:** +```bash +curl -H "Authorization: Bearer your_token" \ + -X POST http://localhost:8888/summarize \ + -H "Content-Type: application/json" \ + -d '{"text":"Long text to summarize..."}' +``` diff --git a/docs/DEPLOIEMENT_OVH.md b/docs/DEPLOIEMENT_OVH.md new file mode 100644 index 0000000..edb9490 --- /dev/null +++ b/docs/DEPLOIEMENT_OVH.md @@ -0,0 +1,395 @@ +# Guide de Mise à Jour - Serveur OVH Existant + +Ce guide explique comment mettre à jour ton serveur OVH existant avec le nouveau système de sécurité. + +## Prérequis + +Tu as déjà : +- ✅ Un VPS chez OVH +- ✅ Git configuré +- ✅ Un service qui tourne (PM2/systemd) + +## Étapes de Mise à Jour + +### 1. Générer un token API sécurisé + +**Sur ton serveur OVH (via SSH):** + +```bash +# Générer un token aléatoire de 64 caractères +openssl rand -hex 32 +``` + +**Ou sur Windows (PowerShell):** +```powershell +-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 64 | % {[char]$_}) +``` + +**Copie ce token**, tu vas en avoir besoin maintenant. + +--- + +### 2. Configurer les variables d'environnement + +Connecte-toi en SSH à ton serveur : + +```bash +ssh user@ton-serveur-ovh.com +``` + +Navigue vers le dossier du projet : + +```bash +cd /chemin/vers/videotoMP3Transcriptor +``` + +Édite le fichier `.env` : + +```bash +nano .env +``` + +**Ajoute ces lignes** (ou modifie si elles existent déjà) : + +```env +# ======================================== +# SÉCURITÉ API +# ======================================== + +# Remplace par le token que tu viens de générer +API_TOKEN=ton_token_de_64_caracteres_ici + +# Domaines autorisés (séparés par des virgules) +# En développement: * (tout le monde) +# En production: https://ton-domaine.com,https://api.ton-domaine.com +ALLOWED_ORIGINS=* + +# Port (optionnel, défaut: 8888) +PORT=8888 + +# OpenAI API Key (tu dois déjà l'avoir) +OPENAI_API_KEY=sk-... +``` + +**Sauvegarde** : `Ctrl + X`, puis `Y`, puis `Enter` + +--- + +### 3. Pull les dernières modifications + +```bash +# Sauvegarder les modifications locales si nécessaire +git stash + +# Récupérer les dernières modifications +git pull origin main + +# Restaurer tes modifications si tu avais stashé +git stash pop +``` + +--- + +### 4. Redémarrer le service + +**Si tu utilises PM2:** + +```bash +# Redémarrer l'application +pm2 restart video-transcriptor + +# Vérifier que ça tourne +pm2 status + +# Voir les logs en temps réel +pm2 logs video-transcriptor +``` + +**Si tu utilises systemd:** + +```bash +# Redémarrer le service +sudo systemctl restart video-transcriptor + +# Vérifier le statut +sudo systemctl status video-transcriptor + +# Voir les logs +sudo journalctl -u video-transcriptor -f +``` + +--- + +### 5. Tester l'API + +**Test de santé (sans token - devrait marcher):** + +```bash +curl http://localhost:8888/health +``` + +**Résultat attendu:** +```json +{"status":"ok","timestamp":"2025-..."} +``` + +**Test avec authentification (devrait échouer sans token):** + +```bash +curl http://localhost:8888/info?url=https://youtube.com/watch?v=test +``` + +**Résultat attendu:** +```json +{"error":"Unauthorized","message":"API key required..."} +``` + +**Test avec token (devrait marcher):** + +```bash +curl -H "X-API-Key: ton_token_ici" \ + "http://localhost:8888/info?url=https://youtube.com/watch?v=dQw4w9WgXcQ" +``` + +**Résultat attendu:** Informations sur la vidéo + +--- + +### 6. Configurer le DNS (si pas déjà fait) + +**Chez OVH, dans l'espace client:** + +1. Va dans **Web Cloud** → **Domaines** → **Ton domaine** +2. Clique sur **Zone DNS** +3. Ajoute un enregistrement **A** : + - Sous-domaine: `api` (ou `@` pour le domaine principal) + - Cible: **L'IP de ton VPS OVH** + - TTL: 3600 + +**Exemple:** +``` +Type: A +Nom: api +Cible: 51.195.XXX.XXX (ton IP OVH) +``` + +4. **Attends 5-10 minutes** pour la propagation DNS + +--- + +### 7. Tester depuis l'interface web + +1. **Ouvre ton navigateur** et va sur : `http://ton-domaine.com` (ou `http://ip-du-serveur:8888`) + +2. **Clique sur le panneau "🔐 API Configuration"** + +3. **Colle ton token** dans le champ + +4. **Clique sur "Save & Test"** + +5. **Résultat attendu :** + - Statut passe en vert "Connected ✓" + - Notification de succès + - Le token est sauvegardé dans le navigateur + +6. **Teste un téléchargement** dans l'onglet "Download" + - Entre une URL YouTube + - Le token sera automatiquement ajouté aux requêtes + +--- + +## Sécurité en Production + +### Option 1 : Limiter les origines CORS + +Si tu veux que SEUL ton domaine puisse utiliser l'API : + +```bash +nano .env +``` + +Change : +```env +ALLOWED_ORIGINS=https://ton-domaine.com,https://api.ton-domaine.com +``` + +### Option 2 : HTTPS avec Nginx + Let's Encrypt + +**Si pas déjà configuré**, installe Nginx et SSL : + +```bash +# Installer Nginx +sudo apt update +sudo apt install -y nginx certbot python3-certbot-nginx + +# Créer la configuration Nginx +sudo nano /etc/nginx/sites-available/video-transcriptor +``` + +**Colle cette configuration :** + +```nginx +server { + listen 80; + server_name api.ton-domaine.com; + + # Redirection vers HTTPS (sera configuré après) + # return 301 https://$server_name$request_uri; + + location / { + proxy_pass http://localhost:8888; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +**Activer et tester:** + +```bash +# Activer le site +sudo ln -s /etc/nginx/sites-available/video-transcriptor /etc/nginx/sites-enabled/ + +# Tester la config +sudo nginx -t + +# Redémarrer Nginx +sudo systemctl restart nginx + +# Obtenir un certificat SSL GRATUIT +sudo certbot --nginx -d api.ton-domaine.com +``` + +Certbot va automatiquement configurer HTTPS et les redirections. + +--- + +## Dépannage + +### ❌ "API token required" + +**Problème:** Le token n'est pas envoyé ou invalide + +**Solution:** +1. Vérifie que le token est bien configuré dans l'interface web +2. Rafraîchis la page et entre le token à nouveau +3. Vérifie que le token dans `.env` est le même que dans l'interface + +--- + +### ❌ Le service ne démarre pas + +```bash +# Voir les logs +pm2 logs video-transcriptor --lines 50 + +# ou pour systemd +sudo journalctl -u video-transcriptor -n 50 +``` + +**Vérifications:** +- La variable `API_TOKEN` est bien dans `.env` +- Pas d'erreurs de syntaxe dans `.env` +- Node modules à jour : `npm ci` + +--- + +### ❌ CORS errors dans le navigateur + +**Problème:** "Access to fetch at ... has been blocked by CORS policy" + +**Solution 1:** En développement +```env +ALLOWED_ORIGINS=* +``` + +**Solution 2:** En production +```env +ALLOWED_ORIGINS=https://ton-domaine.com,https://www.ton-domaine.com +``` + +Redémarre après modification : `pm2 restart video-transcriptor` + +--- + +### ❌ DNS ne fonctionne pas + +**Vérifier la propagation DNS:** + +```bash +# Depuis ton serveur +dig api.ton-domaine.com + +# Ou depuis Windows +nslookup api.ton-domaine.com +``` + +**Si ça ne fonctionne pas:** +- Attends 10-30 minutes +- Vérifie dans l'interface OVH que l'enregistrement A pointe vers la bonne IP +- Vide le cache DNS : `ipconfig /flushdns` (Windows) ou `sudo systemd-resolve --flush-caches` (Linux) + +--- + +## Checklist Finale + +Avant de considérer le déploiement comme terminé : + +- [ ] `.env` configuré avec un `API_TOKEN` fort +- [ ] Service redémarré et en cours d'exécution +- [ ] Test `/health` fonctionne +- [ ] Test avec token fonctionne +- [ ] Interface web accessible +- [ ] Token sauvegardé dans l'interface web +- [ ] Test de téléchargement YouTube réussi +- [ ] DNS configuré (si applicable) +- [ ] HTTPS configuré (recommandé pour production) + +--- + +## Commandes Utiles + +```bash +# Voir les logs en temps réel +pm2 logs video-transcriptor + +# Statut du service +pm2 status + +# Redémarrer +pm2 restart video-transcriptor + +# Vérifier les ports ouverts +sudo netstat -tlnp | grep 8888 + +# Vérifier l'utilisation des ressources +htop + +# Espace disque +df -h + +# Tester l'API locale +curl -H "X-API-Key: ton_token" http://localhost:8888/health +``` + +--- + +## Support + +Si tu rencontres des problèmes : + +1. **Vérifie les logs** : `pm2 logs` +2. **Vérifie le `.env`** : `cat .env | grep API_TOKEN` +3. **Teste en local** : `curl http://localhost:8888/health` +4. **Vérifie le firewall** : `sudo ufw status` + +--- + +**Bon déploiement ! 🚀** + +Si tout fonctionne, tu devrais pouvoir utiliser l'interface web avec le token sauvegardé, et ne plus avoir à le copier-coller à chaque fois ! diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..e0ba210 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,699 @@ +# Guide de Déploiement - Video to MP3 Transcriptor + +Ce guide vous accompagne pour déployer l'API de manière sécurisée sur un serveur de production. + +## Table des matières +1. [Prérequis](#prérequis) +2. [Configuration de sécurité](#configuration-de-sécurité) +3. [Déploiement sur VPS/Serveur](#déploiement-sur-vpsserveur) +4. [Déploiement avec Docker](#déploiement-avec-docker) +5. [Nginx Reverse Proxy](#nginx-reverse-proxy) +6. [SSL/HTTPS avec Let's Encrypt](#sslhttps-avec-lets-encrypt) +7. [Surveillance et logs](#surveillance-et-logs) +8. [Sécurité avancée](#sécurité-avancée) + +--- + +## Prérequis + +### Serveur +- Linux (Ubuntu 20.04+ / Debian 11+ recommandé) +- Minimum 2 GB RAM +- 10 GB espace disque +- Node.js 18+ ou Docker + +### Dépendances système +```bash +# Ubuntu/Debian +sudo apt update +sudo apt install -y ffmpeg python3 + +# Pour téléchargement YouTube +sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp +sudo chmod a+rx /usr/local/bin/yt-dlp +``` + +### Domaine et DNS +- Un nom de domaine pointant vers votre serveur +- Accès aux paramètres DNS + +--- + +## Configuration de sécurité + +### 1. Générer un token API sécurisé + +**Sur votre serveur:** +```bash +# Générer un token de 64 caractères +openssl rand -hex 32 + +# Ou utiliser cette commande alternative +head /dev/urandom | tr -dc A-Za-z0-9 | head -c 64 +``` + +Copiez le token généré, vous en aurez besoin pour le `.env`. + +### 2. Configurer les variables d'environnement + +Créez/éditez le fichier `.env` sur le serveur: + +```bash +cd /path/to/videotoMP3Transcriptor +nano .env +``` + +Configuration minimale de production: + +```env +# ======================================== +# SÉCURITÉ - PRODUCTION +# ======================================== + +# Token API (REMPLACEZ PAR VOTRE TOKEN GÉNÉRÉ) +API_TOKEN=votre_token_securise_de_64_caracteres + +# Origines CORS autorisées (vos domaines uniquement) +ALLOWED_ORIGINS=https://yourdomain.com,https://api.yourdomain.com + +# ======================================== +# CONFIGURATION SERVEUR +# ======================================== + +# Port interne (Nginx fera le reverse proxy) +PORT=8888 + +# Répertoire de sortie +OUTPUT_DIR=/var/www/videotoMP3Transcriptor/output + +# ======================================== +# API KEYS +# ======================================== + +# OpenAI API Key (OBLIGATOIRE) +OPENAI_API_KEY=sk-... + +# ======================================== +# ENVIRONNEMENT +# ======================================== +NODE_ENV=production +``` + +### 3. Permissions du fichier .env + +```bash +# Sécuriser le fichier .env +chmod 600 .env +chown www-data:www-data .env # ou votre utilisateur système +``` + +--- + +## Déploiement sur VPS/Serveur + +### 1. Installation de Node.js + +```bash +# Installation de Node.js 20 LTS +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt install -y nodejs + +# Vérification +node --version # devrait afficher v20.x +npm --version +``` + +### 2. Cloner et installer l'application + +```bash +# Créer le répertoire +sudo mkdir -p /var/www/videotoMP3Transcriptor +sudo chown $USER:$USER /var/www/videotoMP3Transcriptor + +# Cloner (ou copier) votre code +cd /var/www/videotoMP3Transcriptor +# git clone ... ou upload manuel + +# Installer les dépendances +npm ci --only=production + +# Créer le répertoire de sortie +mkdir -p output +chmod 755 output +``` + +### 3. Utiliser PM2 pour la gestion des processus + +PM2 est un gestionnaire de processus pour Node.js qui redémarre automatiquement votre app en cas de crash. + +```bash +# Installer PM2 globalement +sudo npm install -g pm2 + +# Démarrer l'application +pm2 start src/server.js --name "video-transcriptor" + +# Configurer PM2 pour démarrer au boot +pm2 startup systemd +pm2 save + +# Commandes utiles +pm2 status # Voir le statut +pm2 logs video-transcriptor # Voir les logs +pm2 restart video-transcriptor # Redémarrer +pm2 stop video-transcriptor # Arrêter +``` + +### 4. Configuration PM2 avancée (optionnelle) + +Créez un fichier `ecosystem.config.js`: + +```javascript +module.exports = { + apps: [{ + name: 'video-transcriptor', + script: './src/server.js', + instances: 1, + autorestart: true, + watch: false, + max_memory_restart: '1G', + env: { + NODE_ENV: 'production', + PORT: 8888 + }, + error_file: '/var/log/pm2/video-transcriptor-error.log', + out_file: '/var/log/pm2/video-transcriptor-out.log', + log_date_format: 'YYYY-MM-DD HH:mm:ss Z' + }] +}; +``` + +Démarrer avec: +```bash +pm2 start ecosystem.config.js +``` + +--- + +## Déploiement avec Docker + +### 1. Créer un Dockerfile + +Créez `Dockerfile` à la racine du projet: + +```dockerfile +FROM node:20-slim + +# Installer les dépendances système +RUN apt-get update && apt-get install -y \ + ffmpeg \ + python3 \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Installer yt-dlp +RUN curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp \ + && chmod a+rx /usr/local/bin/yt-dlp + +# Créer le répertoire de l'app +WORKDIR /app + +# Copier package.json et installer les dépendances +COPY package*.json ./ +RUN npm ci --only=production + +# Copier le code source +COPY . . + +# Créer le répertoire de sortie +RUN mkdir -p /app/output && chmod 755 /app/output + +# Exposer le port +EXPOSE 8888 + +# Variables d'environnement par défaut +ENV NODE_ENV=production +ENV PORT=8888 +ENV OUTPUT_DIR=/app/output + +# Démarrer l'application +CMD ["node", "src/server.js"] +``` + +### 2. Créer docker-compose.yml + +```yaml +version: '3.8' + +services: + video-transcriptor: + build: . + container_name: video-transcriptor + restart: unless-stopped + ports: + - "8888:8888" + volumes: + - ./output:/app/output + - ./.env:/app/.env:ro + environment: + - NODE_ENV=production + networks: + - transcriptor-network + +networks: + transcriptor-network: + driver: bridge +``` + +### 3. Lancer avec Docker Compose + +```bash +# Build et démarrer +docker-compose up -d + +# Voir les logs +docker-compose logs -f + +# Arrêter +docker-compose down + +# Reconstruire après modification +docker-compose up -d --build +``` + +--- + +## Nginx Reverse Proxy + +### 1. Installer Nginx + +```bash +sudo apt update +sudo apt install -y nginx +``` + +### 2. Configuration Nginx + +Créez `/etc/nginx/sites-available/video-transcriptor`: + +```nginx +# Rate limiting +limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; + +server { + listen 80; + server_name api.yourdomain.com; + + # Logs + access_log /var/log/nginx/video-transcriptor-access.log; + error_log /var/log/nginx/video-transcriptor-error.log; + + # Rate limiting + limit_req zone=api_limit burst=20 nodelay; + + # Augmenter les timeouts pour les longs traitements + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 600; + + # Augmenter la taille max des uploads + client_max_body_size 500M; + + location / { + proxy_pass http://localhost:8888; + proxy_http_version 1.1; + + # Headers + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Pour Server-Sent Events (SSE) + proxy_cache_bypass $http_upgrade; + proxy_buffering off; + proxy_cache off; + } + + # Headers de sécurité supplémentaires + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; +} +``` + +### 3. Activer le site + +```bash +# Créer un lien symbolique +sudo ln -s /etc/nginx/sites-available/video-transcriptor /etc/nginx/sites-enabled/ + +# Tester la configuration +sudo nginx -t + +# Recharger Nginx +sudo systemctl reload nginx +``` + +--- + +## SSL/HTTPS avec Let's Encrypt + +### 1. Installer Certbot + +```bash +sudo apt install -y certbot python3-certbot-nginx +``` + +### 2. Obtenir un certificat SSL + +```bash +# Obtenir et installer automatiquement le certificat +sudo certbot --nginx -d api.yourdomain.com + +# Suivez les instructions à l'écran +``` + +### 3. Renouvellement automatique + +```bash +# Tester le renouvellement +sudo certbot renew --dry-run + +# Le renouvellement automatique est configuré via cron +# Vérifier: sudo systemctl status certbot.timer +``` + +Après SSL, votre configuration Nginx sera automatiquement mise à jour pour HTTPS. + +--- + +## Surveillance et logs + +### 1. Logs de l'application + +```bash +# Avec PM2 +pm2 logs video-transcriptor + +# Avec Docker +docker-compose logs -f video-transcriptor + +# Logs Nginx +sudo tail -f /var/log/nginx/video-transcriptor-access.log +sudo tail -f /var/log/nginx/video-transcriptor-error.log +``` + +### 2. Monitoring avec PM2 (optionnel) + +```bash +# Installer PM2 monitoring +pm2 install pm2-logrotate + +# Configurer la rotation des logs +pm2 set pm2-logrotate:max_size 10M +pm2 set pm2-logrotate:retain 7 +``` + +### 3. Monitoring système + +```bash +# Installer htop pour surveiller les ressources +sudo apt install -y htop + +# Lancer htop +htop + +# Voir l'utilisation disque +df -h + +# Voir l'utilisation mémoire +free -h +``` + +--- + +## Sécurité avancée + +### 1. Firewall (UFW) + +```bash +# Installer UFW +sudo apt install -y ufw + +# Autoriser SSH (IMPORTANT AVANT D'ACTIVER!) +sudo ufw allow ssh +sudo ufw allow 22/tcp + +# Autoriser HTTP et HTTPS +sudo ufw allow 'Nginx Full' + +# Activer le firewall +sudo ufw enable + +# Vérifier le statut +sudo ufw status +``` + +### 2. Fail2Ban (protection contre brute force) + +```bash +# Installer Fail2Ban +sudo apt install -y fail2ban + +# Créer une configuration pour Nginx +sudo nano /etc/fail2ban/jail.local +``` + +Ajouter: +```ini +[nginx-limit-req] +enabled = true +filter = nginx-limit-req +port = http,https +logpath = /var/log/nginx/video-transcriptor-error.log +maxretry = 5 +findtime = 600 +bantime = 3600 +``` + +```bash +# Redémarrer Fail2Ban +sudo systemctl restart fail2ban + +# Vérifier le statut +sudo fail2ban-client status nginx-limit-req +``` + +### 3. Limitations supplémentaires + +**Limiter les tailles de fichiers uploadés** - Déjà configuré dans Nginx (`client_max_body_size 500M`) + +**Rate limiting par IP** - Déjà configuré dans Nginx (`limit_req_zone`) + +### 4. Sauvegardes automatiques + +```bash +# Créer un script de backup +sudo nano /usr/local/bin/backup-video-transcriptor.sh +``` + +```bash +#!/bin/bash +BACKUP_DIR="/backup/video-transcriptor" +APP_DIR="/var/www/videotoMP3Transcriptor" +DATE=$(date +%Y%m%d_%H%M%S) + +mkdir -p $BACKUP_DIR + +# Backup de la configuration +tar -czf $BACKUP_DIR/config_$DATE.tar.gz \ + $APP_DIR/.env \ + $APP_DIR/ecosystem.config.js + +# Backup des fichiers de sortie (optionnel, peut être volumineux) +# tar -czf $BACKUP_DIR/output_$DATE.tar.gz $APP_DIR/output + +# Garder seulement les 7 derniers backups +find $BACKUP_DIR -name "config_*.tar.gz" -mtime +7 -delete + +echo "Backup completed: $DATE" +``` + +```bash +# Rendre exécutable +sudo chmod +x /usr/local/bin/backup-video-transcriptor.sh + +# Ajouter au crontab (backup quotidien à 2h du matin) +sudo crontab -e +# Ajouter: 0 2 * * * /usr/local/bin/backup-video-transcriptor.sh +``` + +--- + +## Checklist finale de déploiement + +Avant de mettre en production, vérifiez: + +- [ ] **Sécurité** + - [ ] Token API fort généré (`API_TOKEN`) + - [ ] CORS configuré avec vos domaines (`ALLOWED_ORIGINS`) + - [ ] Fichier `.env` avec permissions 600 + - [ ] HTTPS configuré et fonctionnel + - [ ] Firewall UFW activé + +- [ ] **Configuration** + - [ ] `OPENAI_API_KEY` valide et fonctionnelle + - [ ] `NODE_ENV=production` + - [ ] Répertoire `output/` créé et accessible + - [ ] FFmpeg et yt-dlp installés + +- [ ] **Infrastructure** + - [ ] PM2 ou Docker en cours d'exécution + - [ ] Nginx reverse proxy configuré + - [ ] SSL/TLS actif (Let's Encrypt) + - [ ] Rate limiting activé + +- [ ] **Monitoring** + - [ ] Logs accessibles + - [ ] PM2 startup configuré (redémarrage auto) + - [ ] Fail2Ban actif + - [ ] Backups automatiques configurés + +- [ ] **Tests** + - [ ] Endpoint `/health` accessible + - [ ] Test d'authentification (avec et sans token) + - [ ] Test d'upload de fichier + - [ ] Test de téléchargement YouTube + +--- + +## Tests post-déploiement + +### 1. Test de santé + +```bash +curl https://api.yourdomain.com/health +# Devrait retourner: {"status":"ok","timestamp":"..."} +``` + +### 2. Test d'authentification + +```bash +# Sans token (devrait échouer avec 401) +curl https://api.yourdomain.com/info?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ + +# Avec token (devrait réussir) +curl -H "X-API-Key: VOTRE_TOKEN" \ + "https://api.yourdomain.com/info?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" +``` + +### 3. Test de download + +```bash +curl -H "X-API-Key: VOTRE_TOKEN" \ + -X POST https://api.yourdomain.com/download \ + -H "Content-Type: application/json" \ + -d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}' +``` + +--- + +## Dépannage + +### L'API ne démarre pas + +```bash +# Vérifier les logs PM2 +pm2 logs video-transcriptor + +# Vérifier les variables d'environnement +pm2 env video-transcriptor + +# Redémarrer +pm2 restart video-transcriptor +``` + +### Erreurs 502 Bad Gateway (Nginx) + +```bash +# Vérifier que l'app tourne +pm2 status + +# Vérifier les logs Nginx +sudo tail -f /var/log/nginx/error.log + +# Vérifier que le port 8888 est ouvert +sudo netstat -tlnp | grep 8888 +``` + +### Problèmes SSL + +```bash +# Vérifier le certificat +sudo certbot certificates + +# Renouveler manuellement +sudo certbot renew --force-renewal + +# Tester la configuration Nginx +sudo nginx -t +``` + +### Mémoire insuffisante + +```bash +# Vérifier l'utilisation mémoire +free -h + +# Créer un swap file (si nécessaire) +sudo fallocate -l 2G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile +echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab +``` + +--- + +## Mises à jour + +### Mise à jour de l'application + +```bash +cd /var/www/videotoMP3Transcriptor + +# Sauvegarder la config +cp .env .env.backup + +# Pull des nouvelles versions (git) +git pull + +# Mettre à jour les dépendances +npm ci --only=production + +# Redémarrer +pm2 restart video-transcriptor + +# Ou avec Docker +docker-compose down +docker-compose up -d --build +``` + +--- + +## Support et ressources + +- **Documentation API**: [docs/API.md](./API.md) +- **CLAUDE.md**: [CLAUDE.md](../CLAUDE.md) - Instructions pour Claude +- **PM2 Documentation**: https://pm2.keymetrics.io/ +- **Nginx Documentation**: https://nginx.org/en/docs/ +- **Let's Encrypt**: https://letsencrypt.org/ + +--- + +**Bon déploiement ! 🚀** diff --git a/public/app.js b/public/app.js index 76c17b3..a9218d5 100644 --- a/public/app.js +++ b/public/app.js @@ -1,5 +1,222 @@ -// API Base URL +// ==================== API CONFIGURATION ==================== const API_URL = ''; +const TOKEN_STORAGE_KEY = 'video_transcriptor_api_token'; + +// Token management +let apiToken = localStorage.getItem(TOKEN_STORAGE_KEY) || ''; + +// Helper: Get API headers with token +function getHeaders(additionalHeaders = {}) { + const headers = { ...additionalHeaders }; + if (apiToken) { + headers['X-API-Key'] = apiToken; + } + return headers; +} + +// Helper: Authenticated fetch +async function apiFetch(url, options = {}) { + const defaultHeaders = getHeaders(options.headers || {}); + const response = await fetch(url, { + ...options, + headers: defaultHeaders, + }); + + // Check for auth errors + if (response.status === 401 || response.status === 403) { + showResult('api-error', false, ` +

⚠️ Authentication Error

+

${response.status === 401 ? 'API token required' : 'Invalid API token'}

+

Please configure your API token in the configuration panel above.

+ `); + throw new Error('Authentication failed'); + } + + return response; +} + +// API Configuration Panel +const configPanel = document.getElementById('api-config-panel'); +const configHeader = document.querySelector('.config-header'); +const configContent = document.getElementById('config-content'); +const toggleConfigBtn = document.getElementById('toggle-config'); +const apiTokenInput = document.getElementById('api-token'); +const toggleVisibilityBtn = document.getElementById('toggle-token-visibility'); +const apiConfigForm = document.getElementById('api-config-form'); +const clearTokenBtn = document.getElementById('clear-token'); +const configStatus = document.getElementById('config-status'); + +// Load token on page load +if (apiToken) { + apiTokenInput.value = apiToken; + updateConnectionStatus(true); +} + +// Toggle configuration panel +function toggleConfigPanel() { + const isExpanded = configContent.style.display !== 'none'; + configContent.style.display = isExpanded ? 'none' : 'block'; + configHeader.classList.toggle('expanded', !isExpanded); +} + +configHeader.addEventListener('click', (e) => { + if (e.target.closest('.btn-toggle') || e.target === configHeader) { + toggleConfigPanel(); + } +}); + +// Toggle token visibility +toggleVisibilityBtn.addEventListener('click', () => { + const isPassword = apiTokenInput.type === 'password'; + apiTokenInput.type = isPassword ? 'text' : 'password'; + + const eyeIcon = document.getElementById('eye-icon'); + if (isPassword) { + eyeIcon.innerHTML = ` + + + `; + } else { + eyeIcon.innerHTML = ` + + + `; + } +}); + +// Update connection status UI +function updateConnectionStatus(connected, message = '') { + const indicator = configStatus.querySelector('.status-indicator'); + const text = configStatus.querySelector('.status-text'); + + if (connected) { + indicator.className = 'status-indicator status-connected'; + text.textContent = message || 'Connected ✓'; + } else { + indicator.className = 'status-indicator status-disconnected'; + text.textContent = message || 'Not configured'; + } +} + +// Test API connection +async function testApiConnection(token) { + try { + const tempToken = apiToken; + apiToken = token; // Temporarily set token for test + + const response = await apiFetch(`${API_URL}/health`); + const data = await response.json(); + + if (data.status === 'ok') { + apiToken = tempToken; // Restore original + return { success: true, message: 'Connected ✓' }; + } else { + apiToken = tempToken; + return { success: false, message: 'API error' }; + } + } catch (error) { + return { success: false, message: error.message }; + } +} + +// Save and test token +apiConfigForm.addEventListener('submit', async (e) => { + e.preventDefault(); + const token = apiTokenInput.value.trim(); + + if (!token) { + updateConnectionStatus(false, 'Please enter a token'); + return; + } + + const submitBtn = apiConfigForm.querySelector('button[type="submit"]'); + const originalText = submitBtn.innerHTML; + submitBtn.innerHTML = 'Testing...'; + submitBtn.disabled = true; + + try { + const result = await testApiConnection(token); + + if (result.success) { + // Save token + localStorage.setItem(TOKEN_STORAGE_KEY, token); + apiToken = token; + updateConnectionStatus(true, result.message); + + // Show success message + showNotification('✓ API token saved successfully!', 'success'); + + // Collapse panel after 2 seconds + setTimeout(() => { + toggleConfigPanel(); + }, 2000); + } else { + updateConnectionStatus(false, 'Connection failed'); + showNotification('✗ Failed to connect to API. Check your token.', 'error'); + } + } catch (error) { + updateConnectionStatus(false, 'Connection error'); + showNotification('✗ Error testing connection: ' + error.message, 'error'); + } finally { + submitBtn.innerHTML = originalText; + submitBtn.disabled = false; + } +}); + +// Clear token +clearTokenBtn.addEventListener('click', () => { + localStorage.removeItem(TOKEN_STORAGE_KEY); + apiToken = ''; + apiTokenInput.value = ''; + updateConnectionStatus(false, 'Token cleared'); + showNotification('Token removed', 'info'); +}); + +// Helper: Show notification toast +function showNotification(message, type = 'info') { + const toast = document.createElement('div'); + toast.className = `notification notification-${type}`; + toast.textContent = message; + toast.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + padding: 1rem 1.5rem; + background: ${type === 'success' ? 'rgba(16, 185, 129, 0.9)' : type === 'error' ? 'rgba(239, 68, 68, 0.9)' : 'rgba(59, 130, 246, 0.9)'}; + color: white; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + z-index: 10000; + animation: slideIn 0.3s ease; + font-weight: 500; + `; + + document.body.appendChild(toast); + + setTimeout(() => { + toast.style.animation = 'slideOut 0.3s ease'; + setTimeout(() => toast.remove(), 300); + }, 3000); +} + +// Add animations to document +if (!document.getElementById('notification-styles')) { + const style = document.createElement('style'); + style.id = 'notification-styles'; + style.textContent = ` + @keyframes slideIn { + from { transform: translateX(400px); opacity: 0; } + to { transform: translateX(0); opacity: 1; } + } + @keyframes slideOut { + from { transform: translateX(0); opacity: 1; } + to { transform: translateX(400px); opacity: 0; } + } + `; + document.head.appendChild(style); +} + +// ==================== TAB SWITCHING ==================== // Tab switching document.querySelectorAll('.tab').forEach(tab => { diff --git a/public/index.html b/public/index.html index 43b9ed7..f6e2617 100644 --- a/public/index.html +++ b/public/index.html @@ -13,6 +13,61 @@

Download YouTube videos, transcribe and translate them

+ +
+
+
+ 🔐 +

API Configuration

+
+ +
+ +
+ + +
+