mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-29 16:15:04 +00:00
feat: add app:register-domain command
This commit is contained in:
61
src/Command/RegisterDomainCommand.php
Normal file
61
src/Command/RegisterDomainCommand.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Repository\DomainRepository;
|
||||
use App\Service\RDAPService;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:register-domain',
|
||||
description: 'Register a domain name in the database',
|
||||
)]
|
||||
class RegisterDomainCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DomainRepository $domainRepository,
|
||||
private readonly RDAPService $rdapService,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('domain', InputArgument::REQUIRED, 'The domain name to register')
|
||||
->addOption('force', 'f', InputOption::VALUE_NEGATABLE, 'Do not check the freshness of the data and still make the query', false);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$ldhName = strtolower(idn_to_ascii($input->getArgument('domain')));
|
||||
$force = (bool) $input->getOption('force');
|
||||
$domain = $this->domainRepository->findOneBy(['ldhName' => $ldhName]);
|
||||
|
||||
try {
|
||||
if (null !== $domain && !$force) {
|
||||
if (!$domain->isToBeUpdated()) {
|
||||
$io->warning('The domain name is already present in the database and does not need to be updated at this time.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
$this->rdapService->registerDomain($ldhName);
|
||||
} catch (\Throwable $e) {
|
||||
$io->error($e->getMessage());
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$io->success('The domain name has been successfully registered in the database.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,7 @@ class DomainRefreshController extends AbstractController
|
||||
// If the domain name exists in the database, recently updated and not important, we return the stored Domain
|
||||
if (null !== $domain
|
||||
&& !$domain->getDeleted()
|
||||
&& ($domain->getUpdatedAt()->diff(new \DateTimeImmutable('now'))->days < 7)
|
||||
&& !$this->RDAPService::isToBeWatchClosely($domain)
|
||||
&& !$domain->isToBeUpdated()
|
||||
&& !$this->kernel->isDebug()
|
||||
) {
|
||||
$this->logger->info('It is not necessary to update the information of the domain name {idnDomain} with the RDAP protocol.', [
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use App\Config\EventAction;
|
||||
use App\Controller\DomainRefreshController;
|
||||
use App\Repository\DomainRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
@@ -105,6 +106,20 @@ class Domain
|
||||
#[Groups(['domain:item', 'domain:list'])]
|
||||
private ?bool $deleted;
|
||||
|
||||
private const IMPORTANT_EVENTS = [EventAction::Deletion->value, EventAction::Expiration->value];
|
||||
private const IMPORTANT_STATUS = [
|
||||
'redemption period',
|
||||
'pending delete',
|
||||
'pending create',
|
||||
'pending renew',
|
||||
'pending restore',
|
||||
'pending transfer',
|
||||
'pending update',
|
||||
'add period',
|
||||
'client hold',
|
||||
'server hold',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->events = new ArrayCollection();
|
||||
@@ -317,4 +332,49 @@ class Domain
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a domain name needs special attention.
|
||||
* These domain names are those whose last event was expiration or deletion.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function isToBeWatchClosely(): bool
|
||||
{
|
||||
$status = $this->getStatus();
|
||||
if ((!empty($status) && count(array_intersect($status, self::IMPORTANT_STATUS))) || $this->getDeleted()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var DomainEvent[] $events */
|
||||
$events = $this->getEvents()
|
||||
->filter(fn (DomainEvent $e) => $e->getDate() <= new \DateTimeImmutable('now'))
|
||||
->toArray();
|
||||
|
||||
usort($events, fn (DomainEvent $e1, DomainEvent $e2) => $e2->getDate() <=> $e1->getDate());
|
||||
|
||||
return !empty($events) && in_array($events[0]->getAction(), self::IMPORTANT_EVENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if one or more of these conditions are met:
|
||||
* - It has been more than 7 days since the domain name was last updated
|
||||
* - It has been more than 1 hour 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
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function isToBeUpdated(bool $fromUser = true): bool
|
||||
{
|
||||
return $this->getUpdatedAt()
|
||||
->diff(new \DateTimeImmutable())->days >= 7
|
||||
|| (
|
||||
($fromUser || ($this->getUpdatedAt()
|
||||
->diff(new \DateTimeImmutable())->h * 60 + $this->getUpdatedAt()
|
||||
->diff(new \DateTimeImmutable())->i) >= 50)
|
||||
&& $this->isToBeWatchClosely()
|
||||
)
|
||||
|| (count(array_intersect($this->getStatus(), ['auto renew period', 'client hold', 'server hold'])) > 0
|
||||
&& $this->getUpdatedAt()->diff(new \DateTimeImmutable())->days >= 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,19 +68,7 @@ final readonly class UpdateDomainsFromWatchlistHandler
|
||||
*/
|
||||
|
||||
/** @var Domain $domain */
|
||||
foreach ($watchList->getDomains()
|
||||
->filter(fn ($domain) => $domain->getUpdatedAt()
|
||||
->diff(new \DateTimeImmutable())->days >= 7
|
||||
|| (
|
||||
($domain->getUpdatedAt()
|
||||
->diff(new \DateTimeImmutable())->h * 60 + $domain->getUpdatedAt()
|
||||
->diff(new \DateTimeImmutable())->i) >= 50
|
||||
&& $this->RDAPService::isToBeWatchClosely($domain)
|
||||
)
|
||||
|| (count(array_intersect($domain->getStatus(), ['auto renew period', 'client hold', 'server hold'])) > 0
|
||||
&& $domain->getUpdatedAt()->diff(new \DateTimeImmutable())->days >= 1
|
||||
)
|
||||
) as $domain
|
||||
foreach ($watchList->getDomains()->filter(fn ($domain) => $domain->isToBeUpdated(false)) as $domain
|
||||
) {
|
||||
$updatedAt = $domain->getUpdatedAt();
|
||||
|
||||
|
||||
@@ -74,18 +74,6 @@ readonly class RDAPService
|
||||
'xn--hlcj6aya9esc7a',
|
||||
];
|
||||
|
||||
private const IMPORTANT_EVENTS = [EventAction::Deletion->value, EventAction::Expiration->value];
|
||||
private const IMPORTANT_STATUS = [
|
||||
'redemption period',
|
||||
'pending delete',
|
||||
'pending create',
|
||||
'pending renew',
|
||||
'pending restore',
|
||||
'pending transfer',
|
||||
'pending update',
|
||||
'add period',
|
||||
];
|
||||
|
||||
public function __construct(private HttpClientInterface $client,
|
||||
private EntityRepository $entityRepository,
|
||||
private DomainRepository $domainRepository,
|
||||
@@ -102,34 +90,10 @@ readonly class RDAPService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a domain name needs special attention.
|
||||
* These domain names are those whose last event was expiration or deletion.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function isToBeWatchClosely(Domain $domain): bool
|
||||
{
|
||||
$status = $domain->getStatus();
|
||||
if ((!empty($status) && count(array_intersect($status, self::IMPORTANT_STATUS))) || $domain->getDeleted()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var DomainEvent[] $events */
|
||||
$events = $domain->getEvents()
|
||||
->filter(fn (DomainEvent $e) => $e->getDate() <= new \DateTimeImmutable('now'))
|
||||
->toArray();
|
||||
|
||||
usort($events, fn (DomainEvent $e1, DomainEvent $e2) => $e2->getDate() <=> $e1->getDate());
|
||||
|
||||
return !empty($events) && in_array($events[0]->getAction(), self::IMPORTANT_EVENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HttpExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws DecodingExceptionInterface
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function registerDomains(array $domains): void
|
||||
{
|
||||
@@ -144,7 +108,7 @@ readonly class RDAPService
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws DecodingExceptionInterface
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws \Throwable
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function registerDomain(string $fqdn): Domain
|
||||
{
|
||||
@@ -179,7 +143,7 @@ readonly class RDAPService
|
||||
$res = $this->client->request(
|
||||
'GET', $rdapServerUrl.'domain/'.$idnDomain
|
||||
)->toArray();
|
||||
} catch (\Throwable $e) {
|
||||
} catch (\Exception $e) {
|
||||
if ($e instanceof ClientException && 404 === $e->getResponse()->getStatusCode()) {
|
||||
if (null !== $domain) {
|
||||
$this->logger->notice('The domain name {idnDomain} has been deleted from the WHOIS database.', [
|
||||
|
||||
Reference in New Issue
Block a user