feat: passer les logs et le sync Meilisearch en asynchrone via Messenger
src/Message/AppLogMessage.php (nouveau): - Message serialisable avec method, url, route, action, userId (int nullable au lieu de l'entite User), ip - Dispatche via le bus Messenger pour traitement asynchrone par Redis src/MessageHandler/AppLogMessageHandler.php (nouveau): - Recharge le User par ID depuis le repository - Cree l'AppLog avec le HMAC et persiste en BDD - Execute en arriere-plan sans bloquer la requete HTTP src/Service/AppLoggerService.php: - log(): dispatch maintenant un AppLogMessage via le bus Messenger au lieu de persister directement (asynchrone) - logDirect(): reste synchrone pour les suppressions de logs qui doivent etre tracees immediatement avant la reponse HTTP - Injection de MessageBusInterface en plus de EntityManager src/Message/MeilisearchSyncMessage.php (nouveau): - Message avec type (customer/revendeur/price), entityId et action (index ou remove) - Constantes TYPE_CUSTOMER, TYPE_REVENDEUR, TYPE_PRICE src/MessageHandler/MeilisearchSyncMessageHandler.php (nouveau): - Recharge l'entite par ID selon le type - Appelle indexCustomer/indexRevendeur/indexPrice ou les methodes remove correspondantes sur MeilisearchService - Execute en arriere-plan via Redis Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
16
src/Message/AppLogMessage.php
Normal file
16
src/Message/AppLogMessage.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Message;
|
||||
|
||||
class AppLogMessage
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $method,
|
||||
public readonly string $url,
|
||||
public readonly string $route,
|
||||
public readonly string $action,
|
||||
public readonly ?int $userId,
|
||||
public readonly ?string $ip,
|
||||
) {
|
||||
}
|
||||
}
|
||||
17
src/Message/MeilisearchSyncMessage.php
Normal file
17
src/Message/MeilisearchSyncMessage.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Message;
|
||||
|
||||
class MeilisearchSyncMessage
|
||||
{
|
||||
public const TYPE_CUSTOMER = 'customer';
|
||||
public const TYPE_REVENDEUR = 'revendeur';
|
||||
public const TYPE_PRICE = 'price';
|
||||
|
||||
public function __construct(
|
||||
public readonly string $type,
|
||||
public readonly int $entityId,
|
||||
public readonly string $action = 'index',
|
||||
) {
|
||||
}
|
||||
}
|
||||
39
src/MessageHandler/AppLogMessageHandler.php
Normal file
39
src/MessageHandler/AppLogMessageHandler.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\MessageHandler;
|
||||
|
||||
use App\Entity\AppLog;
|
||||
use App\Message\AppLogMessage;
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
|
||||
#[AsMessageHandler]
|
||||
class AppLogMessageHandler
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManagerInterface $em,
|
||||
private UserRepository $userRepository,
|
||||
#[Autowire('%env(APP_SECRET)%')] private string $hmacSecret,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(AppLogMessage $message): void
|
||||
{
|
||||
$user = null !== $message->userId ? $this->userRepository->find($message->userId) : null;
|
||||
|
||||
$log = new AppLog(
|
||||
$message->method,
|
||||
$message->url,
|
||||
$message->route,
|
||||
$message->action,
|
||||
$this->hmacSecret,
|
||||
$user,
|
||||
$message->ip,
|
||||
);
|
||||
|
||||
$this->em->persist($log);
|
||||
$this->em->flush();
|
||||
}
|
||||
}
|
||||
67
src/MessageHandler/MeilisearchSyncMessageHandler.php
Normal file
67
src/MessageHandler/MeilisearchSyncMessageHandler.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\MessageHandler;
|
||||
|
||||
use App\Message\MeilisearchSyncMessage;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\PriceAutomaticRepository;
|
||||
use App\Repository\RevendeurRepository;
|
||||
use App\Service\MeilisearchService;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
|
||||
#[AsMessageHandler]
|
||||
class MeilisearchSyncMessageHandler
|
||||
{
|
||||
public function __construct(
|
||||
private MeilisearchService $meilisearch,
|
||||
private CustomerRepository $customerRepository,
|
||||
private RevendeurRepository $revendeurRepository,
|
||||
private PriceAutomaticRepository $priceRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(MeilisearchSyncMessage $message): void
|
||||
{
|
||||
if ('remove' === $message->action) {
|
||||
match ($message->type) {
|
||||
MeilisearchSyncMessage::TYPE_CUSTOMER => $this->meilisearch->removeCustomer($message->entityId),
|
||||
MeilisearchSyncMessage::TYPE_REVENDEUR => $this->meilisearch->removeRevendeur($message->entityId),
|
||||
MeilisearchSyncMessage::TYPE_PRICE => $this->meilisearch->removePrice($message->entityId),
|
||||
default => null,
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
match ($message->type) {
|
||||
MeilisearchSyncMessage::TYPE_CUSTOMER => $this->indexCustomer($message->entityId),
|
||||
MeilisearchSyncMessage::TYPE_REVENDEUR => $this->indexRevendeur($message->entityId),
|
||||
MeilisearchSyncMessage::TYPE_PRICE => $this->indexPrice($message->entityId),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
private function indexCustomer(int $id): void
|
||||
{
|
||||
$customer = $this->customerRepository->find($id);
|
||||
if (null !== $customer) {
|
||||
$this->meilisearch->indexCustomer($customer);
|
||||
}
|
||||
}
|
||||
|
||||
private function indexRevendeur(int $id): void
|
||||
{
|
||||
$revendeur = $this->revendeurRepository->find($id);
|
||||
if (null !== $revendeur) {
|
||||
$this->meilisearch->indexRevendeur($revendeur);
|
||||
}
|
||||
}
|
||||
|
||||
private function indexPrice(int $id): void
|
||||
{
|
||||
$price = $this->priceRepository->find($id);
|
||||
if (null !== $price) {
|
||||
$this->meilisearch->indexPrice($price);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,14 @@ namespace App\Service;
|
||||
|
||||
use App\Entity\AppLog;
|
||||
use App\Entity\User;
|
||||
use App\Message\AppLogMessage;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
class AppLoggerService
|
||||
{
|
||||
/** @var array<string, string> Routes admin → descriptions lisibles */
|
||||
/** @var array<string, string> */
|
||||
private const ROUTE_LABELS = [
|
||||
'app_admin_dashboard' => 'Consultation du tableau de bord',
|
||||
'app_admin_clients_index' => 'Consultation de la liste des clients',
|
||||
@@ -44,14 +46,20 @@ class AppLoggerService
|
||||
'app_admin_tarification_edit' => 'Modification d\'un tarif',
|
||||
'app_admin_services_index' => 'Consultation des services',
|
||||
'app_admin_logs' => 'Consultation des logs',
|
||||
'app_admin_logs_purge' => 'Suppression de tous les logs',
|
||||
'app_admin_logs_delete' => 'Suppression d\'un log',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private MessageBusInterface $bus,
|
||||
private EntityManagerInterface $em,
|
||||
#[Autowire('%env(APP_SECRET)%')] private string $hmacSecret,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Log asynchrone via Messenger.
|
||||
*/
|
||||
public function log(string $method, string $url, string $route, ?User $user = null, ?string $ip = null): void
|
||||
{
|
||||
$action = self::ROUTE_LABELS[$route] ?? 'Acces a '.$route;
|
||||
@@ -60,14 +68,18 @@ class AppLoggerService
|
||||
$action .= ' (soumission)';
|
||||
}
|
||||
|
||||
$log = new AppLog($method, $url, $route, $action, $this->hmacSecret, $user, $ip);
|
||||
|
||||
$this->em->persist($log);
|
||||
$this->em->flush();
|
||||
$this->bus->dispatch(new AppLogMessage(
|
||||
$method,
|
||||
$url,
|
||||
$route,
|
||||
$action,
|
||||
$user?->getId(),
|
||||
$ip,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log direct avec action personnalisee (pour les suppressions de logs).
|
||||
* Log synchrone direct (pour les suppressions qui doivent etre tracees immediatement).
|
||||
*/
|
||||
public function logDirect(string $method, string $url, string $route, string $action, ?User $user = null, ?string $ip = null): void
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user