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,
|
lastUpdated: true,
|
||||||
social: [
|
social: [
|
||||||
{icon: 'github', label: 'GitHub', href: 'https://github.com/maelgangloff/domain-watchdog'},
|
{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: [
|
sidebar: [
|
||||||
{slug: 'features'},
|
{slug: 'features'},
|
||||||
|
|||||||
@ -49,55 +49,59 @@ flowchart LR
|
|||||||
|
|
||||||
### Framework
|
### 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**
|
The API is made possible by the [**API Platform** project](https://api-platform.com/docs/symfony/).
|
||||||
project ([documentation](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
|
Other database types cannot be used because some migrations were specifically written to leverage PostgreSQL-specific features and performance optimizations.
|
||||||
this database management system.
|
|
||||||
|
|
||||||
### 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
|
- Cache certain values
|
||||||
- Implement locks to limit the possibility of conditional raises
|
- 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
|
- Store messages for workers to process asynchronous actions
|
||||||
Watchlist on a high-priority RDAP client queue.
|
(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:
|
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
|
- RDAP requests from your instance: response time, requested domain name, HTTP status code, IP address of the RDAP server, etc.
|
||||||
server, etc.
|
|
||||||
- User notifications: adding events to a domain name, changing EPP statuses, etc.
|
- User notifications: adding events to a domain name, changing EPP statuses, etc.
|
||||||
|
|
||||||
### SSO authentication
|
### 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
|
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
|
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.
|
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
|
## Frontend
|
||||||
|
|
||||||
### Framework
|
### 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
|
### 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()]);
|
$newDomain = $this->domainRepository->findOneBy(['ldhName' => $domain->getLdhName()]);
|
||||||
|
|
||||||
if (!$deleted && null !== $newDomain && $newDomain->getDeleted()) {
|
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);
|
$notification = new DomainDeletedNotification($this->sender, $domain);
|
||||||
$this->mailer->send($notification->asEmailMessage(new Recipient($watchlist->getUser()->getEmail()))->getMessage());
|
$this->mailer->send($notification->asEmailMessage(new Recipient($watchlist->getUser()->getEmail()))->getMessage());
|
||||||
$this->chatNotificationService->sendChatNotification($watchlist, $notification);
|
$this->chatNotificationService->sendChatNotification($watchlist, $notification);
|
||||||
|
|||||||
@ -18,7 +18,8 @@ final readonly class UpdateRdapServersHandler
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private OfficialDataService $officialDataService,
|
private OfficialDataService $officialDataService,
|
||||||
private ParameterBagInterface $bag, private DomainRepository $domainRepository,
|
private ParameterBagInterface $bag,
|
||||||
|
private DomainRepository $domainRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -159,13 +159,13 @@ class OfficialDataService
|
|||||||
));
|
));
|
||||||
array_shift($tldList);
|
array_shift($tldList);
|
||||||
|
|
||||||
|
$this->tldRepository->setAllTldAsDeleted();
|
||||||
|
|
||||||
foreach ($tldList as $tld) {
|
foreach ($tldList as $tld) {
|
||||||
if ('' === $tld) {
|
if ('' === $tld) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->tldRepository->setAllTldAsDeleted();
|
|
||||||
|
|
||||||
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
||||||
|
|
||||||
if (null === $tldEntity) {
|
if (null === $tldEntity) {
|
||||||
|
|||||||
@ -65,6 +65,10 @@ class RDAPService
|
|||||||
'Private',
|
'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(
|
public function __construct(
|
||||||
private readonly HttpClientInterface $client,
|
private readonly HttpClientInterface $client,
|
||||||
private readonly EntityRepository $entityRepository,
|
private readonly EntityRepository $entityRepository,
|
||||||
@ -740,13 +744,13 @@ class RDAPService
|
|||||||
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'.self::PENDING_DELETE_DURATION_DAYS.'D')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($domain->isRedemptionPeriod()
|
if ($domain->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'.(self::REDEMPTION_PERIOD_DURATION_DAYS + self::PENDING_DELETE_DURATION_DAYS).'D')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -772,7 +776,7 @@ class RDAPService
|
|||||||
[$expiredAt, $deletedAt] = $this->getRelevantDates($domain);
|
[$expiredAt, $deletedAt] = $this->getRelevantDates($domain);
|
||||||
|
|
||||||
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'.(self::AUTO_RENEW_PERIOD_DURATION_DAYS + self::REDEMPTION_PERIOD_DURATION_DAYS + self::PENDING_DELETE_DURATION_DAYS).'D')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($deletedAt) {
|
if ($deletedAt) {
|
||||||
@ -781,7 +785,7 @@ class RDAPService
|
|||||||
return 0;
|
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([
|
return self::returnExpiresIn([
|
||||||
|
|||||||
@ -72,6 +72,7 @@ readonly class AutoRegisterDomainProvider implements ProviderInterface
|
|||||||
$idnDomain = RDAPService::convertToIdn($uriVariables['ldhName']);
|
$idnDomain = RDAPService::convertToIdn($uriVariables['ldhName']);
|
||||||
|
|
||||||
$user = $this->security->getUser();
|
$user = $this->security->getUser();
|
||||||
|
$request = $this->requestStack->getCurrentRequest();
|
||||||
|
|
||||||
if (null !== $user) {
|
if (null !== $user) {
|
||||||
$this->logger->info('User wants to update a domain name', [
|
$this->logger->info('User wants to update a domain name', [
|
||||||
@ -81,11 +82,10 @@ readonly class AutoRegisterDomainProvider implements ProviderInterface
|
|||||||
} else {
|
} else {
|
||||||
$this->logger->info('Anonymous wants to update a domain name', [
|
$this->logger->info('Anonymous wants to update a domain name', [
|
||||||
'ldhName' => $idnDomain,
|
'ldhName' => $idnDomain,
|
||||||
|
'ipAddress' => $request->getClientIp(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$request = $this->requestStack->getCurrentRequest();
|
|
||||||
|
|
||||||
/** @var ?Domain $domain */
|
/** @var ?Domain $domain */
|
||||||
$domain = $this->domainRepository->findOneBy(['ldhName' => $idnDomain]);
|
$domain = $this->domainRepository->findOneBy(['ldhName' => $idnDomain]);
|
||||||
// If the domain name exists in the database, recently updated and not important, we return the stored Domain
|
// 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