feat: notification system via Mercure for scraping events

- NotificationInterface: add sendInfo() and sendWarning() levels
- SymfonyNotification: implement new levels (publishes to 'notifications' topic)
- ChapterScrapingStarted: carry mangaTitle + chapterNumber, now dispatched
- ScrapeChapterHandler: dispatch ChapterScrapingStarted before scraping loop
- ScrapingEventSubscriber: wire NotificationInterface for started/scraped/failed events
- useMercureNotifications: new global Vue composable subscribing to 'notifications' topic
- App.vue: mount useMercureNotifications() at app root
- SendTestNotificationCommand: `app:notify:test --type --message` for dev/prod testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2026-03-12 00:57:21 +01:00
parent 95f224d69a
commit 41ca08f20e
9 changed files with 171 additions and 50 deletions

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Domain\Shared\Domain\Contract\NotificationInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(
name: 'app:notify:test',
description: 'Envoie une notification de test via Mercure (utile en dev/prod pour vérifier le système)',
)]
class SendTestNotificationCommand extends Command
{
public function __construct(
private readonly NotificationInterface $notification
) {
parent::__construct();
}
protected function configure(): void
{
$this
->addOption('type', 't', InputOption::VALUE_REQUIRED, 'Type de notification : info, success, error, warning', 'info')
->addOption('message', 'm', InputOption::VALUE_REQUIRED, 'Message à envoyer', 'Notification de test depuis Mangarr');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$type = $input->getOption('type');
$message = $input->getOption('message');
$allowed = ['info', 'success', 'error', 'warning'];
if (!in_array($type, $allowed, true)) {
$output->writeln(sprintf('<error>Type invalide "%s". Valeurs acceptées : %s</error>', $type, implode(', ', $allowed)));
return Command::FAILURE;
}
match ($type) {
'success' => $this->notification->sendSuccess($message),
'error' => $this->notification->sendError($message),
'warning' => $this->notification->sendWarning($message),
default => $this->notification->sendInfo($message),
};
$output->writeln(sprintf('<info>[%s] Notification envoyée : %s</info>', strtoupper($type), $message));
return Command::SUCCESS;
}
}