Aller au contenu

Backend

Introduction

Le backend de Diyae est développé avec Fastify, un framework Node.js performant et moderne. Il gère l'ensemble de l'API REST, la communication MQTT avec les enceintes, et la planification automatique des adhans.

Ports utilisés

  • Port 3010 : API REST Fastify (définissable via PORT dans .env)
  • Port 1883 : Broker MQTT pour la communication avec les enceintes (définissable via MQTT_BROKER_PORT)

Architecture technique

Le projet est organisé selon une structure modulaire :

/config       # Configuration base de données et environnement
/controllers  # Logique métier des routes API
/model        # Modèles Sequelize (ORM)
/mqtt         # Broker et gestion MQTT
/services     # Services métier (Adhan, etc.)
/utils        # Utilitaires divers
/middleware   # Middlewares (auth, etc.)

Sécurité et middlewares

Le backend utilise plusieurs couches de sécurité :

  • Helmet : Protection contre les vulnérabilités web courantes
  • Rate limiting : 100 requêtes par minute maximum
  • CORS : Configuration pour les appels cross-origin
  • JWT Authentication : Authentification par token pour les routes protégées

Routes API

🏥 Health Check

GET /health

Vérification de l'état du serveur.

Réponse :

{
  "status": "OK",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "uptime": 12345.67
}


📚 Gestion des contenus (Content)

GET /api/contents

Récupérer tous les contenus avec pagination et filtres.

Query Parameters : - page (optionnel) : Numéro de page (défaut: 1) - limit (optionnel) : Nombre d'éléments par page (défaut: 20) - category_id (optionnel) : Filtrer par catégorie - type (optionnel) : Filtrer par type (audio, video, pdf, live) - search (optionnel) : Recherche textuelle

Exemple :

GET /api/contents?page=1&limit=20&category_id=1&type=audio&search=coran

GET /api/contents/:id

Récupérer un contenu spécifique par son ID.

POST /api/contents

Créer un nouveau contenu.

Body :

{
  "type": "audio",
  "category_id": 1,
  "title": "Sourate Al-Fatiha",
  "description": "Récitation de la sourate Al-Fatiha",
  "language": "ar",
  "author": "Sheikh Mishary",
  "media_url": "https://...",
  "thumbnail_url": "https://...",
  "duration_ms": "180000",
  "visibility": "public"
}

PUT /api/contents/:id

Modifier un contenu existant.

DELETE /api/contents/:id

Supprimer un contenu.

GET /api/contents/stats

Obtenir les statistiques des contenus (nombre total, par type, etc.).


📁 Gestion des catégories (Category)

GET /api/categories

Récupérer toutes les catégories avec pagination et filtres.

Query Parameters : - page (optionnel) : Numéro de page - limit (optionnel) : Nombre d'éléments par page - search (optionnel) : Recherche textuelle - with_content_count (optionnel) : Inclure le nombre de contenus

Exemple :

GET /api/categories?page=1&limit=20&search=coran&with_content_count=true

GET /api/categories/search

Rechercher des catégories (auto-complétion).

Query Parameters : - q : Terme de recherche - limit : Nombre de résultats (défaut: 5)

Exemple :

GET /api/categories/search?q=cor&limit=5

GET /api/categories/stats

Obtenir les statistiques des catégories.

GET /api/categories/:id

Récupérer une catégorie par ID.

Query Parameters : - with_contents (optionnel) : Inclure les contenus de la catégorie

POST /api/categories

Créer une nouvelle catégorie.

PUT /api/categories/:id

Modifier une catégorie existante.

DELETE /api/categories/:id

Supprimer une catégorie.

Query Parameters : - force (optionnel) : Forcer la suppression même si elle contient des contenus


📺 Gestion des séries (Series)

GET /api/series

Récupérer toutes les séries avec pagination et filtres.

Query Parameters : - page (optionnel) : Numéro de page - limit (optionnel) : Nombre d'éléments par page - search (optionnel) : Recherche textuelle - with_episode_count (optionnel) : Inclure le nombre d'épisodes - with_latest_episode (optionnel) : Inclure le dernier épisode

Exemple :

GET /api/series?page=1&limit=20&search=prophete&with_episode_count=true

GET /api/series/search

Rechercher des séries (auto-complétion).

Exemple :

GET /api/series/search?q=proph&limit=5

GET /api/series/stats

Obtenir les statistiques des séries.

GET /api/series/:id

Récupérer une série par ID.

Query Parameters : - with_episodes (optionnel) : Inclure les épisodes - episodes_limit (optionnel) : Limiter le nombre d'épisodes retournés

POST /api/series

Créer une nouvelle série.

PUT /api/series/:id

Modifier une série existante.

DELETE /api/series/:id

Supprimer une série.

Query Parameters : - force (optionnel) : Forcer la suppression même si elle contient des épisodes

PUT /api/series/:id/episodes/reorder

Réorganiser l'ordre des épisodes d'une série.

Body :

{
  "episodes": [
    { "id": 1, "order": 1 },
    { "id": 2, "order": 2 }
  ]
}


🔐 Authentification et utilisateurs

POST /api/auth/register

Inscription d'un nouvel utilisateur.

Body :

{
  "email": "user@example.com",
  "password": "SecurePassword123",
  "locale": "fr",
  "timezone": "Europe/Paris"
}

POST /api/auth/login

Connexion utilisateur.

Body :

{
  "email": "user@example.com",
  "password": "SecurePassword123"
}

Réponse :

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": 1,
    "email": "user@example.com"
  }
}

GET /api/auth/me 🔒

Récupérer le profil de l'utilisateur connecté.

Headers requis :

Authorization: Bearer <token>

PUT /api/auth/profile 🔒

Mettre à jour le profil utilisateur.

Headers requis :

Authorization: Bearer <token>

Body :

{
  "locale": "en",
  "timezone": "America/New_York"
}

PUT /api/auth/password 🔒

Changer le mot de passe.

Headers requis :

Authorization: Bearer <token>

Body :

{
  "current_password": "OldPassword123",
  "new_password": "NewPassword456"
}

GET /api/auth/speakers 🔒

Récupérer les enceintes de l'utilisateur connecté.

Headers requis :

Authorization: Bearer <token>

POST /api/auth/claim-device 🔒

Associer une enceinte à l'utilisateur connecté.

Headers requis :

Authorization: Bearer <token>

Body :

{
  "device_id": "DY-ABC123"
}


📱 Gestion des appareils (Devices)

POST /api/devices/register 🔒

Enregistrement d'un nouveau device (appelé par l'app avec token utilisateur).

Headers requis :

Authorization: Bearer <token>

Body :

{
  "device_id": "DY-ABC123",
  "serial": "SN-001",
  "model": "DiyaeSpeaker",
  "hw_rev": "A",
  "fw_version": "1.0.0",
  "provision_method": "AP"
}

GET /api/devices/unclaimed

Liste des devices non associés à un utilisateur (admin/debug).

GET /api/devices/:device_id/state 🔒

Obtenir l'état temps réel d'une enceinte.

Headers requis :

Authorization: Bearer <token>

Réponse :

{
  "success": true,
  "device_id": "DY-ABC123",
  "online": true,
  "volume": 50,
  "is_playing": true,
  "playing_src": "https://example.com/audio.mp3",
  "position_ms": 45000,
  "battery_pct": 85,
  "wifi_rssi": -57,
  "last_seen": "2024-01-15T10:30:00.000Z",
  "updated_at": "2024-01-15T10:30:00.000Z"
}


🔊 Contrôle audio des enceintes

Toutes ces routes nécessitent une authentification JWT.

POST /api/audio/:device_id/play 🔒

Lancer la lecture audio sur une enceinte.

Headers requis :

Authorization: Bearer <token>

Body :

{
  "url": "https://example.com/audio.mp3",
  "title": "Sourate Al-Fatiha"
}

POST /api/audio/:device_id/pause 🔒

Mettre en pause la lecture.

Headers requis :

Authorization: Bearer <token>

POST /api/audio/:device_id/stop 🔒

Arrêter la lecture.

Headers requis :

Authorization: Bearer <token>

POST /api/audio/:device_id/volume 🔒

Changer le volume de l'enceinte.

Headers requis :

Authorization: Bearer <token>

Body :

{
  "volume": 75
}

POST /api/audio/:device_id/seek 🔒

Changer la position dans l'audio en cours.

Headers requis :

Authorization: Bearer <token>

Body :

{
  "position_ms": 60000
}

GET /api/audio/:device_id/status 🔒

Obtenir le statut des commandes envoyées.

Headers requis :

Authorization: Bearer <token>


🕌 Gestion des horaires de prière (Adhan)

Toutes ces routes nécessitent une authentification JWT.

GET /api/adhan/:device_id 🔒

Récupérer la configuration Adhan d'une enceinte.

Headers requis :

Authorization: Bearer <token>

PUT /api/adhan/:device_id 🔒

Mettre à jour la configuration Adhan d'une enceinte.

Headers requis :

Authorization: Bearer <token>

Body :

{
  "enabled": true,
  "latitude": 48.8566,
  "longitude": 2.3522,
  "calculation_method": "MWL",
  "prayers": {
    "fajr": true,
    "dhuhr": true,
    "asr": true,
    "maghrib": true,
    "isha": true
  }
}

DELETE /api/adhan/:device_id 🔒

Supprimer la configuration Adhan d'une enceinte.

Headers requis :

Authorization: Bearer <token>

GET /api/adhan 🔒

Lister toutes les configurations Adhan de l'utilisateur.

Headers requis :

Authorization: Bearer <token>

POST /api/adhan/update-all

Déclencher manuellement la mise à jour des horaires de prière pour tous les devices (admin/debug).

Réponse :

{
  "success": true,
  "message": "Mise à jour des horaires terminée",
  "data": {
    "updated": 5,
    "failed": 0
  }
}


🛰️ Communication MQTT

Le backend intègre un broker MQTT et un client MQTT pour communiquer en temps réel avec les enceintes.

Topics MQTT utilisés

Envoi (Server → Device)

  • diyae/{device_id}/ping : Envoi de ping pour vérifier la connexion
  • diyae/{device_id}/cmd : Envoi de commandes (play, pause, volume, etc.)

Réception (Device → Server)

  • diyae/{device_id}/ack : Accusé de réception des commandes
  • diyae/{device_id}/state : État complet de l'enceinte
  • diyae/{device_id}/register : Enregistrement automatique device-user
  • diyae/{device_id}/pong : Réponse au ping avec état de lecture
  • diyae/broadcast/sync : Messages globaux (maintenance, sync, etc.)

Système Ping/Pong

Le backend vérifie automatiquement le statut de connexion des enceintes :

  • Ping envoyé : toutes les 15 secondes
  • Timeout offline : 1 minute sans réponse
  • Mise à jour automatique : Statut online/offline dans la base de données

Lorsqu'une enceinte ne répond plus : - Son statut passe à online: false - L'état de lecture est vidé (is_playing: false, playing_src: null) - Un log d'alerte est généré


📊 Architecture des commandes

Toutes les commandes envoyées aux enceintes suivent ce flux :

  1. Requête API : L'utilisateur envoie une commande via l'API REST
  2. Vérification : Le serveur vérifie que l'utilisateur possède l'enceinte
  3. Enregistrement : La commande est enregistrée dans device_cmd avec statut sent
  4. Publication MQTT : La commande est publiée sur diyae/{device_id}/cmd
  5. Accusé de réception : L'enceinte répond sur diyae/{device_id}/ack
  6. Mise à jour : Le statut de la commande passe à ok ou error

Cette architecture permet : - Un suivi complet de toutes les commandes - Une gestion des erreurs robuste - Un historique pour le debug


🔧 Services automatiques

AdhanService

Le service AdhanService gère automatiquement :

  • Calcul des horaires de prière selon la localisation
  • Planification des adhans pour chaque enceinte configurée
  • Mise à jour quotidienne des horaires
  • Envoi automatique de la commande de lecture à l'heure de la prière

Le service se déclenche automatiquement au démarrage du serveur.


🔐 Notes sur l'authentification

Le symbole 🔒 indique les routes protégées nécessitant un token JWT.

Pour utiliser ces routes :

  1. Obtenir un token via /api/auth/login
  2. Inclure le token dans le header :
    Authorization: Bearer <votre_token>
    

Le token contient les informations de l'utilisateur (id, email) et est vérifié à chaque requête protégée.