diff --git a/ansible/deploy.yml.disabled b/ansible/deploy.yml.disabled index 3686f49..f6a8dd0 100644 --- a/ansible/deploy.yml.disabled +++ b/ansible/deploy.yml.disabled @@ -272,6 +272,14 @@ job: "docker compose -f /var/www/crm-siteconseil/docker-compose-prod.yml exec -T php php bin/console app:dns:check --env=prod >> /var/log/crm-siteconseil-dns-check.log 2>&1" user: bot + - name: Configure email tracking purge cron (daily at 5am) + cron: + name: "crm-siteconseil email-tracking purge" + minute: "0" + hour: "5" + job: "docker compose -f /var/www/crm-siteconseil/docker-compose-prod.yml exec -T php php bin/console app:email-tracking:purge --env=prod >> /var/log/crm-siteconseil-email-purge.log 2>&1" + user: bot + - name: Configure Cloudflare acme-challenge cleanup cron (daily at 6am) cron: name: "crm-siteconseil cloudflare clean" diff --git a/config/packages/packages/cache.yaml b/config/packages/packages/cache.yaml index 56df9cd..169fd51 100644 --- a/config/packages/packages/cache.yaml +++ b/config/packages/packages/cache.yaml @@ -17,3 +17,6 @@ framework: adapter: cache.adapter.redis vite_cache_pool: adapter: cache.adapter.redis + dns_infra_cache: + adapter: cache.adapter.redis + default_lifetime: 3600 diff --git a/docker/cron/entrypoint.sh b/docker/cron/entrypoint.sh index ab20df6..748e86d 100644 --- a/docker/cron/entrypoint.sh +++ b/docker/cron/entrypoint.sh @@ -1,10 +1,15 @@ #!/bin/sh echo "=== CRM SITECONSEIL Cron ===" echo "Registered tasks:" -echo " - */5 * * * * app:orders:expire-pending" -echo " - 0 * * * * app:monitor:messenger" -echo " - 0 3 * * * app:meilisearch:check-consistency --fix" -echo " - 0 4 * * * app:attestations:clean" -echo " - */15 * * * app:services:check" +echo " - */5 * * * * app:orders:expire-pending" +echo " - 0 * * * * app:monitor:messenger" +echo " - 0 */2 * * app:dns:check" +echo " - 0 3 * * * app:meilisearch:check-consistency --fix" +echo " - 0 4 * * * app:attestations:clean" +echo " - 0 5 * * * app:email-tracking:purge" +echo " - 0 6 * * * app:cloudflare:clean" +echo " - 0 */6 * * app:stripe:sync" +echo " - */5 * * * * app:infra:snapshot" +echo " - */15 * * * * app:services:check" echo "====================" exec cron -f diff --git a/src/Command/PurgeEmailTrackingCommand.php b/src/Command/PurgeEmailTrackingCommand.php new file mode 100644 index 0000000..67bb3b8 --- /dev/null +++ b/src/Command/PurgeEmailTrackingCommand.php @@ -0,0 +1,52 @@ +addOption('days', null, InputOption::VALUE_REQUIRED, 'Nombre de jours de retention', '90'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $days = (int) $input->getOption('days'); + $threshold = new \DateTimeImmutable("-{$days} days"); + + $io->title("Purge EmailTracking (> $days jours)"); + $io->text('Seuil : '.$threshold->format('d/m/Y H:i:s')); + + $qb = $this->em->createQueryBuilder() + ->delete('App\Entity\EmailTracking', 'e') + ->where('e.sentAt < :threshold') + ->setParameter('threshold', $threshold); + + $deleted = $qb->getQuery()->execute(); + + $io->success("$deleted enregistrement(s) supprime(s)."); + + return Command::SUCCESS; + } +} diff --git a/src/Controller/DnsReportController.php b/src/Controller/DnsReportController.php index 719e473..d2c5993 100644 --- a/src/Controller/DnsReportController.php +++ b/src/Controller/DnsReportController.php @@ -10,6 +10,9 @@ use App\Service\MailcowService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Cache\ItemInterface; class DnsReportController extends AbstractController { @@ -28,6 +31,7 @@ class DnsReportController extends AbstractController CloudflareService $cloudflare, MailcowService $mailcow, EmailTrackingRepository $emailTrackingRepository, + #[Autowire(service: 'dns_infra_cache')] CacheInterface $cache, ): Response { $tracking = $emailTrackingRepository->findOneBy(['messageId' => $token]); @@ -35,43 +39,57 @@ class DnsReportController extends AbstractController throw $this->createNotFoundException('Rapport introuvable.'); } - $errors = []; - $warnings = []; - $successes = []; - $domainResults = []; + $cacheKey = 'dns_infra_check_'.$token; - $cfRecordsByDomain = $this->loadCloudflareRecords($cloudflare); + $data = $cache->get($cacheKey, function (ItemInterface $item) use ($dnsCheck, $awsSes, $cloudflare, $mailcow): array { + $item->expiresAfter(3600); // 1 heure - foreach (self::DOMAINS as $domain) { - $checks = []; - $cfRecords = $cfRecordsByDomain[$domain] ?? []; + $errors = []; + $warnings = []; + $successes = []; + $domainResults = []; - $dnsCheck->checkSpf($domain, $checks, $errors, $warnings, $successes); - $this->enrichWithCloudflare($checks, $domain, 'SPF', 'TXT', $cfRecords); + $cfRecordsByDomain = $this->loadCloudflareRecords($cloudflare); - $dnsCheck->checkDmarc($domain, $checks, $errors, $successes); - $this->enrichWithCloudflare($checks, '_dmarc.'.$domain, 'DMARC', 'TXT', $cfRecords); + foreach (self::DOMAINS as $domain) { + $checks = []; + $cfRecords = $cfRecordsByDomain[$domain] ?? []; - $dnsCheck->checkMx($domain, self::EXPECTED_MX[$domain] ?? '', $checks, $errors, $successes); - $this->enrichWithCloudflare($checks, $domain, 'MX', 'MX', $cfRecords); + $dnsCheck->checkSpf($domain, $checks, $errors, $warnings, $successes); + $this->enrichWithCloudflare($checks, $domain, 'SPF', 'TXT', $cfRecords); - $dnsCheck->checkBounce($domain, $checks, $errors, $warnings, $successes); - $this->enrichLastCheck($checks, 'bounce.'.$domain, 'MX', $cfRecords); + $dnsCheck->checkDmarc($domain, $checks, $errors, $successes); + $this->enrichWithCloudflare($checks, '_dmarc.'.$domain, 'DMARC', 'TXT', $cfRecords); - $dnsCheck->checkWhois($domain, $checks, $errors, $warnings, $successes); + $dnsCheck->checkMx($domain, self::EXPECTED_MX[$domain] ?? '', $checks, $errors, $successes); + $this->enrichWithCloudflare($checks, $domain, 'MX', 'MX', $cfRecords); - $this->checkAwsSes($domain, $awsSes, $dnsCheck, $checks, $errors, $successes, $cfRecords); - $this->checkMailcow($domain, $mailcow, $dnsCheck, $checks, $errors, $warnings, $successes, $cfRecords); + $dnsCheck->checkBounce($domain, $checks, $errors, $warnings, $successes); + $this->enrichLastCheck($checks, 'bounce.'.$domain, 'MX', $cfRecords); - $domainResults[] = ['domain' => $domain, 'checks' => $checks]; - } + $dnsCheck->checkWhois($domain, $checks, $errors, $warnings, $successes); + + $this->checkAwsSes($domain, $awsSes, $dnsCheck, $checks, $errors, $successes, $cfRecords); + $this->checkMailcow($domain, $mailcow, $dnsCheck, $checks, $errors, $warnings, $successes, $cfRecords); + + $domainResults[] = ['domain' => $domain, 'checks' => $checks]; + } + + return [ + 'domainResults' => $domainResults, + 'errors' => $errors, + 'warnings' => $warnings, + 'successes' => $successes, + 'date' => (new \DateTimeImmutable())->format('c'), + ]; + }); return $this->render('dns_report/index.html.twig', [ - 'domainResults' => $domainResults, - 'errors' => $errors, - 'warnings' => $warnings, - 'successes' => $successes, - 'date' => new \DateTimeImmutable(), + 'domainResults' => $data['domainResults'], + 'errors' => $data['errors'], + 'warnings' => $data['warnings'], + 'successes' => $data['successes'], + 'date' => new \DateTimeImmutable($data['date']), ]); }