chore: merge master

This commit is contained in:
Maël Gangloff 2025-05-21 13:20:04 +02:00
commit 62c01251cf
No known key found for this signature in database
GPG Key ID: 11FDC81C24A7F629
4 changed files with 97 additions and 107 deletions

View File

@ -23,9 +23,6 @@ use Eluceo\iCal\Domain\Entity\Calendar;
use Eluceo\iCal\Presentation\Component\Property; use Eluceo\iCal\Presentation\Component\Property;
use Eluceo\iCal\Presentation\Component\Property\Value\TextValue; use Eluceo\iCal\Presentation\Component\Property\Value\TextValue;
use Eluceo\iCal\Presentation\Factory\CalendarFactory; use Eluceo\iCal\Presentation\Factory\CalendarFactory;
use Exception;
use JsonException;
use Laminas\Feed\Writer\Deleted;
use Laminas\Feed\Writer\Entry; use Laminas\Feed\Writer\Entry;
use Laminas\Feed\Writer\Feed; use Laminas\Feed\Writer\Feed;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -58,18 +55,17 @@ class WatchListController extends AbstractController
{ {
public function __construct( public function __construct(
private readonly SerializerInterface&DecoderInterface&DenormalizerInterface $serializer, private readonly SerializerInterface&DecoderInterface&DenormalizerInterface $serializer,
private readonly EntityManagerInterface $em, private readonly EntityManagerInterface $em,
private readonly WatchListRepository $watchListRepository, private readonly WatchListRepository $watchListRepository,
private readonly LoggerInterface $logger, private readonly LoggerInterface $logger,
private readonly ChatNotificationService $chatNotificationService, private readonly ChatNotificationService $chatNotificationService,
private readonly DomainRepository $domainRepository, private readonly DomainRepository $domainRepository,
private readonly RDAPService $RDAPService, private readonly RDAPService $RDAPService,
private readonly RateLimiterFactory $rdapRequestsLimiter, private readonly RateLimiterFactory $rdapRequestsLimiter,
private readonly KernelInterface $kernel, private readonly KernelInterface $kernel,
#[Autowire(service: 'service_container')] #[Autowire(service: 'service_container')]
private readonly ContainerInterface $locator, private readonly ContainerInterface $locator,
) ) {
{
} }
/** /**
@ -79,8 +75,8 @@ class WatchListController extends AbstractController
* @throws ExceptionInterface * @throws ExceptionInterface
* @throws DecodingExceptionInterface * @throws DecodingExceptionInterface
* @throws ClientExceptionInterface * @throws ClientExceptionInterface
* @throws JsonException * @throws \JsonException
* @throws Exception * @throws \Exception
*/ */
#[Route( #[Route(
path: '/api/watchlists', path: '/api/watchlists',
@ -104,7 +100,7 @@ class WatchListController extends AbstractController
* This policy guarantees the equal probability of obtaining a domain name if it is requested by several users. * This policy guarantees the equal probability of obtaining a domain name if it is requested by several users.
*/ */
if ($this->getParameter('limited_features')) { if ($this->getParameter('limited_features')) {
if ($watchList->getDomains()->count() > (int)$this->getParameter('limit_max_watchlist_domains')) { if ($watchList->getDomains()->count() > (int) $this->getParameter('limit_max_watchlist_domains')) {
$this->logger->notice('User {username} tried to create a Watchlist. The maximum number of domains has been reached.', [ $this->logger->notice('User {username} tried to create a Watchlist. The maximum number of domains has been reached.', [
'username' => $user->getUserIdentifier(), 'username' => $user->getUserIdentifier(),
]); ]);
@ -112,7 +108,7 @@ class WatchListController extends AbstractController
} }
$userWatchLists = $user->getWatchLists(); $userWatchLists = $user->getWatchLists();
if ($userWatchLists->count() >= (int)$this->getParameter('limit_max_watchlist')) { if ($userWatchLists->count() >= (int) $this->getParameter('limit_max_watchlist')) {
$this->logger->notice('User {username} tried to create a Watchlist. The maximum number of Watchlists has been reached', [ $this->logger->notice('User {username} tried to create a Watchlist. The maximum number of Watchlists has been reached', [
'username' => $user->getUserIdentifier(), 'username' => $user->getUserIdentifier(),
]); ]);
@ -120,7 +116,7 @@ class WatchListController extends AbstractController
} }
/** @var Domain[] $trackedDomains */ /** @var Domain[] $trackedDomains */
$trackedDomains = $userWatchLists->reduce(fn(array $acc, WatchList $watchList) => [...$acc, ...$watchList->getDomains()->toArray()], []); $trackedDomains = $userWatchLists->reduce(fn (array $acc, WatchList $watchList) => [...$acc, ...$watchList->getDomains()->toArray()], []);
/** @var Domain $domain */ /** @var Domain $domain */
foreach ($watchList->getDomains()->getIterator() as $domain) { foreach ($watchList->getDomains()->getIterator() as $domain) {
@ -136,7 +132,7 @@ class WatchListController extends AbstractController
} }
} }
if (null !== $watchList->getWebhookDsn() && count($watchList->getWebhookDsn()) > (int)$this->getParameter('limit_max_watchlist_webhooks')) { if (null !== $watchList->getWebhookDsn() && count($watchList->getWebhookDsn()) > (int) $this->getParameter('limit_max_watchlist_webhooks')) {
$this->logger->notice('User {username} tried to create a Watchlist. The maximum number of webhooks has been reached.', [ $this->logger->notice('User {username} tried to create a Watchlist. The maximum number of webhooks has been reached.', [
'username' => $user->getUserIdentifier(), 'username' => $user->getUserIdentifier(),
]); ]);
@ -176,7 +172,7 @@ class WatchListController extends AbstractController
} }
/** /**
* @throws Exception * @throws \Exception
* @throws ExceptionInterface * @throws ExceptionInterface
*/ */
private function verifyConnector(WatchList $watchList, ?Connector $connector): void private function verifyConnector(WatchList $watchList, ?Connector $connector): void
@ -224,12 +220,12 @@ class WatchListController extends AbstractController
* @throws RedirectionExceptionInterface * @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface * @throws DecodingExceptionInterface
* @throws ClientExceptionInterface * @throws ClientExceptionInterface
* @throws JsonException * @throws \JsonException
* @throws OptimisticLockException * @throws OptimisticLockException
* @throws TransportExceptionInterface * @throws TransportExceptionInterface
* @throws ServerExceptionInterface * @throws ServerExceptionInterface
* @throws ExceptionInterface * @throws ExceptionInterface
* @throws Exception * @throws \Exception
*/ */
#[Route( #[Route(
path: '/api/watchlists/{token}', path: '/api/watchlists/{token}',
@ -249,7 +245,7 @@ class WatchListController extends AbstractController
$watchList->setUser($user); $watchList->setUser($user);
if ($this->getParameter('limited_features')) { if ($this->getParameter('limited_features')) {
if ($watchList->getDomains()->count() > (int)$this->getParameter('limit_max_watchlist_domains')) { if ($watchList->getDomains()->count() > (int) $this->getParameter('limit_max_watchlist_domains')) {
$this->logger->notice('User {username} tried to update a Watchlist. The maximum number of domains has been reached for this Watchlist', [ $this->logger->notice('User {username} tried to update a Watchlist. The maximum number of domains has been reached for this Watchlist', [
'username' => $user->getUserIdentifier(), 'username' => $user->getUserIdentifier(),
]); ]);
@ -260,8 +256,8 @@ class WatchListController extends AbstractController
/** @var Domain[] $trackedDomains */ /** @var Domain[] $trackedDomains */
$trackedDomains = $userWatchLists $trackedDomains = $userWatchLists
->filter(fn(WatchList $wl) => $wl->getToken() !== $watchList->getToken()) ->filter(fn (WatchList $wl) => $wl->getToken() !== $watchList->getToken())
->reduce(fn(array $acc, WatchList $wl) => [...$acc, ...$wl->getDomains()->toArray()], []); ->reduce(fn (array $acc, WatchList $wl) => [...$acc, ...$wl->getDomains()->toArray()], []);
/** @var Domain $domain */ /** @var Domain $domain */
foreach ($watchList->getDomains()->getIterator() as $domain) { foreach ($watchList->getDomains()->getIterator() as $domain) {
@ -276,7 +272,7 @@ class WatchListController extends AbstractController
} }
} }
if (null !== $watchList->getWebhookDsn() && count($watchList->getWebhookDsn()) > (int)$this->getParameter('limit_max_watchlist_webhooks')) { if (null !== $watchList->getWebhookDsn() && count($watchList->getWebhookDsn()) > (int) $this->getParameter('limit_max_watchlist_webhooks')) {
$this->logger->notice('User {username} tried to update a Watchlist. The maximum number of webhooks has been reached.', [ $this->logger->notice('User {username} tried to update a Watchlist. The maximum number of webhooks has been reached.', [
'username' => $user->getUserIdentifier(), 'username' => $user->getUserIdentifier(),
]); ]);
@ -313,7 +309,7 @@ class WatchListController extends AbstractController
* @throws ParseException * @throws ParseException
* @throws EofException * @throws EofException
* @throws InvalidDataException * @throws InvalidDataException
* @throws Exception * @throws \Exception
*/ */
#[Route( #[Route(
path: '/api/watchlists/{token}/calendar', path: '/api/watchlists/{token}/calendar',
@ -349,7 +345,7 @@ class WatchListController extends AbstractController
} }
/** /**
* @throws Exception * @throws \Exception
*/ */
#[Route( #[Route(
path: '/api/tracked', path: '/api/tracked',
@ -370,7 +366,7 @@ class WatchListController extends AbstractController
/** @var Domain $domain */ /** @var Domain $domain */
foreach ($watchList->getDomains()->getIterator() as $domain) { foreach ($watchList->getDomains()->getIterator() as $domain) {
/** @var DomainEvent|null $exp */ /** @var DomainEvent|null $exp */
$exp = $domain->getEvents()->findFirst(fn(int $key, DomainEvent $e) => !$e->getDeleted() && 'expiration' === $e->getAction()); $exp = $domain->getEvents()->findFirst(fn (int $key, DomainEvent $e) => !$e->getDeleted() && 'expiration' === $e->getAction());
if (!$domain->getDeleted() && null !== $exp && !in_array($domain, $domains)) { if (!$domain->getDeleted() && null !== $exp && !in_array($domain, $domains)) {
$domains[] = $domain; $domains[] = $domain;
@ -378,7 +374,7 @@ class WatchListController extends AbstractController
} }
} }
usort($domains, fn(Domain $d1, Domain $d2) => $d1->getExpiresInDays() - $d2->getExpiresInDays()); usort($domains, fn (Domain $d1, Domain $d2) => $d1->getExpiresInDays() - $d2->getExpiresInDays());
return $domains; return $domains;
} }
@ -389,7 +385,7 @@ class WatchListController extends AbstractController
* @throws RedirectionExceptionInterface * @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface * @throws DecodingExceptionInterface
* @throws ClientExceptionInterface * @throws ClientExceptionInterface
* @throws JsonException * @throws \JsonException
*/ */
private function registerDomainsInWatchlist(string $content, array $groups): WatchList private function registerDomainsInWatchlist(string $content, array $groups): WatchList
{ {
@ -402,11 +398,11 @@ class WatchListController extends AbstractController
throw new BadRequestHttpException('Invalid payload: missing or invalid "domains" field.'); throw new BadRequestHttpException('Invalid payload: missing or invalid "domains" field.');
} }
$domains = array_map(fn(string $d) => str_replace('/api/domains/', '', $d), $data['domains']); $domains = array_map(fn (string $d) => str_replace('/api/domains/', '', $d), $data['domains']);
foreach ($domains as $ldhName) { foreach ($domains as $ldhName) {
/** @var ?Domain $domain */ /** @var ?Domain $domain */
$domain = $this->domainRepository->findOneBy(['ldhName' => $ldhName]); $domain = $this->domainRepository->findOneBy(['ldhName' => RDAPService::convertToIdn($ldhName)]);
if (null === $domain) { if (null === $domain) {
try { try {
@ -439,7 +435,7 @@ class WatchListController extends AbstractController
} }
/** /**
* @throws Exception * @throws \Exception
*/ */
#[Route( #[Route(
path: '/api/watchlists/{token}/rss/events', path: '/api/watchlists/{token}/rss/events',
@ -457,11 +453,11 @@ class WatchListController extends AbstractController
$feed = (new Feed()) $feed = (new Feed())
->setLanguage('en') ->setLanguage('en')
->setCopyright('Domain Watchdog makes this information available "as is," and does not guarantee its accuracy.') ->setCopyright('Domain Watchdog makes this information available "as is," and does not guarantee its accuracy.')
->setTitle('Domain events (' . $watchlist->getName().')') ->setTitle('Domain events ('.$watchlist->getName().')')
->setGenerator("Domain Watchdog - RSS Feed", null, "https://github.com/maelgangloff/domain-watchdog") ->setGenerator('Domain Watchdog - RSS Feed', null, 'https://github.com/maelgangloff/domain-watchdog')
->setDescription('The latest events for domain names in your Watchlist') ->setDescription('The latest events for domain names in your Watchlist')
->setLink($request->getSchemeAndHttpHost() . "/#/tracking/watchlist") ->setLink($request->getSchemeAndHttpHost().'/#/tracking/watchlist')
->setFeedLink($request->getSchemeAndHttpHost() . "/api/watchlists/" . $token . '/rss/events', 'atom') ->setFeedLink($request->getSchemeAndHttpHost().'/api/watchlists/'.$token.'/rss/events', 'atom')
->setDateCreated($watchlist->getCreatedAt()); ->setDateCreated($watchlist->getCreatedAt());
/** @var Domain $domain */ /** @var Domain $domain */
@ -471,13 +467,13 @@ class WatchListController extends AbstractController
} }
} }
return new Response($feed->export("atom"), Response::HTTP_OK, [ return new Response($feed->export('atom'), Response::HTTP_OK, [
'Content-Type' => 'application/atom+xml; charset=utf-8', 'Content-Type' => 'application/atom+xml; charset=utf-8',
]); ]);
} }
/** /**
* @throws Exception * @throws \Exception
*/ */
#[Route( #[Route(
path: '/api/watchlists/{token}/rss/status', path: '/api/watchlists/{token}/rss/status',
@ -495,11 +491,11 @@ class WatchListController extends AbstractController
$feed = (new Feed()) $feed = (new Feed())
->setLanguage('en') ->setLanguage('en')
->setCopyright('Domain Watchdog makes this information available "as is," and does not guarantee its accuracy.') ->setCopyright('Domain Watchdog makes this information available "as is," and does not guarantee its accuracy.')
->setTitle('Domain EPP status (' . $watchlist->getName().')') ->setTitle('Domain EPP status ('.$watchlist->getName().')')
->setGenerator("Domain Watchdog - RSS Feed", null, "https://github.com/maelgangloff/domain-watchdog") ->setGenerator('Domain Watchdog - RSS Feed', null, 'https://github.com/maelgangloff/domain-watchdog')
->setDescription('The latest changes to the EPP status of the domain names in your Watchlist') ->setDescription('The latest changes to the EPP status of the domain names in your Watchlist')
->setLink($request->getSchemeAndHttpHost() . "/#/tracking/watchlist") ->setLink($request->getSchemeAndHttpHost().'/#/tracking/watchlist')
->setFeedLink($request->getSchemeAndHttpHost() . "/api/watchlists/" . $token . '/rss/status', 'atom') ->setFeedLink($request->getSchemeAndHttpHost().'/api/watchlists/'.$token.'/rss/status', 'atom')
->setDateCreated($watchlist->getCreatedAt()); ->setDateCreated($watchlist->getCreatedAt());
/** @var Domain $domain */ /** @var Domain $domain */
@ -509,67 +505,65 @@ class WatchListController extends AbstractController
} }
} }
return new Response($feed->export("atom"), Response::HTTP_OK, [ return new Response($feed->export('atom'), Response::HTTP_OK, [
'Content-Type' => 'application/atom+xml; charset=utf-8', 'Content-Type' => 'application/atom+xml; charset=utf-8',
]); ]);
} }
/** /**
* @throws Exception * @throws \Exception
*/ */
private function getRssEventEntries(string $baseUrl, Domain $domain): array private function getRssEventEntries(string $baseUrl, Domain $domain): array
{ {
$entries = []; $entries = [];
foreach ($domain->getEvents()->filter(fn (DomainEvent $e) => $e->getDate()->diff(new \DateTimeImmutable('now'))->y <= 10)->getIterator() as $event) { foreach ($domain->getEvents()->filter(fn (DomainEvent $e) => $e->getDate()->diff(new \DateTimeImmutable('now'))->y <= 10)->getIterator() as $event) {
$entries[] = (new Entry()) $entries[] = (new Entry())
->setId($baseUrl . "/api/domains/" . $domain->getLdhName() . '#events-' . $event->getId()) ->setId($baseUrl.'/api/domains/'.$domain->getLdhName().'#events-'.$event->getId())
->setTitle($domain->getLdhName() . ': ' . $event->getAction(). ' - event update') ->setTitle($domain->getLdhName().': '.$event->getAction().' - event update')
->setDescription("Domain name event") ->setDescription('Domain name event')
->setLink($baseUrl . "/#/search/domain/" . $domain->getLdhName()) ->setLink($baseUrl.'/#/search/domain/'.$domain->getLdhName())
->setContent($this->render('rss/event_entry.html.twig', [ ->setContent($this->render('rss/event_entry.html.twig', [
'event' => $event 'event' => $event,
])->getContent()) ])->getContent())
->setDateModified($event->getDate()) ->setDateModified($event->getDate())
->addAuthor(["name" => strtoupper($domain->getTld()->getTld()) . " Registry"]); ->addAuthor(['name' => strtoupper($domain->getTld()->getTld()).' Registry']);
} }
return $entries; return $entries;
} }
/** /**
* @throws Exception * @throws \Exception
*/ */
private function getRssStatusEntries(string $baseUrl, Domain $domain): array private function getRssStatusEntries(string $baseUrl, Domain $domain): array
{ {
$entries = []; $entries = [];
foreach ($domain->getDomainStatuses()->filter(fn (DomainStatus $e) => $e->getDate()->diff(new \DateTimeImmutable('now'))->y <= 10)->getIterator() as $domainStatus) { foreach ($domain->getDomainStatuses()->filter(fn (DomainStatus $e) => $e->getDate()->diff(new \DateTimeImmutable('now'))->y <= 10)->getIterator() as $domainStatus) {
$authors = [["name" => strtoupper($domain->getTld()->getTld()) . " Registry"]]; $authors = [['name' => strtoupper($domain->getTld()->getTld()).' Registry']];
/** @var string $status */ /** @var string $status */
foreach ([...$domainStatus->getAddStatus(), ...$domainStatus->getDeleteStatus()] as $status) { foreach ([...$domainStatus->getAddStatus(), ...$domainStatus->getDeleteStatus()] as $status) {
if(str_starts_with($status, 'client')) { if (str_starts_with($status, 'client')) {
$authors[] = ["name" => "Registrar"]; $authors[] = ['name' => 'Registrar'];
break; break;
} }
} }
$entries[] = (new Entry()) $entries[] = (new Entry())
->setId($baseUrl . "/api/domains/" . $domain->getLdhName() . '#status-' . $domainStatus->getId()) ->setId($baseUrl.'/api/domains/'.$domain->getLdhName().'#status-'.$domainStatus->getId())
->setTitle($domain->getLdhName() . ' - EPP status update') ->setTitle($domain->getLdhName().' - EPP status update')
->setDescription("There has been a change in the EPP status of the domain name.") ->setDescription('There has been a change in the EPP status of the domain name.')
->setLink($baseUrl . "/#/search/domain/" . $domain->getLdhName()) ->setLink($baseUrl.'/#/search/domain/'.$domain->getLdhName())
->setContent($this->render('rss/status_entry.html.twig', [ ->setContent($this->render('rss/status_entry.html.twig', [
'domainStatus' => $domainStatus 'domainStatus' => $domainStatus,
])->getContent()) ])->getContent())
->setDateCreated($domainStatus->getCreatedAt()) ->setDateCreated($domainStatus->getCreatedAt())
->setDateModified($domainStatus->getDate()) ->setDateModified($domainStatus->getDate())
->addCategory(["term" => $domain->getLdhName()]) ->addCategory(['term' => $domain->getLdhName()])
->addCategory(["term" => strtoupper($domain->getTld()->getTld())]) ->addCategory(['term' => strtoupper($domain->getTld()->getTld())])
->addAuthors($authors) ->addAuthors($authors)
; ;
} }
return $entries; return $entries;
} }
} }

View File

@ -7,9 +7,7 @@ use ApiPlatform\Metadata\Get;
use App\Config\EventAction; use App\Config\EventAction;
use App\Controller\DomainRefreshController; use App\Controller\DomainRefreshController;
use App\Repository\DomainRepository; use App\Repository\DomainRepository;
use DateInterval; use App\Service\RDAPService;
use DateMalformedIntervalStringException;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
@ -22,9 +20,6 @@ use Eluceo\iCal\Domain\ValueObject\Date;
use Eluceo\iCal\Domain\ValueObject\EmailAddress; use Eluceo\iCal\Domain\ValueObject\EmailAddress;
use Eluceo\iCal\Domain\ValueObject\SingleDay; use Eluceo\iCal\Domain\ValueObject\SingleDay;
use Eluceo\iCal\Domain\ValueObject\Timestamp; use Eluceo\iCal\Domain\ValueObject\Timestamp;
use Exception;
use Laminas\Feed\Writer\Entry;
use Laminas\Feed\Writer\Source;
use Sabre\VObject\EofException; use Sabre\VObject\EofException;
use Sabre\VObject\InvalidDataException; use Sabre\VObject\InvalidDataException;
use Sabre\VObject\ParseException; use Sabre\VObject\ParseException;
@ -109,11 +104,11 @@ class Domain
private Collection $nameservers; private Collection $nameservers;
#[ORM\Column(type: Types::DATE_IMMUTABLE)] #[ORM\Column(type: Types::DATE_IMMUTABLE)]
private ?DateTimeImmutable $createdAt; private ?\DateTimeImmutable $createdAt;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)] #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
#[Groups(['domain:item', 'domain:list'])] #[Groups(['domain:item', 'domain:list'])]
private ?DateTimeImmutable $updatedAt; private ?\DateTimeImmutable $updatedAt;
#[ORM\ManyToOne] #[ORM\ManyToOne]
#[ORM\JoinColumn(referencedColumnName: 'tld', nullable: false)] #[ORM\JoinColumn(referencedColumnName: 'tld', nullable: false)]
@ -157,7 +152,7 @@ class Domain
$this->domainEntities = new ArrayCollection(); $this->domainEntities = new ArrayCollection();
$this->watchLists = new ArrayCollection(); $this->watchLists = new ArrayCollection();
$this->nameservers = new ArrayCollection(); $this->nameservers = new ArrayCollection();
$this->updatedAt = new DateTimeImmutable('now'); $this->updatedAt = new \DateTimeImmutable('now');
$this->createdAt = $this->updatedAt; $this->createdAt = $this->updatedAt;
$this->deleted = false; $this->deleted = false;
$this->domainStatuses = new ArrayCollection(); $this->domainStatuses = new ArrayCollection();
@ -170,7 +165,7 @@ class Domain
public function setLdhName(string $ldhName): static public function setLdhName(string $ldhName): static
{ {
$this->ldhName = strtolower($ldhName); $this->ldhName = RDAPService::convertToIdn($ldhName);
return $this; return $this;
} }
@ -310,7 +305,7 @@ class Domain
return $this; return $this;
} }
public function getUpdatedAt(): ?DateTimeImmutable public function getUpdatedAt(): ?\DateTimeImmutable
{ {
return $this->updatedAt; return $this->updatedAt;
} }
@ -319,7 +314,7 @@ class Domain
#[ORM\PreUpdate] #[ORM\PreUpdate]
public function updateTimestamps(): static public function updateTimestamps(): static
{ {
$this->setUpdatedAt(new DateTimeImmutable('now')); $this->setUpdatedAt(new \DateTimeImmutable('now'));
if (null === $this->getCreatedAt()) { if (null === $this->getCreatedAt()) {
$this->setCreatedAt($this->getUpdatedAt()); $this->setCreatedAt($this->getUpdatedAt());
} }
@ -327,17 +322,17 @@ class Domain
return $this; return $this;
} }
private function setUpdatedAt(?DateTimeImmutable $updatedAt): void private function setUpdatedAt(?\DateTimeImmutable $updatedAt): void
{ {
$this->updatedAt = $updatedAt; $this->updatedAt = $updatedAt;
} }
public function getCreatedAt(): ?DateTimeImmutable public function getCreatedAt(): ?\DateTimeImmutable
{ {
return $this->createdAt; return $this->createdAt;
} }
private function setCreatedAt(?DateTimeImmutable $createdAt): void private function setCreatedAt(?\DateTimeImmutable $createdAt): void
{ {
$this->createdAt = $createdAt; $this->createdAt = $createdAt;
} }
@ -370,7 +365,7 @@ class Domain
* Determines if a domain name needs special attention. * Determines if a domain name needs special attention.
* These domain names are those whose last event was expiration or deletion. * These domain names are those whose last event was expiration or deletion.
* *
* @throws Exception * @throws \Exception
*/ */
private function isToBeWatchClosely(): bool private function isToBeWatchClosely(): bool
{ {
@ -381,10 +376,10 @@ class Domain
/** @var DomainEvent[] $events */ /** @var DomainEvent[] $events */
$events = $this->getEvents() $events = $this->getEvents()
->filter(fn(DomainEvent $e) => !$e->getDeleted() && $e->getDate() <= new DateTimeImmutable('now')) ->filter(fn (DomainEvent $e) => !$e->getDeleted() && $e->getDate() <= new \DateTimeImmutable('now'))
->toArray(); ->toArray();
usort($events, fn(DomainEvent $e1, DomainEvent $e2) => $e2->getDate() <=> $e1->getDate()); usort($events, fn (DomainEvent $e1, DomainEvent $e2) => $e2->getDate() <=> $e1->getDate());
return !empty($events) && in_array($events[0]->getAction(), self::IMPORTANT_EVENTS); return !empty($events) && in_array($events[0]->getAction(), self::IMPORTANT_EVENTS);
} }
@ -395,11 +390,11 @@ class Domain
* - It has been more than 12 minutes and the domain name has statuses that suggest it is not stable * - It has been more than 12 minutes and the domain name has statuses that suggest it is not stable
* - It has been more than 1 day and the domain name is blocked in DNS * - It has been more than 1 day and the domain name is blocked in DNS
* *
* @throws Exception * @throws \Exception
*/ */
public function isToBeUpdated(bool $fromUser = true, bool $intensifyLastDay = false): bool public function isToBeUpdated(bool $fromUser = true, bool $intensifyLastDay = false): bool
{ {
$updatedAtDiff = $this->getUpdatedAt()->diff(new DateTimeImmutable()); $updatedAtDiff = $this->getUpdatedAt()->diff(new \DateTimeImmutable());
if ($updatedAtDiff->days >= 7) { if ($updatedAtDiff->days >= 7) {
return true; return true;
@ -495,9 +490,9 @@ class Domain
} }
/** /**
* @throws DateMalformedIntervalStringException * @throws \DateMalformedIntervalStringException
*/ */
private function calculateDaysFromStatus(DateTimeImmutable $now): ?int private function calculateDaysFromStatus(\DateTimeImmutable $now): ?int
{ {
$lastStatus = $this->getDomainStatuses()->last(); $lastStatus = $this->getDomainStatuses()->last();
if (false === $lastStatus) { if (false === $lastStatus) {
@ -505,16 +500,16 @@ class Domain
} }
if ($this->isPendingDelete() && ( if ($this->isPendingDelete() && (
in_array('pending delete', $lastStatus->getAddStatus()) in_array('pending delete', $lastStatus->getAddStatus())
|| in_array('redemption period', $lastStatus->getDeleteStatus())) || in_array('redemption period', $lastStatus->getDeleteStatus()))
) { ) {
return self::daysBetween($now, $lastStatus->getCreatedAt()->add(new DateInterval('P' . 5 . 'D'))); return self::daysBetween($now, $lastStatus->getCreatedAt()->add(new \DateInterval('P'. 5 .'D')));
} }
if ($this->isRedemptionPeriod() if ($this->isRedemptionPeriod()
&& in_array('redemption period', $lastStatus->getAddStatus()) && in_array('redemption period', $lastStatus->getAddStatus())
) { ) {
return self::daysBetween($now, $lastStatus->getCreatedAt()->add(new DateInterval('P' . (30 + 5) . 'D'))); return self::daysBetween($now, $lastStatus->getCreatedAt()->add(new \DateInterval('P'.(30 + 5).'D')));
} }
return null; return null;
@ -539,7 +534,7 @@ class Domain
} }
*/ */
private static function daysBetween(DateTimeImmutable $start, DateTimeImmutable $end): int private static function daysBetween(\DateTimeImmutable $start, \DateTimeImmutable $end): int
{ {
$interval = $start->setTime(0, 0)->diff($end->setTime(0, 0)); $interval = $start->setTime(0, 0)->diff($end->setTime(0, 0));
@ -560,7 +555,7 @@ class Domain
} }
/** /**
* @throws Exception * @throws \Exception
*/ */
private function getRelevantDates(): array private function getRelevantDates(): array
{ {
@ -579,7 +574,7 @@ class Domain
} }
/** /**
* @throws Exception * @throws \Exception
*/ */
#[Groups(['domain:item', 'domain:list'])] #[Groups(['domain:item', 'domain:list'])]
public function getExpiresInDays(): ?int public function getExpiresInDays(): ?int
@ -588,11 +583,11 @@ class Domain
return null; return null;
} }
$now = new DateTimeImmutable(); $now = new \DateTimeImmutable();
[$expiredAt, $deletedAt] = $this->getRelevantDates(); [$expiredAt, $deletedAt] = $this->getRelevantDates();
if ($expiredAt) { if ($expiredAt) {
$guess = self::daysBetween($now, $expiredAt->add(new DateInterval('P' . (45 + 30 + 5) . 'D'))); $guess = self::daysBetween($now, $expiredAt->add(new \DateInterval('P'.(45 + 30 + 5).'D')));
} }
if ($deletedAt) { if ($deletedAt) {
@ -601,7 +596,7 @@ class Domain
return 0; return 0;
} }
$guess = self::daysBetween($now, $deletedAt->add(new DateInterval('P' . 30 . 'D'))); $guess = self::daysBetween($now, $deletedAt->add(new \DateInterval('P'. 30 .'D')));
} }
return self::returnExpiresIn([ return self::returnExpiresIn([
@ -614,7 +609,7 @@ class Domain
* @throws ParseException * @throws ParseException
* @throws EofException * @throws EofException
* @throws InvalidDataException * @throws InvalidDataException
* @throws Exception * @throws \Exception
*/ */
public function getDomainCalendarEvents(): array public function getDomainCalendarEvents(): array
{ {
@ -653,24 +648,24 @@ class Domain
->addCategory(new Category($event->getAction())) ->addCategory(new Category($event->getAction()))
->setAttendees($attendees) ->setAttendees($attendees)
->setOccurrence(new SingleDay(new Date($event->getDate())) ->setOccurrence(new SingleDay(new Date($event->getDate()))
); );
} }
$expiresInDays = $this->getExpiresInDays(); $expiresInDays = $this->getExpiresInDays();
if (null !== $expiresInDays) { if (null !== $expiresInDays) {
$events[] = (new Event()) $events[] = (new Event())
->setLastModified(new Timestamp($this->getUpdatedAt())) ->setLastModified(new Timestamp($this->getUpdatedAt()))
->setStatus(EventStatus::CONFIRMED()) ->setStatus(EventStatus::CONFIRMED())
->setSummary($this->getLdhName() . ': estimated WHOIS release date') ->setSummary($this->getLdhName().': estimated WHOIS release date')
->addCategory(new Category('release')) ->addCategory(new Category('release'))
->setAttendees($attendees) ->setAttendees($attendees)
->setOccurrence(new SingleDay(new Date( ->setOccurrence(new SingleDay(new Date(
(new \DateTimeImmutable())->setTime(0, 0)->add(new \DateInterval('P' . $expiresInDays . 'D')) (new \DateTimeImmutable())->setTime(0, 0)->add(new \DateInterval('P'.$expiresInDays.'D'))
)) ))
); );
} }
return $events; return $events;
} }
} }

View File

@ -9,6 +9,7 @@ use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\GetCollection;
use App\Config\TldType; use App\Config\TldType;
use App\Repository\TldRepository; use App\Repository\TldRepository;
use App\Service\RDAPService;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
@ -119,7 +120,7 @@ class Tld
public function setTld(string $tld): static public function setTld(string $tld): static
{ {
$this->tld = strtolower($tld); $this->tld = RDAPService::convertToIdn($tld);
return $this; return $this;
} }

View File

@ -127,7 +127,7 @@ readonly class RDAPService
*/ */
public function registerDomain(string $fqdn): Domain public function registerDomain(string $fqdn): Domain
{ {
$idnDomain = $this->convertToIdn($fqdn); $idnDomain = RDAPService::convertToIdn($fqdn);
$tld = $this->getTld($idnDomain); $tld = $this->getTld($idnDomain);
$this->logger->info('An update request for domain name {idnDomain} is requested.', [ $this->logger->info('An update request for domain name {idnDomain} is requested.', [
@ -198,7 +198,7 @@ readonly class RDAPService
return $tldEntity; return $tldEntity;
} }
private function convertToIdn(string $fqdn): string public static function convertToIdn(string $fqdn): string
{ {
return strtolower(idn_to_ascii($fqdn)); return strtolower(idn_to_ascii($fqdn));
} }