mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-17 09:45:29 +00:00
feat: set domain as deleted when tld is deleted
This commit is contained in:
parent
4409124ba8
commit
dad4f98035
32
migrations/Version20251008094821.php
Normal file
32
migrations/Version20251008094821.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20251008094821 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add deleted_at column on tld table';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE tld ADD deleted_at DATE DEFAULT NULL');
|
||||||
|
$this->addSql('COMMENT ON COLUMN tld.deleted_at IS \'(DC2Type:date_immutable)\'');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE tld DROP deleted_at');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -77,6 +77,9 @@ class Tld
|
|||||||
#[ORM\OneToMany(targetEntity: Entity::class, mappedBy: 'tld')]
|
#[ORM\OneToMany(targetEntity: Entity::class, mappedBy: 'tld')]
|
||||||
private Collection $entities;
|
private Collection $entities;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)]
|
||||||
|
private ?\DateTimeImmutable $deletedAt = null;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->rdapServers = new ArrayCollection();
|
$this->rdapServers = new ArrayCollection();
|
||||||
@ -241,4 +244,16 @@ class Tld
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDeletedAt(): ?\DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->deletedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDeletedAt(?\DateTimeImmutable $deletedAt): static
|
||||||
|
{
|
||||||
|
$this->deletedAt = $deletedAt;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,7 @@ final readonly class UpdateRdapServersHandler
|
|||||||
try {
|
try {
|
||||||
$this->RDAPService->updateTldListIANA();
|
$this->RDAPService->updateTldListIANA();
|
||||||
$this->RDAPService->updateGTldListICANN();
|
$this->RDAPService->updateGTldListICANN();
|
||||||
|
$this->RDAPService->updateDomainsWhenTldIsDeleted();
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
$throws[] = $throwable;
|
$throws[] = $throwable;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,17 @@ class DomainRepository extends ServiceEntityRepository
|
|||||||
parent::__construct($registry, Domain::class);
|
parent::__construct($registry, Domain::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByTld(string $tld): array
|
||||||
|
{
|
||||||
|
return $this->createQueryBuilder('d')
|
||||||
|
->addSelect('events')
|
||||||
|
->leftJoin('d.events', 'events')
|
||||||
|
->where('d.tld = :tld')
|
||||||
|
->setParameter('tld', $tld)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @return Domain[] Returns an array of Domain objects
|
// * @return Domain[] Returns an array of Domain objects
|
||||||
// */
|
// */
|
||||||
|
|||||||
@ -16,6 +16,18 @@ class TldRepository extends ServiceEntityRepository
|
|||||||
parent::__construct($registry, Tld::class);
|
parent::__construct($registry, Tld::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Tld[] Returns an array of deleted Tld
|
||||||
|
*/
|
||||||
|
public function findDeleted(): array
|
||||||
|
{
|
||||||
|
return $this->createQueryBuilder('t')
|
||||||
|
->andWhere('t.deletedAt IS NOT NULL')
|
||||||
|
->orderBy('t.deletedAt', 'DESC')
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @return Tld[] Returns an array of Tld objects
|
// * @return Tld[] Returns an array of Tld objects
|
||||||
// */
|
// */
|
||||||
|
|||||||
@ -52,9 +52,9 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
|
|||||||
class RDAPService
|
class RDAPService
|
||||||
{
|
{
|
||||||
/* @see https://www.iana.org/domains/root/db */
|
/* @see https://www.iana.org/domains/root/db */
|
||||||
public const ISO_TLD_EXCEPTION = ['ac', 'eu', 'uk', 'su', 'tp'];
|
private const ISO_TLD_EXCEPTION = ['ac', 'eu', 'uk', 'su', 'tp'];
|
||||||
public const INFRA_TLD = ['arpa'];
|
private const INFRA_TLD = ['arpa'];
|
||||||
public const SPONSORED_TLD = [
|
private const SPONSORED_TLD = [
|
||||||
'aero',
|
'aero',
|
||||||
'asia',
|
'asia',
|
||||||
'cat',
|
'cat',
|
||||||
@ -70,7 +70,7 @@ class RDAPService
|
|||||||
'travel',
|
'travel',
|
||||||
'xxx',
|
'xxx',
|
||||||
];
|
];
|
||||||
public const TEST_TLD = [
|
private const TEST_TLD = [
|
||||||
'xn--kgbechtv',
|
'xn--kgbechtv',
|
||||||
'xn--hgbk6aj7f53bba',
|
'xn--hgbk6aj7f53bba',
|
||||||
'xn--0zwm56d',
|
'xn--0zwm56d',
|
||||||
@ -84,7 +84,7 @@ class RDAPService
|
|||||||
'xn--hlcj6aya9esc7a',
|
'xn--hlcj6aya9esc7a',
|
||||||
];
|
];
|
||||||
|
|
||||||
public const ENTITY_HANDLE_BLACKLIST = [
|
private const ENTITY_HANDLE_BLACKLIST = [
|
||||||
'REDACTED_FOR_PRIVACY',
|
'REDACTED_FOR_PRIVACY',
|
||||||
'ANO00-FRNIC',
|
'ANO00-FRNIC',
|
||||||
'not applicable',
|
'not applicable',
|
||||||
@ -99,6 +99,12 @@ class RDAPService
|
|||||||
'Private',
|
'Private',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private const DOMAIN_DOT = '.';
|
||||||
|
private const IANA_REGISTRAR_IDS_URL = 'https://www.iana.org/assignments/registrar-ids/registrar-ids.xml';
|
||||||
|
private const IANA_RDAP_SERVER_LIST_URL = 'https://data.iana.org/rdap/dns.json';
|
||||||
|
private const IANA_TLD_LIST_URL = 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt';
|
||||||
|
private const ICANN_GTLD_LIST_URL = 'https://www.icann.org/resources/registries/gtlds/v2/gtlds.json';
|
||||||
|
|
||||||
public function __construct(private HttpClientInterface $client,
|
public function __construct(private HttpClientInterface $client,
|
||||||
private EntityRepository $entityRepository,
|
private EntityRepository $entityRepository,
|
||||||
private DomainRepository $domainRepository,
|
private DomainRepository $domainRepository,
|
||||||
@ -204,30 +210,30 @@ class RDAPService
|
|||||||
*/
|
*/
|
||||||
public function getTld(string $domain): Tld
|
public function getTld(string $domain): Tld
|
||||||
{
|
{
|
||||||
if (!str_contains($domain, '.')) {
|
if (!str_contains($domain, self::DOMAIN_DOT)) {
|
||||||
$tldEntity = $this->tldRepository->findOneBy(['tld' => '.']);
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => self::DOMAIN_DOT]);
|
||||||
|
|
||||||
if (null == $tldEntity) {
|
if (null == $tldEntity) {
|
||||||
throw TldNotSupportedException::fromTld($domain);
|
throw TldNotSupportedException::fromTld(self::DOMAIN_DOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tldEntity;
|
return $tldEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
$lastDotPosition = strrpos($domain, '.');
|
$lastDotPosition = strrpos($domain, self::DOMAIN_DOT);
|
||||||
|
|
||||||
if (false === $lastDotPosition) {
|
if (false === $lastDotPosition) {
|
||||||
throw MalformedDomainException::fromDomain($domain);
|
throw MalformedDomainException::fromDomain($domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
$tld = self::convertToIdn(substr($domain, $lastDotPosition + 1));
|
$tld = self::convertToIdn(substr($domain, $lastDotPosition + 1));
|
||||||
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld, 'deletedAt' => null]);
|
||||||
|
|
||||||
if (null === $tldEntity) {
|
if (null === $tldEntity) {
|
||||||
$this->logger->warning('Domain name cannot be updated because the TLD is not supported', [
|
$this->logger->warning('Domain name cannot be updated because the TLD is not supported', [
|
||||||
'ldhName' => $domain,
|
'ldhName' => $domain,
|
||||||
]);
|
]);
|
||||||
throw TldNotSupportedException::fromTld($domain);
|
throw TldNotSupportedException::fromTld($tld);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tldEntity;
|
return $tldEntity;
|
||||||
@ -383,9 +389,14 @@ class RDAPService
|
|||||||
*/
|
*/
|
||||||
private function updateDomainEvents(Domain $domain, array $rdapData): void
|
private function updateDomainEvents(Domain $domain, array $rdapData): void
|
||||||
{
|
{
|
||||||
foreach ($domain->getEvents()->getIterator() as $event) {
|
$this->domainEventRepository->createQueryBuilder('de')
|
||||||
$event->setDeleted(true);
|
->update()
|
||||||
}
|
->set('de.deleted', ':deleted')
|
||||||
|
->where('de.domain = :domain')
|
||||||
|
->setParameter('deleted', true)
|
||||||
|
->setParameter('domain', $domain)
|
||||||
|
->getQuery()
|
||||||
|
->execute();
|
||||||
|
|
||||||
if (isset($rdapData['events']) && is_array($rdapData['events'])) {
|
if (isset($rdapData['events']) && is_array($rdapData['events'])) {
|
||||||
foreach ($rdapData['events'] as $rdapEvent) {
|
foreach ($rdapData['events'] as $rdapEvent) {
|
||||||
@ -419,11 +430,15 @@ class RDAPService
|
|||||||
private function updateDomainEntities(Domain $domain, array $rdapData): void
|
private function updateDomainEntities(Domain $domain, array $rdapData): void
|
||||||
{
|
{
|
||||||
$now = new \DateTimeImmutable();
|
$now = new \DateTimeImmutable();
|
||||||
foreach ($domain->getDomainEntities()->getIterator() as $domainEntity) {
|
|
||||||
if (null !== $domainEntity->getDeletedAt()) {
|
$this->domainEntityRepository->createQueryBuilder('de')
|
||||||
$domainEntity->setDeletedAt($now);
|
->update()
|
||||||
}
|
->set('de.deletedAt', ':now')
|
||||||
}
|
->where('de.domain = :domain')
|
||||||
|
->andWhere('de.deletedAt IS NOT NULL')
|
||||||
|
->setParameter('now', $now)
|
||||||
|
->setParameter('domain', $domain)
|
||||||
|
->getQuery()->execute();
|
||||||
|
|
||||||
if (!isset($rdapData['entities']) || !is_array($rdapData['entities'])) {
|
if (!isset($rdapData['entities']) || !is_array($rdapData['entities'])) {
|
||||||
return;
|
return;
|
||||||
@ -758,7 +773,7 @@ class RDAPService
|
|||||||
$this->logger->info('Start of update the RDAP server list from IANA');
|
$this->logger->info('Start of update the RDAP server list from IANA');
|
||||||
|
|
||||||
$dnsRoot = $this->client->request(
|
$dnsRoot = $this->client->request(
|
||||||
'GET', 'https://data.iana.org/rdap/dns.json'
|
'GET', self::IANA_RDAP_SERVER_LIST_URL
|
||||||
)->toArray();
|
)->toArray();
|
||||||
|
|
||||||
$this->updateRDAPServers($dnsRoot);
|
$this->updateRDAPServers($dnsRoot);
|
||||||
@ -771,8 +786,8 @@ class RDAPService
|
|||||||
{
|
{
|
||||||
foreach ($dnsRoot['services'] as $service) {
|
foreach ($dnsRoot['services'] as $service) {
|
||||||
foreach ($service[0] as $tld) {
|
foreach ($service[0] as $tld) {
|
||||||
if ('.' === $tld && null === $this->tldRepository->findOneBy(['tld' => $tld])) {
|
if (self::DOMAIN_DOT === $tld && null === $this->tldRepository->findOneBy(['tld' => $tld])) {
|
||||||
$this->em->persist((new Tld())->setTld('.')->setType(TldType::root));
|
$this->em->persist((new Tld())->setTld(self::DOMAIN_DOT)->setType(TldType::root));
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,7 +842,7 @@ class RDAPService
|
|||||||
fn ($tld) => strtolower($tld),
|
fn ($tld) => strtolower($tld),
|
||||||
explode(PHP_EOL,
|
explode(PHP_EOL,
|
||||||
$this->client->request(
|
$this->client->request(
|
||||||
'GET', 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt'
|
'GET', self::IANA_TLD_LIST_URL
|
||||||
)->getContent()
|
)->getContent()
|
||||||
));
|
));
|
||||||
array_shift($tldList);
|
array_shift($tldList);
|
||||||
@ -837,6 +852,12 @@ class RDAPService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->tldRepository->createQueryBuilder('t')
|
||||||
|
->update()
|
||||||
|
->set('t.deletedAt', 'COALESCE(t.removalDate, CURRENT_TIMESTAMP())')
|
||||||
|
->where('t.tld != :tld')
|
||||||
|
->setParameter('tld', self::DOMAIN_DOT);
|
||||||
|
|
||||||
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
||||||
|
|
||||||
if (null === $tldEntity) {
|
if (null === $tldEntity) {
|
||||||
@ -858,6 +879,7 @@ class RDAPService
|
|||||||
$tldEntity->setType(TldType::gTLD);
|
$tldEntity->setType(TldType::gTLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$tldEntity->setDeletedAt(null);
|
||||||
$this->em->persist($tldEntity);
|
$this->em->persist($tldEntity);
|
||||||
}
|
}
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
@ -874,7 +896,7 @@ class RDAPService
|
|||||||
{
|
{
|
||||||
$this->logger->info('Start of retrieval of the list of Registrar IDs according to IANA');
|
$this->logger->info('Start of retrieval of the list of Registrar IDs according to IANA');
|
||||||
$registrarList = $this->client->request(
|
$registrarList = $this->client->request(
|
||||||
'GET', 'https://www.iana.org/assignments/registrar-ids/registrar-ids.xml'
|
'GET', self::IANA_REGISTRAR_IDS_URL
|
||||||
);
|
);
|
||||||
|
|
||||||
$data = new \SimpleXMLElement($registrarList->getContent());
|
$data = new \SimpleXMLElement($registrarList->getContent());
|
||||||
@ -929,7 +951,7 @@ class RDAPService
|
|||||||
$this->logger->info('Start of retrieval of the list of gTLDs according to ICANN');
|
$this->logger->info('Start of retrieval of the list of gTLDs according to ICANN');
|
||||||
|
|
||||||
$gTldList = $this->client->request(
|
$gTldList = $this->client->request(
|
||||||
'GET', 'https://www.icann.org/resources/registries/gtlds/v2/gtlds.json'
|
'GET', self::ICANN_GTLD_LIST_URL
|
||||||
)->toArray()['gTLDs'];
|
)->toArray()['gTLDs'];
|
||||||
|
|
||||||
foreach ($gTldList as $gTld) {
|
foreach ($gTldList as $gTld) {
|
||||||
@ -967,4 +989,13 @@ class RDAPService
|
|||||||
|
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateDomainsWhenTldIsDeleted(): void
|
||||||
|
{
|
||||||
|
$this->domainRepository->createQueryBuilder('d')
|
||||||
|
->update()
|
||||||
|
->set('d.deleted', ':deleted')
|
||||||
|
->where('d.tld IN (SELECT t FROM '.Tld::class.' t WHERE t.deletedAt IS NOT NULL)')
|
||||||
|
->setParameter('deleted', true)->getQuery()->execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user