diff --git a/.gitea/workflows/deploy-prod.yaml b/.gitea/workflows/deploy-prod.yaml
new file mode 100644
index 0000000..0bb9c60
--- /dev/null
+++ b/.gitea/workflows/deploy-prod.yaml
@@ -0,0 +1,26 @@
+name: Deploiement API Prod Docker
+
+on:
+ push:
+ branches:
+ - dev
+
+jobs:
+ deploy:
+ name: Build et Déploiement
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: đ„ RĂ©cupĂ©ration du code source
+ uses: actions/checkout@v4
+
+ - name: đ Injection des variables d'environnement
+ run: |
+ echo "PGSQL_CONNECTION=${{ secrets.PGSQL_CONNECTION }}" > .env
+
+ - name: đł RedĂ©marrage Docker
+ run: |
+ echo "đ DĂ©marrage du dĂ©ploiement Docker sur api-prod..."
+ docker compose down
+ docker compose up -d --build
+ echo "â
Déploiement terminé !"
\ No newline at end of file
diff --git a/.gitea/workflows/pr-endpoint-check.yml b/.gitea/workflows/pr-endpoint-check.yml
index 1aba34b..9cd7435 100644
--- a/.gitea/workflows/pr-endpoint-check.yml
+++ b/.gitea/workflows/pr-endpoint-check.yml
@@ -74,7 +74,6 @@ jobs:
run: |
chmod +x scripts/test-endpoints.sh
bash scripts/test-endpoints.sh http://localhost:5038 1000 2>&1 | tee /tmp/webzine_endpoint_output.txt
- EXIT_CODE=${PIPESTATUS[0]}
FAIL_COUNT=$(grep -cE "^\[ĂCHEC\]" /tmp/webzine_endpoint_output.txt 2>/dev/null || echo 0)
SLOW_COUNT=$(grep -cE "^\[LENT\]" /tmp/webzine_endpoint_output.txt 2>/dev/null || echo 0)
@@ -137,10 +136,4 @@ jobs:
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg body "$BODY" '{body: $body}')" \
- "$GITEA_SERVER_URL/api/v1/repos/$REPO/issues/$PR_NUMBER/comments"
-
- - name: Fail job if performance issues detected
- if: steps.perf_test.outputs.failed > 0 || steps.perf_test.outputs.slow > 0
- run: |
- echo "â Job failed due to performance issues"
- exit 1
\ No newline at end of file
+ "$GITEA_SERVER_URL/api/v1/repos/$REPO/issues/$PR_NUMBER/comments"
\ No newline at end of file
diff --git a/Webzine.WebApplication/Areas/Administration/ViewModels/DashboardViewModel.cs b/Webzine.Business.Contracts/Dto/DashboardDTO.cs
similarity index 90%
rename from Webzine.WebApplication/Areas/Administration/ViewModels/DashboardViewModel.cs
rename to Webzine.Business.Contracts/Dto/DashboardDTO.cs
index 106779d..9509c6c 100644
--- a/Webzine.WebApplication/Areas/Administration/ViewModels/DashboardViewModel.cs
+++ b/Webzine.Business.Contracts/Dto/DashboardDTO.cs
@@ -1,9 +1,9 @@
-namespace Webzine.WebApplication.Areas.Administration.ViewModels;
+namespace Webzine.Business.Contracts.Dto;
///
-/// ViewModel pour le tableau de bord de l'administration du webzine.
+/// DTO pour le tableau de bord de l'administration du webzine.
///
-public class DashboardViewModel
+public class DashboardDTO
{
///
/// Définit le nombre total d'artistes chroniqués dans le webzine.
diff --git a/Webzine.Business.Contracts/Dto/TitreAdminDTO.cs b/Webzine.Business.Contracts/Dto/TitreAdminDTO.cs
new file mode 100644
index 0000000..2c2454b
--- /dev/null
+++ b/Webzine.Business.Contracts/Dto/TitreAdminDTO.cs
@@ -0,0 +1,57 @@
+namespace Webzine.Business.Contracts.Dto;
+
+///
+/// Dto transportant les données métier d'un titre saisi en administration.
+///
+public class TitreAdminDTO
+{
+ ///
+ /// Identifiant du titre (0 lors d'une création).
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// Identifiant de l'artiste sélectionné.
+ ///
+ public int IdArtiste { get; set; }
+
+ ///
+ /// Libellé du titre.
+ ///
+ public string Libelle { get; set; } = string.Empty;
+
+ ///
+ /// Nom de l'album.
+ ///
+ public string Album { get; set; } = string.Empty;
+
+ ///
+ /// Texte de la chronique.
+ ///
+ public string Chronique { get; set; } = string.Empty;
+
+ ///
+ /// Date de sortie du titre.
+ ///
+ public DateTime DateSortie { get; set; }
+
+ ///
+ /// Durée en secondes.
+ ///
+ public int Duree { get; set; }
+
+ ///
+ /// URL de la jaquette.
+ ///
+ public string UrlJaquette { get; set; } = string.Empty;
+
+ ///
+ /// URL d'écoute.
+ ///
+ public string? UrlEcoute { get; set; }
+
+ ///
+ /// Identifiants des styles sélectionnés.
+ ///
+ public List Styles { get; set; } = new ();
+}
\ No newline at end of file
diff --git a/Webzine.Business.Contracts/IDashboardService.cs b/Webzine.Business.Contracts/IDashboardService.cs
new file mode 100644
index 0000000..423880f
--- /dev/null
+++ b/Webzine.Business.Contracts/IDashboardService.cs
@@ -0,0 +1,16 @@
+namespace Webzine.Business.Contracts;
+
+using Webzine.Business.Contracts.Dto;
+
+///
+/// Service responsable du calcul des statistiques affichées sur le tableau de bord d'administration.
+/// AgrÚge les données provenant de plusieurs repositories pour produire un résumé cohérent.
+///
+public interface IDashboardService
+{
+ ///
+ /// Calcule et retourne toutes les statistiques du tableau de bord en une seule passe.
+ ///
+ /// Un contenant les agrégats calculés.
+ DashboardDTO GetDashboardData();
+}
\ No newline at end of file
diff --git a/Webzine.Business.Contracts/ITitreAdminService.cs b/Webzine.Business.Contracts/ITitreAdminService.cs
new file mode 100644
index 0000000..e14a7a7
--- /dev/null
+++ b/Webzine.Business.Contracts/ITitreAdminService.cs
@@ -0,0 +1,22 @@
+namespace Webzine.Business.Contracts;
+
+using Webzine.Business.Contracts.Dto;
+
+///
+/// Service responsable des opérations d'administration sur les titres.
+/// Orchestre la résolution des dépendances (artiste, styles) et la persistance.
+///
+public interface ITitreAdminService
+{
+ ///
+ /// Crée un nouveau titre à partir des données du formulaire d'administration.
+ ///
+ /// Les données saisies dans le formulaire de création.
+ void CreerTitre(TitreAdminDTO commande);
+
+ ///
+ /// Met à jour un titre existant à partir des données du formulaire d'administration.
+ ///
+ /// Les données saisies dans le formulaire de modification.
+ void ModifierTitre(TitreAdminDTO commande);
+}
\ No newline at end of file
diff --git a/Webzine.Business/DashboardService.cs b/Webzine.Business/DashboardService.cs
new file mode 100644
index 0000000..f1b566d
--- /dev/null
+++ b/Webzine.Business/DashboardService.cs
@@ -0,0 +1,70 @@
+namespace Webzine.Business;
+
+using Webzine.Business.Contracts;
+using Webzine.Business.Contracts.Dto;
+using Webzine.Entity;
+using Webzine.Repository.Contracts;
+
+///
+/// Implémentation de .
+/// Orchestre plusieurs appels aux repositories pour produire les statistiques du tableau de bord.
+///
+public class DashboardService : IDashboardService
+{
+ private readonly IArtisteRepository artisteRepository;
+ private readonly ITitreRepository titreRepository;
+ private readonly IStyleRepository styleRepository;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Repository des artistes.
+ /// Repository des titres.
+ /// Repository des styles.
+ public DashboardService(
+ IArtisteRepository artisteRepository,
+ ITitreRepository titreRepository,
+ IStyleRepository styleRepository)
+ {
+ this.artisteRepository = artisteRepository;
+ this.titreRepository = titreRepository;
+ this.styleRepository = styleRepository;
+ }
+
+ ///
+ public DashboardDTO GetDashboardData()
+ {
+ IEnumerable titres = this.titreRepository.FindAll();
+
+ Artiste? artisteLePlusChronique = titres
+ .GroupBy(t => t.Artiste)
+ .OrderByDescending(g => g.Count())
+ .FirstOrDefault()
+ ?.Key;
+
+ Artiste? albumLePlusChronique = titres
+ .GroupBy(t => (t.Artiste, t.Album))
+ .GroupBy(g => g.Key.Artiste)
+ .OrderByDescending(g => g.Count())
+ .FirstOrDefault()
+ ?.Key;
+
+ Titre? musiqueLaPlusJouee = titres
+ .OrderByDescending(t => t.NbLectures)
+ .FirstOrDefault();
+
+ return new DashboardDTO
+ {
+ NombreArtistes = this.artisteRepository.Count(),
+ ArtisteLePlusChronique = artisteLePlusChronique.Nom,
+ AlbumLePlusChronique = albumLePlusChronique.Nom,
+ NombreBiographies = this.artisteRepository.Count(a => !string.IsNullOrEmpty(a.Biographie)),
+ IdMusiqueLaPlusJouee = musiqueLaPlusJouee.IdTitre,
+ MusiqueLaPlusJouee = musiqueLaPlusJouee.Libelle,
+ NombreTitres = this.titreRepository.Count(),
+ NombreGenres = this.styleRepository.Count(),
+ NombreLectures = titres.Sum(t => t.NbLectures),
+ NombreLikes = titres.Sum(t => t.NbLikes),
+ };
+ }
+}
\ No newline at end of file
diff --git a/Webzine.Business/TitreAdminService.cs b/Webzine.Business/TitreAdminService.cs
new file mode 100644
index 0000000..e23af11
--- /dev/null
+++ b/Webzine.Business/TitreAdminService.cs
@@ -0,0 +1,124 @@
+namespace Webzine.Business;
+
+using Microsoft.Extensions.Logging;
+
+using Webzine.Business.Contracts;
+using Webzine.Business.Contracts.Dto;
+using Webzine.Entity;
+using Webzine.Repository.Contracts;
+
+///
+/// Implémentation de .
+/// Orchestre la résolution des styles, la construction de l'entité
+/// et la délégation au repository.
+///
+public class TitreAdminService : ITitreAdminService
+{
+ private readonly ITitreRepository titreRepository;
+ private readonly IArtisteRepository artisteRepository;
+ private readonly IStyleRepository styleRepository;
+ private readonly ILogger logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Repository des titres.
+ /// Repository des artistes.
+ /// Repository des styles.
+ /// Service de journalisation.
+ public TitreAdminService(
+ ITitreRepository titreRepository,
+ IArtisteRepository artisteRepository,
+ IStyleRepository styleRepository,
+ ILogger logger)
+ {
+ this.titreRepository = titreRepository;
+ this.artisteRepository = artisteRepository;
+ this.styleRepository = styleRepository;
+ this.logger = logger;
+ }
+
+ ///
+ public void CreerTitre(TitreAdminDTO commande)
+ {
+ this.logger.LogInformation(
+ "Création d'un nouveau titre '{Libelle}' pour l'artiste ID {IdArtiste}.",
+ commande.Libelle,
+ commande.IdArtiste);
+
+ Artiste artiste = this.artisteRepository.Find(commande.IdArtiste);
+ List