Merge remote-tracking branch 'origin/main' into j3/feat/pagination

This commit is contained in:
Loic Masi
2026-04-08 22:59:55 +02:00
7 changed files with 57 additions and 19 deletions

View File

@@ -3,7 +3,7 @@ name: Deploiement API Prod Docker
on: on:
push: push:
branches: branches:
- dev - main
jobs: jobs:
deploy: deploy:

View File

@@ -20,4 +20,4 @@ RUN dotnet publish "./Webzine.WebApplication.csproj" -c $BUILD_CONFIGURATION -o
FROM base AS final FROM base AS final
WORKDIR /app WORKDIR /app
COPY --from=publish /app/publish . COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Webzine.WebApplication.dll"] ENTRYPOINT ["dotnet", "Webzine.WebApplication.dll", "--seed"]

View File

@@ -3,6 +3,8 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Npgsql;
using Webzine.EntitiesContext; using Webzine.EntitiesContext;
using Webzine.Entity; using Webzine.Entity;
using Webzine.Repository.Contracts; using Webzine.Repository.Contracts;
@@ -38,7 +40,19 @@ public class DbCommentaireRepository : ICommentaireRepository
} }
catch (DbUpdateException dbex) catch (DbUpdateException dbex)
{ {
this.logger.LogError(dbex, "Erreur de base de données lors de l'ajout du commentaire de l'auteur : {Auteur}", commentaire?.Auteur); PostgresException? postgresException = dbex.InnerException as PostgresException;
this.logger.LogError(
dbex,
"Erreur de base de données lors de l'ajout du commentaire. Auteur: {Auteur} | IdTitre: {IdTitre} | DateCreation: {DateCreation:o} | PostgresCode: {PostgresCode} | Detail: {Detail} | Constraint: {Constraint} | Column: {Column} | Table: {Table}",
commentaire?.Auteur,
commentaire?.IdTitre,
commentaire?.DateCreation,
postgresException?.SqlState,
postgresException?.Detail,
postgresException?.ConstraintName,
postgresException?.ColumnName,
postgresException?.TableName);
throw; throw;
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -421,7 +421,8 @@ public class DbTitreRepository : ITitreRepository
.AsNoTracking() .AsNoTracking()
.OrderByDescending(t => t.DateCreation) .OrderByDescending(t => t.DateCreation)
.Paginate(offset, limit) .Paginate(offset, limit)
.Include(t => t.Artiste); .Include(t => t.Artiste)
.Include(a => a.Styles);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -20,16 +20,23 @@ namespace Webzine.WebApplication.Controllers
private readonly ILogger<TitreController> logger; private readonly ILogger<TitreController> logger;
private readonly ITitreRepository titreRepository; private readonly ITitreRepository titreRepository;
// Pour les commentaires.
private readonly ICommentaireRepository commentaireRepository;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TitreController"/> class. /// Initializes a new instance of the <see cref="TitreController"/> class.
/// Initialise une nouvelle instance de la classe <see cref="TitreController"/>. /// Initialise une nouvelle instance de la classe <see cref="TitreController"/>.
/// </summary> /// </summary>
/// <param name="logger">Service de journalisation injecte.</param> /// <param name="logger">Service de journalisation injecte.</param>
/// <param name="titreRepository">Repository des titres injecte.</param> /// <param name="titreRepository">Repository des titres injecte.</param>
public TitreController(ILogger<TitreController> logger, ITitreRepository titreRepository) public TitreController(
ILogger<TitreController> logger,
ITitreRepository titreRepository,
ICommentaireRepository commentaireRepository)
{ {
this.logger = logger; this.logger = logger;
this.titreRepository = titreRepository; this.titreRepository = titreRepository;
this.commentaireRepository = commentaireRepository;
this.logger.LogInformation("Initialisation du controleur TitreController."); this.logger.LogInformation("Initialisation du controleur TitreController.");
} }
@@ -101,31 +108,30 @@ namespace Webzine.WebApplication.Controllers
[HttpPost] [HttpPost]
public IActionResult Comment([Bind(Prefix = "CommentForm")] TitreComment model) public IActionResult Comment([Bind(Prefix = "CommentForm")] TitreComment model)
{ {
var titreToUpdate = this.titreRepository.Find(model.IdTitre);
if (!this.ModelState.IsValid) if (!this.ModelState.IsValid)
{ {
var titre = this.titreRepository.Find(model.IdTitre); if (titreToUpdate == null)
if (titre == null)
{ {
this.logger.LogWarning("Titre avec ID {Id} introuvable pour ajout de commentaire.", model.IdTitre); this.logger.LogWarning("Titre avec ID {Id} introuvable pour ajout de commentaire.", model.IdTitre);
return this.RedirectToAction("Index", "Accueil"); return this.RedirectToAction("Index", "Accueil");
} }
return this.View("Index", this.BuildTitreDetailViewModel(titre, model)); return this.View("Index", this.BuildTitreDetailViewModel(titreToUpdate, model));
} }
var titreToUpdate = this.titreRepository.Find(model.IdTitre);
if (titreToUpdate != null) if (titreToUpdate != null)
{ {
var commentaire = new Commentaire var commentaire = new Commentaire
{ {
Auteur = model.Auteur, Auteur = model.Auteur,
Contenu = model.Contenu, Contenu = model.Contenu,
DateCreation = DateTime.Now, DateCreation = DateTime.UtcNow,
IdTitre = model.IdTitre, IdTitre = model.IdTitre,
}; };
titreToUpdate.Commentaires.Add(commentaire); this.commentaireRepository.Add(commentaire);
this.titreRepository.Update(titreToUpdate);
} }
return this.RedirectToAction("Index", new { id = model.IdTitre }); return this.RedirectToAction("Index", new { id = model.IdTitre });

View File

@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
/// <summary> /// <summary>
/// Filtre d'exception global qui intercepte toute exception non gérée et la journalise automatiquement. /// Filtre d'exception global qui intercepte toute exception non geree et la journalise automatiquement.
/// </summary> /// </summary>
public class GlobalExceptionFilter : IExceptionFilter public class GlobalExceptionFilter : IExceptionFilter
{ {
@@ -13,7 +13,7 @@ public class GlobalExceptionFilter : IExceptionFilter
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GlobalExceptionFilter"/> class. /// Initializes a new instance of the <see cref="GlobalExceptionFilter"/> class.
/// </summary> /// </summary>
/// <param name="logger">Service de journalisation injecté.</param> /// <param name="logger">Service de journalisation injecte.</param>
public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger) public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
{ {
this.logger = logger; this.logger = logger;
@@ -22,11 +22,14 @@ public class GlobalExceptionFilter : IExceptionFilter
/// <inheritdoc/> /// <inheritdoc/>
public void OnException(ExceptionContext context) public void OnException(ExceptionContext context)
{ {
string detail = BuildExceptionDetail(context.Exception);
this.logger.LogError( this.logger.LogError(
context.Exception, context.Exception,
"Erreur non gérée dans {Action} : {Message}", "Erreur non geree dans {Action} : {Message} | Details: {Details}",
context.ActionDescriptor.DisplayName, context.ActionDescriptor.DisplayName,
context.Exception.Message); context.Exception.Message,
detail);
context.Result = new ObjectResult(new context.Result = new ObjectResult(new
{ {
@@ -39,4 +42,18 @@ public class GlobalExceptionFilter : IExceptionFilter
context.ExceptionHandled = true; context.ExceptionHandled = true;
} }
private static string BuildExceptionDetail(Exception exception)
{
var messages = new List<string>();
Exception? current = exception;
while (current != null)
{
messages.Add($"{current.GetType().Name}: {current.Message}");
current = current.InnerException;
}
return string.Join(" --> ", messages);
}
} }

View File

@@ -1,8 +1,8 @@
{ {
"Seeder": "Local", "Seeder": "Spotify",
"Repository": "Db", "Repository": "Db",
"SpotifySeeder": { "SpotifySeeder": {
"ClientId": "", "ClientId": "390689c2fc79408b830d2f518375ef84",
"ClientSecret": "" "ClientSecret": "6e98a09c77ad43ae93bc0f0560cfcbe1"
} }
} }