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/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/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/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/Webzine.Business.csproj b/Webzine.Business/Webzine.Business.csproj index 33c0658..6e5a6aa 100644 --- a/Webzine.Business/Webzine.Business.csproj +++ b/Webzine.Business/Webzine.Business.csproj @@ -21,4 +21,9 @@ + + + + + diff --git a/Webzine.Documentation/git_hooks/commit-msg b/Webzine.Documentation/git_hooks/commit-msg index eb15525..6d124fa 100644 --- a/Webzine.Documentation/git_hooks/commit-msg +++ b/Webzine.Documentation/git_hooks/commit-msg @@ -6,6 +6,11 @@ COMMIT_MSG=$(cat "$1") +# Skip validation for rebase or CI commits +if echo "$COMMIT_MSG" | grep -qiE "(Rebase|rebase|CI|merge|Merge)"; then + exit 0 +fi + if [ ${#COMMIT_MSG} -le 10 ]; then echo "❌ Erreur : Le message doit faire plus de 10 caractères." exit 1 diff --git a/Webzine.Repository.Contracts/IArtisteRepository.cs b/Webzine.Repository.Contracts/IArtisteRepository.cs index 936e694..1985ddc 100644 --- a/Webzine.Repository.Contracts/IArtisteRepository.cs +++ b/Webzine.Repository.Contracts/IArtisteRepository.cs @@ -51,5 +51,18 @@ namespace Webzine.Repository.Contracts /// Nom de l'artiste. /// IEnumarble. qui contient la chaine de caractere. IEnumerable Search(string nom); + + /// + /// Récupère le nombre total d'artistes dans la collection. + /// + /// Le nombre total d'artistes. + int Count(); + + /// + /// Récupère le nombre d'artistes correspondant au prédicat fourni. + /// + /// Le prédicat de filtrage. + /// Le nombre d'artistes correspondants. + int Count(Func predicate); } } \ No newline at end of file diff --git a/Webzine.Repository.Contracts/IStyleRepository.cs b/Webzine.Repository.Contracts/IStyleRepository.cs index d260039..0ea9ba3 100644 --- a/Webzine.Repository.Contracts/IStyleRepository.cs +++ b/Webzine.Repository.Contracts/IStyleRepository.cs @@ -37,5 +37,11 @@ namespace Webzine.Repository.Contracts /// /// L'objet style à mettre à jour. + /// Récupère le nombre total de styles dans la liste des styles. + /// + /// Le nombre total de styles présents dans la liste. + int Count(); } } \ No newline at end of file diff --git a/Webzine.Repository/DbArtisteRepository.cs b/Webzine.Repository/DbArtisteRepository.cs index 6d34837..2c50cdc 100644 --- a/Webzine.Repository/DbArtisteRepository.cs +++ b/Webzine.Repository/DbArtisteRepository.cs @@ -168,5 +168,37 @@ namespace Webzine.Repository throw new Exception("Erreur lors de la recherche d'artiste {error}", ex); } } + + /// + public int Count() + { + try + { + int count = Enumerable.Count(this.context.Artistes); + this.logger.LogDebug("Nombre total d'artistes dans la base: {Count}", count); + return count; + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors du comptage des artistes."); + throw; + } + } + + /// + public int Count(Func predicate) + { + try + { + int count = this.context.Artistes.Count(predicate); + this.logger.LogDebug("Nombre d'artistes (avec prédicat): {Count}", count); + return count; + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors du comptage des artistes avec prédicat."); + throw; + } + } } } \ No newline at end of file diff --git a/Webzine.Repository/DbStyleRepository.cs b/Webzine.Repository/DbStyleRepository.cs index 9e69837..7b8c0c7 100644 --- a/Webzine.Repository/DbStyleRepository.cs +++ b/Webzine.Repository/DbStyleRepository.cs @@ -167,4 +167,20 @@ public class DbStyleRepository : IStyleRepository throw; } } + + /// + public int Count() + { + try + { + int count = Enumerable.Count(this.context.Styles); + this.logger.LogDebug("Nombre total de styles: {Count}", count); + return count; + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors du comptage des styles"); + throw; + } + } } \ No newline at end of file diff --git a/Webzine.Repository/LocalArtisteRepository.cs b/Webzine.Repository/LocalArtisteRepository.cs index 2824780..1aa7137 100644 --- a/Webzine.Repository/LocalArtisteRepository.cs +++ b/Webzine.Repository/LocalArtisteRepository.cs @@ -94,5 +94,17 @@ namespace Webzine.Repository .Where(a => a.Nom.ToLower().Contains(mot.ToLower())) .ToList(); } + + /// + public int Count() + { + return this.dataStore.Artistes.Count; + } + + /// + public int Count(Func predicate) + { + return this.dataStore.Artistes.Count(predicate); + } } } \ No newline at end of file diff --git a/Webzine.Repository/LocalStyleRepository.cs b/Webzine.Repository/LocalStyleRepository.cs index 5bf5979..6fbcc41 100644 --- a/Webzine.Repository/LocalStyleRepository.cs +++ b/Webzine.Repository/LocalStyleRepository.cs @@ -65,4 +65,10 @@ public class LocalStyleRepository : IStyleRepository stored.Libelle = style.Libelle; stored.Titres = style.Titres; } + + /// + public int Count() + { + return this.dataStore.Styles.Count; + } } \ No newline at end of file diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs index 753ebeb..a882a68 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs @@ -2,29 +2,25 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers; using Microsoft.AspNetCore.Mvc; -using Webzine.Repository.Contracts; -using Webzine.WebApplication.Areas.Administration.ViewModels; +using Webzine.Business.Contracts; +using Webzine.Business.Contracts.Dto; [Area("Administration")] public class DashboardController : Controller // TODO à refaire { private readonly ILogger logger; - private readonly IStyleRepository styleRepository; - private readonly IArtisteRepository artisteRepository; - private readonly ITitreRepository titreRepository; + private readonly IDashboardService dashboardService; /// /// Initializes a new instance of the class. /// Initialise une nouvelle instance de la classe . /// /// Service de journalisation injecté. - /// Repository des styles injecté. - public DashboardController(ILogger logger, IStyleRepository styleRepository, IArtisteRepository artisteRepository, ITitreRepository titreRepository) + /// Service de calcul des statistiques du tableau de bord. + public DashboardController(ILogger logger, IDashboardService dashboardService) { this.logger = logger; - this.styleRepository = styleRepository; - this.artisteRepository = artisteRepository; - this.titreRepository = titreRepository; + this.dashboardService = dashboardService; this.logger.LogInformation("Initialisation du contrôleur TitreController."); } @@ -35,42 +31,8 @@ public class DashboardController : Controller // TODO à refaire /// La vue Index du tableau de bord. public IActionResult Index() { - var artisteLePlusChronique = this.titreRepository.FindAll() - .GroupBy(t => t.Artiste) - .OrderByDescending(g => g.Count()) - .First(); + DashboardDTO data = this.dashboardService.GetDashboardData(); - var albumLePlusChronique = this.titreRepository.FindAll() - .GroupBy(t => t.Artiste) - .OrderByDescending(g => g.Select(t => t.Album).Distinct().Count()) - .First(); - - var musiqueLaPlusJouee = this.titreRepository.FindAll() - .OrderByDescending(t => t.NbLectures) - .First(); - - var model = new DashboardViewModel - { - NombreArtistes = this.artisteRepository.FindAll().Count(), - - ArtisteLePlusChronique = artisteLePlusChronique.Key.Nom, - - AlbumLePlusChronique = albumLePlusChronique.Key.Nom, - - NombreBiographies = this.artisteRepository.FindAll().Count(a => !string.IsNullOrEmpty(a.Biographie)), - - IdMusiqueLaPlusJouee = musiqueLaPlusJouee.IdTitre, - MusiqueLaPlusJouee = musiqueLaPlusJouee.Libelle, - - NombreTitres = this.titreRepository.Count(), - - NombreGenres = this.styleRepository.FindAll().Count(), - - NombreLectures = this.titreRepository.FindAll().Sum(t => t.NbLectures), - - NombreLikes = this.titreRepository.FindAll().Sum(t => t.NbLikes), - }; - - return this.View(model); + return this.View(data); } } \ No newline at end of file diff --git a/Webzine.WebApplication/Areas/Administration/Views/Commentaire/Index.cshtml b/Webzine.WebApplication/Areas/Administration/Views/Commentaire/Index.cshtml index c8608b7..1c8a9db 100644 --- a/Webzine.WebApplication/Areas/Administration/Views/Commentaire/Index.cshtml +++ b/Webzine.WebApplication/Areas/Administration/Views/Commentaire/Index.cshtml @@ -25,7 +25,7 @@ { - + @commentaire.Titre.Libelle diff --git a/Webzine.WebApplication/Areas/Administration/Views/Dashboard/Index.cshtml b/Webzine.WebApplication/Areas/Administration/Views/Dashboard/Index.cshtml index ac676f2..eb54db1 100644 --- a/Webzine.WebApplication/Areas/Administration/Views/Dashboard/Index.cshtml +++ b/Webzine.WebApplication/Areas/Administration/Views/Dashboard/Index.cshtml @@ -1,4 +1,5 @@ -@model Webzine.WebApplication.Areas.Administration.ViewModels.DashboardViewModel +@using Webzine.Business.Contracts.Dto +@model DashboardDTO

Tableau de bord

@@ -94,7 +95,7 @@
diff --git a/Webzine.WebApplication/Controllers/ArtisteController.cs b/Webzine.WebApplication/Controllers/ArtisteController.cs index 965b402..bc3c40a 100644 --- a/Webzine.WebApplication/Controllers/ArtisteController.cs +++ b/Webzine.WebApplication/Controllers/ArtisteController.cs @@ -34,7 +34,6 @@ /// /// Le nom de l'artiste à rechercher, formaté en kebab-case (ex: "fatal-bazooka"). /// La vue de l'artiste avec son ViewModel, ou une redirection vers l'accueil si le nom est vide, ou une erreur 404 si l'artiste n'est pas trouvé. - [HttpGet("/artiste/{nom}")] public IActionResult Index(string nom) { this.logger.LogInformation("Tentative d'accès à l'artiste avec le nom : {NomArtiste}", nom); diff --git a/Webzine.WebApplication/Controllers/TitreController.cs b/Webzine.WebApplication/Controllers/TitreController.cs index 60c3ae0..a741043 100644 --- a/Webzine.WebApplication/Controllers/TitreController.cs +++ b/Webzine.WebApplication/Controllers/TitreController.cs @@ -15,7 +15,6 @@ namespace Webzine.WebApplication.Controllers /// affichage des details, filtrage par style, /// ajout de likes, commentaires et recherche. /// - [Route("titre")] public class TitreController : Controller { private readonly ILogger logger; @@ -40,8 +39,7 @@ namespace Webzine.WebApplication.Controllers /// /// Identifiant du titre. /// Vue des details ou 404 si introuvable. - [HttpGet("{id}")] - public IActionResult Details(int id) + public IActionResult Index(int id) { this.logger.LogInformation("Demande d'affichage du detail pour le titre ID {Id}.", id); @@ -82,7 +80,6 @@ namespace Webzine.WebApplication.Controllers /// /// Nom du style musical. /// Vue contenant la liste filtree. - [HttpGet("style/{style}")] // TODO pas de route écrite en dur dans le controller public IActionResult Style(string style) { this.logger.LogInformation("Recherche des titres pour le style : {Style}.", style); @@ -103,7 +100,7 @@ namespace Webzine.WebApplication.Controllers /// /// Modele contenant l'identifiant du titre. /// Redirection vers la page detail. - [HttpPost("like")] + [HttpPost] public IActionResult Like(TitreLike model) { this.logger.LogInformation("Ajout d'un like pour le titre ID {Id}.", model.IdTitre); @@ -126,7 +123,7 @@ namespace Webzine.WebApplication.Controllers /// /// Donnees du commentaire. /// Redirection vers la page detail. - [HttpPost("comment")] + [HttpPost] public IActionResult Comment(TitreComment model) { if (!this.ModelState.IsValid) diff --git a/Webzine.WebApplication/Extensions/RouteConfiguration.cs b/Webzine.WebApplication/Extensions/RouteConfiguration.cs index caa0efa..0b9b428 100644 --- a/Webzine.WebApplication/Extensions/RouteConfiguration.cs +++ b/Webzine.WebApplication/Extensions/RouteConfiguration.cs @@ -7,6 +7,37 @@ public static class RouteConfiguration /// public static void MapCustomRoutes(this IEndpointRouteBuilder endpoints) { + // ----------- TITRE ----------- + endpoints.MapControllerRoute( + name: "TitreStyle", + pattern: "titres/style/{style}", + defaults: new { controller = "Titre", action = "Style" }); + + endpoints.MapControllerRoute( + name: "TitreIndex", + pattern: "titre/{id}", + defaults: new { controller = "Titre", action = "Index" }); + + endpoints.MapControllerRoute( + name: "ArtisteIndex", + pattern: "artiste/{nom}", + defaults: new { controller = "Artiste", action = "Index" }); + + // ----------- ADMIN ----------- + var adminRoutes = new Dictionary + { + { "artistes", "Artiste" }, { "commentaires", "Commentaire" }, { "styles", "Style" }, { "titres", "Titre" }, + }; + + foreach (var route in adminRoutes) + { + endpoints.MapControllerRoute( + name: $"Admin{route.Value}Index", + pattern: $"administration/{route.Key}", + defaults: new { area = "Administration", controller = route.Value, action = "Index" }); + } + + // --- AUTRE PROUTES --- endpoints.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); diff --git a/Webzine.WebApplication/Program.cs b/Webzine.WebApplication/Program.cs index 916d86e..d118c67 100644 --- a/Webzine.WebApplication/Program.cs +++ b/Webzine.WebApplication/Program.cs @@ -6,6 +6,8 @@ using Microsoft.EntityFrameworkCore; using NLog; using NLog.Web; +using Webzine.Business; +using Webzine.Business.Contracts; using Webzine.EntitiesContext; using Webzine.Entity; using Webzine.Entity.Fixtures; @@ -67,6 +69,8 @@ try builder.Services.AddSingleton(); } + builder.Services.AddScoped(); + // https://learn.microsoft.com/fr-fr/aspnet/core/performance/response-compression?view=aspnetcore-10.0#configuration // Ajoute le service de compression des réponses HTTP pour réduire la taille des données envoyées au client et améliorer les performances de l'application. builder.Services.AddResponseCompression(); @@ -78,6 +82,8 @@ try using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); + if (shouldSeed) { db.Database.EnsureDeleted(); diff --git a/Webzine.WebApplication/Views/Accueil/Index.cshtml b/Webzine.WebApplication/Views/Accueil/Index.cshtml index 6b11176..defe1e8 100644 --- a/Webzine.WebApplication/Views/Accueil/Index.cshtml +++ b/Webzine.WebApplication/Views/Accueil/Index.cshtml @@ -29,7 +29,7 @@ @titre.Artiste.Nom - - @titre.Libelle @@ -43,7 +43,7 @@
- @@ -90,7 +90,7 @@ @titre.Album
- + @titre.Libelle
diff --git a/Webzine.WebApplication/Views/Artiste/Index.cshtml b/Webzine.WebApplication/Views/Artiste/Index.cshtml index 59716b8..fe66f72 100644 --- a/Webzine.WebApplication/Views/Artiste/Index.cshtml +++ b/Webzine.WebApplication/Views/Artiste/Index.cshtml @@ -56,7 +56,7 @@ else @dureeFormatee @titre.Libelle diff --git a/Webzine.WebApplication/Views/Recherche/Index.cshtml b/Webzine.WebApplication/Views/Recherche/Index.cshtml index 53e3853..ef89879 100644 --- a/Webzine.WebApplication/Views/Recherche/Index.cshtml +++ b/Webzine.WebApplication/Views/Recherche/Index.cshtml @@ -15,7 +15,7 @@ @if (!Model.Artistes.Any()) {
-

Aucun artiste n'a t trouv.

+

Aucun artiste n'a été trouvé.

} @@ -35,7 +35,7 @@ @if (!Model.Titres.Any()) {
-

Aucun titre n'a t trouv.

+

Aucun titre n'a été trouvé.

} @@ -43,7 +43,7 @@ {
@titre.Libelle @@ -58,7 +58,7 @@ - @titre.Libelle diff --git a/Webzine.WebApplication/Views/Titre/Details.cshtml b/Webzine.WebApplication/Views/Titre/Index.cshtml similarity index 100% rename from Webzine.WebApplication/Views/Titre/Details.cshtml rename to Webzine.WebApplication/Views/Titre/Index.cshtml diff --git a/Webzine.WebApplication/Webzine.WebApplication.csproj b/Webzine.WebApplication/Webzine.WebApplication.csproj index f47a272..5f8c0ca 100644 --- a/Webzine.WebApplication/Webzine.WebApplication.csproj +++ b/Webzine.WebApplication/Webzine.WebApplication.csproj @@ -34,6 +34,8 @@ + + diff --git a/compose.yaml b/compose.yaml index 979fbd6..dd0bc07 100644 --- a/compose.yaml +++ b/compose.yaml @@ -3,5 +3,7 @@ image: webzine.webapplication build: context: . - dockerfile: Webzine.WebApplication/Dockerfile - + ports: + - "8080:8080" + environment: + - ConnectionStrings__PostGreSQLConnection=${PGSQL_CONNECTION} \ No newline at end of file diff --git a/scripts/test-endpoints.sh b/scripts/test-endpoints.sh index 146cfcb..0c96983 100644 --- a/scripts/test-endpoints.sh +++ b/scripts/test-endpoints.sh @@ -131,7 +131,7 @@ info "── Titre – Par style ─────────────── STYLES=("Rock" "Pop" "Rap" "Jazz" "Metal" "Electronic" "Hip-Hop" "Soul" "Funk") for STYLE in "${STYLES[@]}"; do ENCODE=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$STYLE'))" 2>/dev/null || echo "$STYLE") - verifier_endpoint GET "$BASE_URL/titre/style/$ENCODE" "GET /titre/style/$STYLE" + verifier_endpoint GET "$BASE_URL/titres/style/$ENCODE" "GET /titres/style/$STYLE" done log "" @@ -160,26 +160,26 @@ verifier_endpoint GET "$BASE_URL/Administration/Dashboard" "GET /A log "" info "── Administration – Artiste ──────────────────────────────" -verifier_endpoint GET "$BASE_URL/Administration/Artiste" "GET /Administration/Artiste (liste)" +verifier_endpoint GET "$BASE_URL/Administration/Artistes" "GET /Administration/Artistes (liste)" verifier_endpoint GET "$BASE_URL/Administration/Artiste/Create" "GET /Administration/Artiste/Create" verifier_endpoint GET "$BASE_URL/Administration/Artiste/Edit/1" "GET /Administration/Artiste/Edit/1" verifier_endpoint GET "$BASE_URL/Administration/Artiste/Delete/1" "GET /Administration/Artiste/Delete/1" log "" info "── Administration – Commentaire ──────────────────────────" -verifier_endpoint GET "$BASE_URL/Administration/Commentaire" "GET /Administration/Commentaire (liste)" +verifier_endpoint GET "$BASE_URL/Administration/Commentaires" "GET /Administration/Commentaires (liste)" verifier_endpoint GET "$BASE_URL/Administration/Commentaire/Delete/1" "GET /Administration/Commentaire/Delete/1" log "" info "── Administration – Style ────────────────────────────────" -verifier_endpoint GET "$BASE_URL/Administration/Style" "GET /Administration/Style (liste)" +verifier_endpoint GET "$BASE_URL/Administration/Styles" "GET /Administration/Styles (liste)" verifier_endpoint GET "$BASE_URL/Administration/Style/Create" "GET /Administration/Style/Create" verifier_endpoint GET "$BASE_URL/Administration/Style/Edit/1" "GET /Administration/Style/Edit/1" verifier_endpoint GET "$BASE_URL/Administration/Style/Delete/1" "GET /Administration/Style/Delete/1" log "" info "── Administration – Titre ────────────────────────────────" -verifier_endpoint GET "$BASE_URL/Administration/Titre" "GET /Administration/Titre (liste)" +verifier_endpoint GET "$BASE_URL/Administration/Titres" "GET /Administration/Titres (liste)" verifier_endpoint GET "$BASE_URL/Administration/Titre/Create" "GET /Administration/Titre/Create" verifier_endpoint GET "$BASE_URL/Administration/Titre/Edit/1" "GET /Administration/Titre/Edit/1" verifier_endpoint GET "$BASE_URL/Administration/Titre/Delete/1" "GET /Administration/Titre/Delete/1"