2024-07-18 13:40:49 +02:00
< ? php
namespace App\Controller ;
2024-08-19 16:34:08 +02:00
use App\Entity\Connector ;
2024-08-03 17:55:39 +02:00
use App\Entity\Domain ;
2024-08-01 14:37:23 +02:00
use App\Entity\DomainEvent ;
2025-04-27 12:13:06 +02:00
use App\Entity\DomainStatus ;
2024-07-18 13:40:49 +02:00
use App\Entity\User ;
use App\Entity\WatchList ;
2024-08-16 23:23:51 +02:00
use App\Notifier\TestChatNotification ;
2025-03-01 18:52:19 +01:00
use App\Repository\DomainRepository ;
2024-08-01 14:37:23 +02:00
use App\Repository\WatchListRepository ;
2024-08-28 17:35:32 +02:00
use App\Service\ChatNotificationService ;
2024-09-18 13:37:07 +02:00
use App\Service\Connector\AbstractProvider ;
2025-03-01 18:52:19 +01:00
use App\Service\RDAPService ;
2024-07-18 13:40:49 +02:00
use Doctrine\Common\Collections\Collection ;
2025-01-19 13:17:26 +01:00
use Doctrine\DBAL\LockMode ;
2024-07-18 13:40:49 +02:00
use Doctrine\ORM\EntityManagerInterface ;
2024-08-15 03:04:31 +02:00
use Doctrine\ORM\Exception\ORMException ;
2025-03-02 20:58:26 +01:00
use Doctrine\ORM\OptimisticLockException ;
2024-08-01 14:37:23 +02:00
use Eluceo\iCal\Domain\Entity\Calendar ;
2024-08-03 18:05:05 +02:00
use Eluceo\iCal\Presentation\Component\Property ;
use Eluceo\iCal\Presentation\Component\Property\Value\TextValue ;
2024-08-01 14:37:23 +02:00
use Eluceo\iCal\Presentation\Factory\CalendarFactory ;
2025-04-27 12:13:06 +02:00
use Exception ;
use JsonException ;
use Laminas\Feed\Writer\Deleted ;
use Laminas\Feed\Writer\Entry ;
use Laminas\Feed\Writer\Feed ;
2024-08-04 14:45:27 +02:00
use Psr\Log\LoggerInterface ;
2024-08-01 14:37:23 +02:00
use Sabre\VObject\EofException ;
use Sabre\VObject\InvalidDataException ;
use Sabre\VObject\ParseException ;
2024-07-18 13:40:49 +02:00
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController ;
2024-09-25 14:02:40 +02:00
use Symfony\Component\DependencyInjection\Attribute\Autowire ;
use Symfony\Component\DependencyInjection\ContainerInterface ;
2024-07-18 13:40:49 +02:00
use Symfony\Component\HttpFoundation\Request ;
2024-08-01 14:37:23 +02:00
use Symfony\Component\HttpFoundation\Response ;
2024-08-04 22:00:20 +02:00
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException ;
2024-08-16 23:23:51 +02:00
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException ;
2025-05-19 09:47:21 +02:00
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException ;
2025-03-01 18:52:19 +01:00
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException ;
use Symfony\Component\HttpKernel\KernelInterface ;
use Symfony\Component\RateLimiter\RateLimiterFactory ;
2024-07-18 13:40:49 +02:00
use Symfony\Component\Routing\Attribute\Route ;
2025-03-01 18:52:19 +01:00
use Symfony\Component\Serializer\Encoder\DecoderInterface ;
2025-02-27 09:01:05 +01:00
use Symfony\Component\Serializer\Exception\ExceptionInterface ;
2025-03-01 18:52:19 +01:00
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface ;
2024-07-18 13:40:49 +02:00
use Symfony\Component\Serializer\SerializerInterface ;
2025-03-01 18:52:19 +01:00
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface ;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface ;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface ;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface ;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface ;
2024-07-18 13:40:49 +02:00
class WatchListController extends AbstractController
{
public function __construct (
2025-03-01 18:52:19 +01:00
private readonly SerializerInterface & DecoderInterface & DenormalizerInterface $serializer ,
2025-04-27 12:13:06 +02:00
private readonly EntityManagerInterface $em ,
private readonly WatchListRepository $watchListRepository ,
private readonly LoggerInterface $logger ,
private readonly ChatNotificationService $chatNotificationService ,
private readonly DomainRepository $domainRepository ,
private readonly RDAPService $RDAPService ,
private readonly RateLimiterFactory $rdapRequestsLimiter ,
private readonly KernelInterface $kernel ,
2024-09-25 14:02:40 +02:00
#[Autowire(service: 'service_container')]
2025-04-27 12:13:06 +02:00
private readonly ContainerInterface $locator ,
)
{
2024-07-18 13:40:49 +02:00
}
2024-07-29 16:01:32 +02:00
/**
2025-03-01 18:52:19 +01:00
* @ throws TransportExceptionInterface
* @ throws ServerExceptionInterface
* @ throws RedirectionExceptionInterface
* @ throws ExceptionInterface
* @ throws DecodingExceptionInterface
* @ throws ClientExceptionInterface
2025-04-27 12:13:06 +02:00
* @ throws JsonException
* @ throws Exception
2024-07-29 16:01:32 +02:00
*/
2024-07-18 13:40:49 +02:00
#[Route(
path : '/api/watchlists' ,
name : 'watchlist_create' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'create' ,
],
methods : [ 'POST' ]
)]
public function createWatchList ( Request $request ) : WatchList
{
2025-03-02 20:58:26 +01:00
$watchList = $this -> registerDomainsInWatchlist ( $request -> getContent (), [ 'watchlist:create' ]);
2025-03-01 18:52:19 +01:00
2024-08-03 00:06:38 +02:00
/** @var User $user */
$user = $this -> getUser ();
$watchList -> setUser ( $user );
2024-07-29 16:01:32 +02:00
2024-08-04 22:00:20 +02:00
/*
* In the limited version , we do not want a user to be able to register the same domain more than once in their watchlists .
* This policy guarantees the equal probability of obtaining a domain name if it is requested by several users .
*/
if ( $this -> getParameter ( 'limited_features' )) {
2025-04-27 12:13:06 +02:00
if ( $watchList -> getDomains () -> count () > ( int ) $this -> getParameter ( 'limit_max_watchlist_domains' )) {
2024-08-18 18:25:11 +02:00
$this -> logger -> notice ( 'User {username} tried to create a Watchlist. The maximum number of domains has been reached.' , [
2024-08-07 14:31:44 +02:00
'username' => $user -> getUserIdentifier (),
]);
throw new AccessDeniedHttpException ( 'You have exceeded the maximum number of domain names allowed in this Watchlist' );
}
$userWatchLists = $user -> getWatchLists ();
2025-04-27 12:13:06 +02:00
if ( $userWatchLists -> count () >= ( int ) $this -> getParameter ( 'limit_max_watchlist' )) {
2024-08-16 14:19:59 +02:00
$this -> logger -> notice ( 'User {username} tried to create a Watchlist. The maximum number of Watchlists has been reached' , [
2024-08-07 14:31:44 +02:00
'username' => $user -> getUserIdentifier (),
]);
throw new AccessDeniedHttpException ( 'You have exceeded the maximum number of Watchlists allowed' );
}
2024-08-04 22:00:20 +02:00
/** @var Domain[] $trackedDomains */
2025-04-27 12:13:06 +02:00
$trackedDomains = $userWatchLists -> reduce ( fn ( array $acc , WatchList $watchList ) => [ ... $acc , ... $watchList -> getDomains () -> toArray ()], []);
2024-08-04 22:00:20 +02:00
/** @var Domain $domain */
foreach ( $watchList -> getDomains () -> getIterator () as $domain ) {
if ( in_array ( $domain , $trackedDomains )) {
2024-08-16 14:19:59 +02:00
$ldhName = $domain -> getLdhName ();
$this -> logger -> notice ( 'User {username} tried to create a watchlist with domain name {ldhName}. It is forbidden to register the same domain name twice with limited mode' , [
2024-08-04 22:00:20 +02:00
'username' => $user -> getUserIdentifier (),
2024-08-16 14:19:59 +02:00
'ldhName' => $ldhName ,
2024-08-04 22:00:20 +02:00
]);
2024-08-16 14:19:59 +02:00
throw new AccessDeniedHttpException ( " It is forbidden to register the same domain name twice in your watchlists with limited mode ( $ldhName ) " );
2024-08-04 22:00:20 +02:00
}
}
2024-08-18 18:25:11 +02:00
2025-04-27 12:13:06 +02:00
if ( null !== $watchList -> getWebhookDsn () && count ( $watchList -> getWebhookDsn ()) > ( int ) $this -> getParameter ( 'limit_max_watchlist_webhooks' )) {
2024-08-18 18:25:11 +02:00
$this -> logger -> notice ( 'User {username} tried to create a Watchlist. The maximum number of webhooks has been reached.' , [
'username' => $user -> getUserIdentifier (),
]);
throw new AccessDeniedHttpException ( 'You have exceeded the maximum number of webhooks allowed in this Watchlist' );
}
2024-08-04 22:00:20 +02:00
}
2024-08-28 17:35:32 +02:00
$this -> chatNotificationService -> sendChatNotification ( $watchList , new TestChatNotification ());
2024-08-19 16:34:08 +02:00
$this -> verifyConnector ( $watchList , $watchList -> getConnector ());
2024-08-18 18:30:01 +02:00
2024-08-15 03:42:41 +02:00
$this -> logger -> info ( 'User {username} registers a Watchlist ({token}).' , [
2024-08-04 14:45:27 +02:00
'username' => $user -> getUserIdentifier (),
2024-08-04 22:00:20 +02:00
'token' => $watchList -> getToken (),
2024-08-04 14:45:27 +02:00
]);
2024-07-18 13:40:49 +02:00
$this -> em -> persist ( $watchList );
$this -> em -> flush ();
return $watchList ;
}
2024-08-14 23:23:32 +02:00
#[Route(
path : '/api/watchlists' ,
2024-08-15 03:04:31 +02:00
name : 'watchlist_get_all_mine' ,
2024-08-14 23:23:32 +02:00
defaults : [
'_api_resource_class' => WatchList :: class ,
2024-08-15 03:04:31 +02:00
'_api_operation_name' => 'get_all_mine' ,
2024-08-14 23:23:32 +02:00
],
2024-08-15 03:04:31 +02:00
methods : [ 'GET' ]
2024-08-14 23:23:32 +02:00
)]
2024-08-15 03:04:31 +02:00
public function getWatchLists () : Collection
2024-08-14 23:23:32 +02:00
{
2024-08-15 03:04:31 +02:00
/** @var User $user */
2024-08-14 23:23:32 +02:00
$user = $this -> getUser ();
2024-08-15 03:04:31 +02:00
return $user -> getWatchLists ();
2024-08-14 23:23:32 +02:00
}
2024-08-04 22:00:20 +02:00
2024-08-19 16:34:08 +02:00
/**
2025-04-27 12:13:06 +02:00
* @ throws Exception
2025-02-27 09:01:05 +01:00
* @ throws ExceptionInterface
2024-08-19 16:34:08 +02:00
*/
private function verifyConnector ( WatchList $watchList , ? Connector $connector ) : void
{
/** @var User $user */
$user = $this -> getUser ();
2024-09-07 01:14:50 +02:00
if ( null === $connector ) {
return ;
}
if ( ! $user -> getConnectors () -> contains ( $connector )) {
$this -> logger -> notice ( 'The Connector ({connector}) does not belong to the user.' , [
'username' => $user -> getUserIdentifier (),
'connector' => $connector -> getId (),
]);
throw new AccessDeniedHttpException ( 'You cannot create a Watchlist with a connector that does not belong to you' );
}
/** @var Domain $domain */
foreach ( $watchList -> getDomains () -> getIterator () as $domain ) {
if ( $domain -> getDeleted ()) {
$ldhName = $domain -> getLdhName ();
throw new BadRequestHttpException ( " To add a connector, no domain in this Watchlist must have already expired ( $ldhName ) " );
2024-08-19 16:34:08 +02:00
}
2024-09-07 01:14:50 +02:00
}
2024-08-19 16:34:08 +02:00
2024-09-07 01:14:50 +02:00
$connectorProviderClass = $connector -> getProvider () -> getConnectorProvider ();
/** @var AbstractProvider $connectorProvider */
2024-09-25 14:02:40 +02:00
$connectorProvider = $this -> locator -> get ( $connectorProviderClass );
2024-08-19 16:34:08 +02:00
2024-09-25 14:02:40 +02:00
$connectorProvider -> authenticate ( $connector -> getAuthData ());
2024-09-07 01:14:50 +02:00
$supported = $connectorProvider -> isSupported ( ... $watchList -> getDomains () -> toArray ());
2024-08-19 16:34:08 +02:00
2024-09-07 01:14:50 +02:00
if ( ! $supported ) {
$this -> logger -> notice ( 'The Connector ({connector}) does not support all TLDs in this Watchlist' , [
'username' => $user -> getUserIdentifier (),
'connector' => $connector -> getId (),
]);
throw new BadRequestHttpException ( 'This connector does not support all TLDs in this Watchlist' );
2024-08-19 16:34:08 +02:00
}
}
2024-08-15 03:04:31 +02:00
/**
* @ throws ORMException
2025-03-02 20:58:26 +01:00
* @ throws RedirectionExceptionInterface
* @ throws DecodingExceptionInterface
* @ throws ClientExceptionInterface
2025-04-27 12:13:06 +02:00
* @ throws JsonException
2025-03-02 20:58:26 +01:00
* @ throws OptimisticLockException
* @ throws TransportExceptionInterface
* @ throws ServerExceptionInterface
* @ throws ExceptionInterface
2025-04-27 12:13:06 +02:00
* @ throws Exception
2024-08-15 03:04:31 +02:00
*/
2024-08-14 23:23:32 +02:00
#[Route(
path : '/api/watchlists/{token}' ,
name : 'watchlist_update' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'update' ,
],
2024-08-15 03:04:31 +02:00
methods : [ 'PUT' ]
2024-08-14 23:23:32 +02:00
)]
2025-03-02 20:58:26 +01:00
public function putWatchList ( Request $request ) : WatchList
2024-08-14 23:23:32 +02:00
{
2025-03-02 20:58:26 +01:00
$watchList = $this -> registerDomainsInWatchlist ( $request -> getContent (), [ 'watchlist:create' , 'watchlist:token' ]);
2024-08-15 03:04:31 +02:00
/** @var User $user */
$user = $this -> getUser ();
2024-08-15 03:42:41 +02:00
$watchList -> setUser ( $user );
if ( $this -> getParameter ( 'limited_features' )) {
2025-04-27 12:13:06 +02:00
if ( $watchList -> getDomains () -> count () > ( int ) $this -> getParameter ( 'limit_max_watchlist_domains' )) {
2024-08-16 14:19:59 +02:00
$this -> logger -> notice ( 'User {username} tried to update a Watchlist. The maximum number of domains has been reached for this Watchlist' , [
2024-08-15 03:42:41 +02:00
'username' => $user -> getUserIdentifier (),
]);
throw new AccessDeniedHttpException ( 'You have exceeded the maximum number of domain names allowed in this Watchlist' );
}
$userWatchLists = $user -> getWatchLists ();
/** @var Domain[] $trackedDomains */
2024-08-15 03:50:29 +02:00
$trackedDomains = $userWatchLists
2025-04-27 12:13:06 +02:00
-> filter ( fn ( WatchList $wl ) => $wl -> getToken () !== $watchList -> getToken ())
-> reduce ( fn ( array $acc , WatchList $wl ) => [ ... $acc , ... $wl -> getDomains () -> toArray ()], []);
2024-08-15 03:42:41 +02:00
/** @var Domain $domain */
foreach ( $watchList -> getDomains () -> getIterator () as $domain ) {
if ( in_array ( $domain , $trackedDomains )) {
2024-08-16 14:19:59 +02:00
$ldhName = $domain -> getLdhName ();
$this -> logger -> notice ( 'User {username} tried to update a watchlist with domain name {ldhName}. It is forbidden to register the same domain name twice with limited mode' , [
2024-08-15 03:42:41 +02:00
'username' => $user -> getUserIdentifier (),
2024-08-16 14:19:59 +02:00
'ldhName' => $ldhName ,
2024-08-15 03:42:41 +02:00
]);
2024-08-15 03:04:31 +02:00
2024-08-16 14:19:59 +02:00
throw new AccessDeniedHttpException ( " It is forbidden to register the same domain name twice in your watchlists with limited mode ( $ldhName ) " );
2024-08-15 03:42:41 +02:00
}
}
2024-08-18 18:25:11 +02:00
2025-04-27 12:13:06 +02:00
if ( null !== $watchList -> getWebhookDsn () && count ( $watchList -> getWebhookDsn ()) > ( int ) $this -> getParameter ( 'limit_max_watchlist_webhooks' )) {
2024-08-18 18:25:11 +02:00
$this -> logger -> notice ( 'User {username} tried to update a Watchlist. The maximum number of webhooks has been reached.' , [
'username' => $user -> getUserIdentifier (),
]);
throw new AccessDeniedHttpException ( 'You have exceeded the maximum number of webhooks allowed in this Watchlist' );
}
2024-08-15 03:42:41 +02:00
}
2024-08-14 23:23:32 +02:00
2024-08-28 17:35:32 +02:00
$this -> chatNotificationService -> sendChatNotification ( $watchList , new TestChatNotification ());
2024-08-19 16:34:08 +02:00
$this -> verifyConnector ( $watchList , $watchList -> getConnector ());
2024-08-18 18:30:01 +02:00
2024-08-14 23:23:32 +02:00
$this -> logger -> info ( 'User {username} updates a Watchlist ({token}).' , [
2024-08-04 14:45:27 +02:00
'username' => $user -> getUserIdentifier (),
2024-08-04 22:00:20 +02:00
'token' => $watchList -> getToken (),
2024-08-04 14:45:27 +02:00
]);
2025-01-19 13:17:26 +01:00
$this -> em -> beginTransaction ();
/** @var WatchList $oldWatchlist */
$oldWatchlist = $this -> em -> getReference ( WatchList :: class , $watchList -> getToken ());
$this -> em -> lock ( $oldWatchlist , LockMode :: PESSIMISTIC_WRITE );
$this -> em -> remove ( $oldWatchlist );
2024-08-15 03:04:31 +02:00
$this -> em -> flush ();
2024-07-18 13:40:49 +02:00
$this -> em -> persist ( $watchList );
$this -> em -> flush ();
2025-01-19 13:17:26 +01:00
$this -> em -> commit ();
2024-07-18 13:40:49 +02:00
return $watchList ;
}
2024-08-01 14:37:23 +02:00
/**
* @ throws ParseException
* @ throws EofException
* @ throws InvalidDataException
2025-04-27 12:13:06 +02:00
* @ throws Exception
2024-08-01 14:37:23 +02:00
*/
#[Route(
path : '/api/watchlists/{token}/calendar' ,
name : 'watchlist_calendar' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'calendar' ,
]
)]
public function getWatchlistCalendar ( string $token ) : Response
{
2024-08-02 01:06:49 +02:00
/** @var WatchList $watchList */
2024-08-02 23:24:52 +02:00
$watchList = $this -> watchListRepository -> findOneBy ([ 'token' => $token ]);
2024-08-01 14:37:23 +02:00
$calendar = new Calendar ();
2024-08-03 17:55:39 +02:00
/** @var Domain $domain */
2024-08-01 14:37:23 +02:00
foreach ( $watchList -> getDomains () -> getIterator () as $domain ) {
2025-04-27 12:13:06 +02:00
foreach ( $domain -> getEvents () as $event ) {
$domain -> addEvent ( $event );
2024-08-01 14:37:23 +02:00
}
}
2024-08-03 18:05:05 +02:00
$calendarResponse = ( new CalendarFactory ()) -> createCalendar ( $calendar );
$calendarName = $watchList -> getName ();
if ( null !== $calendarName ) {
$calendarResponse -> withProperty ( new Property ( 'X-WR-CALNAME' , new TextValue ( $calendarName )));
}
return new Response ( $calendarResponse , Response :: HTTP_OK , [
2024-08-02 23:24:52 +02:00
'Content-Type' => 'text/calendar; charset=utf-8' ,
2024-08-01 14:37:23 +02:00
]);
}
2024-09-09 11:31:33 +02:00
/**
2025-04-27 12:13:06 +02:00
* @ throws Exception
2024-09-09 11:31:33 +02:00
*/
2024-08-01 14:37:23 +02:00
#[Route(
2024-09-09 11:31:33 +02:00
path : '/api/tracked' ,
name : 'watchlist_get_tracked_domains' ,
2024-08-01 14:37:23 +02:00
defaults : [
'_api_resource_class' => WatchList :: class ,
2024-09-09 11:31:33 +02:00
'_api_operation_name' => 'get_tracked_domains' ,
]
2024-08-01 14:37:23 +02:00
)]
2024-09-09 11:31:33 +02:00
public function getTrackedDomains () : array
2024-08-01 14:37:23 +02:00
{
/** @var User $user */
$user = $this -> getUser ();
2024-08-02 23:24:52 +02:00
2024-09-09 11:31:33 +02:00
$domains = [];
/** @var WatchList $watchList */
foreach ( $user -> getWatchLists () -> getIterator () as $watchList ) {
/** @var Domain $domain */
foreach ( $watchList -> getDomains () -> getIterator () as $domain ) {
/** @var DomainEvent|null $exp */
2025-04-27 12:13:06 +02:00
$exp = $domain -> getEvents () -> findFirst ( fn ( int $key , DomainEvent $e ) => ! $e -> getDeleted () && 'expiration' === $e -> getAction ());
2024-09-09 11:31:33 +02:00
2024-11-15 19:26:17 +01:00
if ( ! $domain -> getDeleted () && null !== $exp && ! in_array ( $domain , $domains )) {
2024-09-09 11:31:33 +02:00
$domains [] = $domain ;
}
}
}
2025-04-27 12:13:06 +02:00
usort ( $domains , fn ( Domain $d1 , Domain $d2 ) => $d1 -> getExpiresInDays () - $d2 -> getExpiresInDays ());
2024-09-09 11:31:33 +02:00
return $domains ;
2024-08-01 14:37:23 +02:00
}
2025-03-02 20:58:26 +01:00
/**
* @ throws TransportExceptionInterface
* @ throws ServerExceptionInterface
* @ throws RedirectionExceptionInterface
* @ throws DecodingExceptionInterface
* @ throws ClientExceptionInterface
2025-04-27 12:13:06 +02:00
* @ throws JsonException
2025-03-02 20:58:26 +01:00
*/
private function registerDomainsInWatchlist ( string $content , array $groups ) : WatchList
{
/** @var WatchList $watchList */
$watchList = $this -> serializer -> deserialize ( $content , WatchList :: class , 'json' , [ 'groups' => $groups ]);
$data = json_decode ( $content , true , 512 , JSON_THROW_ON_ERROR );
if ( ! is_array ( $data ) || ! isset ( $data [ 'domains' ]) || ! is_array ( $data [ 'domains' ])) {
throw new BadRequestHttpException ( 'Invalid payload: missing or invalid "domains" field.' );
}
2025-04-27 12:13:06 +02:00
$domains = array_map ( fn ( string $d ) => str_replace ( '/api/domains/' , '' , $d ), $data [ 'domains' ]);
2025-03-02 20:58:26 +01:00
foreach ( $domains as $ldhName ) {
/** @var ?Domain $domain */
$domain = $this -> domainRepository -> findOneBy ([ 'ldhName' => $ldhName ]);
if ( null === $domain ) {
2025-05-19 09:47:21 +02:00
try {
$domain = $this -> RDAPService -> registerDomain ( $ldhName );
} catch ( NotFoundHttpException ) {
$domain = ( new Domain ())
-> setLdhName ( $ldhName )
-> setTld ( $this -> RDAPService -> getTld ( $ldhName ))
-> setDelegationSigned ( false )
-> setDeleted ( true );
$this -> em -> persist ( $domain );
$this -> em -> flush ();
}
2025-03-02 20:58:26 +01:00
if ( false === $this -> kernel -> isDebug () && true === $this -> getParameter ( 'limited_features' )) {
$limiter = $this -> rdapRequestsLimiter -> create ( $this -> getUser () -> getUserIdentifier ());
$limit = $limiter -> consume ();
if ( ! $limit -> isAccepted ()) {
throw new TooManyRequestsHttpException ( $limit -> getRetryAfter () -> getTimestamp () - time ());
}
}
}
$watchList -> addDomain ( $domain );
}
return $watchList ;
}
2025-04-27 12:13:06 +02:00
/**
* @ throws Exception
*/
#[Route(
path : '/api/watchlists/{token}/rss/events' ,
name : 'watchlist_rss_events' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'rss_events' ,
]
)]
public function getWatchlistRssEventsFeed ( string $token , Request $request ) : Response
{
/** @var WatchList $watchlist */
$watchlist = $this -> watchListRepository -> findOneBy ([ 'token' => $token ]);
$feed = ( new Feed ())
-> setLanguage ( 'en' )
-> setCopyright ( 'Domain Watchdog makes this information available "as is," and does not guarantee its accuracy.' )
-> setTitle ( 'Domain events (' . $watchlist -> getName () . ')' )
-> setGenerator ( " Domain Watchdog - RSS Feed " , null , " https://github.com/maelgangloff/domain-watchdog " )
-> setDescription ( 'The latest events for domain names in your Watchlist' )
-> setLink ( $request -> getSchemeAndHttpHost () . " /#/tracking/watchlist " )
-> setFeedLink ( $request -> getSchemeAndHttpHost () . " /api/watchlists/ " . $token . '/rss/events' , 'atom' )
-> setDateCreated ( $watchlist -> getCreatedAt ());
/** @var Domain $domain */
foreach ( $watchlist -> getDomains () -> getIterator () as $domain ) {
foreach ( $this -> getRssEventEntries ( $request -> getSchemeAndHttpHost (), $domain ) as $entry ) {
$feed -> addEntry ( $entry );
}
}
return new Response ( $feed -> export ( " atom " ), Response :: HTTP_OK , [
'Content-Type' => 'application/atom+xml; charset=utf-8' ,
]);
}
/**
* @ throws Exception
*/
#[Route(
path : '/api/watchlists/{token}/rss/status' ,
name : 'watchlist_rss_status' ,
defaults : [
'_api_resource_class' => WatchList :: class ,
'_api_operation_name' => 'rss_status' ,
]
)]
public function getWatchlistRssStatusFeed ( string $token , Request $request ) : Response
{
/** @var WatchList $watchlist */
$watchlist = $this -> watchListRepository -> findOneBy ([ 'token' => $token ]);
$feed = ( new Feed ())
-> setLanguage ( 'en' )
-> setCopyright ( 'Domain Watchdog makes this information available "as is," and does not guarantee its accuracy.' )
-> setTitle ( 'Domain EPP status (' . $watchlist -> getName () . ')' )
-> 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' )
-> setLink ( $request -> getSchemeAndHttpHost () . " /#/tracking/watchlist " )
-> setFeedLink ( $request -> getSchemeAndHttpHost () . " /api/watchlists/ " . $token . '/rss/status' , 'atom' )
-> setDateCreated ( $watchlist -> getCreatedAt ());
/** @var Domain $domain */
foreach ( $watchlist -> getDomains () -> getIterator () as $domain ) {
foreach ( $this -> getRssStatusEntries ( $request -> getSchemeAndHttpHost (), $domain ) as $entry ) {
$feed -> addEntry ( $entry );
}
}
return new Response ( $feed -> export ( " atom " ), Response :: HTTP_OK , [
'Content-Type' => 'application/atom+xml; charset=utf-8' ,
]);
}
/**
* @ throws Exception
*/
private function getRssEventEntries ( string $baseUrl , Domain $domain ) : array
{
$entries = [];
foreach ( $domain -> getEvents () -> filter ( fn ( DomainEvent $e ) => $e -> getDate () -> diff ( new \DateTimeImmutable ( 'now' )) -> y <= 10 ) -> getIterator () as $event ) {
$entries [] = ( new Entry ())
2025-04-27 17:44:31 +02:00
-> setId ( $baseUrl . " /api/domains/ " . $domain -> getLdhName () . '#events-' . $event -> getId ())
2025-04-27 12:13:06 +02:00
-> setTitle ( $domain -> getLdhName () . ': ' . $event -> getAction () . ' - event update' )
-> setDescription ( " Domain name event " )
-> setLink ( $baseUrl . " /#/search/domain/ " . $domain -> getLdhName ())
-> setContent ( $this -> render ( 'rss/event_entry.html.twig' , [
'event' => $event
]) -> getContent ())
-> setDateModified ( $event -> getDate ())
-> addAuthor ([ " name " => strtoupper ( $domain -> getTld () -> getTld ()) . " Registry " ]);
}
return $entries ;
}
/**
* @ throws Exception
*/
private function getRssStatusEntries ( string $baseUrl , Domain $domain ) : array
{
$entries = [];
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 " ]];
/** @var string $status */
foreach ([ ... $domainStatus -> getAddStatus (), ... $domainStatus -> getDeleteStatus ()] as $status ) {
if ( str_starts_with ( $status , 'client' )) {
$authors [] = [ " name " => " Registrar " ];
break ;
}
}
$entries [] = ( new Entry ())
2025-04-27 17:44:31 +02:00
-> setId ( $baseUrl . " /api/domains/ " . $domain -> getLdhName () . '#status-' . $domainStatus -> getId ())
2025-04-27 12:13:06 +02:00
-> setTitle ( $domain -> getLdhName () . ' - EPP status update' )
-> setDescription ( " There has been a change in the EPP status of the domain name. " )
-> setLink ( $baseUrl . " /#/search/domain/ " . $domain -> getLdhName ())
-> setContent ( $this -> render ( 'rss/status_entry.html.twig' , [
'domainStatus' => $domainStatus
]) -> getContent ())
-> setDateCreated ( $domainStatus -> getCreatedAt ())
-> setDateModified ( $domainStatus -> getDate ())
-> addCategory ([ " term " => $domain -> getLdhName ()])
-> addCategory ([ " term " => strtoupper ( $domain -> getTld () -> getTld ())])
-> addAuthors ( $authors )
;
}
return $entries ;
}
2024-08-02 23:24:52 +02:00
}