mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-22 20:25:43 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76377116ce | ||
|
|
545677ddf7 | ||
|
|
94a29bc8df |
@ -31,7 +31,8 @@ export default defineConfig({
|
||||
lastUpdated: true,
|
||||
social: [
|
||||
{icon: 'github', label: 'GitHub', href: 'https://github.com/maelgangloff/domain-watchdog'},
|
||||
{icon: 'seti:docker', label: 'Docker', href: 'https://hub.docker.com/r/maelgangloff/domain-watchdog'}
|
||||
{icon: 'seti:docker', label: 'Docker', href: 'https://hub.docker.com/r/maelgangloff/domain-watchdog'},
|
||||
{icon: 'external', label: 'Demo', href: 'https://demo.domainwatchdog.eu'}
|
||||
],
|
||||
sidebar: [
|
||||
{slug: 'features'},
|
||||
|
||||
@ -49,55 +49,59 @@ flowchart LR
|
||||
|
||||
### Framework
|
||||
|
||||
The programming language is **PHP**.
|
||||
The backend is written in [**PHP**](https://www.php.net/docs.php).
|
||||
|
||||
The backend is developed using the **Symfony** framework ([documentation](https://symfony.com/doc)).
|
||||
The backend is developed using the [**Symfony** framework](https://symfony.com/doc).
|
||||
|
||||
The API is made possible by the **API Platform**
|
||||
project ([documentation](https://api-platform.com/docs/symfony/)).
|
||||
The API is made possible by the [**API Platform** project](https://api-platform.com/docs/symfony/).
|
||||
|
||||
### SQL database
|
||||
### SQL Database
|
||||
|
||||
This project requires a **PostgreSQL** database ([documentation](https://www.postgresql.org/docs/current/)).
|
||||
This project requires a [**PostgreSQL** database](https://www.postgresql.org/docs/current/).
|
||||
|
||||
Other database types cannot be used because some migrations were specifically written to leverage the performance of
|
||||
this database management system.
|
||||
Other database types cannot be used because some migrations were specifically written to leverage PostgreSQL-specific features and performance optimizations.
|
||||
|
||||
### Key-value database
|
||||
### Key-value Database
|
||||
|
||||
A **Redis-compatible** key-value database is required to:
|
||||
A [**Redis-compatible**](https://redis.io/docs/latest/apis/) key-value database is required to:
|
||||
|
||||
- Cache certain values
|
||||
- Implement locks to limit the possibility of conditional raises
|
||||
- Store messages to be distributed to workers to process asynchronous actions. For example: updating domain names in a
|
||||
Watchlist on a high-priority RDAP client queue.
|
||||
- Store messages for workers to process asynchronous actions
|
||||
(e.g. updating domain names in a Watchlist on a high-priority RDAP client queue)
|
||||
|
||||
## Time Series database
|
||||
### Time-Series Database
|
||||
|
||||
The **InfluxDB** database is optional.
|
||||
The [**InfluxDB**](https://docs.influxdata.com/) database is optional.
|
||||
A data point is added for the following events:
|
||||
|
||||
- RDAP requests from your instance: response time, requested domain name, HTTP status code, IP address of the RDAP
|
||||
server, etc.
|
||||
- RDAP requests from your instance: response time, requested domain name, HTTP status code, IP address of the RDAP server, etc.
|
||||
- User notifications: adding events to a domain name, changing EPP statuses, etc.
|
||||
|
||||
### SSO authentication
|
||||
|
||||
An **OAuth 2.0** server is not required to authenticate users.
|
||||
An **OAuth 2.0** server is optional for user authentication.
|
||||
|
||||
Using Single Sign-On (SSO) allows you to delegate user authentication to a third party. This can be useful if you only
|
||||
want people within your organization to be able to use this project instance. Furthermore, you can then configure
|
||||
advanced security policies such as passwordless login, passkeys, multifactor authentication, and more.
|
||||
|
||||
### Error Reporting
|
||||
|
||||
The [**Sentry**](https://docs.sentry.io/platforms/php/guides/symfony/) integration is optional.
|
||||
When enabled, Sentry captures and reports application errors, providing useful insights for debugging.
|
||||
You can configure this feature through your project’s environment variables.
|
||||
|
||||
___
|
||||
|
||||
## Frontend
|
||||
|
||||
### Framework
|
||||
|
||||
The language for frontend development is **TypeScript**.
|
||||
The language for frontend development is [**TypeScript**](https://www.typescriptlang.org/docs/).
|
||||
|
||||
The framework used for the frontend is **React** ([documentation](https://react.dev/reference/react)).
|
||||
The framework used for the frontend is [**React**](https://react.dev/reference/react).
|
||||
|
||||
### Component Library
|
||||
|
||||
The component library used is **Ant Design** ([documentation](https://ant.design/components/overview/)).
|
||||
The component library used is [**Ant Design**](https://ant.design/components/overview/).
|
||||
|
||||
@ -166,6 +166,11 @@ final readonly class UpdateDomainHandler
|
||||
$newDomain = $this->domainRepository->findOneBy(['ldhName' => $domain->getLdhName()]);
|
||||
|
||||
if (!$deleted && null !== $newDomain && $newDomain->getDeleted()) {
|
||||
$this->logger->info('Domain has been deleted from WHOIS: a notification is sent to user', [
|
||||
'ldhName' => $message->ldhName,
|
||||
'username' => $watchlist->getUser()->getUserIdentifier(),
|
||||
]);
|
||||
|
||||
$notification = new DomainDeletedNotification($this->sender, $domain);
|
||||
$this->mailer->send($notification->asEmailMessage(new Recipient($watchlist->getUser()->getEmail()))->getMessage());
|
||||
$this->chatNotificationService->sendChatNotification($watchlist, $notification);
|
||||
|
||||
@ -18,7 +18,8 @@ final readonly class UpdateRdapServersHandler
|
||||
{
|
||||
public function __construct(
|
||||
private OfficialDataService $officialDataService,
|
||||
private ParameterBagInterface $bag, private DomainRepository $domainRepository,
|
||||
private ParameterBagInterface $bag,
|
||||
private DomainRepository $domainRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
@ -159,13 +159,13 @@ class OfficialDataService
|
||||
));
|
||||
array_shift($tldList);
|
||||
|
||||
$this->tldRepository->setAllTldAsDeleted();
|
||||
|
||||
foreach ($tldList as $tld) {
|
||||
if ('' === $tld) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->tldRepository->setAllTldAsDeleted();
|
||||
|
||||
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
||||
|
||||
if (null === $tldEntity) {
|
||||
|
||||
@ -65,6 +65,10 @@ class RDAPService
|
||||
'Private',
|
||||
];
|
||||
|
||||
public const PENDING_DELETE_DURATION_DAYS = 5;
|
||||
public const REDEMPTION_PERIOD_DURATION_DAYS = 30;
|
||||
public const AUTO_RENEW_PERIOD_DURATION_DAYS = 45;
|
||||
|
||||
public function __construct(
|
||||
private readonly HttpClientInterface $client,
|
||||
private readonly EntityRepository $entityRepository,
|
||||
@ -740,13 +744,13 @@ class RDAPService
|
||||
in_array('pending delete', $lastStatus->getAddStatus())
|
||||
|| 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'.self::PENDING_DELETE_DURATION_DAYS.'D')));
|
||||
}
|
||||
|
||||
if ($domain->isRedemptionPeriod()
|
||||
&& 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'.(self::REDEMPTION_PERIOD_DURATION_DAYS + self::PENDING_DELETE_DURATION_DAYS).'D')));
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -772,7 +776,7 @@ class RDAPService
|
||||
[$expiredAt, $deletedAt] = $this->getRelevantDates($domain);
|
||||
|
||||
if ($expiredAt) {
|
||||
$guess = self::daysBetween($now, $expiredAt->add(new \DateInterval('P'.(45 + 30 + 5).'D')));
|
||||
$guess = self::daysBetween($now, $expiredAt->add(new \DateInterval('P'.(self::AUTO_RENEW_PERIOD_DURATION_DAYS + self::REDEMPTION_PERIOD_DURATION_DAYS + self::PENDING_DELETE_DURATION_DAYS).'D')));
|
||||
}
|
||||
|
||||
if ($deletedAt) {
|
||||
@ -781,7 +785,7 @@ class RDAPService
|
||||
return 0;
|
||||
}
|
||||
|
||||
$guess = self::daysBetween($now, $deletedAt->add(new \DateInterval('P'. 30 .'D')));
|
||||
$guess = self::daysBetween($now, $deletedAt->add(new \DateInterval('P'.self::REDEMPTION_PERIOD_DURATION_DAYS.'D')));
|
||||
}
|
||||
|
||||
return self::returnExpiresIn([
|
||||
|
||||
@ -72,6 +72,7 @@ readonly class AutoRegisterDomainProvider implements ProviderInterface
|
||||
$idnDomain = RDAPService::convertToIdn($uriVariables['ldhName']);
|
||||
|
||||
$user = $this->security->getUser();
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
if (null !== $user) {
|
||||
$this->logger->info('User wants to update a domain name', [
|
||||
@ -81,11 +82,10 @@ readonly class AutoRegisterDomainProvider implements ProviderInterface
|
||||
} else {
|
||||
$this->logger->info('Anonymous wants to update a domain name', [
|
||||
'ldhName' => $idnDomain,
|
||||
'ipAddress' => $request->getClientIp(),
|
||||
]);
|
||||
}
|
||||
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
/** @var ?Domain $domain */
|
||||
$domain = $this->domainRepository->findOneBy(['ldhName' => $idnDomain]);
|
||||
// If the domain name exists in the database, recently updated and not important, we return the stored Domain
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user