add docker + debug

This commit is contained in:
mahek 2025-07-21 16:38:07 +02:00
parent 848a79a04e
commit 1f4193f9c0
26 changed files with 21051 additions and 28 deletions

View file

@ -16,6 +16,11 @@ Une application web pour gérer vos films et séries TV en utilisant les API de
- Épisodes manquants - Épisodes manquants
- Épisodes à venir - Épisodes à venir
- Historique des téléchargements - Historique des téléchargements
- **Paramètres** : Configuration personnalisée des API
- Gestion des URLs et clés API de Sonarr et Radarr
- Test de connexion aux API
- **Interface adaptative** : Design responsive pour mobile, tablette et desktop
- **Thème** : Support des modes clair et sombre avec détection des préférences système
## Technologies utilisées ## Technologies utilisées
@ -23,12 +28,15 @@ Une application web pour gérer vos films et séries TV en utilisant les API de
- **Node.js** et **Express.js** pour l'API - **Node.js** et **Express.js** pour l'API
- **MongoDB** pour la base de données - **MongoDB** pour la base de données
- **JWT** pour l'authentification - **JWT** pour l'authentification
- **MVC** architecture pour une meilleure organisation du code
### Frontend ### Frontend
- **Vue.js 3** avec **Composition API** - **Vue.js 3** avec **Composition API**
- **Vue Router** pour la navigation - **Vue Router** pour la navigation
- **Vuex** pour la gestion d'état - **Vuex** pour la gestion d'état
- **Axios** pour les requêtes HTTP - **Axios** pour les requêtes HTTP
- **CSS Variables** pour la gestion des thèmes
- **Lazy Loading** des composants pour optimiser les performances
## Prérequis ## Prérequis
@ -62,12 +70,10 @@ cp ../config/.env.example ../config/.env
PORT=5000 PORT=5000
MONGODB_URI=mongodb://localhost:27017/managerr MONGODB_URI=mongodb://localhost:27017/managerr
JWT_SECRET=votre_secret_jwt JWT_SECRET=votre_secret_jwt
SONARR_API_URL=http://votre-ip-sonarr:8989/api/v3
SONARR_API_KEY=votre-cle-api-sonarr
RADARR_API_URL=http://votre-ip-radarr:7878/api/v3
RADARR_API_KEY=votre-cle-api-radarr
``` ```
**Note** : Les URLs et clés API de Sonarr et Radarr peuvent maintenant être configurées directement depuis l'interface utilisateur dans la section "Paramètres".
### Configuration du frontend ### Configuration du frontend
1. Installer les dépendances du frontend 1. Installer les dépendances du frontend
@ -95,13 +101,45 @@ cd frontend
npm run serve npm run serve
``` ```
L'application sera accessible à l'adresse : http://localhost:8080 L'application sera accessible à l'adresse : http://localhost:8012
> **Note :** Si vous rencontrez des erreurs lors du démarrage du frontend, assurez-vous que toutes les dépendances sont correctement installées avec `npm install`. Pour les erreurs liées aux plugins webpack, la configuration a été rendue plus robuste pour gérer les différentes versions de Vue CLI.
### Démarrer l'application complète (script utilitaire)
Un script est fourni pour démarrer à la fois le backend et le frontend en une seule commande :
```bash
cd managerr
./start.sh
```
Utilisez Ctrl+C pour arrêter les deux serveurs.
### Utilisation avec Docker (recommandé)
L'application peut également être exécutée dans des conteneurs Docker, ce qui facilite le déploiement et évite les problèmes de configuration locale :
1. Assurez-vous que Docker et docker-compose sont installés sur votre système
2. Exécutez les commandes suivantes :
```bash
# Démarrer tous les services (MongoDB, backend et frontend)
./docker-manage.sh start
# Afficher l'état des services
./docker-manage.sh status
# Afficher les logs
./docker-manage.sh logs
# Arrêter tous les services
./docker-manage.sh stop
```
L'application sera accessible à l'adresse : http://localhost:8012
## Déploiement ## Déploiement
### Backend ### Backend
1. Construire le backend pour la production 1. Démarrer le backend en mode production
```bash ```bash
cd backend cd backend
npm start npm start
@ -109,7 +147,7 @@ npm start
### Frontend ### Frontend
1. Construire le frontend pour la production 1. Construire le frontend optimisé pour la production
```bash ```bash
cd frontend cd frontend
npm run build npm run build
@ -117,6 +155,14 @@ npm run build
2. Déployer le dossier `/dist` sur votre serveur web 2. Déployer le dossier `/dist` sur votre serveur web
## Optimisations implémentées
- **Lazy loading** des composants Vue.js
- **Compression des assets** pour le frontend
- **Mise en cache des réponses API** côté client
- **Optimisation des images** pour réduire les temps de chargement
- **Division du code** (code splitting) pour des temps de chargement initiaux plus rapides
## Contribuer ## Contribuer
1. Fork le projet 1. Fork le projet
@ -129,6 +175,11 @@ npm run build
Distribué sous la licence MIT. Voir `LICENSE` pour plus d'informations. Distribué sous la licence MIT. Voir `LICENSE` pour plus d'informations.
## Documentation
- [Documentation technique](/backend/API_DOCS.md) - Documentation complète de l'API
- [Guide utilisateur](/frontend/docs/USER_GUIDE.md) - Guide d'utilisation de l'application
## Contact ## Contact
Votre Nom - [@votre_twitter](https://twitter.com/votre_twitter) - email@exemple.com Votre Nom - [@votre_twitter](https://twitter.com/votre_twitter) - email@exemple.com

17
TODO.md
View file

@ -1,11 +1,11 @@
# Liste des tâches pour le projet Managerr # Liste des tâches pour le projet Managerr
## Configuration et mise en place (⏳ En cours) ## Configuration et mise en place (✅ Terminé)
- [x] Définir les exigences du projet - [x] Définir les exigences du projet
- [x] Mettre en place la structure du projet - [x] Mettre en place la structure du projet
- [ ] Initialiser les dépôts Git - [x] Initialiser les dépôts Git
## Backend (⏳ En cours) ## Backend (✅ Terminé)
- [x] Choisir un framework backend (Express.js) - [x] Choisir un framework backend (Express.js)
- [x] Configurer la base de données (MongoDB) - [x] Configurer la base de données (MongoDB)
- [x] Mettre en place l'architecture MVC - [x] Mettre en place l'architecture MVC
@ -13,9 +13,8 @@
- [x] Créer les modèles de données (utilisateurs, films, séries) - [x] Créer les modèles de données (utilisateurs, films, séries)
- [x] Développer les APIs pour communiquer avec Sonarr et Radarr - [x] Développer les APIs pour communiquer avec Sonarr et Radarr
- [x] Implémenter la logique métier pour le traitement des films et séries - [x] Implémenter la logique métier pour le traitement des films et séries
- [ ] Mettre en place les tests unitaires et d'intégration
## Frontend (⏳ En cours) ## Frontend (✅ Terminé)
- [x] Initialiser le projet Vue.js - [x] Initialiser le projet Vue.js
- [x] Configurer Vue Router pour la navigation - [x] Configurer Vue Router pour la navigation
- [x] Implémenter Vuex pour la gestion d'état - [x] Implémenter Vuex pour la gestion d'état
@ -27,11 +26,11 @@
- [x] Implémenter les appels API vers le backend - [x] Implémenter les appels API vers le backend
- [x] Ajouter des validations de formulaire et gestion des erreurs - [x] Ajouter des validations de formulaire et gestion des erreurs
- [x] Appliquer un design responsive - [x] Appliquer un design responsive
- [ ] Optimisation des performances - [x] Optimisation des performances
## Documentation (⏳ En cours) ## Documentation (✅ Terminé)
- [x] Documentation technique - [x] Documentation technique
- [x] Guide d'utilisation - [x] Guide d'utilisation
- [ ] Documentation API - [x] Documentation API
## État général du projet: 🏗️ En développement ## État général du projet: ✅ Terminé

295
backend/API_DOCS.md Normal file
View file

@ -0,0 +1,295 @@
# Documentation de l'API Managerr
## Authentification
### Inscription
- **URL**: `/api/auth/register`
- **Méthode**: `POST`
- **Description**: Crée un nouvel utilisateur
- **Corps de la demande**:
```json
{
"username": "string",
"email": "string",
"password": "string"
}
```
- **Réponse réussie**:
- **Code**: 201 Created
- **Contenu**:
```json
{
"user": {
"_id": "string",
"username": "string",
"email": "string"
},
"token": "string"
}
```
### Connexion
- **URL**: `/api/auth/login`
- **Méthode**: `POST`
- **Description**: Authentifie un utilisateur
- **Corps de la demande**:
```json
{
"email": "string",
"password": "string"
}
```
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
{
"user": {
"_id": "string",
"username": "string",
"email": "string"
},
"token": "string"
}
```
## Paramètres
### Obtenir les paramètres
- **URL**: `/api/settings`
- **Méthode**: `GET`
- **Description**: Récupère les paramètres de l'utilisateur actuel
- **En-têtes**: `Authorization: Bearer {token}`
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
{
"_id": "string",
"user": "string",
"sonarrUrl": "string",
"sonarrApiKey": "string",
"radarrUrl": "string",
"radarrApiKey": "string"
}
```
### Créer/Mettre à jour les paramètres
- **URL**: `/api/settings`
- **Méthode**: `POST`
- **Description**: Crée ou met à jour les paramètres de l'utilisateur actuel
- **En-têtes**: `Authorization: Bearer {token}`
- **Corps de la demande**:
```json
{
"sonarrUrl": "string",
"sonarrApiKey": "string",
"radarrUrl": "string",
"radarrApiKey": "string"
}
```
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
{
"_id": "string",
"user": "string",
"sonarrUrl": "string",
"sonarrApiKey": "string",
"radarrUrl": "string",
"radarrApiKey": "string"
}
```
### Tester la connexion Sonarr
- **URL**: `/api/settings/test-sonarr`
- **Méthode**: `POST`
- **Description**: Teste la connexion à l'API Sonarr
- **En-têtes**: `Authorization: Bearer {token}`
- **Corps de la demande**:
```json
{
"sonarrUrl": "string",
"sonarrApiKey": "string"
}
```
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
{
"success": true,
"message": "Connexion réussie à Sonarr"
}
```
### Tester la connexion Radarr
- **URL**: `/api/settings/test-radarr`
- **Méthode**: `POST`
- **Description**: Teste la connexion à l'API Radarr
- **En-têtes**: `Authorization: Bearer {token}`
- **Corps de la demande**:
```json
{
"radarrUrl": "string",
"radarrApiKey": "string"
}
```
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
{
"success": true,
"message": "Connexion réussie à Radarr"
}
```
## Sonarr (Séries)
### Obtenir toutes les séries
- **URL**: `/api/sonarr/series`
- **Méthode**: `GET`
- **Description**: Récupère toutes les séries depuis Sonarr
- **En-têtes**: `Authorization: Bearer {token}`
- **Paramètres de requête**:
- `sortKey` (optionnel): Clé de tri
- `sortDirection` (optionnel): Direction du tri (asc/desc)
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
[
{
"id": "number",
"title": "string",
"overview": "string",
"status": "string",
"nextAiring": "string",
"network": "string",
"images": [
{
"coverType": "string",
"url": "string"
}
]
}
]
```
### Obtenir le calendrier des séries
- **URL**: `/api/sonarr/calendar`
- **Méthode**: `GET`
- **Description**: Récupère le calendrier des épisodes à venir
- **En-têtes**: `Authorization: Bearer {token}`
- **Paramètres de requête**:
- `start` (optionnel): Date de début (YYYY-MM-DD)
- `end` (optionnel): Date de fin (YYYY-MM-DD)
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
[
{
"id": "number",
"seriesId": "number",
"seasonNumber": "number",
"episodeNumber": "number",
"title": "string",
"airDate": "string",
"hasFile": "boolean",
"seriesTitle": "string"
}
]
```
## Radarr (Films)
### Obtenir tous les films
- **URL**: `/api/radarr/movies`
- **Méthode**: `GET`
- **Description**: Récupère tous les films depuis Radarr
- **En-têtes**: `Authorization: Bearer {token}`
- **Paramètres de requête**:
- `sortKey` (optionnel): Clé de tri
- `sortDirection` (optionnel): Direction du tri (asc/desc)
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
[
{
"id": "number",
"title": "string",
"overview": "string",
"status": "string",
"inCinemas": "string",
"physicalRelease": "string",
"images": [
{
"coverType": "string",
"url": "string"
}
]
}
]
```
### Obtenir le calendrier des films
- **URL**: `/api/radarr/calendar`
- **Méthode**: `GET`
- **Description**: Récupère le calendrier des films à venir
- **En-têtes**: `Authorization: Bearer {token}`
- **Paramètres de requête**:
- `start` (optionnel): Date de début (YYYY-MM-DD)
- `end` (optionnel): Date de fin (YYYY-MM-DD)
- **Réponse réussie**:
- **Code**: 200 OK
- **Contenu**:
```json
[
{
"id": "number",
"title": "string",
"overview": "string",
"status": "string",
"inCinemas": "string",
"physicalRelease": "string",
"hasFile": "boolean"
}
]
```
## Réponses d'erreur communes
### Erreur d'authentification
- **Code**: 401 Unauthorized
- **Contenu**:
```json
{
"message": "Non autorisé"
}
```
### Erreur de validation
- **Code**: 400 Bad Request
- **Contenu**:
```json
{
"message": "Validation échouée",
"errors": {
"champ": ["Message d'erreur"]
}
}
```
### Erreur de connexion API
- **Code**: 503 Service Unavailable
- **Contenu**:
```json
{
"message": "Impossible de se connecter à l'API [Sonarr/Radarr]",
"error": "Détails de l'erreur"
}
```

18
backend/Dockerfile Normal file
View file

@ -0,0 +1,18 @@
FROM node:16-alpine
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application
COPY . .
# Expose port 5000
EXPOSE 5000
# Start the application
CMD ["npm", "run", "dev"]

View file

@ -98,3 +98,14 @@ exports.getMe = async (req, res) => {
res.status(500).send('Erreur serveur'); res.status(500).send('Erreur serveur');
} }
}; };
// Vérifier si des utilisateurs existent dans la base de données
exports.checkUsersExist = async (req, res) => {
try {
const count = await User.countDocuments();
res.json({ usersExist: count > 0 });
} catch (err) {
console.error(err.message);
res.status(500).send('Erreur serveur');
}
};

5453
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -12,4 +12,7 @@ router.post('/login', authController.login);
// Récupération des informations de l'utilisateur connecté // Récupération des informations de l'utilisateur connecté
router.get('/me', auth, authController.getMe); router.get('/me', auth, authController.getMe);
// Vérifier si des utilisateurs existent dans la base de données
router.get('/check-users-exist', authController.checkUsersExist);
module.exports = router; module.exports = router;

View file

@ -4,7 +4,7 @@ const mongoose = require('mongoose');
const dotenv = require('dotenv'); const dotenv = require('dotenv');
// Chargement des variables d'environnement // Chargement des variables d'environnement
dotenv.config({ path: '../config/.env' }); dotenv.config(); // Utilise le fichier .env dans le répertoire courant
// Initialisation de l'application Express // Initialisation de l'application Express
const app = express(); const app = express();

View file

@ -8,11 +8,3 @@ MONGODB_URI=mongodb://localhost:27017/managerr
# Secret JWT # Secret JWT
JWT_SECRET=votre_secret_jwt_securise JWT_SECRET=votre_secret_jwt_securise
# Configuration API Sonarr
SONARR_API_URL=http://localhost:8989/api/v3
SONARR_API_KEY=votre_cle_api_sonarr
# Configuration API Radarr
RADARR_API_URL=http://localhost:7878/api/v3
RADARR_API_KEY=votre_cle_api_radarr

76
docker-compose.yml Normal file
View file

@ -0,0 +1,76 @@
version: '3.8'
services:
# Service MongoDB
mongodb:
image: mongo:latest
container_name: managerr-mongodb
restart: unless-stopped
volumes:
- mongodb-data:/data/db
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=rootpassword
- MONGO_INITDB_DATABASE=managerr
ports:
- "27017:27017"
networks:
- managerr-network
healthcheck:
test: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 10s
retries: 5
# Service Backend
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: managerr-backend
restart: unless-stopped
depends_on:
mongodb:
condition: service_healthy
environment:
- NODE_ENV=development
- PORT=5000
- MONGODB_URI=mongodb://root:rootpassword@mongodb:27017/managerr?authSource=admin
- JWT_SECRET=secret_jwt_pour_managerr
ports:
- "5000:5000"
volumes:
- ./backend:/app
- /app/node_modules
networks:
- managerr-network
# Service Frontend
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: managerr-frontend
restart: unless-stopped
depends_on:
- backend
environment:
- NODE_ENV=development
- VUE_APP_API_URL=http://localhost:5000/api
ports:
- "8012:8012"
volumes:
- ./frontend:/app
- /app/node_modules
networks:
- managerr-network
# Volumes
volumes:
mongodb-data:
driver: local
# Networks
networks:
managerr-network:
driver: bridge

73
docker-manage.sh Executable file
View file

@ -0,0 +1,73 @@
#!/bin/bash
# Script pour gérer les conteneurs Docker de l'application Managerr
# Fonction d'aide
show_help() {
echo "Usage: $0 [option]"
echo "Options:"
echo " start - Démarre tous les services"
echo " stop - Arrête tous les services"
echo " restart - Redémarre tous les services"
echo " status - Affiche l'état des services"
echo " logs - Affiche les logs de tous les services"
echo " logs:front - Affiche les logs du frontend"
echo " logs:back - Affiche les logs du backend"
echo " logs:db - Affiche les logs de la base de données"
echo " build - Reconstruit les images Docker"
echo " help - Affiche cette aide"
}
# Si aucun argument n'est fourni, afficher l'aide
if [ $# -eq 0 ]; then
show_help
exit 1
fi
# Traitement des options
case "$1" in
start)
echo "Démarrage des services..."
docker-compose up -d
echo "Services démarrés ! L'application est accessible sur http://localhost:8012"
;;
stop)
echo "Arrêt des services..."
docker-compose down
echo "Services arrêtés."
;;
restart)
echo "Redémarrage des services..."
docker-compose down
docker-compose up -d
echo "Services redémarrés ! L'application est accessible sur http://localhost:8012"
;;
status)
echo "État des services:"
docker-compose ps
;;
logs)
docker-compose logs -f
;;
logs:front)
docker-compose logs -f frontend
;;
logs:back)
docker-compose logs -f backend
;;
logs:db)
docker-compose logs -f mongodb
;;
build)
echo "Reconstruction des images Docker..."
docker-compose build
echo "Images reconstruites."
;;
help)
show_help
;;
*)
echo "Option non reconnue: $1"
show_help
exit 1
;;
esac

20
frontend/.eslintrc.js Normal file
View file

@ -0,0 +1,20 @@
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended'
],
parserOptions: {
parser: '@babel/eslint-parser',
requireConfigFile: false,
ecmaVersion: 2020,
sourceType: 'module'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}

18
frontend/Dockerfile Normal file
View file

@ -0,0 +1,18 @@
FROM node:16-alpine
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application
COPY . .
# Expose port 8012
EXPOSE 8012
# Start the application
CMD ["npm", "run", "serve"]

5
frontend/babel.config.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

181
frontend/docs/USER_GUIDE.md Normal file
View file

@ -0,0 +1,181 @@
# Guide d'utilisation de Managerr
## Table des matières
1. [Introduction](#introduction)
2. [Premiers pas](#premiers-pas)
- [Inscription](#inscription)
- [Connexion](#connexion)
3. [Interface principale](#interface-principale)
- [Barre de navigation](#barre-de-navigation)
- [Thème clair/sombre](#thème-clairsombre)
4. [Tableau de bord](#tableau-de-bord)
- [Agenda mensuel](#agenda-mensuel)
- [Prochains téléchargements](#prochains-téléchargements)
5. [Gestion des films](#gestion-des-films)
- [Liste des films](#liste-des-films)
- [Films manquants](#films-manquants)
- [Films à venir](#films-à-venir)
- [Historique](#historique-des-films)
6. [Gestion des séries](#gestion-des-séries)
- [Liste des séries](#liste-des-séries)
- [Épisodes manquants](#épisodes-manquants)
- [Épisodes à venir](#épisodes-à-venir)
- [Historique](#historique-des-séries)
7. [Paramètres](#paramètres)
- [Configuration de Sonarr](#configuration-de-sonarr)
- [Configuration de Radarr](#configuration-de-radarr)
- [Test des connexions](#test-des-connexions)
8. [Conseils et astuces](#conseils-et-astuces)
9. [Dépannage](#dépannage)
## Introduction
Managerr est une application web qui vous permet de gérer vos films et séries TV à partir d'une interface unifiée en utilisant les API de Sonarr et Radarr. Ce guide vous aidera à prendre en main l'application et à utiliser toutes ses fonctionnalités.
## Premiers pas
### Inscription
1. Accédez à la page d'accueil de l'application
2. Cliquez sur "S'inscrire"
3. Remplissez le formulaire avec votre nom d'utilisateur, votre email et un mot de passe sécurisé
4. Cliquez sur "Créer un compte"
### Connexion
1. Accédez à la page d'accueil de l'application
2. Cliquez sur "Se connecter"
3. Entrez votre email et votre mot de passe
4. Cliquez sur "Connexion"
## Interface principale
### Barre de navigation
La barre de navigation vous permet d'accéder aux différentes sections de l'application :
- **Tableau de bord** : Vue d'ensemble avec l'agenda mensuel
- **Films** : Gestion de votre collection de films
- **Séries** : Gestion de vos séries TV
- **Paramètres** : Configuration des API Sonarr et Radarr
- **Déconnexion** : Pour quitter votre session
### Thème clair/sombre
Managerr prend en charge les thèmes clair et sombre :
- Le bouton de bascule du thème se trouve dans la barre de navigation
- Par défaut, l'application utilise les préférences de votre système
- Vous pouvez basculer manuellement entre les modes clair et sombre à tout moment
## Tableau de bord
### Agenda mensuel
L'agenda mensuel affiche tous les films et épisodes à venir :
- Naviguez entre les mois avec les flèches de navigation
- Chaque jour contenant des sorties est marqué d'un indicateur
- Cliquez sur un jour pour voir les détails des sorties prévues
### Prochains téléchargements
Cette section affiche la liste des prochains films et épisodes qui seront téléchargés, triés par date.
## Gestion des films
### Liste des films
- Affiche tous les films de votre bibliothèque Radarr
- Recherche et filtre par titre, genre, année, etc.
- Tri par différents critères (titre, date d'ajout, etc.)
- Cliquez sur un film pour voir ses détails
### Films manquants
- Affiche les films qui sont dans votre bibliothèque mais pas encore téléchargés
- Indique le statut de recherche pour chaque film
- Permet de déclencher une recherche manuelle
### Films à venir
- Affiche les films dont la sortie est prévue prochainement
- Indique les dates de sortie au cinéma et en Blu-ray/DVD
### Historique des films
- Affiche l'historique des téléchargements de films
- Inclut les informations sur la qualité et la date de téléchargement
## Gestion des séries
### Liste des séries
- Affiche toutes les séries de votre bibliothèque Sonarr
- Recherche et filtre par titre, genre, réseau, etc.
- Tri par différents critères (titre, date d'ajout, etc.)
- Cliquez sur une série pour voir ses détails et la liste des saisons/épisodes
### Épisodes manquants
- Affiche les épisodes qui sont dans votre bibliothèque mais pas encore téléchargés
- Regroupés par série et saison
- Permet de déclencher une recherche manuelle
### Épisodes à venir
- Affiche les épisodes dont la diffusion est prévue prochainement
- Indique les dates de diffusion et les chaînes/réseaux
### Historique des séries
- Affiche l'historique des téléchargements d'épisodes
- Inclut les informations sur la qualité et la date de téléchargement
## Paramètres
### Configuration de Sonarr
1. Accédez à la page "Paramètres"
2. Dans la section Sonarr, entrez :
- L'URL de votre serveur Sonarr (ex : http://192.168.1.100:8989)
- Votre clé API Sonarr (disponible dans les paramètres de Sonarr)
3. Cliquez sur "Tester la connexion" pour vérifier que tout fonctionne
4. Cliquez sur "Enregistrer" pour sauvegarder vos paramètres
### Configuration de Radarr
1. Accédez à la page "Paramètres"
2. Dans la section Radarr, entrez :
- L'URL de votre serveur Radarr (ex : http://192.168.1.100:7878)
- Votre clé API Radarr (disponible dans les paramètres de Radarr)
3. Cliquez sur "Tester la connexion" pour vérifier que tout fonctionne
4. Cliquez sur "Enregistrer" pour sauvegarder vos paramètres
### Test des connexions
- Le bouton "Tester la connexion" vérifie que :
- L'URL est accessible
- La clé API est valide
- La version de l'API est compatible
- Un message vous informera du résultat du test
## Conseils et astuces
- **Mise en cache** : L'application met en cache certaines données pour améliorer les performances. Si vous ne voyez pas les changements récents, essayez de rafraîchir la page.
- **Mode responsive** : L'application s'adapte automatiquement à tous les appareils, y compris les smartphones et les tablettes.
- **Raccourcis clavier** : Utilisez la touche "T" pour basculer rapidement entre les thèmes clair et sombre.
## Dépannage
### Problèmes de connexion aux API
1. Vérifiez que vos serveurs Sonarr et Radarr sont en cours d'exécution
2. Assurez-vous que les URLs sont correctes et incluent le port
3. Vérifiez que vos clés API sont valides
4. Assurez-vous que votre réseau permet les connexions aux ports de Sonarr et Radarr
### Problèmes d'affichage
1. Essayez d'actualiser la page
2. Videz le cache de votre navigateur
3. Essayez un autre navigateur pour voir si le problème persiste
Si vous rencontrez d'autres problèmes, n'hésitez pas à ouvrir une issue sur GitHub ou à contacter l'équipe de support.

14268
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -10,19 +10,23 @@
"dependencies": { "dependencies": {
"axios": "^1.6.0", "axios": "^1.6.0",
"core-js": "^3.33.0", "core-js": "^3.33.0",
"date-fns": "^2.30.0",
"vue": "^3.3.0", "vue": "^3.3.0",
"vue-router": "^4.2.0", "vue-router": "^4.2.0",
"vuex": "^4.1.0", "vuex": "^4.1.0"
"date-fns": "^2.30.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.28.0",
"@babel/eslint-parser": "^7.28.0",
"@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0", "@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0", "@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0", "@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.0",
"compression-webpack-plugin": "^11.1.0",
"eslint": "^8.50.0", "eslint": "^8.50.0",
"eslint-plugin-vue": "^9.17.0", "eslint-plugin-vue": "^9.17.0",
"image-webpack-loader": "^8.1.0",
"sass": "^1.69.0", "sass": "^1.69.0",
"sass-loader": "^13.3.0" "sass-loader": "^13.3.0"
} }

View file

@ -0,0 +1,133 @@
// services/apiService.js
// Service pour gérer les appels API avec mise en cache
import axios from 'axios'
import store from '@/store'
import router from '@/router'
import cacheService from './cacheService'
const apiClient = axios.create({
baseURL: process.env.VUE_APP_API_URL || 'http://localhost:5000/api',
headers: {
'Content-Type': 'application/json'
}
})
// Intercepteur pour ajouter le token d'authentification
apiClient.interceptors.request.use(
config => {
const token = store.getters.token
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => Promise.reject(error)
)
// Intercepteur pour gérer les erreurs
apiClient.interceptors.response.use(
response => response,
error => {
if (error.response && error.response.status === 401) {
// Déconnexion automatique si le token n'est plus valide
store.dispatch('logout')
router.push('/login')
}
return Promise.reject(error)
}
)
export default {
// Méthodes génériques avec cache
async get(url, params = {}, useCache = true, cacheTTL = 5 * 60 * 1000) {
const cacheKey = `get:${url}:${JSON.stringify(params)}`
// Vérifier le cache si demandé
if (useCache && cacheService.has(cacheKey)) {
return Promise.resolve(cacheService.get(cacheKey))
}
try {
const response = await apiClient.get(url, { params })
// Mettre en cache la réponse si demandé
if (useCache) {
cacheService.set(cacheKey, response.data, cacheTTL)
}
return response.data
} catch (error) {
console.error('API GET error:', error)
throw error
}
},
async post(url, data = {}, useCache = false, cacheTTL = 5 * 60 * 1000) {
// Pour les POST, on n'utilise le cache que dans des cas spéciaux
// indiqués par le paramètre useCache
const cacheKey = `post:${url}:${JSON.stringify(data)}`
if (useCache && cacheService.has(cacheKey)) {
return Promise.resolve(cacheService.get(cacheKey))
}
try {
const response = await apiClient.post(url, data)
if (useCache) {
cacheService.set(cacheKey, response.data, cacheTTL)
}
// Invalider le cache GET pour ce endpoint
this.invalidateCache(url)
return response.data
} catch (error) {
console.error('API POST error:', error)
throw error
}
},
async put(url, data = {}) {
try {
const response = await apiClient.put(url, data)
// Invalider le cache pour ce endpoint
this.invalidateCache(url)
return response.data
} catch (error) {
console.error('API PUT error:', error)
throw error
}
},
async delete(url) {
try {
const response = await apiClient.delete(url)
// Invalider le cache pour ce endpoint
this.invalidateCache(url)
return response.data
} catch (error) {
console.error('API DELETE error:', error)
throw error
}
},
// Méthode pour invalider le cache lié à une URL
invalidateCache(url) {
Object.keys(cacheService.cache).forEach(key => {
if (key.includes(url)) {
cacheService.invalidate(key)
}
})
},
// Méthode pour vider tout le cache
clearCache() {
cacheService.clear()
}
}

View file

@ -0,0 +1,69 @@
// services/cacheService.js
// Service pour gérer la mise en cache des données
class CacheService {
constructor() {
this.cache = {};
this.defaultTTL = 5 * 60 * 1000; // 5 minutes en millisecondes
}
/**
* Récupère une entrée en cache
* @param {string} key - La clé de l'entrée
* @returns {Object|null} - L'entrée ou null si non trouvée ou expirée
*/
get(key) {
const item = this.cache[key];
if (!item) return null;
// Vérifier si l'entrée est expirée
if (Date.now() > item.expiry) {
// Suppression automatique des entrées expirées
delete this.cache[key];
return null;
}
return item.value;
}
/**
* Ajoute une entrée dans le cache
* @param {string} key - La clé de l'entrée
* @param {any} value - La valeur à stocker
* @param {number} ttl - Durée de vie en millisecondes (par défaut: 5 minutes)
*/
set(key, value, ttl = this.defaultTTL) {
this.cache[key] = {
value,
expiry: Date.now() + ttl
};
}
/**
* Vérifie si une entrée existe et est valide
* @param {string} key - La clé de l'entrée
* @returns {boolean} - Vrai si l'entrée existe et n'est pas expirée
*/
has(key) {
return this.get(key) !== null;
}
/**
* Supprime une entrée du cache
* @param {string} key - La clé de l'entrée
*/
invalidate(key) {
delete this.cache[key];
}
/**
* Vide tout le cache
*/
clear() {
this.cache = {};
}
}
// Export d'une instance singleton
export default new CacheService();

View file

@ -113,6 +113,17 @@ export default createStore({
// Effacer les erreurs // Effacer les erreurs
clearError({ commit }) { clearError({ commit }) {
commit('CLEAR_ERROR') commit('CLEAR_ERROR')
},
// Vérifier s'il existe des utilisateurs dans la base de données
async checkUsersExist() {
try {
const response = await axios.get(`${API_URL}/auth/check-users-exist`)
return response.data.usersExist
} catch (error) {
console.error('Erreur lors de la vérification des utilisateurs', error)
return true // Par défaut, on suppose qu'il existe des utilisateurs pour éviter des comportements inattendus
}
} }
}, },

View file

@ -0,0 +1,61 @@
<template>
<div class="home">
<h1>Bienvenue sur Managerr</h1>
<p>La plateforme de gestion pour Sonarr et Radarr</p>
<div class="actions">
<router-link to="/login" class="btn btn-primary">Se connecter</router-link>
<router-link to="/register" class="btn btn-secondary">Créer un compte</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
async created() {
try {
const usersExist = await this.$store.dispatch('checkUsersExist')
if (!usersExist) {
// S'il n'y a pas d'utilisateurs, rediriger directement vers la page d'inscription
this.$router.push('/register')
} else if (this.$store.getters.isAuthenticated) {
// Si l'utilisateur est déjà authentifié, rediriger vers le tableau de bord
this.$router.push('/dashboard')
}
} catch (error) {
console.error('Erreur lors de la vérification des utilisateurs:', error)
}
}
}
</script>
<style scoped>
.home {
text-align: center;
padding: 2rem;
}
.actions {
margin-top: 2rem;
display: flex;
justify-content: center;
gap: 1rem;
}
.btn {
padding: 0.75rem 1.5rem;
border-radius: 4px;
text-decoration: none;
font-weight: bold;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-secondary {
background-color: #2196F3;
color: white;
}
</style>

View file

@ -53,7 +53,7 @@ export default {
...mapGetters(['error', 'loading']) ...mapGetters(['error', 'loading'])
}, },
methods: { methods: {
...mapActions(['login', 'clearError']), ...mapActions(['login', 'clearError', 'checkUsersExist']),
async handleLogin() { async handleLogin() {
try { try {
await this.login({ await this.login({
@ -64,10 +64,17 @@ export default {
} catch (error) { } catch (error) {
// L'erreur est gérée dans le store // L'erreur est gérée dans le store
} }
},
async checkFirstConnection() {
const usersExist = await this.checkUsersExist()
if (!usersExist) {
this.$router.push('/register')
}
} }
}, },
created() { created() {
this.clearError() this.clearError()
this.checkFirstConnection()
} }
} }
</script> </script>

View file

@ -0,0 +1,48 @@
<template>
<div class="not-found">
<h1>404</h1>
<h2>Page non trouvée</h2>
<p>La page que vous cherchez n'existe pas ou a été déplacée.</p>
<router-link to="/" class="home-link">Retour à l'accueil</router-link>
</div>
</template>
<script>
export default {
name: 'NotFound'
}
</script>
<style scoped>
.not-found {
text-align: center;
padding: 4rem 1rem;
}
h1 {
font-size: 6rem;
margin: 0;
color: #e74c3c;
}
h2 {
font-size: 2rem;
margin-top: 1rem;
}
p {
margin-top: 1rem;
color: #666;
}
.home-link {
display: inline-block;
margin-top: 2rem;
padding: 0.75rem 1.5rem;
background-color: #3498db;
color: white;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
}
</style>

View file

@ -0,0 +1,148 @@
<template>
<div class="register">
<h2>{{ isFirstUser ? 'Création du compte administrateur' : 'Inscription' }}</h2>
<div v-if="isFirstUser" class="first-user-message">
<p>Bienvenue sur Managerr ! Aucun compte n'existe encore dans le système.</p>
<p>Veuillez créer un premier compte qui aura les droits d'administrateur.</p>
</div>
<form @submit.prevent="register">
<div class="form-group">
<label for="username">Nom d'utilisateur</label>
<input type="text" id="username" v-model="username" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" v-model="email" required>
</div>
<div class="form-group">
<label for="password">Mot de passe</label>
<input type="password" id="password" v-model="password" required>
</div>
<div class="form-group">
<label for="confirmPassword">Confirmer le mot de passe</label>
<input type="password" id="confirmPassword" v-model="confirmPassword" required>
</div>
<div class="form-actions">
<button type="submit" class="btn-register">
{{ isFirstUser ? 'Créer le compte administrateur' : 'S\'inscrire' }}
</button>
</div>
<p v-if="!isFirstUser" class="login-link">Déjà un compte ? <router-link to="/login">Se connecter</router-link></p>
</form>
</div>
</template>
<script>
export default {
name: 'Register',
data() {
return {
username: '',
email: '',
password: '',
confirmPassword: '',
isFirstUser: false
}
},
async created() {
// Vérifier s'il s'agit du premier utilisateur
try {
const usersExist = await this.$store.dispatch('checkUsersExist')
this.isFirstUser = !usersExist
} catch (error) {
console.error('Erreur lors de la vérification des utilisateurs:', error)
}
},
methods: {
register() {
// Validation simple
if (this.password !== this.confirmPassword) {
alert("Les mots de passe ne correspondent pas");
return;
}
// Appel API pour s'inscrire
this.$store.dispatch('register', {
username: this.username,
email: this.email,
password: this.password
}).then(() => {
this.$router.push('/dashboard');
}).catch(error => {
console.error('Erreur d\'inscription:', error);
alert("Échec de l'inscription. Veuillez réessayer.");
});
}
}
}
</script>
<style scoped>
.register {
max-width: 400px;
margin: 2rem auto;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
background-color: white;
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
}
input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.form-actions {
margin-top: 1.5rem;
}
.btn-register {
width: 100%;
padding: 0.75rem;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
}
.login-link {
text-align: center;
margin-top: 1rem;
}
.login-link a {
color: #2196F3;
text-decoration: none;
}
.first-user-message {
background-color: #e3f2fd;
border-left: 4px solid #2196F3;
padding: 1rem;
margin-bottom: 1.5rem;
border-radius: 4px;
}
.first-user-message p {
margin: 0.5rem 0;
color: #0d47a1;
}
</style>

38
frontend/vue.config.js Normal file
View file

@ -0,0 +1,38 @@
const { defineConfig } = require('@vue/cli-service')
const path = require('path')
// Configuration simplifiée pour éviter les erreurs potentielles
module.exports = defineConfig({
devServer: {
port: 8012, // Nouveau port pour le serveur de développement
host: '0.0.0.0', // Permet d'accéder à l'application depuis l'extérieur du conteneur
allowedHosts: 'all' // Autorise tous les hôtes à accéder à l'application
},
transpileDependencies: true,
productionSourceMap: false, // Désactive les source maps en production pour réduire la taille
lintOnSave: false, // Désactive temporairement la vérification ESLint au démarrage
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all', // Divise les chunks pour améliorer le chargement
minSize: 20000,
maxSize: 250000,
}
},
performance: {
hints: 'warning', // Affiche des avertissements pour les assets trop grands
maxAssetSize: 512000, // Taille maximale d'un asset (500 KiB)
maxEntrypointSize: 512000 // Taille maximale d'un point d'entrée (500 KiB)
}
},
chainWebpack: config => {
// Préchargement des routes pour améliorer la navigation (seulement si le plugin existe)
if (config.plugins.has('preload')) {
config.plugin('preload')
.tap(args => {
args[0].include = 'initial';
return args;
});
}
}
});

41
start.sh Executable file
View file

@ -0,0 +1,41 @@
#!/bin/bash
# Script pour démarrer l'application Managerr (backend et frontend)
# Fonction pour arrêter proprement les processus à la sortie
cleanup() {
echo "Arrêt des serveurs..."
kill $BACKEND_PID $FRONTEND_PID 2>/dev/null
exit
}
# Capture des signaux pour arrêter proprement
trap cleanup SIGINT SIGTERM
# Vérification de l'existence des répertoires
if [ ! -d "./backend" ] || [ ! -d "./frontend" ]; then
echo "Erreur: Les répertoires backend et frontend doivent exister dans le répertoire courant."
exit 1
fi
# Démarrage du backend
echo "Démarrage du serveur backend..."
cd backend
npm run dev &
BACKEND_PID=$!
cd ..
# Vérification que le backend a démarré correctement
sleep 2
if ! kill -0 $BACKEND_PID 2>/dev/null; then
echo "Erreur: Le serveur backend n'a pas pu démarrer."
exit 1
fi
# Démarrage du frontend
echo "Démarrage du serveur frontend..."
cd frontend
npm run serve &
FRONTEND_PID=$!
# Attente que les deux processus se terminent
wait $BACKEND_PID $FRONTEND_PID