add docker + debug
This commit is contained in:
parent
848a79a04e
commit
1f4193f9c0
26 changed files with 21051 additions and 28 deletions
65
README.md
65
README.md
|
|
@ -16,6 +16,11 @@ Une application web pour gérer vos films et séries TV en utilisant les API de
|
|||
- Épisodes manquants
|
||||
- Épisodes à venir
|
||||
- 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
|
||||
|
||||
|
|
@ -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
|
||||
- **MongoDB** pour la base de données
|
||||
- **JWT** pour l'authentification
|
||||
- **MVC** architecture pour une meilleure organisation du code
|
||||
|
||||
### Frontend
|
||||
- **Vue.js 3** avec **Composition API**
|
||||
- **Vue Router** pour la navigation
|
||||
- **Vuex** pour la gestion d'état
|
||||
- **Axios** pour les requêtes HTTP
|
||||
- **CSS Variables** pour la gestion des thèmes
|
||||
- **Lazy Loading** des composants pour optimiser les performances
|
||||
|
||||
## Prérequis
|
||||
|
||||
|
|
@ -62,12 +70,10 @@ cp ../config/.env.example ../config/.env
|
|||
PORT=5000
|
||||
MONGODB_URI=mongodb://localhost:27017/managerr
|
||||
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
|
||||
|
||||
1. Installer les dépendances du frontend
|
||||
|
|
@ -95,13 +101,45 @@ cd frontend
|
|||
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
|
||||
|
||||
### Backend
|
||||
|
||||
1. Construire le backend pour la production
|
||||
1. Démarrer le backend en mode production
|
||||
```bash
|
||||
cd backend
|
||||
npm start
|
||||
|
|
@ -109,7 +147,7 @@ npm start
|
|||
|
||||
### Frontend
|
||||
|
||||
1. Construire le frontend pour la production
|
||||
1. Construire le frontend optimisé pour la production
|
||||
```bash
|
||||
cd frontend
|
||||
npm run build
|
||||
|
|
@ -117,6 +155,14 @@ npm run build
|
|||
|
||||
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
|
||||
|
||||
1. Fork le projet
|
||||
|
|
@ -129,6 +175,11 @@ npm run build
|
|||
|
||||
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
|
||||
|
||||
Votre Nom - [@votre_twitter](https://twitter.com/votre_twitter) - email@exemple.com
|
||||
|
|
|
|||
17
TODO.md
17
TODO.md
|
|
@ -1,11 +1,11 @@
|
|||
# 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] 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] Configurer la base de données (MongoDB)
|
||||
- [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] 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
|
||||
- [ ] Mettre en place les tests unitaires et d'intégration
|
||||
|
||||
## Frontend (⏳ En cours)
|
||||
## Frontend (✅ Terminé)
|
||||
- [x] Initialiser le projet Vue.js
|
||||
- [x] Configurer Vue Router pour la navigation
|
||||
- [x] Implémenter Vuex pour la gestion d'état
|
||||
|
|
@ -27,11 +26,11 @@
|
|||
- [x] Implémenter les appels API vers le backend
|
||||
- [x] Ajouter des validations de formulaire et gestion des erreurs
|
||||
- [x] Appliquer un design responsive
|
||||
- [ ] Optimisation des performances
|
||||
- [x] Optimisation des performances
|
||||
|
||||
## Documentation (⏳ En cours)
|
||||
## Documentation (✅ Terminé)
|
||||
- [x] Documentation technique
|
||||
- [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
295
backend/API_DOCS.md
Normal 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
18
backend/Dockerfile
Normal 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"]
|
||||
|
|
@ -98,3 +98,14 @@ exports.getMe = async (req, res) => {
|
|||
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
5453
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -12,4 +12,7 @@ router.post('/login', authController.login);
|
|||
// Récupération des informations de l'utilisateur connecté
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const mongoose = require('mongoose');
|
|||
const dotenv = require('dotenv');
|
||||
|
||||
// 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
|
||||
const app = express();
|
||||
|
|
|
|||
|
|
@ -8,11 +8,3 @@ MONGODB_URI=mongodb://localhost:27017/managerr
|
|||
|
||||
# Secret JWT
|
||||
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
76
docker-compose.yml
Normal 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
73
docker-manage.sh
Executable 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
20
frontend/.eslintrc.js
Normal 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
18
frontend/Dockerfile
Normal 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
5
frontend/babel.config.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
181
frontend/docs/USER_GUIDE.md
Normal file
181
frontend/docs/USER_GUIDE.md
Normal 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
14268
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -10,19 +10,23 @@
|
|||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"core-js": "^3.33.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"vue": "^3.3.0",
|
||||
"vue-router": "^4.2.0",
|
||||
"vuex": "^4.1.0",
|
||||
"date-fns": "^2.30.0"
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/eslint-parser": "^7.28.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-plugin-vuex": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"compression-webpack-plugin": "^11.1.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"image-webpack-loader": "^8.1.0",
|
||||
"sass": "^1.69.0",
|
||||
"sass-loader": "^13.3.0"
|
||||
}
|
||||
|
|
|
|||
133
frontend/src/services/apiService.js
Normal file
133
frontend/src/services/apiService.js
Normal 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()
|
||||
}
|
||||
}
|
||||
69
frontend/src/services/cacheService.js
Normal file
69
frontend/src/services/cacheService.js
Normal 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();
|
||||
|
|
@ -113,6 +113,17 @@ export default createStore({
|
|||
// Effacer les erreurs
|
||||
clearError({ commit }) {
|
||||
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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
61
frontend/src/views/Home.vue
Normal file
61
frontend/src/views/Home.vue
Normal 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>
|
||||
|
|
@ -53,7 +53,7 @@ export default {
|
|||
...mapGetters(['error', 'loading'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['login', 'clearError']),
|
||||
...mapActions(['login', 'clearError', 'checkUsersExist']),
|
||||
async handleLogin() {
|
||||
try {
|
||||
await this.login({
|
||||
|
|
@ -64,10 +64,17 @@ export default {
|
|||
} catch (error) {
|
||||
// L'erreur est gérée dans le store
|
||||
}
|
||||
},
|
||||
async checkFirstConnection() {
|
||||
const usersExist = await this.checkUsersExist()
|
||||
if (!usersExist) {
|
||||
this.$router.push('/register')
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.clearError()
|
||||
this.checkFirstConnection()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
48
frontend/src/views/NotFound.vue
Normal file
48
frontend/src/views/NotFound.vue
Normal 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>
|
||||
148
frontend/src/views/Register.vue
Normal file
148
frontend/src/views/Register.vue
Normal 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
38
frontend/vue.config.js
Normal 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
41
start.sh
Executable 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue