From e1625cbfac89ada9868b82191c7a0fead9f5fb68 Mon Sep 17 00:00:00 2001 From: vinceh121 Date: Sun, 11 Aug 2024 15:44:34 +0200 Subject: [PATCH 01/24] chore: eclipse stuff in gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 0f20979..f1b0b01 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,11 @@ yarn-error.log phpstan.neon ###< phpstan/phpstan ### +# Eclipse +.project +.buildpath +/.settings/ + public/images/*.png public/content/*.md public/favicon.ico From 4ac43e9ca972787e93a76280bc4861baad42ab40 Mon Sep 17 00:00:00 2001 From: vinceh121 Date: Sun, 11 Aug 2024 15:53:17 +0200 Subject: [PATCH 02/24] fix: HttpException namespace --- src/Config/Connector/GandiConnector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config/Connector/GandiConnector.php b/src/Config/Connector/GandiConnector.php index d9196b7..d4bf3d4 100644 --- a/src/Config/Connector/GandiConnector.php +++ b/src/Config/Connector/GandiConnector.php @@ -79,7 +79,7 @@ readonly class GandiConnector implements ConnectorInterface if ((!$dryRun && Response::HTTP_ACCEPTED !== $res->getStatusCode()) || ($dryRun && Response::HTTP_OK !== $res->getStatusCode())) { - throw new \HttpException($res->toArray()['message']); + throw new HttpException($res->toArray()['message']); } } From f225213c496aec43964c079b0a15409653a4f2cf Mon Sep 17 00:00:00 2001 From: vinceh121 Date: Mon, 19 Aug 2024 21:17:57 +0200 Subject: [PATCH 03/24] chore: progress towards namecheap --- src/Config/ConnectorProvider.php | 9 ++-- .../ProcessDomainTriggerHandler.php | 8 ++-- .../Connector/ConnectorInterface.php | 2 +- .../Connector/GandiConnector.php | 2 +- src/Service/Connector/NamecheapConnector.php | 48 +++++++++++++++++++ .../Connector/OvhConnector.php | 5 +- 6 files changed, 64 insertions(+), 10 deletions(-) rename src/{Config => Service}/Connector/ConnectorInterface.php (91%) rename src/{Config => Service}/Connector/GandiConnector.php (99%) create mode 100644 src/Service/Connector/NamecheapConnector.php rename src/{Config => Service}/Connector/OvhConnector.php (99%) diff --git a/src/Config/ConnectorProvider.php b/src/Config/ConnectorProvider.php index 62d8e4a..c64d2ad 100644 --- a/src/Config/ConnectorProvider.php +++ b/src/Config/ConnectorProvider.php @@ -2,19 +2,22 @@ namespace App\Config; -use App\Config\Connector\GandiConnector; -use App\Config\Connector\OvhConnector; +use App\Service\Connector\OvhConnector; +use App\Service\Connector\GandiConnector; +use App\Service\Connector\NamecheapConnector; enum ConnectorProvider: string { case OVH = 'ovh'; case GANDI = 'gandi'; + case NAMECHEAP = 'namecheap'; public function getConnectorProvider(): string { return match ($this) { ConnectorProvider::OVH => OvhConnector::class, - ConnectorProvider::GANDI => GandiConnector::class + ConnectorProvider::GANDI => GandiConnector::class, + ConnectorProvider::NAMECHEAP => NamecheapConnector::class }; } } diff --git a/src/MessageHandler/ProcessDomainTriggerHandler.php b/src/MessageHandler/ProcessDomainTriggerHandler.php index 6c85a31..78ac52d 100644 --- a/src/MessageHandler/ProcessDomainTriggerHandler.php +++ b/src/MessageHandler/ProcessDomainTriggerHandler.php @@ -2,7 +2,6 @@ namespace App\MessageHandler; -use App\Config\Connector\ConnectorInterface; use App\Config\TriggerAction; use App\Entity\Connector; use App\Entity\Domain; @@ -22,6 +21,8 @@ use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; use Symfony\Contracts\HttpClient\HttpClientInterface; +use App\Service\Connector\ConnectorInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; #[AsMessageHandler] final readonly class ProcessDomainTriggerHandler @@ -34,7 +35,8 @@ final readonly class ProcessDomainTriggerHandler private DomainRepository $domainRepository, private KernelInterface $kernel, private LoggerInterface $logger, - private HttpClientInterface $client + private HttpClientInterface $client, + private ContainerBagInterface $container ) { } @@ -66,7 +68,7 @@ final readonly class ProcessDomainTriggerHandler $connectorProviderClass = $provider->getConnectorProvider(); /** @var ConnectorInterface $connectorProvider */ - $connectorProvider = new $connectorProviderClass($connector->getAuthData(), $this->client); + $connectorProvider = $this->container->get($connectorProviderClass); $connectorProvider->orderDomain($domain, $this->kernel->isDebug()); diff --git a/src/Config/Connector/ConnectorInterface.php b/src/Service/Connector/ConnectorInterface.php similarity index 91% rename from src/Config/Connector/ConnectorInterface.php rename to src/Service/Connector/ConnectorInterface.php index 46f473e..3989b5e 100644 --- a/src/Config/Connector/ConnectorInterface.php +++ b/src/Service/Connector/ConnectorInterface.php @@ -1,6 +1,6 @@ authData; + } + + $actualParams = array_merge([ + 'ApiUser' => $authData['ApiUser'], + 'ApiKey' => $authData['ApiKey'] + ], $parameters); + + $response = $this->client->request('POST', BASE_URL, [ + 'query' => $actualParams + ]); + + $data = new \SimpleXMLElement($response->getContent()); + + if ($data->errors->error) { + throw new \Exception(implode(', ', $data->errors->error)); // FIXME better exception type + } + + return $data->CommandResponse; + } + + public static function verifyAuthData(array $authData, HttpClientInterface $client): array + { + } +} + diff --git a/src/Config/Connector/OvhConnector.php b/src/Service/Connector/OvhConnector.php similarity index 99% rename from src/Config/Connector/OvhConnector.php rename to src/Service/Connector/OvhConnector.php index f46021e..b99dc73 100644 --- a/src/Config/Connector/OvhConnector.php +++ b/src/Service/Connector/OvhConnector.php @@ -1,6 +1,6 @@ 'GET', 'path' => '/order/cart', - ], [ + ], + [ 'method' => 'GET', 'path' => '/order/cart/*', ], From 700e531e2250a7fe77eab4a1c08281f4eebc4850 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 20 Aug 2024 13:56:50 +0200 Subject: [PATCH 04/24] feat: domain order call a bisto de nas --- composer.json | 3 +- src/Service/Connector/NamecheapConnector.php | 56 +++++++++++++++----- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 6a8c06f..08f9798 100644 --- a/composer.json +++ b/composer.json @@ -73,7 +73,8 @@ "symfony/yaml": "7.1.*", "symfonycasts/verify-email-bundle": "*", "twig/extra-bundle": "^2.12|^3.0", - "twig/twig": "^2.12|^3.0" + "twig/twig": "^2.12|^3.0", + "ext-simplexml": "*" }, "config": { "allow-plugins": { diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index e4f14ae..c8ab383 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -1,35 +1,68 @@ call('namecheap.users.address.getList'); - private function call(string $command, array $parameters, array $authData = null): object + if (count($addresses) < 1) { + throw new \Exception('Namecheap account requires at least one address to purchase a domain'); + } + + $addressId = $addresses->{0}->AddressId; + $address = $this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId]); + + $domainAddresses = []; + + self::mergePrefixKeys('Registrant', $address, $domainAddresses); + self::mergePrefixKeys('Tech', $address, $domainAddresses); + self::mergePrefixKeys('Admin', $address, $domainAddresses); + self::mergePrefixKeys('AuxBilling', $address, $domainAddresses); + + $this->call('namecheap.domains.create', array_merge([ + 'DomainName' => $domain->getLdhName(), + 'Years' => 1, + 'AddFreeWhoisguard' => 'yes', + 'WGEnabled' => 'yes', + ], $domainAddresses)); + } + + private static function mergePrefixKeys(string $prefix, array|object $src, array &$dest) + { + foreach ($src as $key => $value) { + $dest[$prefix.$key] = $value; + } + } + + private function call(string $command, array $parameters = [], ?array $authData = null): object { if (is_null($authData)) { $authData = $this->authData; } $actualParams = array_merge([ + 'Command' => $command, 'ApiUser' => $authData['ApiUser'], - 'ApiKey' => $authData['ApiKey'] + 'ApiKey' => $authData['ApiKey'], + 'ClientIp' => '', // TODO DW instance IP envvar ], $parameters); - $response = $this->client->request('POST', BASE_URL, [ - 'query' => $actualParams + $response = $this->client->request('POST', self::BASE_URL, [ + 'query' => $actualParams, ]); $data = new \SimpleXMLElement($response->getContent()); @@ -45,4 +78,3 @@ class NamecheapConnector implements ConnectorInterface { } } - From 2f841f012723e46d22db302b3e39ced8e77d1fd2 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 23 Aug 2024 13:38:16 +0200 Subject: [PATCH 05/24] refactor: connector abstraction --- src/Service/Connector/AbstractConnector.php | 13 +++++++++++++ src/Service/Connector/ConnectorInterface.php | 2 +- src/Service/Connector/GandiConnector.php | 4 ++-- src/Service/Connector/NamecheapConnector.php | 4 ++-- src/Service/Connector/OvhConnector.php | 4 ++-- 5 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 src/Service/Connector/AbstractConnector.php diff --git a/src/Service/Connector/AbstractConnector.php b/src/Service/Connector/AbstractConnector.php new file mode 100644 index 0000000..dd6ab4b --- /dev/null +++ b/src/Service/Connector/AbstractConnector.php @@ -0,0 +1,13 @@ +authData = $authData; + } +} diff --git a/src/Service/Connector/ConnectorInterface.php b/src/Service/Connector/ConnectorInterface.php index 3989b5e..b7d9bf5 100644 --- a/src/Service/Connector/ConnectorInterface.php +++ b/src/Service/Connector/ConnectorInterface.php @@ -7,7 +7,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; interface ConnectorInterface { - public function __construct(array $authData, HttpClientInterface $client); + public function authenticate(array $authData); public function orderDomain(Domain $domain, bool $dryRun): void; diff --git a/src/Service/Connector/GandiConnector.php b/src/Service/Connector/GandiConnector.php index 9a35bc8..3ce7eac 100644 --- a/src/Service/Connector/GandiConnector.php +++ b/src/Service/Connector/GandiConnector.php @@ -12,11 +12,11 @@ use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; -readonly class GandiConnector implements ConnectorInterface +class GandiConnector extends AbstractConnector { private const BASE_URL = 'https://api.gandi.net/v5'; - public function __construct(private array $authData, private HttpClientInterface $client) + public function __construct(private HttpClientInterface $client) { } diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index c8ab383..609182d 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -5,13 +5,13 @@ namespace App\Service\Connector; use App\Entity\Domain; use Symfony\Contracts\HttpClient\HttpClientInterface; -readonly class NamecheapConnector implements ConnectorInterface +class NamecheapConnector extends AbstractConnector { public const BASE_URL = 'https://api.namecheap.com/xml.response'; public const SANDBOX_BASE_URL = 'http://api.sandbox.namecheap.com/xml.response'; - public function __construct(private array $authData, private HttpClientInterface $client) + public function __construct(private HttpClientInterface $client) { } diff --git a/src/Service/Connector/OvhConnector.php b/src/Service/Connector/OvhConnector.php index b99dc73..3651370 100644 --- a/src/Service/Connector/OvhConnector.php +++ b/src/Service/Connector/OvhConnector.php @@ -8,7 +8,7 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Contracts\HttpClient\HttpClientInterface; -readonly class OvhConnector implements ConnectorInterface +class OvhConnector extends AbstractConnector { public const REQUIRED_ROUTES = [ [ @@ -33,7 +33,7 @@ readonly class OvhConnector implements ConnectorInterface ], ]; - public function __construct(private array $authData, private HttpClientInterface $client) + public function __construct(private HttpClientInterface $client) { } From 3e3ea66848e4c54d0f0a189e55074331ba5f1705 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 23 Aug 2024 14:01:02 +0200 Subject: [PATCH 06/24] feat: instance IP address envvar --- .env | 5 ++++ assets/utils/api/connectors.ts | 3 ++- assets/utils/providers/index.tsx | 4 ++++ config/services.yaml | 1 + src/Service/Connector/NamecheapConnector.php | 24 ++++++++------------ 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.env b/.env index 221b2c4..7ae15ac 100644 --- a/.env +++ b/.env @@ -67,6 +67,11 @@ OAUTH_TOKEN_URL= OAUTH_USERINFO_URL= OAUTH_SCOPE= +# Typically your IP address, this envvar is required for +# some connectors that need to be provided with your host's +# outgoing IP address. +OUTGOING_IP= + LIMITED_FEATURES=false LIMIT_MAX_WATCHLIST=0 LIMIT_MAX_WATCHLIST_DOMAINS=0 diff --git a/assets/utils/api/connectors.ts b/assets/utils/api/connectors.ts index c1e87f5..7fd692d 100644 --- a/assets/utils/api/connectors.ts +++ b/assets/utils/api/connectors.ts @@ -2,7 +2,8 @@ import {request} from "./index"; export enum ConnectorProvider { OVH = 'ovh', - GANDI = 'gandi' + GANDI = 'gandi', + NAMECHEAP = 'namecheap' } export type Connector = { diff --git a/assets/utils/providers/index.tsx b/assets/utils/providers/index.tsx index 6867436..ce6beb1 100644 --- a/assets/utils/providers/index.tsx +++ b/assets/utils/providers/index.tsx @@ -15,6 +15,10 @@ export const helpGetTokenLink = (provider?: string) => { return {t`Retrieve a Personal Access Token from your customer account on the Provider's website`} + case ConnectorProvider.NAMECHEAP: + return + {t`Retreive an API key and whitelist this instance's IP address on Namecheap's website`} + default: return <> diff --git a/config/services.yaml b/config/services.yaml index bc8ea08..ac49546 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -21,6 +21,7 @@ services: bind: $mailerSenderEmail: '%mailer_sender_email%' $mailerSenderName: '%mailer_sender_name%' + $outgoingIp: '%env(string:OUTGOING_IP)%' # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index 609182d..3e70f0f 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -11,20 +11,20 @@ class NamecheapConnector extends AbstractConnector public const SANDBOX_BASE_URL = 'http://api.sandbox.namecheap.com/xml.response'; - public function __construct(private HttpClientInterface $client) + public function __construct(private HttpClientInterface $client, private string $outgoingIp) { } public function orderDomain(Domain $domain, $dryRun): void { - $addresses = $this->call('namecheap.users.address.getList'); + $addresses = $this->call('namecheap.users.address.getList', [], $dryRun); if (count($addresses) < 1) { throw new \Exception('Namecheap account requires at least one address to purchase a domain'); } $addressId = $addresses->{0}->AddressId; - $address = $this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId]); + $address = $this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId], $dryRun); $domainAddresses = []; @@ -38,30 +38,26 @@ class NamecheapConnector extends AbstractConnector 'Years' => 1, 'AddFreeWhoisguard' => 'yes', 'WGEnabled' => 'yes', - ], $domainAddresses)); + ], $domainAddresses), $dryRun); } private static function mergePrefixKeys(string $prefix, array|object $src, array &$dest) { foreach ($src as $key => $value) { - $dest[$prefix.$key] = $value; + $dest[$prefix . $key] = $value; } } - private function call(string $command, array $parameters = [], ?array $authData = null): object + private function call(string $command, array $parameters = [], bool $dryRun = true): object { - if (is_null($authData)) { - $authData = $this->authData; - } - $actualParams = array_merge([ 'Command' => $command, - 'ApiUser' => $authData['ApiUser'], - 'ApiKey' => $authData['ApiKey'], - 'ClientIp' => '', // TODO DW instance IP envvar + 'ApiUser' => $this->authData['ApiUser'], + 'ApiKey' => $this->authData['ApiKey'], + 'ClientIp' => $this->outgoingIp, ], $parameters); - $response = $this->client->request('POST', self::BASE_URL, [ + $response = $this->client->request('POST', $dryRun ? self::SANDBOX_BASE_URL : self::BASE_URL, [ 'query' => $actualParams, ]); From 7861eaf5db5dae4d9a60095d6bd75ea7741ace62 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 26 Aug 2024 14:02:12 +0200 Subject: [PATCH 07/24] feat: some stuff --- assets/components/tracking/ConnectorForm.tsx | 16 ++++++++++++++++ assets/utils/providers/index.tsx | 2 +- config/services.yaml | 4 +++- package.json | 3 ++- .../ProcessDomainTriggerHandler.php | 5 +++-- src/Security/JWTAuthenticator.php | 2 +- src/Service/Connector/NamecheapConnector.php | 5 +++-- src/Service/RDAPService.php | 1 + 8 files changed, 30 insertions(+), 8 deletions(-) diff --git a/assets/components/tracking/ConnectorForm.tsx b/assets/components/tracking/ConnectorForm.tsx index 738a441..c97b732 100644 --- a/assets/components/tracking/ConnectorForm.tsx +++ b/assets/components/tracking/ConnectorForm.tsx @@ -132,6 +132,22 @@ export function ConnectorForm({form, onCreate}: { form: FormInstance, onCreate: } + { + provider === ConnectorProvider.NAMECHEAP && <> + + + + + + + + } { provider !== undefined && <> diff --git a/assets/utils/providers/index.tsx b/assets/utils/providers/index.tsx index ce6beb1..7631cec 100644 --- a/assets/utils/providers/index.tsx +++ b/assets/utils/providers/index.tsx @@ -16,7 +16,7 @@ export const helpGetTokenLink = (provider?: string) => { {t`Retrieve a Personal Access Token from your customer account on the Provider's website`} case ConnectorProvider.NAMECHEAP: - return + return {t`Retreive an API key and whitelist this instance's IP address on Namecheap's website`} default: diff --git a/config/services.yaml b/config/services.yaml index ac49546..c7dffa1 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -13,6 +13,8 @@ parameters: limit_max_watchlist: '%env(int:LIMIT_MAX_WATCHLIST)%' limit_max_watchlist_domains: '%env(int:LIMIT_MAX_WATCHLIST_DOMAINS)%' + outgoing_ip: '%env(string:OUTGOING_IP)%' + services: # default configuration for services in *this* file _defaults: @@ -21,7 +23,7 @@ services: bind: $mailerSenderEmail: '%mailer_sender_email%' $mailerSenderName: '%mailer_sender_name%' - $outgoingIp: '%env(string:OUTGOING_IP)%' + $outgoingIp: '%outgoing_ip%' # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name diff --git a/package.json b/package.json index b4d3eed..1b22f63 100644 --- a/package.json +++ b/package.json @@ -60,5 +60,6 @@ }, "dependencies": { "remove": "^0.1.5" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/MessageHandler/ProcessDomainTriggerHandler.php b/src/MessageHandler/ProcessDomainTriggerHandler.php index 78ac52d..2ddd606 100644 --- a/src/MessageHandler/ProcessDomainTriggerHandler.php +++ b/src/MessageHandler/ProcessDomainTriggerHandler.php @@ -73,9 +73,10 @@ final readonly class ProcessDomainTriggerHandler $connectorProvider->orderDomain($domain, $this->kernel->isDebug()); $this->sendEmailDomainOrdered($domain, $connector, $watchList->getUser()); - } catch (\Throwable) { - $this->logger->warning('Unable to complete purchase. An error message is sent to user {username}.', [ + } catch (\Throwable $t) { + $this->logger->error('Unable to complete purchase. An error message is sent to user {username}.', [ 'username' => $watchList->getUser()->getUserIdentifier(), + 'error' => $t, ]); $this->sendEmailDomainOrderError($domain, $watchList->getUser()); } diff --git a/src/Security/JWTAuthenticator.php b/src/Security/JWTAuthenticator.php index bcd0fb0..d43a240 100644 --- a/src/Security/JWTAuthenticator.php +++ b/src/Security/JWTAuthenticator.php @@ -46,7 +46,7 @@ class JWTAuthenticator implements AuthenticationSuccessHandlerInterface time() + 7200, // expiration '/', null, - true, + false, true, false, 'strict' diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index 3e70f0f..7695bd6 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -11,7 +11,7 @@ class NamecheapConnector extends AbstractConnector public const SANDBOX_BASE_URL = 'http://api.sandbox.namecheap.com/xml.response'; - public function __construct(private HttpClientInterface $client, private string $outgoingIp) + public function __construct(private HttpClientInterface $client, private readonly string $outgoingIp) { } @@ -44,7 +44,7 @@ class NamecheapConnector extends AbstractConnector private static function mergePrefixKeys(string $prefix, array|object $src, array &$dest) { foreach ($src as $key => $value) { - $dest[$prefix . $key] = $value; + $dest[$prefix.$key] = $value; } } @@ -72,5 +72,6 @@ class NamecheapConnector extends AbstractConnector public static function verifyAuthData(array $authData, HttpClientInterface $client): array { + return $authData; } } diff --git a/src/Service/RDAPService.php b/src/Service/RDAPService.php index 60a6126..7e6e38c 100644 --- a/src/Service/RDAPService.php +++ b/src/Service/RDAPService.php @@ -152,6 +152,7 @@ readonly class RDAPService /** @var ?Domain $domain */ $domain = $this->domainRepository->findOneBy(['ldhName' => $idnDomain]); + return $domain; $rdapServerUrl = $rdapServer->getUrl(); From d932d735a3edf0286c2fe8fd7c770314408a021a Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 9 Sep 2024 14:02:33 +0200 Subject: [PATCH 08/24] fix: XML traversal, container autowire --- .../ProcessDomainTriggerHandler.php | 14 +++++++++----- src/Service/Connector/NamecheapConnector.php | 19 ++++++++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/MessageHandler/ProcessDomainTriggerHandler.php b/src/MessageHandler/ProcessDomainTriggerHandler.php index 2ddd606..0507094 100644 --- a/src/MessageHandler/ProcessDomainTriggerHandler.php +++ b/src/MessageHandler/ProcessDomainTriggerHandler.php @@ -12,8 +12,11 @@ use App\Entity\WatchListTrigger; use App\Message\ProcessDomainTrigger; use App\Repository\DomainRepository; use App\Repository\WatchListRepository; +use App\Service\Connector\ConnectorInterface; use Psr\Log\LoggerInterface; use Symfony\Bridge\Twig\Mime\TemplatedEmail; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Symfony\Component\Mailer\MailerInterface; @@ -21,8 +24,6 @@ use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; use Symfony\Contracts\HttpClient\HttpClientInterface; -use App\Service\Connector\ConnectorInterface; -use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; #[AsMessageHandler] final readonly class ProcessDomainTriggerHandler @@ -36,7 +37,8 @@ final readonly class ProcessDomainTriggerHandler private KernelInterface $kernel, private LoggerInterface $logger, private HttpClientInterface $client, - private ContainerBagInterface $container + #[Autowire(service: 'service_container')] + private ContainerInterface $locator ) { } @@ -68,12 +70,14 @@ final readonly class ProcessDomainTriggerHandler $connectorProviderClass = $provider->getConnectorProvider(); /** @var ConnectorInterface $connectorProvider */ - $connectorProvider = $this->container->get($connectorProviderClass); + $connectorProvider = $this->locator->get($connectorProviderClass); - $connectorProvider->orderDomain($domain, $this->kernel->isDebug()); + $connectorProvider->authenticate($connector->getAuthData()); + $connectorProvider->orderDomain($domain, /* $this->kernel->isDebug() */ false); $this->sendEmailDomainOrdered($domain, $connector, $watchList->getUser()); } catch (\Throwable $t) { + dump($t); $this->logger->error('Unable to complete purchase. An error message is sent to user {username}.', [ 'username' => $watchList->getUser()->getUserIdentifier(), 'error' => $t, diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index 7695bd6..01fe649 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -3,8 +3,10 @@ namespace App\Service\Connector; use App\Entity\Domain; +use Symfony\Component\DependencyInjection\Attribute\Autoconfigure; use Symfony\Contracts\HttpClient\HttpClientInterface; +#[Autoconfigure(public: true)] class NamecheapConnector extends AbstractConnector { public const BASE_URL = 'https://api.namecheap.com/xml.response'; @@ -17,14 +19,19 @@ class NamecheapConnector extends AbstractConnector public function orderDomain(Domain $domain, $dryRun): void { - $addresses = $this->call('namecheap.users.address.getList', [], $dryRun); + $addressesRes = $this->call('namecheap.users.address.getList', [], $dryRun); + $addresses = $addressesRes->AddressGetListResult->List; if (count($addresses) < 1) { throw new \Exception('Namecheap account requires at least one address to purchase a domain'); } - $addressId = $addresses->{0}->AddressId; - $address = $this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId], $dryRun); + $addressId = (string) $addresses->attributes()['AddressId']; + $address = (array) $this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId], $dryRun)->GetAddressInfoResult; + + if (empty($address['PostalCode'])) { + $address['PostalCode'] = $address['Zip']; + } $domainAddresses = []; @@ -33,6 +40,7 @@ class NamecheapConnector extends AbstractConnector self::mergePrefixKeys('Admin', $address, $domainAddresses); self::mergePrefixKeys('AuxBilling', $address, $domainAddresses); + $this->call('namecheap.domains.create', array_merge([ 'DomainName' => $domain->getLdhName(), 'Years' => 1, @@ -52,6 +60,7 @@ class NamecheapConnector extends AbstractConnector { $actualParams = array_merge([ 'Command' => $command, + 'UserName' => $this->authData['ApiUser'], 'ApiUser' => $this->authData['ApiUser'], 'ApiKey' => $this->authData['ApiKey'], 'ClientIp' => $this->outgoingIp, @@ -63,8 +72,8 @@ class NamecheapConnector extends AbstractConnector $data = new \SimpleXMLElement($response->getContent()); - if ($data->errors->error) { - throw new \Exception(implode(', ', $data->errors->error)); // FIXME better exception type + if ($data->Errors->Error) { + throw new \Exception($data->Errors->Error); // FIXME better exception type } return $data->CommandResponse; From bbd56d4f0ccd5667c50898d982270faf8df96172 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 18 Sep 2024 13:52:51 +0200 Subject: [PATCH 09/24] refactor: fix merge mistakes --- src/Config/ConnectorProvider.php | 9 ++++++--- src/MessageHandler/OrderDomainHandler.php | 4 ++-- src/Service/RDAPService.php | 1 - 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Config/ConnectorProvider.php b/src/Config/ConnectorProvider.php index 9d3d16c..3454d16 100644 --- a/src/Config/ConnectorProvider.php +++ b/src/Config/ConnectorProvider.php @@ -2,19 +2,22 @@ namespace App\Config; -use App\Config\Provider\GandiProvider; -use App\Config\Provider\OvhProvider; +use App\Service\Connector\GandiProvider; +use App\Service\Connector\NamecheapConnector; +use App\Service\Connector\OvhProvider; enum ConnectorProvider: string { case OVH = 'ovh'; case GANDI = 'gandi'; + case NAMECHEAP = 'namecheap'; public function getConnectorProvider(): string { return match ($this) { ConnectorProvider::OVH => OvhProvider::class, - ConnectorProvider::GANDI => GandiProvider::class + ConnectorProvider::GANDI => GandiProvider::class, + ConnectorProvider::NAMECHEAP => NamecheapConnector::class, }; } } diff --git a/src/MessageHandler/OrderDomainHandler.php b/src/MessageHandler/OrderDomainHandler.php index b725dc2..bed9f96 100644 --- a/src/MessageHandler/OrderDomainHandler.php +++ b/src/MessageHandler/OrderDomainHandler.php @@ -14,6 +14,8 @@ use App\Service\Connector\AbstractProvider; use App\Service\StatService; use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Symfony\Component\Mailer\MailerInterface; @@ -33,8 +35,6 @@ final readonly class OrderDomainHandler private WatchListRepository $watchListRepository, private DomainRepository $domainRepository, private KernelInterface $kernel, - private HttpClientInterface $client, - private CacheItemPoolInterface $cacheItemPool, private MailerInterface $mailer, private LoggerInterface $logger, private StatService $statService, diff --git a/src/Service/RDAPService.php b/src/Service/RDAPService.php index 46dfe18..2805b27 100644 --- a/src/Service/RDAPService.php +++ b/src/Service/RDAPService.php @@ -165,7 +165,6 @@ readonly class RDAPService /** @var ?Domain $domain */ $domain = $this->domainRepository->findOneBy(['ldhName' => $idnDomain]); - return $domain; $rdapServerUrl = $rdapServer->getUrl(); From 69ce4d5a24edbae625ad538045693a2ba7ea4bbc Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 18 Sep 2024 14:02:25 +0200 Subject: [PATCH 10/24] style: make cs fixer happy --- src/MessageHandler/OrderDomainHandler.php | 2 -- src/Service/Connector/NamecheapConnector.php | 1 - 2 files changed, 3 deletions(-) diff --git a/src/MessageHandler/OrderDomainHandler.php b/src/MessageHandler/OrderDomainHandler.php index bed9f96..dc3dc6b 100644 --- a/src/MessageHandler/OrderDomainHandler.php +++ b/src/MessageHandler/OrderDomainHandler.php @@ -12,7 +12,6 @@ use App\Repository\WatchListRepository; use App\Service\ChatNotificationService; use App\Service\Connector\AbstractProvider; use App\Service\StatService; -use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -22,7 +21,6 @@ use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Mime\Address; use Symfony\Component\Notifier\Recipient\Recipient; -use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsMessageHandler] final readonly class OrderDomainHandler diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index 9adc61c..2c5b55e 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -41,7 +41,6 @@ class NamecheapConnector extends AbstractProvider self::mergePrefixKeys('Admin', $address, $domainAddresses); self::mergePrefixKeys('AuxBilling', $address, $domainAddresses); - $this->call('namecheap.domains.create', array_merge([ 'DomainName' => $domain->getLdhName(), 'Years' => 1, From 0657891b8d8773a371ce49c4949de197aecd9eb5 Mon Sep 17 00:00:00 2001 From: Stefan Warnat Date: Sun, 22 Sep 2024 18:48:32 +0200 Subject: [PATCH 11/24] fix: RDAP Server of DENIC do not return events Signed-off-by: Stefan Warnat --- src/Service/RDAPService.php | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Service/RDAPService.php b/src/Service/RDAPService.php index 2805b27..39c9a3e 100644 --- a/src/Service/RDAPService.php +++ b/src/Service/RDAPService.php @@ -232,25 +232,27 @@ readonly class RDAPService $event->setDeleted(true); } - foreach ($res['events'] as $rdapEvent) { - if ($rdapEvent['eventAction'] === EventAction::LastUpdateOfRDAPDatabase->value) { - continue; + if(!empty($res["events"])) { + foreach ($res['events'] as $rdapEvent) { + if ($rdapEvent['eventAction'] === EventAction::LastUpdateOfRDAPDatabase->value) { + continue; + } + + $event = $this->domainEventRepository->findOneBy([ + 'action' => $rdapEvent['eventAction'], + 'date' => new \DateTimeImmutable($rdapEvent['eventDate']), + 'domain' => $domain, + ]); + + if (null === $event) { + $event = new DomainEvent(); + } + $domain->addEvent($event + ->setAction($rdapEvent['eventAction']) + ->setDate(new \DateTimeImmutable($rdapEvent['eventDate'])) + ->setDeleted(false) + ); } - - $event = $this->domainEventRepository->findOneBy([ - 'action' => $rdapEvent['eventAction'], - 'date' => new \DateTimeImmutable($rdapEvent['eventDate']), - 'domain' => $domain, - ]); - - if (null === $event) { - $event = new DomainEvent(); - } - $domain->addEvent($event - ->setAction($rdapEvent['eventAction']) - ->setDate(new \DateTimeImmutable($rdapEvent['eventDate'])) - ->setDeleted(false) - ); } /** @var DomainEntity $domainEntity */ From a94a775ee6a4da932d00680ec29b7e5c380e0c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Gangloff?= Date: Sun, 22 Sep 2024 19:49:35 +0200 Subject: [PATCH 12/24] chore: reformat code --- src/Service/RDAPService.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Service/RDAPService.php b/src/Service/RDAPService.php index 39c9a3e..f20c2cb 100644 --- a/src/Service/RDAPService.php +++ b/src/Service/RDAPService.php @@ -232,18 +232,18 @@ readonly class RDAPService $event->setDeleted(true); } - if(!empty($res["events"])) { + if (array_key_exists('events', $res) && is_array($res['events'])) { foreach ($res['events'] as $rdapEvent) { if ($rdapEvent['eventAction'] === EventAction::LastUpdateOfRDAPDatabase->value) { continue; } - + $event = $this->domainEventRepository->findOneBy([ 'action' => $rdapEvent['eventAction'], 'date' => new \DateTimeImmutable($rdapEvent['eventDate']), 'domain' => $domain, ]); - + if (null === $event) { $event = new DomainEvent(); } From a00cc7e75dc035d02755416347aa19f6c716e1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Gangloff?= Date: Mon, 23 Sep 2024 12:48:40 +0200 Subject: [PATCH 13/24] fix: sort strings before generating the translation file Co-authored-by: Vincent --- package.json | 2 +- translations/translations.pot | 462 +++++++++++++++++----------------- 2 files changed, 232 insertions(+), 232 deletions(-) diff --git a/package.json b/package.json index 7af1b8c..26c0d49 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,6 @@ "watch": "encore dev --watch", "build": "encore production --progress", "ttag:po2json": "cd translations; for i in $(find . -name \"*.po\"); do ttag po2json $i > ../public/locales/$i.json; done; cd ..", - "ttag:extract": "ttag extract $(find assets -name '*.ts' -or -name '*.tsx') -o translations/translations.pot" + "ttag:extract": "ttag extract $(find assets -name '*.ts' -or -name '*.tsx' | sort) -o translations/translations.pot" } } diff --git a/translations/translations.pot b/translations/translations.pot index f16e03b..966ca3e 100644 --- a/translations/translations.pot +++ b/translations/translations.pot @@ -3,6 +3,29 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" +#: assets/App.tsx:101 +msgid "TOS" +msgstr "" + +#: assets/App.tsx:102 +msgid "Privacy Policy" +msgstr "" + +#: assets/App.tsx:103 +msgid "FAQ" +msgstr "" + +#: assets/App.tsx:105 +msgid "Documentation" +msgstr "" + +#: assets/App.tsx:108 +#, javascript-format +msgid "" +"${ ProjectLink } is an open source project distributed under the ${ " +"LicenseLink } license." +msgstr "" + #: assets/components/LoginForm.tsx:52 #: assets/components/RegisterForm.tsx:39 msgid "Email address" @@ -40,21 +63,9 @@ msgstr "" msgid "Log in with SSO" msgstr "" -#: assets/components/search/DomainResult.tsx:45 -msgid "EPP Status Codes" -msgstr "" - -#: assets/components/search/DomainResult.tsx:61 -msgid "Timeline" -msgstr "" - -#: assets/components/search/DomainResult.tsx:68 -msgid "Entities" -msgstr "" - -#: assets/components/search/DomainSearchBar.tsx:23 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:118 -msgid "This domain name does not appear to be valid" +#: assets/components/RegisterForm.tsx:56 +#: assets/pages/LoginPage.tsx:25 +msgid "Register" msgstr "" #: assets/components/search/DomainLifecycleSteps.tsx:15 @@ -74,6 +85,96 @@ msgstr "" msgid "Pending Delete" msgstr "" +#: assets/components/search/DomainResult.tsx:45 +msgid "EPP Status Codes" +msgstr "" + +#: assets/components/search/DomainResult.tsx:61 +msgid "Timeline" +msgstr "" + +#: assets/components/search/DomainResult.tsx:68 +msgid "Entities" +msgstr "" + +#: assets/components/search/DomainSearchBar.tsx:23 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:118 +msgid "This domain name does not appear to be valid" +msgstr "" + +#: assets/components/Sider.tsx:28 +msgid "Home" +msgstr "" + +#: assets/components/Sider.tsx:34 +msgid "Search" +msgstr "" + +#: assets/components/Sider.tsx:40 +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:46 +msgid "Domain" +msgstr "" + +#: assets/components/Sider.tsx:41 +msgid "Domain Finder" +msgstr "" + +#: assets/components/Sider.tsx:48 +#: assets/pages/search/TldPage.tsx:65 +msgid "TLD" +msgstr "" + +#: assets/components/Sider.tsx:49 +msgid "TLD list" +msgstr "" + +#: assets/components/Sider.tsx:56 +msgid "Entity" +msgstr "" + +#: assets/components/Sider.tsx:57 +msgid "Entity Finder" +msgstr "" + +#: assets/components/Sider.tsx:64 +msgid "Nameserver" +msgstr "" + +#: assets/components/Sider.tsx:65 +msgid "Nameserver Finder" +msgstr "" + +#: assets/components/Sider.tsx:73 +msgid "Tracking" +msgstr "" + +#: assets/components/Sider.tsx:79 +msgid "My Watchlists" +msgstr "" + +#: assets/components/Sider.tsx:86 +msgid "My Connectors" +msgstr "" + +#: assets/components/Sider.tsx:95 +msgid "Statistics" +msgstr "" + +#: assets/components/Sider.tsx:105 +#: assets/pages/UserPage.tsx:16 +msgid "My Account" +msgstr "" + +#: assets/components/Sider.tsx:110 +msgid "Log out" +msgstr "" + +#: assets/components/Sider.tsx:118 +#: assets/pages/LoginPage.tsx:25 +#: assets/pages/LoginPage.tsx:33 +msgid "Log in" +msgstr "" + #: assets/components/tracking/connector/ConnectorForm.tsx:40 msgid "Provider" msgstr "" @@ -183,6 +284,82 @@ msgstr "" msgid "No" msgstr "" +#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:14 +msgid "QR Code for iCalendar export" +msgstr "" + +#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:17 +msgid "Export events to iCalendar format" +msgstr "" + +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:12 +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:19 +msgid "Delete the Watchlist" +msgstr "" + +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:13 +msgid "Are you sure to delete this Watchlist?" +msgstr "" + +#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:39 +msgid "View the Watchlist Entity Diagram" +msgstr "" + +#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:44 +msgid "Watchlist Entity Diagram" +msgstr "" + +#: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:37 +msgid "Registry" +msgstr "" + +#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:30 +#, javascript-format +msgid ".${ tld.tld } Registry" +msgstr "" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:50 +msgid "Expiration date" +msgstr "" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:54 +msgid "Status" +msgstr "" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:58 +msgid "Updated at" +msgstr "" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:31 +msgid "Edit the Watchlist" +msgstr "" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:44 +msgid "Update a Watchlist" +msgstr "" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:54 +msgid "Cancel" +msgstr "" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:29 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:106 +msgid "Domain names" +msgstr "" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:33 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:148 +msgid "Tracked events" +msgstr "" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:47 +msgid "This Watchlist is not linked to a Connector." +msgstr "" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:52 +msgid "Watchlist" +msgstr "" + #: assets/components/tracking/watchlist/WatchlistForm.tsx:72 msgid "Name" msgstr "" @@ -199,11 +376,6 @@ msgstr "" msgid "At least one domain name" msgstr "" -#: assets/components/tracking/watchlist/WatchlistCard.tsx:29 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:106 -msgid "Domain names" -msgstr "" - #: assets/components/tracking/watchlist/WatchlistForm.tsx:124 msgid "Domain name" msgstr "" @@ -212,11 +384,6 @@ msgstr "" msgid "Add a Domain name" msgstr "" -#: assets/components/tracking/watchlist/WatchlistCard.tsx:33 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:148 -msgid "Tracked events" -msgstr "" - #: assets/components/tracking/watchlist/WatchlistForm.tsx:150 msgid "At least one trigger" msgstr "" @@ -252,148 +419,12 @@ msgstr "" msgid "Update" msgstr "" -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:31 -msgid "Edit the Watchlist" -msgstr "" - -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:44 -msgid "Update a Watchlist" -msgstr "" - -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:54 -msgid "Cancel" -msgstr "" - -#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:39 -msgid "View the Watchlist Entity Diagram" -msgstr "" - -#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:44 -msgid "Watchlist Entity Diagram" -msgstr "" - -#: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:37 -msgid "Registry" -msgstr "" - -#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:30 -#, javascript-format -msgid ".${ tld.tld } Registry" -msgstr "" - -#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:14 -msgid "QR Code for iCalendar export" -msgstr "" - -#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:17 -msgid "Export events to iCalendar format" -msgstr "" - -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:12 -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:19 -msgid "Delete the Watchlist" -msgstr "" - -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:13 -msgid "Are you sure to delete this Watchlist?" -msgstr "" - -#: assets/components/Sider.tsx:40 -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:46 -msgid "Domain" -msgstr "" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:50 -msgid "Expiration date" -msgstr "" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:54 -msgid "Status" -msgstr "" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:58 -msgid "Updated at" -msgstr "" - -#: assets/components/tracking/watchlist/WatchlistCard.tsx:47 -msgid "This Watchlist is not linked to a Connector." -msgstr "" - -#: assets/components/tracking/watchlist/WatchlistCard.tsx:52 -msgid "Watchlist" -msgstr "" - -#: assets/components/Sider.tsx:28 -msgid "Home" -msgstr "" - -#: assets/components/Sider.tsx:34 -msgid "Search" -msgstr "" - -#: assets/components/Sider.tsx:41 -msgid "Domain Finder" -msgstr "" - -#: assets/components/Sider.tsx:48 -#: assets/pages/search/TldPage.tsx:65 -msgid "TLD" -msgstr "" - -#: assets/components/Sider.tsx:49 -msgid "TLD list" -msgstr "" - -#: assets/components/Sider.tsx:56 -msgid "Entity" -msgstr "" - -#: assets/components/Sider.tsx:57 -msgid "Entity Finder" -msgstr "" - -#: assets/components/Sider.tsx:64 -msgid "Nameserver" -msgstr "" - -#: assets/components/Sider.tsx:65 -msgid "Nameserver Finder" -msgstr "" - -#: assets/components/Sider.tsx:73 -msgid "Tracking" -msgstr "" - -#: assets/components/Sider.tsx:79 -msgid "My Watchlists" -msgstr "" - -#: assets/components/Sider.tsx:86 -msgid "My Connectors" -msgstr "" - -#: assets/components/Sider.tsx:95 -msgid "Statistics" -msgstr "" - -#: assets/components/Sider.tsx:105 -#: assets/pages/UserPage.tsx:16 -msgid "My Account" -msgstr "" - -#: assets/components/Sider.tsx:110 -msgid "Log out" -msgstr "" - -#: assets/components/Sider.tsx:118 -#: assets/pages/LoginPage.tsx:25 #: assets/pages/LoginPage.tsx:33 -msgid "Log in" +msgid "Create an account" msgstr "" -#: assets/components/RegisterForm.tsx:56 -#: assets/pages/LoginPage.tsx:25 -msgid "Register" +#: assets/pages/NotFoundPage.tsx:10 +msgid "Sorry, the page you visited does not exist." msgstr "" #: assets/pages/search/DomainSearchPage.tsx:18 @@ -475,6 +506,42 @@ msgid "" "their country of origin." msgstr "" +#: assets/pages/StatisticsPage.tsx:34 +msgid "RDAP queries" +msgstr "" + +#: assets/pages/StatisticsPage.tsx:43 +msgid "Alerts sent" +msgstr "" + +#: assets/pages/StatisticsPage.tsx:57 +msgid "Domain names in database" +msgstr "" + +#: assets/pages/StatisticsPage.tsx:68 +#: assets/pages/tracking/WatchlistPage.tsx:111 +msgid "Tracked domain names" +msgstr "" + +#: assets/pages/StatisticsPage.tsx:82 +msgid "Purchased domain names" +msgstr "" + +#: assets/pages/StatisticsPage.tsx:92 +msgid "" +"This value is based on the status code of the HTTP response from the " +"providers following the domain order." +msgstr "" + +#: assets/pages/StatisticsPage.tsx:95 +msgid "Success rate" +msgstr "" + +#: assets/pages/TextPage.tsx:26 +#, javascript-format +msgid "📝 Please create the /public/content/${ resource } file." +msgstr "" + #: assets/pages/tracking/ConnectorsPage.tsx:20 msgid "Connector created !" msgstr "" @@ -495,45 +562,6 @@ msgstr "" msgid "Create a Watchlist" msgstr "" -#: assets/pages/StatisticsPage.tsx:68 -#: assets/pages/tracking/WatchlistPage.tsx:111 -msgid "Tracked domain names" -msgstr "" - -#: assets/pages/NotFoundPage.tsx:10 -msgid "Sorry, the page you visited does not exist." -msgstr "" - -#: assets/pages/StatisticsPage.tsx:34 -msgid "RDAP queries" -msgstr "" - -#: assets/pages/StatisticsPage.tsx:43 -msgid "Alerts sent" -msgstr "" - -#: assets/pages/StatisticsPage.tsx:57 -msgid "Domain names in database" -msgstr "" - -#: assets/pages/StatisticsPage.tsx:82 -msgid "Purchased domain names" -msgstr "" - -#: assets/pages/StatisticsPage.tsx:92 -msgid "" -"This value is based on the status code of the HTTP response from the " -"providers following the domain order." -msgstr "" - -#: assets/pages/StatisticsPage.tsx:95 -msgid "Success rate" -msgstr "" - -#: assets/pages/LoginPage.tsx:33 -msgid "Create an account" -msgstr "" - #: assets/pages/UserPage.tsx:18 msgid "Username" msgstr "" @@ -542,11 +570,6 @@ msgstr "" msgid "Roles" msgstr "" -#: assets/pages/TextPage.tsx:26 -#, javascript-format -msgid "📝 Please create the /public/content/${ resource } file." -msgstr "" - #: assets/utils/functions/rdapTranslation.ts:7 msgid "Registrant" msgstr "" @@ -1042,26 +1065,3 @@ msgid "" "The domain is free but can be premium. Its price varies from one domain to " "another" msgstr "" - -#: assets/App.tsx:101 -msgid "TOS" -msgstr "" - -#: assets/App.tsx:102 -msgid "Privacy Policy" -msgstr "" - -#: assets/App.tsx:103 -msgid "FAQ" -msgstr "" - -#: assets/App.tsx:105 -msgid "Documentation" -msgstr "" - -#: assets/App.tsx:108 -#, javascript-format -msgid "" -"${ ProjectLink } is an open source project distributed under the ${ " -"LicenseLink } license." -msgstr "" From 8d5d35c10d16b589f8accf5487a05895b742d650 Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 23 Sep 2024 10:51:41 +0000 Subject: [PATCH 14/24] Update translation files Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate. --- translations/de.po | 468 ++++++++++++++++++++++----------------------- translations/fr.po | 468 ++++++++++++++++++++++----------------------- 2 files changed, 468 insertions(+), 468 deletions(-) diff --git a/translations/de.po b/translations/de.po index 2d65aab..71295d9 100644 --- a/translations/de.po +++ b/translations/de.po @@ -10,6 +10,31 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7.2\n" +#: assets/App.tsx:101 +msgid "TOS" +msgstr "Nutzungsbedingungen" + +#: assets/App.tsx:102 +msgid "Privacy Policy" +msgstr "Datenschutzrichtlinie" + +#: assets/App.tsx:103 +msgid "FAQ" +msgstr "HĂ€ufig gestellte Fragen" + +#: assets/App.tsx:105 +msgid "Documentation" +msgstr "Dokumentation" + +#: assets/App.tsx:108 +#, javascript-format +msgid "" +"${ ProjectLink } is an open source project distributed under the " +"${ LicenseLink } license." +msgstr "" +"${ ProjectLink } ist ein Open-Source-Projekt, das unter der ${ LicenseLink }-" +"Lizenz vertrieben wird." + #: assets/components/LoginForm.tsx:52 assets/components/RegisterForm.tsx:39 msgid "Email address" msgstr "E-Mail-Adresse" @@ -43,22 +68,9 @@ msgstr "Senden" msgid "Log in with SSO" msgstr "Mit SSO anmelden" -#: assets/components/search/DomainResult.tsx:45 -msgid "EPP Status Codes" -msgstr "EPP-Statuscodes" - -#: assets/components/search/DomainResult.tsx:61 -msgid "Timeline" -msgstr "Zeitleiste" - -#: assets/components/search/DomainResult.tsx:68 -msgid "Entities" -msgstr "EntitĂ€ten" - -#: assets/components/search/DomainSearchBar.tsx:23 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:118 -msgid "This domain name does not appear to be valid" -msgstr "Dieser Domainname scheint nicht gĂŒltig zu sein" +#: assets/components/RegisterForm.tsx:56 assets/pages/LoginPage.tsx:25 +msgid "Register" +msgstr "Registrieren" #: assets/components/search/DomainLifecycleSteps.tsx:15 #: assets/utils/functions/rdapTranslation.ts:43 @@ -77,6 +89,93 @@ msgstr "Einlösezeitraum" msgid "Pending Delete" msgstr "Ausstehende Löschung" +#: assets/components/search/DomainResult.tsx:45 +msgid "EPP Status Codes" +msgstr "EPP-Statuscodes" + +#: assets/components/search/DomainResult.tsx:61 +msgid "Timeline" +msgstr "Zeitleiste" + +#: assets/components/search/DomainResult.tsx:68 +msgid "Entities" +msgstr "EntitĂ€ten" + +#: assets/components/search/DomainSearchBar.tsx:23 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:118 +msgid "This domain name does not appear to be valid" +msgstr "Dieser Domainname scheint nicht gĂŒltig zu sein" + +#: assets/components/Sider.tsx:28 +msgid "Home" +msgstr "Startseite" + +#: assets/components/Sider.tsx:34 +msgid "Search" +msgstr "Suchen" + +#: assets/components/Sider.tsx:40 +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:46 +msgid "Domain" +msgstr "Domain" + +#: assets/components/Sider.tsx:41 +msgid "Domain Finder" +msgstr "DomĂ€nenfinder" + +#: assets/components/Sider.tsx:48 assets/pages/search/TldPage.tsx:65 +msgid "TLD" +msgstr "TLD" + +#: assets/components/Sider.tsx:49 +msgid "TLD list" +msgstr "TLD-Liste" + +#: assets/components/Sider.tsx:56 +msgid "Entity" +msgstr "EntitĂ€t" + +#: assets/components/Sider.tsx:57 +msgid "Entity Finder" +msgstr "EntitĂ€tsfinder" + +#: assets/components/Sider.tsx:64 +msgid "Nameserver" +msgstr "Nameserver" + +#: assets/components/Sider.tsx:65 +msgid "Nameserver Finder" +msgstr "Nameserver-Finder" + +#: assets/components/Sider.tsx:73 +msgid "Tracking" +msgstr "Nachverfolgung" + +#: assets/components/Sider.tsx:79 +msgid "My Watchlists" +msgstr "Meine Watchlists" + +#: assets/components/Sider.tsx:86 +msgid "My Connectors" +msgstr "Meine Konnektoren" + +#: assets/components/Sider.tsx:95 +msgid "Statistics" +msgstr "Statistiken" + +#: assets/components/Sider.tsx:105 assets/pages/UserPage.tsx:16 +msgid "My Account" +msgstr "Mein Konto" + +#: assets/components/Sider.tsx:110 +msgid "Log out" +msgstr "Abmelden" + +#: assets/components/Sider.tsx:118 assets/pages/LoginPage.tsx:25 +#: assets/pages/LoginPage.tsx:33 +msgid "Log in" +msgstr "Anmelden" + #: assets/components/tracking/connector/ConnectorForm.tsx:40 msgid "Provider" msgstr "Anbieter" @@ -196,6 +295,82 @@ msgstr "Ja" msgid "No" msgstr "Nein" +#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:14 +msgid "QR Code for iCalendar export" +msgstr "QR-Code fĂŒr iCalendar-Export" + +#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:17 +msgid "Export events to iCalendar format" +msgstr "Ereignisse in das iCalendar-Format exportieren" + +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:12 +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:19 +msgid "Delete the Watchlist" +msgstr "Löschen der Watchlist" + +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:13 +msgid "Are you sure to delete this Watchlist?" +msgstr "Möchten Sie diese Watchlist wirklich löschen?" + +#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:39 +msgid "View the Watchlist Entity Diagram" +msgstr "Sehen Sie sich das Watchlist-EntitĂ€tsdiagramm an" + +#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:44 +msgid "Watchlist Entity Diagram" +msgstr "Watchlist-EntitĂ€ten Diagramm" + +#: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:37 +msgid "Registry" +msgstr "Registrierung" + +#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:30 +#, javascript-format +msgid ".${ tld.tld } Registry" +msgstr ".${ tld.tld } Registrierung" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:50 +msgid "Expiration date" +msgstr "Verfallsdatum" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:54 +msgid "Status" +msgstr "Status" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:58 +msgid "Updated at" +msgstr "Aktualisiert am" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:31 +msgid "Edit the Watchlist" +msgstr "Bearbeiten der Watchlist" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:44 +msgid "Update a Watchlist" +msgstr "Aktualisieren einer Watchlist" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:54 +msgid "Cancel" +msgstr "Abbrechen" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:29 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:106 +msgid "Domain names" +msgstr "DomĂ€nennamen" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:33 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:148 +msgid "Tracked events" +msgstr "Verfolgte Ereignisse" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:47 +msgid "This Watchlist is not linked to a Connector." +msgstr "Diese Beobachtungsliste ist nicht mit einem Connector verknĂŒpft." + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:52 +msgid "Watchlist" +msgstr "Watchlist" + #: assets/components/tracking/watchlist/WatchlistForm.tsx:72 msgid "Name" msgstr "Name" @@ -214,11 +389,6 @@ msgstr "" msgid "At least one domain name" msgstr "Mindestens ein DomĂ€nenname" -#: assets/components/tracking/watchlist/WatchlistCard.tsx:29 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:106 -msgid "Domain names" -msgstr "DomĂ€nennamen" - #: assets/components/tracking/watchlist/WatchlistForm.tsx:124 msgid "Domain name" msgstr "DomĂ€nenname" @@ -227,11 +397,6 @@ msgstr "DomĂ€nenname" msgid "Add a Domain name" msgstr "Einen DomĂ€nennamen hinzufĂŒgen" -#: assets/components/tracking/watchlist/WatchlistCard.tsx:33 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:148 -msgid "Tracked events" -msgstr "Verfolgte Ereignisse" - #: assets/components/tracking/watchlist/WatchlistForm.tsx:150 msgid "At least one trigger" msgstr "Mindestens ein Auslöser" @@ -272,145 +437,13 @@ msgstr "Einen Webhook hinzufĂŒgen" msgid "Update" msgstr "Aktualisieren" -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:31 -msgid "Edit the Watchlist" -msgstr "Bearbeiten der Watchlist" - -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:44 -msgid "Update a Watchlist" -msgstr "Aktualisieren einer Watchlist" - -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:54 -msgid "Cancel" -msgstr "Abbrechen" - -#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:39 -msgid "View the Watchlist Entity Diagram" -msgstr "Sehen Sie sich das Watchlist-EntitĂ€tsdiagramm an" - -#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:44 -msgid "Watchlist Entity Diagram" -msgstr "Watchlist-EntitĂ€ten Diagramm" - -#: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:37 -msgid "Registry" -msgstr "Registrierung" - -#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:30 -#, javascript-format -msgid ".${ tld.tld } Registry" -msgstr ".${ tld.tld } Registrierung" - -#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:14 -msgid "QR Code for iCalendar export" -msgstr "QR-Code fĂŒr iCalendar-Export" - -#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:17 -msgid "Export events to iCalendar format" -msgstr "Ereignisse in das iCalendar-Format exportieren" - -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:12 -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:19 -msgid "Delete the Watchlist" -msgstr "Löschen der Watchlist" - -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:13 -msgid "Are you sure to delete this Watchlist?" -msgstr "Möchten Sie diese Watchlist wirklich löschen?" - -#: assets/components/Sider.tsx:40 -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:46 -msgid "Domain" -msgstr "Domain" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:50 -msgid "Expiration date" -msgstr "Verfallsdatum" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:54 -msgid "Status" -msgstr "Status" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:58 -msgid "Updated at" -msgstr "Aktualisiert am" - -#: assets/components/tracking/watchlist/WatchlistCard.tsx:47 -msgid "This Watchlist is not linked to a Connector." -msgstr "Diese Beobachtungsliste ist nicht mit einem Connector verknĂŒpft." - -#: assets/components/tracking/watchlist/WatchlistCard.tsx:52 -msgid "Watchlist" -msgstr "Watchlist" - -#: assets/components/Sider.tsx:28 -msgid "Home" -msgstr "Startseite" - -#: assets/components/Sider.tsx:34 -msgid "Search" -msgstr "Suchen" - -#: assets/components/Sider.tsx:41 -msgid "Domain Finder" -msgstr "DomĂ€nenfinder" - -#: assets/components/Sider.tsx:48 assets/pages/search/TldPage.tsx:65 -msgid "TLD" -msgstr "TLD" - -#: assets/components/Sider.tsx:49 -msgid "TLD list" -msgstr "TLD-Liste" - -#: assets/components/Sider.tsx:56 -msgid "Entity" -msgstr "EntitĂ€t" - -#: assets/components/Sider.tsx:57 -msgid "Entity Finder" -msgstr "EntitĂ€tsfinder" - -#: assets/components/Sider.tsx:64 -msgid "Nameserver" -msgstr "Nameserver" - -#: assets/components/Sider.tsx:65 -msgid "Nameserver Finder" -msgstr "Nameserver-Finder" - -#: assets/components/Sider.tsx:73 -msgid "Tracking" -msgstr "Nachverfolgung" - -#: assets/components/Sider.tsx:79 -msgid "My Watchlists" -msgstr "Meine Watchlists" - -#: assets/components/Sider.tsx:86 -msgid "My Connectors" -msgstr "Meine Konnektoren" - -#: assets/components/Sider.tsx:95 -msgid "Statistics" -msgstr "Statistiken" - -#: assets/components/Sider.tsx:105 assets/pages/UserPage.tsx:16 -msgid "My Account" -msgstr "Mein Konto" - -#: assets/components/Sider.tsx:110 -msgid "Log out" -msgstr "Abmelden" - -#: assets/components/Sider.tsx:118 assets/pages/LoginPage.tsx:25 #: assets/pages/LoginPage.tsx:33 -msgid "Log in" -msgstr "Anmelden" +msgid "Create an account" +msgstr "Ein Konto erstellen" -#: assets/components/RegisterForm.tsx:56 assets/pages/LoginPage.tsx:25 -msgid "Register" -msgstr "Registrieren" +#: assets/pages/NotFoundPage.tsx:10 +msgid "Sorry, the page you visited does not exist." +msgstr "Es tut uns leid, die von Ihnen besuchte Seite existiert nicht." #: assets/pages/search/DomainSearchPage.tsx:18 msgid "Found !" @@ -513,6 +546,44 @@ msgstr "" "Top-Level-Domains basieren auf LĂ€ndercodes und identifizieren Websites " "entsprechend ihrem Herkunftsland." +#: assets/pages/StatisticsPage.tsx:34 +msgid "RDAP queries" +msgstr "RDAP-Abfragen" + +#: assets/pages/StatisticsPage.tsx:43 +msgid "Alerts sent" +msgstr "Gesendete Warnungen" + +#: assets/pages/StatisticsPage.tsx:57 +msgid "Domain names in database" +msgstr "DomĂ€nennamen in der Datenbank" + +#: assets/pages/StatisticsPage.tsx:68 +#: assets/pages/tracking/WatchlistPage.tsx:111 +msgid "Tracked domain names" +msgstr "Verfolgte DomĂ€nennamen" + +#: assets/pages/StatisticsPage.tsx:82 +msgid "Purchased domain names" +msgstr "Gekaufte DomĂ€nennamen" + +#: assets/pages/StatisticsPage.tsx:92 +msgid "" +"This value is based on the status code of the HTTP response from the " +"providers following the domain order." +msgstr "" +"Dieser Wert basiert auf dem Statuscode der HTTP-Antwort der Anbieter nach " +"dem Domainkauf." + +#: assets/pages/StatisticsPage.tsx:95 +msgid "Success rate" +msgstr "Erfolgsrate" + +#: assets/pages/TextPage.tsx:26 +#, javascript-format +msgid "📝 Please create the /public/content/${ resource } file." +msgstr "📝 Bitte erstellen Sie die Datei /public/content/${ resource }." + #: assets/pages/tracking/ConnectorsPage.tsx:20 msgid "Connector created !" msgstr "Connector erstellt!" @@ -533,47 +604,6 @@ msgstr "Watchlist aktualisiert !" msgid "Create a Watchlist" msgstr "Erstellen Sie eine Watchlist" -#: assets/pages/StatisticsPage.tsx:68 -#: assets/pages/tracking/WatchlistPage.tsx:111 -msgid "Tracked domain names" -msgstr "Verfolgte DomĂ€nennamen" - -#: assets/pages/NotFoundPage.tsx:10 -msgid "Sorry, the page you visited does not exist." -msgstr "Es tut uns leid, die von Ihnen besuchte Seite existiert nicht." - -#: assets/pages/StatisticsPage.tsx:34 -msgid "RDAP queries" -msgstr "RDAP-Abfragen" - -#: assets/pages/StatisticsPage.tsx:43 -msgid "Alerts sent" -msgstr "Gesendete Warnungen" - -#: assets/pages/StatisticsPage.tsx:57 -msgid "Domain names in database" -msgstr "DomĂ€nennamen in der Datenbank" - -#: assets/pages/StatisticsPage.tsx:82 -msgid "Purchased domain names" -msgstr "Gekaufte DomĂ€nennamen" - -#: assets/pages/StatisticsPage.tsx:92 -msgid "" -"This value is based on the status code of the HTTP response from the " -"providers following the domain order." -msgstr "" -"Dieser Wert basiert auf dem Statuscode der HTTP-Antwort der Anbieter nach " -"dem Domainkauf." - -#: assets/pages/StatisticsPage.tsx:95 -msgid "Success rate" -msgstr "Erfolgsrate" - -#: assets/pages/LoginPage.tsx:33 -msgid "Create an account" -msgstr "Ein Konto erstellen" - #: assets/pages/UserPage.tsx:18 msgid "Username" msgstr "Benutzername" @@ -582,11 +612,6 @@ msgstr "Benutzername" msgid "Roles" msgstr "Rollen" -#: assets/pages/TextPage.tsx:26 -#, javascript-format -msgid "📝 Please create the /public/content/${ resource } file." -msgstr "📝 Bitte erstellen Sie die Datei /public/content/${ resource }." - #: assets/utils/functions/rdapTranslation.ts:7 msgid "Registrant" msgstr "Anmelder" @@ -1229,31 +1254,6 @@ msgstr "" "Die Domain ist kostenlos, kann aber Premium sein. Der Preis variiert von " "einer Domain zur anderen" -#: assets/App.tsx:101 -msgid "TOS" -msgstr "Nutzungsbedingungen" - -#: assets/App.tsx:102 -msgid "Privacy Policy" -msgstr "Datenschutzrichtlinie" - -#: assets/App.tsx:103 -msgid "FAQ" -msgstr "HĂ€ufig gestellte Fragen" - -#: assets/App.tsx:105 -msgid "Documentation" -msgstr "Dokumentation" - -#: assets/App.tsx:108 -#, javascript-format -msgid "" -"${ ProjectLink } is an open source project distributed under the " -"${ LicenseLink } license." -msgstr "" -"${ ProjectLink } ist ein Open-Source-Projekt, das unter der ${ LicenseLink }-" -"Lizenz vertrieben wird." - #, fuzzy #~ msgid "Tracked Domains" #~ msgstr "Verfolgte DomĂ€nennamen" diff --git a/translations/fr.po b/translations/fr.po index 7bda790..bb7dcde 100644 --- a/translations/fr.po +++ b/translations/fr.po @@ -11,6 +11,31 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.7.2\n" +#: assets/App.tsx:101 +msgid "TOS" +msgstr "CGU" + +#: assets/App.tsx:102 +msgid "Privacy Policy" +msgstr "Politique de confidentialitĂ©" + +#: assets/App.tsx:103 +msgid "FAQ" +msgstr "FAQ" + +#: assets/App.tsx:105 +msgid "Documentation" +msgstr "Documentation" + +#: assets/App.tsx:108 +#, javascript-format +msgid "" +"${ ProjectLink } is an open source project distributed under the " +"${ LicenseLink } license." +msgstr "" +"${ ProjectLink } est un projet open source distribuĂ© sous licence " +"${ LicenseLink }." + #: assets/components/LoginForm.tsx:52 assets/components/RegisterForm.tsx:39 msgid "Email address" msgstr "Adresse e-mail" @@ -44,22 +69,9 @@ msgstr "Se connecter" msgid "Log in with SSO" msgstr "Se connecter par SSO" -#: assets/components/search/DomainResult.tsx:45 -msgid "EPP Status Codes" -msgstr "Codes de statut EPP" - -#: assets/components/search/DomainResult.tsx:61 -msgid "Timeline" -msgstr "Chronologie" - -#: assets/components/search/DomainResult.tsx:68 -msgid "Entities" -msgstr "EntitĂ©s" - -#: assets/components/search/DomainSearchBar.tsx:23 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:118 -msgid "This domain name does not appear to be valid" -msgstr "Ce nom de domaine ne semble pas ĂȘtre valide" +#: assets/components/RegisterForm.tsx:56 assets/pages/LoginPage.tsx:25 +msgid "Register" +msgstr "S'enregistrer" #: assets/components/search/DomainLifecycleSteps.tsx:15 #: assets/utils/functions/rdapTranslation.ts:43 @@ -78,6 +90,93 @@ msgstr "PĂ©riode de rĂ©demption" msgid "Pending Delete" msgstr "En attente de suppression" +#: assets/components/search/DomainResult.tsx:45 +msgid "EPP Status Codes" +msgstr "Codes de statut EPP" + +#: assets/components/search/DomainResult.tsx:61 +msgid "Timeline" +msgstr "Chronologie" + +#: assets/components/search/DomainResult.tsx:68 +msgid "Entities" +msgstr "EntitĂ©s" + +#: assets/components/search/DomainSearchBar.tsx:23 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:118 +msgid "This domain name does not appear to be valid" +msgstr "Ce nom de domaine ne semble pas ĂȘtre valide" + +#: assets/components/Sider.tsx:28 +msgid "Home" +msgstr "Accueil" + +#: assets/components/Sider.tsx:34 +msgid "Search" +msgstr "Rechercher" + +#: assets/components/Sider.tsx:40 +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:46 +msgid "Domain" +msgstr "Domaine" + +#: assets/components/Sider.tsx:41 +msgid "Domain Finder" +msgstr "Rechercher un domaine" + +#: assets/components/Sider.tsx:48 assets/pages/search/TldPage.tsx:65 +msgid "TLD" +msgstr "TLD" + +#: assets/components/Sider.tsx:49 +msgid "TLD list" +msgstr "Liste des TLD" + +#: assets/components/Sider.tsx:56 +msgid "Entity" +msgstr "EntitĂ©" + +#: assets/components/Sider.tsx:57 +msgid "Entity Finder" +msgstr "Rechercher une entitĂ©" + +#: assets/components/Sider.tsx:64 +msgid "Nameserver" +msgstr "Serveur DNS" + +#: assets/components/Sider.tsx:65 +msgid "Nameserver Finder" +msgstr "Rechercher un serveur DNS" + +#: assets/components/Sider.tsx:73 +msgid "Tracking" +msgstr "Suivi" + +#: assets/components/Sider.tsx:79 +msgid "My Watchlists" +msgstr "Mes Watchlists" + +#: assets/components/Sider.tsx:86 +msgid "My Connectors" +msgstr "Mes Connecteurs" + +#: assets/components/Sider.tsx:95 +msgid "Statistics" +msgstr "Statistiques" + +#: assets/components/Sider.tsx:105 assets/pages/UserPage.tsx:16 +msgid "My Account" +msgstr "Mon compte" + +#: assets/components/Sider.tsx:110 +msgid "Log out" +msgstr "Se dĂ©connecter" + +#: assets/components/Sider.tsx:118 assets/pages/LoginPage.tsx:25 +#: assets/pages/LoginPage.tsx:33 +msgid "Log in" +msgstr "Se connecter" + #: assets/components/tracking/connector/ConnectorForm.tsx:40 msgid "Provider" msgstr "Fournisseur" @@ -196,6 +295,82 @@ msgstr "Oui" msgid "No" msgstr "Non" +#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:14 +msgid "QR Code for iCalendar export" +msgstr "QR Code pour l'exportation iCalendar" + +#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:17 +msgid "Export events to iCalendar format" +msgstr "Exporter les Ă©vĂ©nements au format iCalendar" + +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:12 +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:19 +msgid "Delete the Watchlist" +msgstr "Supprimer la Watchlist" + +#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:13 +msgid "Are you sure to delete this Watchlist?" +msgstr "Êtes-vous sĂ»r de vouloir supprimer cette Watchlist ?" + +#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:39 +msgid "View the Watchlist Entity Diagram" +msgstr "Afficher le diagramme d'entitĂ©s de la Watchlist" + +#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:44 +msgid "Watchlist Entity Diagram" +msgstr "Diagramme d'entitĂ©s de la Watchlist" + +#: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:37 +msgid "Registry" +msgstr "Registre" + +#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:30 +#, javascript-format +msgid ".${ tld.tld } Registry" +msgstr "Registre .${ tld.tld }" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:50 +msgid "Expiration date" +msgstr "Date d'expiration" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:54 +msgid "Status" +msgstr "Statuts" + +#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:58 +msgid "Updated at" +msgstr "Mis Ă  jour le" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:31 +msgid "Edit the Watchlist" +msgstr "Modifier la Watchlist" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:44 +msgid "Update a Watchlist" +msgstr "Mise Ă  jour d'une Watchlist" + +#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:54 +msgid "Cancel" +msgstr "Annuler" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:29 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:106 +msgid "Domain names" +msgstr "Noms de domaines" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:33 +#: assets/components/tracking/watchlist/WatchlistForm.tsx:148 +msgid "Tracked events" +msgstr "ÉvĂ©nements suivis" + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:47 +msgid "This Watchlist is not linked to a Connector." +msgstr "Cette Watchlist n'est pas liĂ©e Ă  un connecteur." + +#: assets/components/tracking/watchlist/WatchlistCard.tsx:52 +msgid "Watchlist" +msgstr "Watchlist" + #: assets/components/tracking/watchlist/WatchlistForm.tsx:72 msgid "Name" msgstr "Nom" @@ -214,11 +389,6 @@ msgstr "" msgid "At least one domain name" msgstr "Au moins un nom de domaine" -#: assets/components/tracking/watchlist/WatchlistCard.tsx:29 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:106 -msgid "Domain names" -msgstr "Noms de domaines" - #: assets/components/tracking/watchlist/WatchlistForm.tsx:124 msgid "Domain name" msgstr "Nom de domaine" @@ -227,11 +397,6 @@ msgstr "Nom de domaine" msgid "Add a Domain name" msgstr "Ajouter un nom de domaine" -#: assets/components/tracking/watchlist/WatchlistCard.tsx:33 -#: assets/components/tracking/watchlist/WatchlistForm.tsx:148 -msgid "Tracked events" -msgstr "ÉvĂ©nements suivis" - #: assets/components/tracking/watchlist/WatchlistForm.tsx:150 msgid "At least one trigger" msgstr "Au moins un dĂ©clencheur" @@ -272,145 +437,13 @@ msgstr "Ajouter un Webhook" msgid "Update" msgstr "Mettre Ă  jour" -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:31 -msgid "Edit the Watchlist" -msgstr "Modifier la Watchlist" - -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:44 -msgid "Update a Watchlist" -msgstr "Mise Ă  jour d'une Watchlist" - -#: assets/components/tracking/watchlist/UpdateWatchlistButton.tsx:54 -msgid "Cancel" -msgstr "Annuler" - -#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:39 -msgid "View the Watchlist Entity Diagram" -msgstr "Afficher le diagramme d'entitĂ©s de la Watchlist" - -#: assets/components/tracking/watchlist/diagram/ViewDiagramWatchlistButton.tsx:44 -msgid "Watchlist Entity Diagram" -msgstr "Diagramme d'entitĂ©s de la Watchlist" - -#: assets/components/tracking/watchlist/diagram/watchlistToEdges.tsx:37 -msgid "Registry" -msgstr "Registre" - -#: assets/components/tracking/watchlist/diagram/watchlistToNodes.tsx:30 -#, javascript-format -msgid ".${ tld.tld } Registry" -msgstr "Registre .${ tld.tld }" - -#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:14 -msgid "QR Code for iCalendar export" -msgstr "QR Code pour l'exportation iCalendar" - -#: assets/components/tracking/watchlist/CalendarWatchlistButton.tsx:17 -msgid "Export events to iCalendar format" -msgstr "Exporter les Ă©vĂ©nements au format iCalendar" - -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:12 -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:19 -msgid "Delete the Watchlist" -msgstr "Supprimer la Watchlist" - -#: assets/components/tracking/watchlist/DeleteWatchlistButton.tsx:13 -msgid "Are you sure to delete this Watchlist?" -msgstr "Êtes-vous sĂ»r de vouloir supprimer cette Watchlist ?" - -#: assets/components/Sider.tsx:40 -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:46 -msgid "Domain" -msgstr "Domaine" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:50 -msgid "Expiration date" -msgstr "Date d'expiration" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:54 -msgid "Status" -msgstr "Statuts" - -#: assets/components/tracking/watchlist/TrackedDomainTable.tsx:58 -msgid "Updated at" -msgstr "Mis Ă  jour le" - -#: assets/components/tracking/watchlist/WatchlistCard.tsx:47 -msgid "This Watchlist is not linked to a Connector." -msgstr "Cette Watchlist n'est pas liĂ©e Ă  un connecteur." - -#: assets/components/tracking/watchlist/WatchlistCard.tsx:52 -msgid "Watchlist" -msgstr "Watchlist" - -#: assets/components/Sider.tsx:28 -msgid "Home" -msgstr "Accueil" - -#: assets/components/Sider.tsx:34 -msgid "Search" -msgstr "Rechercher" - -#: assets/components/Sider.tsx:41 -msgid "Domain Finder" -msgstr "Rechercher un domaine" - -#: assets/components/Sider.tsx:48 assets/pages/search/TldPage.tsx:65 -msgid "TLD" -msgstr "TLD" - -#: assets/components/Sider.tsx:49 -msgid "TLD list" -msgstr "Liste des TLD" - -#: assets/components/Sider.tsx:56 -msgid "Entity" -msgstr "EntitĂ©" - -#: assets/components/Sider.tsx:57 -msgid "Entity Finder" -msgstr "Rechercher une entitĂ©" - -#: assets/components/Sider.tsx:64 -msgid "Nameserver" -msgstr "Serveur DNS" - -#: assets/components/Sider.tsx:65 -msgid "Nameserver Finder" -msgstr "Rechercher un serveur DNS" - -#: assets/components/Sider.tsx:73 -msgid "Tracking" -msgstr "Suivi" - -#: assets/components/Sider.tsx:79 -msgid "My Watchlists" -msgstr "Mes Watchlists" - -#: assets/components/Sider.tsx:86 -msgid "My Connectors" -msgstr "Mes Connecteurs" - -#: assets/components/Sider.tsx:95 -msgid "Statistics" -msgstr "Statistiques" - -#: assets/components/Sider.tsx:105 assets/pages/UserPage.tsx:16 -msgid "My Account" -msgstr "Mon compte" - -#: assets/components/Sider.tsx:110 -msgid "Log out" -msgstr "Se dĂ©connecter" - -#: assets/components/Sider.tsx:118 assets/pages/LoginPage.tsx:25 #: assets/pages/LoginPage.tsx:33 -msgid "Log in" -msgstr "Se connecter" +msgid "Create an account" +msgstr "CrĂ©er un compte" -#: assets/components/RegisterForm.tsx:56 assets/pages/LoginPage.tsx:25 -msgid "Register" -msgstr "S'enregistrer" +#: assets/pages/NotFoundPage.tsx:10 +msgid "Sorry, the page you visited does not exist." +msgstr "DĂ©solĂ©, cette page n'existe pas." #: assets/pages/search/DomainSearchPage.tsx:18 msgid "Found !" @@ -512,6 +545,44 @@ msgstr "" "Domaines de premier niveau basĂ©s sur les codes de pays, identifiant les " "sites web en fonction de leur pays d'origine." +#: assets/pages/StatisticsPage.tsx:34 +msgid "RDAP queries" +msgstr "RequĂȘtes RDAP" + +#: assets/pages/StatisticsPage.tsx:43 +msgid "Alerts sent" +msgstr "Alertes envoyĂ©es" + +#: assets/pages/StatisticsPage.tsx:57 +msgid "Domain names in database" +msgstr "Noms de domaine dans la base de donnĂ©es" + +#: assets/pages/StatisticsPage.tsx:68 +#: assets/pages/tracking/WatchlistPage.tsx:111 +msgid "Tracked domain names" +msgstr "Noms de domaine suivis" + +#: assets/pages/StatisticsPage.tsx:82 +msgid "Purchased domain names" +msgstr "Noms de domaine achetĂ©s" + +#: assets/pages/StatisticsPage.tsx:92 +msgid "" +"This value is based on the status code of the HTTP response from the " +"providers following the domain order." +msgstr "" +"Cette valeur est basĂ©e sur le code d'Ă©tat de la rĂ©ponse HTTP des Providers " +"suivant l'achat du domaine." + +#: assets/pages/StatisticsPage.tsx:95 +msgid "Success rate" +msgstr "Taux de succĂšs" + +#: assets/pages/TextPage.tsx:26 +#, javascript-format +msgid "📝 Please create the /public/content/${ resource } file." +msgstr "📝 Veuillez crĂ©er le fichier /public/content/${ resource }." + #: assets/pages/tracking/ConnectorsPage.tsx:20 msgid "Connector created !" msgstr "Connecteur créé !" @@ -532,47 +603,6 @@ msgstr "Watchlist mise Ă  jour !" msgid "Create a Watchlist" msgstr "CrĂ©er une Watchlist" -#: assets/pages/StatisticsPage.tsx:68 -#: assets/pages/tracking/WatchlistPage.tsx:111 -msgid "Tracked domain names" -msgstr "Noms de domaine suivis" - -#: assets/pages/NotFoundPage.tsx:10 -msgid "Sorry, the page you visited does not exist." -msgstr "DĂ©solĂ©, cette page n'existe pas." - -#: assets/pages/StatisticsPage.tsx:34 -msgid "RDAP queries" -msgstr "RequĂȘtes RDAP" - -#: assets/pages/StatisticsPage.tsx:43 -msgid "Alerts sent" -msgstr "Alertes envoyĂ©es" - -#: assets/pages/StatisticsPage.tsx:57 -msgid "Domain names in database" -msgstr "Noms de domaine dans la base de donnĂ©es" - -#: assets/pages/StatisticsPage.tsx:82 -msgid "Purchased domain names" -msgstr "Noms de domaine achetĂ©s" - -#: assets/pages/StatisticsPage.tsx:92 -msgid "" -"This value is based on the status code of the HTTP response from the " -"providers following the domain order." -msgstr "" -"Cette valeur est basĂ©e sur le code d'Ă©tat de la rĂ©ponse HTTP des Providers " -"suivant l'achat du domaine." - -#: assets/pages/StatisticsPage.tsx:95 -msgid "Success rate" -msgstr "Taux de succĂšs" - -#: assets/pages/LoginPage.tsx:33 -msgid "Create an account" -msgstr "CrĂ©er un compte" - #: assets/pages/UserPage.tsx:18 msgid "Username" msgstr "Nom d'utilisateur" @@ -581,11 +611,6 @@ msgstr "Nom d'utilisateur" msgid "Roles" msgstr "RĂŽles" -#: assets/pages/TextPage.tsx:26 -#, javascript-format -msgid "📝 Please create the /public/content/${ resource } file." -msgstr "📝 Veuillez crĂ©er le fichier /public/content/${ resource }." - #: assets/utils/functions/rdapTranslation.ts:7 msgid "Registrant" msgstr "Registrant" @@ -1228,31 +1253,6 @@ msgstr "" "Le domaine est libre mais il peut ĂȘtre premium. Son prix est variable d'un " "domaine Ă  l'autre" -#: assets/App.tsx:101 -msgid "TOS" -msgstr "CGU" - -#: assets/App.tsx:102 -msgid "Privacy Policy" -msgstr "Politique de confidentialitĂ©" - -#: assets/App.tsx:103 -msgid "FAQ" -msgstr "FAQ" - -#: assets/App.tsx:105 -msgid "Documentation" -msgstr "Documentation" - -#: assets/App.tsx:108 -#, javascript-format -msgid "" -"${ ProjectLink } is an open source project distributed under the " -"${ LicenseLink } license." -msgstr "" -"${ ProjectLink } est un projet open source distribuĂ© sous licence " -"${ LicenseLink }." - #, fuzzy #~ msgid "Tracked Domains" #~ msgstr "Noms de domaine suivis" From f0e708272e27ac080890acb73c133ba2dbebc722 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 25 Sep 2024 14:02:40 +0200 Subject: [PATCH 15/24] feat: get supported tlds by namecheap --- src/Controller/WatchListController.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Controller/WatchListController.php b/src/Controller/WatchListController.php index 060a22a..021bf01 100644 --- a/src/Controller/WatchListController.php +++ b/src/Controller/WatchListController.php @@ -34,6 +34,8 @@ use Sabre\VObject\InvalidDataException; use Sabre\VObject\ParseException; use Sabre\VObject\Reader; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -53,7 +55,9 @@ class WatchListController extends AbstractController private readonly HttpClientInterface $httpClient, private readonly CacheItemPoolInterface $cacheItemPool, private readonly KernelInterface $kernel, - private readonly ChatNotificationService $chatNotificationService + private readonly ChatNotificationService $chatNotificationService, + #[Autowire(service: 'service_container')] + private ContainerInterface $locator ) { } @@ -182,9 +186,10 @@ class WatchListController extends AbstractController $connectorProviderClass = $connector->getProvider()->getConnectorProvider(); /** @var AbstractProvider $connectorProvider */ - $connectorProvider = new $connectorProviderClass($connector->getAuthData(), $this->httpClient, $this->cacheItemPool, $this->kernel); + $connectorProvider = $this->locator->get($connectorProviderClass); $connectorProvider::verifyAuthData($connector->getAuthData(), $this->httpClient); // We want to check if the tokens are OK + $connectorProvider->authenticate($connector->getAuthData()); $supported = $connectorProvider->isSupported(...$watchList->getDomains()->toArray()); if (!$supported) { From f70ddb6b05b8f9620763ff4bc769ce53a018395f Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 25 Sep 2024 14:03:16 +0200 Subject: [PATCH 16/24] feat: get supported tlds by namecheap --- src/Service/Connector/NamecheapConnector.php | 22 ++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index 2c5b55e..80ce488 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -4,6 +4,7 @@ namespace App\Service\Connector; use App\Entity\Domain; use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\DependencyInjection\Attribute\Autoconfigure; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -14,8 +15,9 @@ class NamecheapConnector extends AbstractProvider public const SANDBOX_BASE_URL = 'http://api.sandbox.namecheap.com/xml.response'; - public function __construct(private HttpClientInterface $client, private readonly string $outgoingIp) + public function __construct(CacheItemPoolInterface $cacheItemPool, private HttpClientInterface $client, private readonly string $outgoingIp) { + parent::__construct($cacheItemPool); } public function orderDomain(Domain $domain, $dryRun): void @@ -27,8 +29,8 @@ class NamecheapConnector extends AbstractProvider throw new \Exception('Namecheap account requires at least one address to purchase a domain'); } - $addressId = (string) $addresses->attributes()['AddressId']; - $address = (array) $this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId], $dryRun)->GetAddressInfoResult; + $addressId = (string)$addresses->attributes()['AddressId']; + $address = (array)$this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId], $dryRun)->GetAddressInfoResult; if (empty($address['PostalCode'])) { $address['PostalCode'] = $address['Zip']; @@ -52,7 +54,7 @@ class NamecheapConnector extends AbstractProvider private static function mergePrefixKeys(string $prefix, array|object $src, array &$dest) { foreach ($src as $key => $value) { - $dest[$prefix.$key] = $value; + $dest[$prefix . $key] = $value; } } @@ -86,11 +88,19 @@ class NamecheapConnector extends AbstractProvider protected function getCachedTldList(): CacheItemInterface { - // TODO: Implement getCachedTldList() method. + return $this->cacheItemPool->getItem('app.provider.namecheap.supported-tld'); } protected function getSupportedTldList(): array { - // TODO: Implement getSupportedTldList() method. + $supported = []; + + $tlds = $this->call('namecheap.domains.gettldlist', [], false)->Tlds->Tld; + + for ($i = 0; $i < $tlds->count(); ++$i) { + $supported[] = (string) $tlds[$i]['Name']; + } + + return $supported; } } From cf05d35ded63222b6c3e332c2e41efe72225af28 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 26 Sep 2024 13:18:46 +0200 Subject: [PATCH 17/24] style: nc connector --- src/Service/Connector/NamecheapConnector.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index 80ce488..3b3f082 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -29,8 +29,8 @@ class NamecheapConnector extends AbstractProvider throw new \Exception('Namecheap account requires at least one address to purchase a domain'); } - $addressId = (string)$addresses->attributes()['AddressId']; - $address = (array)$this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId], $dryRun)->GetAddressInfoResult; + $addressId = (string) $addresses->attributes()['AddressId']; + $address = (array) $this->call('namecheap.users.address.getinfo', ['AddressId' => $addressId], $dryRun)->GetAddressInfoResult; if (empty($address['PostalCode'])) { $address['PostalCode'] = $address['Zip']; @@ -54,7 +54,7 @@ class NamecheapConnector extends AbstractProvider private static function mergePrefixKeys(string $prefix, array|object $src, array &$dest) { foreach ($src as $key => $value) { - $dest[$prefix . $key] = $value; + $dest[$prefix.$key] = $value; } } From 38deb2b12b19b7e691d4a04e08f4ecd77c26e21b Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 26 Sep 2024 13:20:28 +0200 Subject: [PATCH 18/24] chore: unused injections --- src/Controller/WatchListController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Controller/WatchListController.php b/src/Controller/WatchListController.php index 021bf01..a2c1871 100644 --- a/src/Controller/WatchListController.php +++ b/src/Controller/WatchListController.php @@ -53,8 +53,6 @@ class WatchListController extends AbstractController private readonly WatchListRepository $watchListRepository, private readonly LoggerInterface $logger, private readonly HttpClientInterface $httpClient, - private readonly CacheItemPoolInterface $cacheItemPool, - private readonly KernelInterface $kernel, private readonly ChatNotificationService $chatNotificationService, #[Autowire(service: 'service_container')] private ContainerInterface $locator From ad170cdc5dcbf323b56284c3435101a314466d58 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 26 Sep 2024 13:22:47 +0200 Subject: [PATCH 19/24] style: unused imports --- src/Controller/WatchListController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Controller/WatchListController.php b/src/Controller/WatchListController.php index a2c1871..814083f 100644 --- a/src/Controller/WatchListController.php +++ b/src/Controller/WatchListController.php @@ -27,7 +27,6 @@ use Eluceo\iCal\Domain\ValueObject\Timestamp; use Eluceo\iCal\Presentation\Component\Property; use Eluceo\iCal\Presentation\Component\Property\Value\TextValue; use Eluceo\iCal\Presentation\Factory\CalendarFactory; -use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerInterface; use Sabre\VObject\EofException; use Sabre\VObject\InvalidDataException; @@ -40,7 +39,6 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; From b9f38e4e85618fdac39246655fa5a86b02517f62 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 26 Sep 2024 13:27:01 +0200 Subject: [PATCH 20/24] docs: namecheap supported --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90c4c07..d594f18 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ The table below lists the supported API connector providers: |:---------:|---------------------------------------------------------------|:---------:| | OVH | https://api.ovh.com | **Yes** | | GANDI | https://api.gandi.net/docs/domains/ | **Yes** | -| NAMECHEAP | https://www.namecheap.com/support/api/methods/domains/create/ | | +| NAMECHEAP | https://www.namecheap.com/support/api/methods/domains/create/ | **Yes** | If a domain has expired and a connector is linked to the Watchlist, then Domain Watchdog will try to order it via the connector provider's API. From a7b09833c6b69d160f9fce0e4a36ae5ebf04db87 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 26 Sep 2024 13:38:15 +0200 Subject: [PATCH 21/24] feat: clean authentication data --- src/Service/Connector/NamecheapConnector.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapConnector.php index 3b3f082..5631c37 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapConnector.php @@ -83,7 +83,16 @@ class NamecheapConnector extends AbstractProvider public static function verifyAuthData(array $authData, HttpClientInterface $client): array { - return $authData; + // TODO call gettldlist to introspect authentication + // need to make verifyAuthData local to do this properly... + + return [ + 'ApiUser' => $authData['ApiUser'], + 'ApiKey' => $authData['ApiKey'], + 'acceptConditions' => $authData['acceptConditions'], + 'ownerLegalAge' => $authData['ownerLegalAge'], + 'waiveRetractionPeriod' => $authData['waiveRetractionPeriod'], + ]; } protected function getCachedTldList(): CacheItemInterface From b162f2313e5d28263764178a3fb5efe697bbeff3 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 26 Sep 2024 13:39:35 +0200 Subject: [PATCH 22/24] fix: missing constructor supercall --- src/Service/Connector/GandiProvider.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Service/Connector/GandiProvider.php b/src/Service/Connector/GandiProvider.php index 1742c28..54913d6 100644 --- a/src/Service/Connector/GandiProvider.php +++ b/src/Service/Connector/GandiProvider.php @@ -4,6 +4,7 @@ namespace App\Service\Connector; use App\Entity\Domain; use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\HttpClient\HttpOptions; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -19,8 +20,9 @@ class GandiProvider extends AbstractProvider { private const BASE_URL = 'https://api.gandi.net'; - public function __construct(private HttpClientInterface $client) + public function __construct(CacheItemPoolInterface $cacheItemPool, private HttpClientInterface $client) { + parent::__construct($cacheItemPool); } /** From 81512bebb4bea929f12754188149f9816b8e73f5 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 30 Sep 2024 13:48:15 +0200 Subject: [PATCH 23/24] refactor: provider authentication flow --- src/Config/ConnectorProvider.php | 4 +- src/Controller/ConnectorController.php | 13 ++-- src/Controller/WatchListController.php | 3 - src/Service/Connector/AbstractProvider.php | 26 ++++++- src/Service/Connector/GandiProvider.php | 41 +++++----- ...eapConnector.php => NamecheapProvider.php} | 13 ++-- src/Service/Connector/OvhProvider.php | 76 +++++++++---------- 7 files changed, 98 insertions(+), 78 deletions(-) rename src/Service/Connector/{NamecheapConnector.php => NamecheapProvider.php} (91%) diff --git a/src/Config/ConnectorProvider.php b/src/Config/ConnectorProvider.php index 3454d16..42ea0b0 100644 --- a/src/Config/ConnectorProvider.php +++ b/src/Config/ConnectorProvider.php @@ -3,7 +3,7 @@ namespace App\Config; use App\Service\Connector\GandiProvider; -use App\Service\Connector\NamecheapConnector; +use App\Service\Connector\NamecheapProvider; use App\Service\Connector\OvhProvider; enum ConnectorProvider: string @@ -17,7 +17,7 @@ enum ConnectorProvider: string return match ($this) { ConnectorProvider::OVH => OvhProvider::class, ConnectorProvider::GANDI => GandiProvider::class, - ConnectorProvider::NAMECHEAP => NamecheapConnector::class, + ConnectorProvider::NAMECHEAP => NamecheapProvider::class, }; } } diff --git a/src/Controller/ConnectorController.php b/src/Controller/ConnectorController.php index a8b050e..996ab91 100644 --- a/src/Controller/ConnectorController.php +++ b/src/Controller/ConnectorController.php @@ -9,6 +9,8 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; @@ -20,7 +22,9 @@ class ConnectorController extends AbstractController public function __construct( private readonly SerializerInterface $serializer, private readonly EntityManagerInterface $em, - private readonly LoggerInterface $logger + private readonly LoggerInterface $logger, + #[Autowire(service: 'service_container')] + private ContainerInterface $locator ) { } @@ -71,10 +75,9 @@ class ConnectorController extends AbstractController throw new BadRequestHttpException('Provider not found'); } - /** @var AbstractProvider $connectorProviderClass */ - $connectorProviderClass = $provider->getConnectorProvider(); - - $authData = $connectorProviderClass::verifyAuthData($connector->getAuthData(), $client); + /** @var AbstractProvider $providerClient */ + $providerClient = $this->locator->get($provider->getConnectorProvider()); + $authData = $providerClient->verifyAuthData($connector->getAuthData()); $connector->setAuthData($authData); $this->logger->info('User {username} authentication data with the {provider} provider has been validated.', [ diff --git a/src/Controller/WatchListController.php b/src/Controller/WatchListController.php index 814083f..dcc30e1 100644 --- a/src/Controller/WatchListController.php +++ b/src/Controller/WatchListController.php @@ -41,7 +41,6 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Serializer\SerializerInterface; -use Symfony\Contracts\HttpClient\HttpClientInterface; class WatchListController extends AbstractController { @@ -50,7 +49,6 @@ class WatchListController extends AbstractController private readonly EntityManagerInterface $em, private readonly WatchListRepository $watchListRepository, private readonly LoggerInterface $logger, - private readonly HttpClientInterface $httpClient, private readonly ChatNotificationService $chatNotificationService, #[Autowire(service: 'service_container')] private ContainerInterface $locator @@ -184,7 +182,6 @@ class WatchListController extends AbstractController /** @var AbstractProvider $connectorProvider */ $connectorProvider = $this->locator->get($connectorProviderClass); - $connectorProvider::verifyAuthData($connector->getAuthData(), $this->httpClient); // We want to check if the tokens are OK $connectorProvider->authenticate($connector->getAuthData()); $supported = $connectorProvider->isSupported(...$watchList->getDomains()->toArray()); diff --git a/src/Service/Connector/AbstractProvider.php b/src/Service/Connector/AbstractProvider.php index d167a26..3e83994 100644 --- a/src/Service/Connector/AbstractProvider.php +++ b/src/Service/Connector/AbstractProvider.php @@ -5,8 +5,14 @@ namespace App\Service\Connector; use App\Entity\Domain; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; -use Symfony\Contracts\HttpClient\HttpClientInterface; +/** + * The typical flow of a provider will go as follows: + * + * MyProvider $provider; // gotten from DI + * $provider->authenticate($authData); + * $provider->orderDomain($domain, $dryRun); + */ abstract class AbstractProvider { protected array $authData; @@ -16,7 +22,17 @@ abstract class AbstractProvider ) { } - abstract public static function verifyAuthData(array $authData, HttpClientInterface $client): array; + /** + * @param array $authData raw authentication data as supplied by the user + * + * @return array a cleaned up version of the authentication data + */ + abstract public function verifyAuthData(array $authData): array; + + /** + * @throws \Exception when the registrar denies the authentication + */ + abstract public function assertAuthentication(): void; // TODO use dedicated exception type abstract public function orderDomain(Domain $domain, bool $dryRun): void; @@ -53,9 +69,13 @@ abstract class AbstractProvider return true; } + /** + * @throws \Exception + */ public function authenticate(array $authData): void { - $this->authData = $authData; + $this->authData = $this->verifyAuthData($authData); + $this->assertAuthentication(); } abstract protected function getCachedTldList(): CacheItemInterface; diff --git a/src/Service/Connector/GandiProvider.php b/src/Service/Connector/GandiProvider.php index 54913d6..da3d957 100644 --- a/src/Service/Connector/GandiProvider.php +++ b/src/Service/Connector/GandiProvider.php @@ -43,17 +43,15 @@ class GandiProvider extends AbstractProvider throw new \InvalidArgumentException('Domain name cannot be null'); } - $authData = self::verifyAuthData($this->authData, $this->client); - $user = $this->client->request('GET', '/v5/organization/user-info', (new HttpOptions()) - ->setAuthBearer($authData['token']) + ->setAuthBearer($this->authData['token']) ->setHeader('Accept', 'application/json') ->setBaseUri(self::BASE_URL) ->toArray() )->toArray(); $httpOptions = (new HttpOptions()) - ->setAuthBearer($authData['token']) + ->setAuthBearer($this->authData['token']) ->setHeader('Accept', 'application/json') ->setBaseUri(self::BASE_URL) ->setHeader('Dry-Run', $dryRun ? '1' : '0') @@ -74,9 +72,9 @@ class GandiProvider extends AbstractProvider 'tld_period' => 'golive', ]); - if (array_key_exists('sharingId', $authData)) { + if (array_key_exists('sharingId', $this->authData)) { $httpOptions->setQuery([ - 'sharing_id' => $authData['sharingId'], + 'sharing_id' => $this->authData['sharingId'], ]); } @@ -91,7 +89,7 @@ class GandiProvider extends AbstractProvider /** * @throws TransportExceptionInterface */ - public static function verifyAuthData(array $authData, HttpClientInterface $client): array + public function verifyAuthData(array $authData): array { $token = $authData['token']; @@ -111,17 +109,6 @@ class GandiProvider extends AbstractProvider throw new HttpException(451, 'The user has not given explicit consent'); } - $response = $client->request('GET', '/v5/organization/user-info', (new HttpOptions()) - ->setAuthBearer($token) - ->setHeader('Accept', 'application/json') - ->setBaseUri(self::BASE_URL) - ->toArray() - ); - - if (Response::HTTP_OK !== $response->getStatusCode()) { - throw new BadRequestHttpException('The status of these credentials is not valid'); - } - $authDataReturned = [ 'token' => $token, 'acceptConditions' => $acceptConditions, @@ -136,6 +123,20 @@ class GandiProvider extends AbstractProvider return $authDataReturned; } + public function assertAuthentication(): void + { + $response = $this->client->request('GET', '/v5/organization/user-info', (new HttpOptions()) + ->setAuthBearer($this->authData['token']) + ->setHeader('Accept', 'application/json') + ->setBaseUri(self::BASE_URL) + ->toArray() + ); + + if (Response::HTTP_OK !== $response->getStatusCode()) { + throw new BadRequestHttpException('The status of these credentials is not valid'); + } + } + /** * @throws TransportExceptionInterface * @throws ServerExceptionInterface @@ -145,10 +146,8 @@ class GandiProvider extends AbstractProvider */ protected function getSupportedTldList(): array { - $authData = self::verifyAuthData($this->authData, $this->client); - $response = $this->client->request('GET', '/v5/domain/tlds', (new HttpOptions()) - ->setAuthBearer($authData['token']) + ->setAuthBearer($this->authData['token']) ->setHeader('Accept', 'application/json') ->setBaseUri(self::BASE_URL) ->toArray())->toArray(); diff --git a/src/Service/Connector/NamecheapConnector.php b/src/Service/Connector/NamecheapProvider.php similarity index 91% rename from src/Service/Connector/NamecheapConnector.php rename to src/Service/Connector/NamecheapProvider.php index 5631c37..06168bf 100644 --- a/src/Service/Connector/NamecheapConnector.php +++ b/src/Service/Connector/NamecheapProvider.php @@ -9,7 +9,7 @@ use Symfony\Component\DependencyInjection\Attribute\Autoconfigure; use Symfony\Contracts\HttpClient\HttpClientInterface; #[Autoconfigure(public: true)] -class NamecheapConnector extends AbstractProvider +class NamecheapProvider extends AbstractProvider { public const BASE_URL = 'https://api.namecheap.com/xml.response'; @@ -81,20 +81,21 @@ class NamecheapConnector extends AbstractProvider return $data->CommandResponse; } - public static function verifyAuthData(array $authData, HttpClientInterface $client): array + public function verifyAuthData(array $authData): array { - // TODO call gettldlist to introspect authentication - // need to make verifyAuthData local to do this properly... - return [ 'ApiUser' => $authData['ApiUser'], 'ApiKey' => $authData['ApiKey'], 'acceptConditions' => $authData['acceptConditions'], 'ownerLegalAge' => $authData['ownerLegalAge'], - 'waiveRetractionPeriod' => $authData['waiveRetractionPeriod'], ]; } + public function assertAuthentication(): void + { + $this->call('namecheap.domains.gettldlist', [], false); + } + protected function getCachedTldList(): CacheItemInterface { return $this->cacheItemPool->getItem('app.provider.namecheap.supported-tld'); diff --git a/src/Service/Connector/OvhProvider.php b/src/Service/Connector/OvhProvider.php index c34b59c..1d917e9 100644 --- a/src/Service/Connector/OvhProvider.php +++ b/src/Service/Connector/OvhProvider.php @@ -7,10 +7,10 @@ use GuzzleHttp\Exception\ClientException; use Ovh\Api; use Ovh\Exceptions\InvalidParameterException; use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\InvalidArgumentException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; -use Symfony\Contracts\HttpClient\HttpClientInterface; class OvhProvider extends AbstractProvider { @@ -41,8 +41,9 @@ class OvhProvider extends AbstractProvider ], ]; - public function __construct(private HttpClientInterface $client) + public function __construct(CacheItemPoolInterface $cacheItemPool) { + parent::__construct($cacheItemPool); } /** @@ -61,21 +62,19 @@ class OvhProvider extends AbstractProvider throw new \InvalidArgumentException('Domain name cannot be null'); } - $authData = self::verifyAuthData($this->authData, $this->client); - - $acceptConditions = $authData['acceptConditions']; - $ownerLegalAge = $authData['ownerLegalAge']; - $waiveRetractationPeriod = $authData['waiveRetractationPeriod']; + $acceptConditions = $this->authData['acceptConditions']; + $ownerLegalAge = $this->authData['ownerLegalAge']; + $waiveRetractationPeriod = $this->authData['waiveRetractationPeriod']; $conn = new Api( - $authData['appKey'], - $authData['appSecret'], - $authData['apiEndpoint'], - $authData['consumerKey'] + $this->authData['appKey'], + $this->authData['appSecret'], + $this->authData['apiEndpoint'], + $this->authData['consumerKey'] ); $cart = $conn->post('/order/cart', [ - 'ovhSubsidiary' => $authData['ovhSubsidiary'], + 'ovhSubsidiary' => $this->authData['ovhSubsidiary'], 'description' => 'Domain Watchdog', ]); $cartId = $cart['cartId']; @@ -85,8 +84,8 @@ class OvhProvider extends AbstractProvider ]); $pricingModes = ['create-default']; - if ('create-default' !== $authData['pricingMode']) { - $pricingModes[] = $authData['pricingMode']; + if ('create-default' !== $this->authData['pricingMode']) { + $pricingModes[] = $this->authData['pricingMode']; } $offer = array_filter($offers, fn ($offer) => 'create' === $offer['action'] @@ -135,7 +134,7 @@ class OvhProvider extends AbstractProvider /** * @throws \Exception */ - public static function verifyAuthData(array $authData, HttpClientInterface $client): array + public function verifyAuthData(array $authData): array { $appKey = $authData['appKey']; $appSecret = $authData['appSecret']; @@ -164,11 +163,26 @@ class OvhProvider extends AbstractProvider throw new HttpException(451, 'The user has not given explicit consent'); } + return [ + 'appKey' => $appKey, + 'appSecret' => $appSecret, + 'apiEndpoint' => $apiEndpoint, + 'consumerKey' => $consumerKey, + 'ovhSubsidiary' => $ovhSubsidiary, + 'pricingMode' => $pricingMode, + 'acceptConditions' => $acceptConditions, + 'ownerLegalAge' => $ownerLegalAge, + 'waiveRetractationPeriod' => $waiveRetractationPeriod, + ]; + } + + public function assertAuthentication(): void + { $conn = new Api( - $appKey, - $appSecret, - $apiEndpoint, - $consumerKey + $this->authData['appKey'], + $this->authData['appSecret'], + $this->authData['apiEndpoint'], + $this->authData['consumerKey'], ); try { @@ -201,18 +215,6 @@ class OvhProvider extends AbstractProvider throw new BadRequestHttpException('This Connector does not have enough permissions on the Provider API. Please recreate this Connector.'); } } - - return [ - 'appKey' => $appKey, - 'appSecret' => $appSecret, - 'apiEndpoint' => $apiEndpoint, - 'consumerKey' => $consumerKey, - 'ovhSubsidiary' => $ovhSubsidiary, - 'pricingMode' => $pricingMode, - 'acceptConditions' => $acceptConditions, - 'ownerLegalAge' => $ownerLegalAge, - 'waiveRetractationPeriod' => $waiveRetractationPeriod, - ]; } /** @@ -222,17 +224,15 @@ class OvhProvider extends AbstractProvider */ protected function getSupportedTldList(): array { - $authData = self::verifyAuthData($this->authData, $this->client); - $conn = new Api( - $authData['appKey'], - $authData['appSecret'], - $authData['apiEndpoint'], - $authData['consumerKey'] + $this->authData['appKey'], + $this->authData['appSecret'], + $this->authData['apiEndpoint'], + $this->authData['consumerKey'] ); return $conn->get('/domain/extensions', [ - 'ovhSubsidiary' => $authData['ovhSubsidiary'], + 'ovhSubsidiary' => $this->authData['ovhSubsidiary'], ]); } From e6bf43693d956c83de2c13b92aee7b16e2a8e135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Gangloff?= Date: Wed, 2 Oct 2024 12:01:07 +0200 Subject: [PATCH 24/24] fix: https instead of http --- src/Service/Connector/GandiProvider.php | 6 ++-- src/Service/Connector/NamecheapProvider.php | 36 +++++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/Service/Connector/GandiProvider.php b/src/Service/Connector/GandiProvider.php index da3d957..35205aa 100644 --- a/src/Service/Connector/GandiProvider.php +++ b/src/Service/Connector/GandiProvider.php @@ -86,9 +86,6 @@ class GandiProvider extends AbstractProvider } } - /** - * @throws TransportExceptionInterface - */ public function verifyAuthData(array $authData): array { $token = $authData['token']; @@ -123,6 +120,9 @@ class GandiProvider extends AbstractProvider return $authDataReturned; } + /** + * @throws TransportExceptionInterface + */ public function assertAuthentication(): void { $response = $this->client->request('GET', '/v5/organization/user-info', (new HttpOptions()) diff --git a/src/Service/Connector/NamecheapProvider.php b/src/Service/Connector/NamecheapProvider.php index 06168bf..e85fa44 100644 --- a/src/Service/Connector/NamecheapProvider.php +++ b/src/Service/Connector/NamecheapProvider.php @@ -3,9 +3,15 @@ namespace App\Service\Connector; use App\Entity\Domain; +use Exception; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; +use Psr\Cache\InvalidArgumentException; use Symfony\Component\DependencyInjection\Attribute\Autoconfigure; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[Autoconfigure(public: true)] @@ -13,13 +19,17 @@ class NamecheapProvider extends AbstractProvider { public const BASE_URL = 'https://api.namecheap.com/xml.response'; - public const SANDBOX_BASE_URL = 'http://api.sandbox.namecheap.com/xml.response'; + public const SANDBOX_BASE_URL = 'https://api.sandbox.namecheap.com/xml.response'; public function __construct(CacheItemPoolInterface $cacheItemPool, private HttpClientInterface $client, private readonly string $outgoingIp) { parent::__construct($cacheItemPool); } + /** + * @throws \Exception + * @throws TransportExceptionInterface + */ public function orderDomain(Domain $domain, $dryRun): void { $addressesRes = $this->call('namecheap.users.address.getList', [], $dryRun); @@ -51,13 +61,20 @@ class NamecheapProvider extends AbstractProvider ], $domainAddresses), $dryRun); } - private static function mergePrefixKeys(string $prefix, array|object $src, array &$dest) + private static function mergePrefixKeys(string $prefix, array|object $src, array &$dest): void { foreach ($src as $key => $value) { $dest[$prefix.$key] = $value; } } + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + * @throws \Exception + */ private function call(string $command, array $parameters = [], bool $dryRun = true): object { $actualParams = array_merge([ @@ -91,16 +108,31 @@ class NamecheapProvider extends AbstractProvider ]; } + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ public function assertAuthentication(): void { $this->call('namecheap.domains.gettldlist', [], false); } + /** + * @throws InvalidArgumentException + */ protected function getCachedTldList(): CacheItemInterface { return $this->cacheItemPool->getItem('app.provider.namecheap.supported-tld'); } + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ protected function getSupportedTldList(): array { $supported = [];