Merge remote-tracking branch 'forked/master'

This commit is contained in:
vinceh121 2024-07-30 22:33:37 +02:00
commit cd1c555973
No known key found for this signature in database
GPG Key ID: 780725DCACF96F16
23 changed files with 561 additions and 365 deletions

View File

@ -42,12 +42,6 @@ Clone the repository:
git clone https://github.com/maelgangloff/domain-watchdog.git git clone https://github.com/maelgangloff/domain-watchdog.git
``` ```
Navigate to the project directory:
```shell
cd domain-watchdog
```
#### Backend #### Backend
1. Install dependencies: 1. Install dependencies:
@ -66,7 +60,6 @@ cd domain-watchdog
```shell ```shell
php bin/console doctrine:migrations:migrate php bin/console doctrine:migrations:migrate
``` ```
```
5. Start the Symfony server: 5. Start the Symfony server:
```shell ```shell
symfony server:start symfony server:start
@ -74,6 +67,46 @@ cd domain-watchdog
#### Frontend #### Frontend
1. Install dependencies:
```shell
yarn install
```
2. Generate language files:
```shell
yarn run ttag:po2json
```
3. Make the final build:
```shell
yarn build
```
## Update
**Any updates are your responsibility. Make a backup of the data if necessary.**
Fetch updates from the remote repository:
```shell
git pull origin master
```
### Backend
1. Install dependencies:
```shell
composer install
```
2. Run database migrations:
```shell
php bin/console doctrine:migrations:migrate
```
3. Clearing the Symfony cache:
```shell
php bin/console cache:clear
```
### Frontend
1. Install dependencies: 1. Install dependencies:
```shell ```shell
yarn install yarn install

View File

@ -142,7 +142,7 @@ export default function App() {
{ {
key: 'connectors', key: 'connectors',
icon: <ApiOutlined/>, icon: <ApiOutlined/>,
label: t`My connectors`, label: t`My Connectors`,
disabled: !isAuthenticated, disabled: !isAuthenticated,
onClick: () => navigate('/tracking/connectors') onClick: () => navigate('/tracking/connectors')
} }

View File

@ -1,9 +1,15 @@
import {Button, Form, FormInstance, Input, Select, Space, Typography} from "antd"; import {Button, Checkbox, Form, FormInstance, Input, Select, Space, Typography} from "antd";
import React, {useState} from "react"; import React, {useState} from "react";
import {Connector, ConnectorProvider} from "../../utils/api/connectors"; import {Connector, ConnectorProvider} from "../../utils/api/connectors";
import {t} from "ttag"; import {t} from "ttag";
import {BankOutlined} from "@ant-design/icons"; import {BankOutlined} from "@ant-design/icons";
import {regionNames} from "../../i18n"; import {
ovhEndpointList as ovhEndpointListFunction,
ovhFields as ovhFieldsFunction,
ovhPricingMode as ovhPricingModeFunction,
ovhSubsidiaryList as ovhSubsidiaryListFunction
} from "../../utils/providers/ovh";
import {helpGetTokenLink, tosHyperlink} from "../../utils/providers";
const formItemLayoutWithOutLabel = { const formItemLayoutWithOutLabel = {
wrapperCol: { wrapperCol: {
@ -14,31 +20,10 @@ const formItemLayoutWithOutLabel = {
export function ConnectorForm({form, onCreate}: { form: FormInstance, onCreate: (values: Connector) => void }) { export function ConnectorForm({form, onCreate}: { form: FormInstance, onCreate: (values: Connector) => void }) {
const [provider, setProvider] = useState<string>() const [provider, setProvider] = useState<string>()
const ovhFields = ovhFieldsFunction()
const ovhFields = { const ovhEndpointList = ovhEndpointListFunction()
appKey: t`Application key`, const ovhSubsidiaryList = ovhSubsidiaryListFunction()
appSecret: t`Application secret`, const ovhPricingMode = ovhPricingModeFunction()
consumerKey: t`Consumer key`
}
const ovhEndpointList = [
{
label: t`European Region`,
value: 'ovh-eu'
}
]
const ovhSubsidiaryList = [{value: 'EU', label: t`Europa`}, ...[
'CZ', 'DE', 'ES', 'FI', 'FR', 'GB', 'IE', 'IT', 'LT', 'MA', 'NL', 'PL', 'PT', 'SN', 'TN'
].map(c => ({value: c, label: regionNames.of(c) ?? c}))]
const ovhPricingMode = [
{value: 'create-default', label: t`The domain is free and at the standard price`},
{
value: 'create-premium',
label: t`The domain is free but is a premium. Its price varies from one domain to another`
}
]
return <Form return <Form
{...formItemLayoutWithOutLabel} {...formItemLayoutWithOutLabel}
@ -51,9 +36,11 @@ export function ConnectorForm({form, onCreate}: { form: FormInstance, onCreate:
<Form.Item <Form.Item
label={t`Provider`} label={t`Provider`}
name="provider" name="provider"
help={helpGetTokenLink(provider)}
rules={[{required: true, message: t`Required`}]} rules={[{required: true, message: t`Required`}]}
> >
<Select <Select
allowClear
placeholder={t`Please select a Provider`} placeholder={t`Please select a Provider`}
suffixIcon={<BankOutlined/>} suffixIcon={<BankOutlined/>}
options={Object.keys(ConnectorProvider).map((c) => ({ options={Object.keys(ConnectorProvider).map((c) => ({
@ -66,22 +53,19 @@ export function ConnectorForm({form, onCreate}: { form: FormInstance, onCreate:
}))} }))}
value={provider} value={provider}
onChange={setProvider} onChange={setProvider}
autoFocus
/> />
</Form.Item> </Form.Item>
{ {
provider === ConnectorProvider.OVH && <> provider === ConnectorProvider.OVH && <>
<Typography.Link target='_blank'
href="https://api.ovh.com/createToken/index.cgi?GET=/*&PUT=/*&POST=/*&DELETE=/*">
Retrieve a token set from the OVH API
</Typography.Link>
{ {
Object.keys(ovhFields).map(fieldName => <Form.Item Object.keys(ovhFields).map(fieldName => <Form.Item
label={ovhFields[fieldName as keyof typeof ovhFields]} label={ovhFields[fieldName as keyof typeof ovhFields]}
name={['authData', fieldName]} name={['authData', fieldName]}
rules={[{required: true, message: t`Required`}]} rules={[{required: true, message: t`Required`}]}
> >
<Input/> <Input autoComplete='off'/>
</Form.Item>) </Form.Item>)
} }
<Form.Item <Form.Item
@ -106,6 +90,37 @@ export function ConnectorForm({form, onCreate}: { form: FormInstance, onCreate:
> >
<Select options={ovhPricingMode} optionFilterProp="label"/> <Select options={ovhPricingMode} optionFilterProp="label"/>
</Form.Item> </Form.Item>
<Form.Item
valuePropName="checked"
label={t`API Terms of Service`}
name={['authData', 'acceptConditions']}
rules={[{required: true, message: t`Required`}]}
>
<Checkbox
required={true}>
<Typography.Link target='_blank' href={tosHyperlink(provider)}>
{t`I certify that I have read and accepted the conditions of use of the Provider API, accessible from this hyperlink`}
</Typography.Link>
</Checkbox>
</Form.Item>
<Form.Item
valuePropName="checked"
label={t`Legal age`}
name={['authData', 'ownerLegalAge']}
rules={[{required: true, message: t`Required`}]}
>
<Checkbox
required={true}>{t`I certify on my honor that I am of the minimum age required to consent to these conditions`}</Checkbox>
</Form.Item>
<Form.Item
valuePropName="checked"
label={t`Withdrawal period`}
name={['authData', 'waiveRetractationPeriod']}
rules={[{required: true, message: t`Required`}]}
>
<Checkbox
required={true}>{t`I expressly waive my right of withdrawal regarding the purchase of domain names via the Provider's API`}</Checkbox>
</Form.Item>
</> </>
} }

View File

@ -1,7 +1,7 @@
import {Button, Form, FormInstance, Input, Select, Space} from "antd"; import {Button, Form, FormInstance, Input, Select, Space} from "antd";
import {t} from "ttag"; import {t} from "ttag";
import {MinusCircleOutlined, PlusOutlined, ThunderboltFilled} from "@ant-design/icons"; import {ApiOutlined, MinusCircleOutlined, PlusOutlined, ThunderboltFilled} from "@ant-design/icons";
import React, {useState} from "react"; import React from "react";
import {EventAction} from "../../utils/api"; import {EventAction} from "../../utils/api";
import {Connector} from "../../utils/api/connectors"; import {Connector} from "../../utils/api/connectors";
@ -73,13 +73,8 @@ export function WatchlistForm({form, connectors, onCreateWatchlist}: {
{ {
label: t`Send me an email`, label: t`Send me an email`,
value: 'email' value: 'email'
},
{
label: t`Buy the domain if available`,
value: 'buy'
} }
] ]
const [actionsSelect, setActionsSelect] = useState<{ [key: number]: string }>({})
return <Form return <Form
{...formItemLayoutWithOutLabel} {...formItemLayoutWithOutLabel}
@ -186,27 +181,8 @@ export function WatchlistForm({form, connectors, onCreateWatchlist}: {
noStyle name={[field.name, 'action']}> noStyle name={[field.name, 'action']}>
<Select style={{minWidth: 300}} options={triggerActionItems} showSearch <Select style={{minWidth: 300}} options={triggerActionItems} showSearch
placeholder={t`Then do that`} placeholder={t`Then do that`}
optionFilterProp="label" value={actionsSelect[field.key]} optionFilterProp="label"/>
onChange={(e) => setActionsSelect({...actionsSelect, [field.key]: e})}/>
</Form.Item> </Form.Item>
{actionsSelect[field.key] === 'buy' && <Form.Item {...field}
validateTrigger={['onChange', 'onBlur']}
rules={[{
required: true,
message: t`Required`
}]}
noStyle
name={[field.name, 'connector']}>
<Select style={{minWidth: 500}} showSearch
placeholder={t`Connector`}
optionFilterProp="label"
options={connectors.map(c => ({
label: `${c.provider} (${c.id})`,
value: c.id
}))}
/>
</Form.Item>
}
</Space> </Space>
{fields.length > 1 ? ( {fields.length > 1 ? (
@ -231,6 +207,21 @@ export function WatchlistForm({form, connectors, onCreateWatchlist}: {
</> </>
)} )}
</Form.List> </Form.List>
<Form.Item label={t`Connector`}
name='connector'
>
<Select showSearch
allowClear
style={{width: '60%'}}
placeholder={t`Connector`}
suffixIcon={<ApiOutlined/>}
optionFilterProp="label"
options={connectors.map(c => ({
label: `${c.provider} (${c.id})`,
value: c.id
}))}
/>
</Form.Item>
<Form.Item> <Form.Item>
<Space> <Space>
<Button type="primary" htmlType="submit"> <Button type="primary" htmlType="submit">

View File

@ -52,7 +52,7 @@ export default function Page() {
name="username" name="username"
rules={[{required: true, message: t`Required`}]} rules={[{required: true, message: t`Required`}]}
> >
<Input/> <Input autoFocus/>
</Form.Item> </Form.Item>
<Form.Item<FieldType> <Form.Item<FieldType>

View File

@ -20,13 +20,14 @@ export default function WatchlistPage() {
const onCreateWatchlist = (values: { const onCreateWatchlist = (values: {
domains: string[], domains: string[],
triggers: { event: string, action: string, connector?: string }[] triggers: { event: string, action: string, connector?: string }[]
connector?: string
}) => { }) => {
const domainsURI = values.domains.map(d => '/api/domains/' + d) const domainsURI = values.domains.map(d => '/api/domains/' + d)
postWatchlist(domainsURI, values.triggers.map(({action, event, connector}) => ({ postWatchlist({
action, domains: domainsURI,
event, triggers: values.triggers,
connector: connector !== undefined ? '/api/connectors/' + connector : undefined connector: values.connector !== undefined ? '/api/connectors/' + values.connector : undefined
}))).then((w) => { }).then((w) => {
form.resetFields() form.resetFields()
refreshWatchlists() refreshWatchlists()
messageApi.success(t`Watchlist created !`) messageApi.success(t`Watchlist created !`)

View File

@ -16,6 +16,8 @@ export type EventAction =
| 'enum validation expiration' | 'enum validation expiration'
| string | string
export type TriggerAction = 'email' | string
export interface Event { export interface Event {
action: EventAction action: EventAction
date: string date: string
@ -63,8 +65,9 @@ export interface User {
} }
export interface Watchlist { export interface Watchlist {
domains: string[] domains: string[],
triggers: Event[] triggers: { event: EventAction, action: TriggerAction }[],
connector?: string
} }
export async function request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig): Promise<R> { export async function request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig): Promise<R> {

View File

@ -1,4 +1,4 @@
import {Event, EventAction, request, Watchlist} from "./index"; import {Event, request, Watchlist} from "./index";
export async function getWatchlists() { export async function getWatchlists() {
const response = await request({ const response = await request({
@ -8,24 +8,17 @@ export async function getWatchlists() {
} }
export async function getWatchlist(token: string) { export async function getWatchlist(token: string) {
const response = await request<Watchlist>({ const response = await request<Watchlist & { token: string }>({
url: 'watchlists/' + token url: 'watchlists/' + token
}) })
return response.data return response.data
} }
export async function postWatchlist(domains: string[], triggers: { export async function postWatchlist(watchlist: Watchlist) {
action: string,
event: EventAction,
connector?: string
}[]) {
const response = await request<{ token: string }>({ const response = await request<{ token: string }>({
method: 'POST', method: 'POST',
url: 'watchlists', url: 'watchlists',
data: { data: watchlist,
domains,
triggers
},
headers: { headers: {
"Content-Type": 'application/json' "Content-Type": 'application/json'
} }

View File

@ -0,0 +1,25 @@
import {ConnectorProvider} from "../api/connectors";
import {Typography} from "antd";
import {t} from "ttag";
import React from "react";
export const helpGetTokenLink = (provider?: string) => {
switch (provider) {
case ConnectorProvider.OVH:
return <Typography.Link target='_blank'
href="https://api.ovh.com/createToken/index.cgi?GET=/order/cart/*&POST=/order/cart&POST=/order/cart/*&DELETE=/order/cart/*">
{t`Retrieve a set of tokens from your customer account on the Provider's website`}
</Typography.Link>
default:
return <></>
}
}
export const tosHyperlink = (provider?: string) => {
switch (provider) {
case ConnectorProvider.OVH:
return 'https://www.ovhcloud.com/fr/terms-and-conditions/contracts/'
default:
return ''
}
}

View File

@ -0,0 +1,27 @@
import {t} from "ttag";
import {regionNames} from "../../i18n";
export const ovhFields = () => ({
appKey: t`Application key`,
appSecret: t`Application secret`,
consumerKey: t`Consumer key`
})
export const ovhEndpointList = () => [
{
label: t`European Region`,
value: 'ovh-eu'
}
]
export const ovhSubsidiaryList = () => [...[
'CZ', 'DE', 'ES', 'FI', 'FR', 'GB', 'IE', 'IT', 'LT', 'MA', 'NL', 'PL', 'PT', 'SN', 'TN'
].map(c => ({value: c, label: regionNames.of(c) ?? c})), {value: 'EU', label: t`Europe`}]
export const ovhPricingMode = () => [
{value: 'create-default', label: t`The domain is free and at the standard price`},
{
value: 'create-premium',
label: t`The domain is free but is a premium. Its price varies from one domain to another`
}
]

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240730193422 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE watch_list ADD connector_id UUID DEFAULT NULL');
$this->addSql('COMMENT ON COLUMN watch_list.connector_id IS \'(DC2Type:uuid)\'');
$this->addSql('ALTER TABLE watch_list ADD CONSTRAINT FK_152B584B4D085745 FOREIGN KEY (connector_id) REFERENCES connector (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('CREATE INDEX IDX_152B584B4D085745 ON watch_list (connector_id)');
$this->addSql('ALTER TABLE watch_list_trigger DROP CONSTRAINT fk_cf857a4c4d085745');
$this->addSql('DROP INDEX idx_cf857a4c4d085745');
$this->addSql('ALTER TABLE watch_list_trigger DROP connector_id');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE watch_list_trigger ADD connector_id UUID DEFAULT NULL');
$this->addSql('COMMENT ON COLUMN watch_list_trigger.connector_id IS \'(DC2Type:uuid)\'');
$this->addSql('ALTER TABLE watch_list_trigger ADD CONSTRAINT fk_cf857a4c4d085745 FOREIGN KEY (connector_id) REFERENCES connector (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('CREATE INDEX idx_cf857a4c4d085745 ON watch_list_trigger (connector_id)');
$this->addSql('ALTER TABLE watch_list DROP CONSTRAINT FK_152B584B4D085745');
$this->addSql('DROP INDEX IDX_152B584B4D085745');
$this->addSql('ALTER TABLE watch_list DROP connector_id');
}
}

View File

@ -8,10 +8,5 @@ interface ConnectorInterface
{ {
public static function verifyAuthData(array $authData): array; public static function verifyAuthData(array $authData): array;
public function orderDomain(Domain $domain, public function orderDomain(Domain $domain, bool $dryRun): void;
bool $acceptConditions,
bool $ownerLegalAge,
bool $waiveRetractationPeriod,
bool $dryRyn
): void;
} }

View File

@ -3,6 +3,7 @@
namespace App\Config\Connector; namespace App\Config\Connector;
use App\Entity\Domain; use App\Entity\Domain;
use DateTime;
use Exception; use Exception;
use Ovh\Api; use Ovh\Api;
@ -18,11 +19,7 @@ readonly class OvhConnector implements ConnectorInterface
* Order a domain name with the OVH API * Order a domain name with the OVH API
* @throws Exception * @throws Exception
*/ */
public function orderDomain(Domain $domain, public function orderDomain(Domain $domain, bool $dryRun = false
bool $acceptConditions,
bool $ownerLegalAge,
bool $waiveRetractationPeriod,
bool $dryRyn = false
): void ): void
{ {
if (!$domain->getDeleted()) throw new Exception('The domain name still appears in the WHOIS database'); if (!$domain->getDeleted()) throw new Exception('The domain name still appears in the WHOIS database');
@ -32,22 +29,19 @@ readonly class OvhConnector implements ConnectorInterface
$authData = self::verifyAuthData($this->authData); $authData = self::verifyAuthData($this->authData);
$appKey = $authData['appKey']; $acceptConditions = $authData['acceptConditions'];
$appSecret = $authData['appSecret']; $ownerLegalAge = $authData['ownerLegalAge'];
$apiEndpoint = $authData['apiEndpoint']; $waiveRetractationPeriod = $authData['waiveRetractationPeriod'];
$consumerKey = $authData['consumerKey'];
$ovhSubsidiary = $authData['ovhSubsidiary'];
$pricingMode = $authData['pricingMode'];
$conn = new Api( $conn = new Api(
$appKey, $authData['appKey'],
$appSecret, $authData['appSecret'],
$apiEndpoint, $authData['apiEndpoint'],
$consumerKey $authData['consumerKey']
); );
$cart = $conn->post('/order/cart', [ $cart = $conn->post('/order/cart', [
"ovhSubsidiary" => $ovhSubsidiary, "ovhSubsidiary" => $authData['ovhSubsidiary'],
"description" => "Domain Watchdog" "description" => "Domain Watchdog"
]); ]);
$cartId = $cart['cartId']; $cartId = $cart['cartId'];
@ -57,7 +51,7 @@ readonly class OvhConnector implements ConnectorInterface
]); ]);
$offer = array_filter($offers, fn($offer) => $offer['action'] === 'create' && $offer = array_filter($offers, fn($offer) => $offer['action'] === 'create' &&
$offer['orderable'] === true && $offer['orderable'] === true &&
$offer['pricingMode'] === $pricingMode $offer['pricingMode'] === $authData['pricingMode']
); );
if (empty($offer)) { if (empty($offer)) {
$conn->delete("/order/cart/{$cartId}"); $conn->delete("/order/cart/{$cartId}");
@ -89,7 +83,7 @@ readonly class OvhConnector implements ConnectorInterface
} }
$conn->get("/order/cart/{$cartId}/checkout"); $conn->get("/order/cart/{$cartId}/checkout");
if ($dryRyn) return; if ($dryRun) return;
$conn->post("/order/cart/{$cartId}/checkout", [ $conn->post("/order/cart/{$cartId}/checkout", [
"autoPayWithPreferredPaymentMethod" => true, "autoPayWithPreferredPaymentMethod" => true,
"waiveRetractationPeriod" => $waiveRetractationPeriod "waiveRetractationPeriod" => $waiveRetractationPeriod
@ -108,13 +102,36 @@ readonly class OvhConnector implements ConnectorInterface
$ovhSubsidiary = $authData['ovhSubsidiary']; $ovhSubsidiary = $authData['ovhSubsidiary'];
$pricingMode = $authData['pricingMode']; $pricingMode = $authData['pricingMode'];
$acceptConditions = $authData['acceptConditions'];
$ownerLegalAge = $authData['ownerLegalAge'];
$waiveRetractationPeriod = $authData['waiveRetractationPeriod'];
if (!is_string($appKey) || empty($appKey) || if (!is_string($appKey) || empty($appKey) ||
!is_string($appSecret) || empty($appSecret) || !is_string($appSecret) || empty($appSecret) ||
!is_string($consumerKey) || empty($consumerKey) || !is_string($consumerKey) || empty($consumerKey) ||
!is_string($apiEndpoint) || empty($apiEndpoint) || !is_string($apiEndpoint) || empty($apiEndpoint) ||
!is_string($ovhSubsidiary) || empty($ovhSubsidiary) || !is_string($ovhSubsidiary) || empty($ovhSubsidiary) ||
!is_string($pricingMode) || empty($pricingMode) !is_string($pricingMode) || empty($pricingMode) ||
) throw new Exception("Bad data schema.");
true !== $acceptConditions ||
true !== $ownerLegalAge ||
true !== $waiveRetractationPeriod
) throw new Exception("Bad authData schema");
$conn = new Api(
$appKey,
$appSecret,
$apiEndpoint,
$consumerKey
);
$res = $conn->get('/auth/currentCredential');
if ($res['expiration'] !== null && new DateTime($res['expiration']) < new DateTime())
throw new Exception('These credentials have expired');
$status = $res['status'];
if ($status !== 'validated') throw new Exception("The status of these credentials is not valid ($status)");
return [ return [
"appKey" => $appKey, "appKey" => $appKey,
@ -122,7 +139,10 @@ readonly class OvhConnector implements ConnectorInterface
"apiEndpoint" => $apiEndpoint, "apiEndpoint" => $apiEndpoint,
"consumerKey" => $consumerKey, "consumerKey" => $consumerKey,
"ovhSubsidiary" => $ovhSubsidiary, "ovhSubsidiary" => $ovhSubsidiary,
"pricingMode" => $pricingMode "pricingMode" => $pricingMode,
"acceptConditions" => $acceptConditions,
"ownerLegalAge" => $ownerLegalAge,
"waiveRetractationPeriod" => $waiveRetractationPeriod
]; ];
} }
} }

View File

@ -6,5 +6,4 @@ namespace App\Config;
enum TriggerAction: string enum TriggerAction: string
{ {
case SendEmail = 'email'; case SendEmail = 'email';
case BuyDomain = 'buy';
} }

View File

@ -9,7 +9,6 @@ use App\Entity\User;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Exception; use Exception;
use Ovh\Api;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
@ -59,14 +58,6 @@ class ConnectorController extends AbstractController
if ($connector->getProvider() === ConnectorProvider::OVH) { if ($connector->getProvider() === ConnectorProvider::OVH) {
$authData = OvhConnector::verifyAuthData($connector->getAuthData()); $authData = OvhConnector::verifyAuthData($connector->getAuthData());
$connector->setAuthData($authData); $connector->setAuthData($authData);
$ovh = new Api(
$authData['appKey'],
$authData['appSecret'],
$authData['apiEndpoint'],
$authData['consumerKey']
);
} else throw new Exception('Unknown provider'); } else throw new Exception('Unknown provider');
$this->em->persist($connector); $this->em->persist($connector);

View File

@ -2,10 +2,8 @@
namespace App\Controller; namespace App\Controller;
use App\Config\TriggerAction;
use App\Entity\User; use App\Entity\User;
use App\Entity\WatchList; use App\Entity\WatchList;
use App\Entity\WatchListTrigger;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Exception; use Exception;
@ -55,13 +53,6 @@ class WatchListController extends AbstractController
{ {
$watchList = $this->serializer->deserialize($request->getContent(), WatchList::class, 'json', ['groups' => 'watchlist:create']); $watchList = $this->serializer->deserialize($request->getContent(), WatchList::class, 'json', ['groups' => 'watchlist:create']);
$watchList->setUser($this->getUser()); $watchList->setUser($this->getUser());
/** @var WatchListTrigger $trigger */
foreach ($watchList->getWatchListTriggers()->toArray() as $trigger) {
if ($trigger->getAction() === TriggerAction::SendEmail && $trigger->getConnector() !== null)
throw new Exception('No connector needed to send email');
if ($trigger->getAction() === TriggerAction::BuyDomain && $trigger->getConnector() === null)
throw new Exception('Unable to order a domain name without a Connector');
}
$this->em->persist($watchList); $this->em->persist($watchList);
$this->em->flush(); $this->em->flush();

View File

@ -56,16 +56,16 @@ class Connector
private array $authData = []; private array $authData = [];
/** /**
* @var Collection<int, WatchListTrigger> * @var Collection<int, WatchList>
*/ */
#[ORM\OneToMany(targetEntity: WatchListTrigger::class, mappedBy: 'connector')] #[ORM\OneToMany(targetEntity: WatchList::class, mappedBy: 'connector')]
private Collection $watchListTriggers; private Collection $watchLists;
public function __construct() public function __construct()
{ {
$this->id = Uuid::v4(); $this->id = Uuid::v4();
$this->watchListTriggers = new ArrayCollection(); $this->watchLists = new ArrayCollection();
} }
public function getId(): ?string public function getId(): ?string
@ -97,36 +97,6 @@ class Connector
return $this; return $this;
} }
/**
* @return Collection<int, WatchListTrigger>
*/
public function getWatchListTriggers(): Collection
{
return $this->watchListTriggers;
}
public function addWatchListTrigger(WatchListTrigger $watchListTrigger): static
{
if (!$this->watchListTriggers->contains($watchListTrigger)) {
$this->watchListTriggers->add($watchListTrigger);
$watchListTrigger->setConnector($this);
}
return $this;
}
public function removeWatchListTrigger(WatchListTrigger $watchListTrigger): static
{
if ($this->watchListTriggers->removeElement($watchListTrigger)) {
// set the owning side to null (unless already changed)
if ($watchListTrigger->getConnector() === $this) {
$watchListTrigger->setConnector(null);
}
}
return $this;
}
public function getProvider(): ?ConnectorProvider public function getProvider(): ?ConnectorProvider
{ {
return $this->provider; return $this->provider;
@ -139,4 +109,34 @@ class Connector
return $this; return $this;
} }
/**
* @return Collection<int, WatchList>
*/
public function getWatchLists(): Collection
{
return $this->watchLists;
}
public function addWatchList(WatchList $watchList): static
{
if (!$this->watchLists->contains($watchList)) {
$this->watchLists->add($watchList);
$watchList->setConnector($this);
}
return $this;
}
public function removeWatchList(WatchList $watchList): static
{
if ($this->watchLists->removeElement($watchList)) {
// set the owning side to null (unless already changed)
if ($watchList->getConnector() === $this) {
$watchList->setConnector(null);
}
}
return $this;
}
} }

View File

@ -69,6 +69,11 @@ class WatchList
#[SerializedName("triggers")] #[SerializedName("triggers")]
private Collection $watchListTriggers; private Collection $watchListTriggers;
#[ORM\ManyToOne(inversedBy: 'watchLists')]
#[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create', 'watchlist:update'])]
private ?Connector $connector = null;
public function __construct() public function __construct()
{ {
$this->token = Uuid::v4(); $this->token = Uuid::v4();
@ -146,4 +151,16 @@ class WatchList
return $this; return $this;
} }
public function getConnector(): ?Connector
{
return $this->connector;
}
public function setConnector(?Connector $connector): static
{
$this->connector = $connector;
return $this;
}
} }

View File

@ -25,10 +25,6 @@ class WatchListTrigger
#[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create', 'watchlist:update'])] #[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create', 'watchlist:update'])]
private ?TriggerAction $action = null; private ?TriggerAction $action = null;
#[ORM\ManyToOne(inversedBy: 'watchListTriggers')]
#[Groups(['watchlist:list', 'watchlist:item', 'watchlist:create', 'watchlist:update'])]
private ?Connector $connector = null;
public function getEvent(): ?string public function getEvent(): ?string
{ {
return $this->event; return $this->event;
@ -64,16 +60,4 @@ class WatchListTrigger
return $this; return $this;
} }
public function getConnector(): ?Connector
{
return $this->connector;
}
public function setConnector(?Connector $connector): static
{
$this->connector = $connector;
return $this;
}
} }

View File

@ -48,28 +48,14 @@ final readonly class ProcessDomainTriggerHandler
/** @var Domain $domain */ /** @var Domain $domain */
$domain = $this->domainRepository->findOneBy(["ldhName" => $message->ldhName]); $domain = $this->domainRepository->findOneBy(["ldhName" => $message->ldhName]);
$watchListTriggers = $watchList->getWatchListTriggers(); $connector = $watchList->getConnector();
if (null !== $connector && $domain->getDeleted()) {
/** @var WatchListTrigger $watchListTrigger */
foreach ($watchListTriggers->getIterator() as $watchListTrigger) {
if ($watchListTrigger->getAction() === TriggerAction::BuyDomain) {
try { try {
if ($watchListTrigger->getConnector() === null) throw new Exception('Connector is missing');
$connector = $watchListTrigger->getConnector();
if ($connector->getProvider() === ConnectorProvider::OVH) { if ($connector->getProvider() === ConnectorProvider::OVH) {
$ovh = new OVHConnector($connector->getAuthData()); $ovh = new OVHConnector($connector->getAuthData());
$isDebug = $this->kernel->isDebug(); $isDebug = $this->kernel->isDebug();
$ovh->orderDomain( $ovh->orderDomain($domain, $isDebug);
$domain,
true, // TODO: Infer from the user
true, // TODO: Infer from the user
true, // TODO: Infer from the user
$isDebug
);
$this->sendEmailDomainOrdered($domain, $connector, $watchList->getUser()); $this->sendEmailDomainOrdered($domain, $connector, $watchList->getUser());
} else throw new Exception("Unknown provider"); } else throw new Exception("Unknown provider");
} catch (Throwable) { } catch (Throwable) {
@ -79,6 +65,11 @@ final readonly class ProcessDomainTriggerHandler
/** @var DomainEvent $event */ /** @var DomainEvent $event */
foreach ($domain->getEvents()->filter(fn($event) => $message->updatedAt < $event->getDate()) as $event) { foreach ($domain->getEvents()->filter(fn($event) => $message->updatedAt < $event->getDate()) as $event) {
$watchListTriggers = $watchList->getWatchListTriggers()
->filter(fn($trigger) => $trigger->getEvent() === $event->getAction());
/** @var WatchListTrigger $watchListTrigger */
foreach ($watchListTriggers->getIterator() as $watchListTrigger) {
if ($watchListTrigger->getAction() == TriggerAction::SendEmail) { if ($watchListTrigger->getAction() == TriggerAction::SendEmail) {
$this->sendEmailDomainUpdated($event, $watchList->getUser()); $this->sendEmailDomainUpdated($event, $watchList->getUser());
} }

View File

@ -51,7 +51,7 @@
<p>Hello,</p> <p>Hello,</p>
<p>We are pleased to inform you that a new action has been detected on a domain name in your watchlist.</p> <p>We are pleased to inform you that a new action has been detected on a domain name in your watchlist.</p>
<p><strong>Domain name:</strong> {{ event.domain.ldhName }}</p> <p><strong>Domain name:</strong> {{ event.domain.ldhName }}</p>
<p><strong>Action:</strong> {{ event.action.value }}</p> <p><strong>Action:</strong> {{ event.action }}</p>
<p><strong>Effective Date:</strong> {{ event.date | date("c") }}</p> <p><strong>Effective Date:</strong> {{ event.date | date("c") }}</p>
<br/> <br/>
<p>Thank you for your understanding,</p> <p>Thank you for your understanding,</p>

View File

@ -1,6 +1,6 @@
msgid "" msgid ""
msgstr "" msgstr ""
"PO-Revision-Date: 2024-07-30 00:46+0000\n" "PO-Revision-Date: 2024-07-30 18:40+0000\n"
"Last-Translator: Maël Gangloff <contact@maelgangloff.fr>\n" "Last-Translator: Maël Gangloff <contact@maelgangloff.fr>\n"
"Language-Team: French <https://weblate.vinceh121.me/projects/domain-watchdog/" "Language-Team: French <https://weblate.vinceh121.me/projects/domain-watchdog/"
"domain-watchdog-dashboard/fr/>\n" "domain-watchdog-dashboard/fr/>\n"
@ -56,21 +56,23 @@ msgid "ENUM validation expiration"
msgstr "Expiration de la validation ENUM" msgstr "Expiration de la validation ENUM"
#: assets/components/search/DomainSearchBar.tsx:23 #: assets/components/search/DomainSearchBar.tsx:23
#: assets/components/tracking/ConnectorForm.tsx:54 #: assets/components/tracking/ConnectorForm.tsx:40
#: assets/components/tracking/ConnectorForm.tsx:82 #: assets/components/tracking/ConnectorForm.tsx:66
#: assets/components/tracking/ConnectorForm.tsx:90 #: assets/components/tracking/ConnectorForm.tsx:74
#: assets/components/tracking/ConnectorForm.tsx:81
#: assets/components/tracking/ConnectorForm.tsx:89
#: assets/components/tracking/ConnectorForm.tsx:97 #: assets/components/tracking/ConnectorForm.tsx:97
#: assets/components/tracking/ConnectorForm.tsx:105 #: assets/components/tracking/ConnectorForm.tsx:110
#: assets/components/tracking/WatchlistForm.tsx:115 #: assets/components/tracking/ConnectorForm.tsx:119
#: assets/components/tracking/WatchlistForm.tsx:174 #: assets/components/tracking/WatchlistForm.tsx:110
#: assets/components/tracking/WatchlistForm.tsx:184 #: assets/components/tracking/WatchlistForm.tsx:169
#: assets/components/tracking/WatchlistForm.tsx:196 #: assets/components/tracking/WatchlistForm.tsx:179
#: assets/pages/LoginPage.tsx:53 assets/pages/LoginPage.tsx:61 #: assets/pages/LoginPage.tsx:53 assets/pages/LoginPage.tsx:61
msgid "Required" msgid "Required"
msgstr "Requis" msgstr "Requis"
#: assets/components/search/DomainSearchBar.tsx:26 #: assets/components/search/DomainSearchBar.tsx:26
#: assets/components/tracking/WatchlistForm.tsx:118 #: assets/components/tracking/WatchlistForm.tsx:113
msgid "This domain name does not appear to be valid" msgid "This domain name does not appear to be valid"
msgstr "Ce nom de domaine ne semble pas être valide" msgstr "Ce nom de domaine ne semble pas être valide"
@ -158,114 +160,115 @@ msgstr "Lorsqu'un domaine est enregistré"
msgid "Send me an email" msgid "Send me an email"
msgstr "Envoie-moi un email" msgstr "Envoie-moi un email"
#: assets/components/tracking/WatchlistForm.tsx:78 #: assets/components/tracking/WatchlistForm.tsx:90
msgid "Buy the domain if available"
msgstr "Acheter le domaine s'il est disponible"
#: assets/components/tracking/WatchlistForm.tsx:95
msgid "At least one domain name" msgid "At least one domain name"
msgstr "Au moins un nom de domaine" msgstr "Au moins un nom de domaine"
#: assets/components/tracking/WatchlistForm.tsx:106 #: assets/components/tracking/WatchlistForm.tsx:101
msgid "Domain names" msgid "Domain names"
msgstr "Noms de domaines" msgstr "Noms de domaines"
#: assets/components/tracking/WatchlistForm.tsx:124 #: assets/components/tracking/WatchlistForm.tsx:119
#: assets/components/tracking/WatchlistsList.tsx:22 #: assets/components/tracking/WatchlistsList.tsx:22
msgid "Domain name" msgid "Domain name"
msgstr "Nom de domaine" msgstr "Nom de domaine"
#: assets/components/tracking/WatchlistForm.tsx:141 #: assets/components/tracking/WatchlistForm.tsx:136
msgid "Add a Domain name" msgid "Add a Domain name"
msgstr "Ajouter un nom de domaine" msgstr "Ajouter un nom de domaine"
#: assets/components/tracking/WatchlistForm.tsx:154 #: assets/components/tracking/WatchlistForm.tsx:149
msgid "At least one domain trigger" msgid "At least one domain trigger"
msgstr "Au moins une action" msgstr "Au moins une action"
#: assets/components/tracking/WatchlistForm.tsx:165 #: assets/components/tracking/WatchlistForm.tsx:160
msgid "Domain trigger" msgid "Domain trigger"
msgstr "Action" msgstr "Action"
#: assets/components/tracking/WatchlistForm.tsx:178 #: assets/components/tracking/WatchlistForm.tsx:173
msgid "If this happens" msgid "If this happens"
msgstr "Si ça arrive" msgstr "Si ça arrive"
#: assets/components/tracking/WatchlistForm.tsx:188 #: assets/components/tracking/WatchlistForm.tsx:183
msgid "Then do that" msgid "Then do that"
msgstr "Alors fais ça" msgstr "Alors fais ça"
#: assets/components/tracking/WatchlistForm.tsx:201 #: assets/components/tracking/WatchlistForm.tsx:203
msgid "Connector"
msgstr "Connecteur"
#: assets/components/tracking/WatchlistForm.tsx:227
msgid "Add a Trigger" msgid "Add a Trigger"
msgstr "Ajouter une action" msgstr "Ajouter une action"
#: assets/components/tracking/ConnectorForm.tsx:116 #: assets/components/tracking/WatchlistForm.tsx:210
#: assets/components/tracking/WatchlistForm.tsx:237 #: assets/components/tracking/WatchlistForm.tsx:216
msgid "Connector"
msgstr "Connecteur"
#: assets/components/tracking/ConnectorForm.tsx:131
#: assets/components/tracking/WatchlistForm.tsx:228
msgid "Create" msgid "Create"
msgstr "Créer" msgstr "Créer"
#: assets/components/tracking/ConnectorForm.tsx:119 #: assets/components/tracking/ConnectorForm.tsx:134
#: assets/components/tracking/WatchlistForm.tsx:240 #: assets/components/tracking/WatchlistForm.tsx:231
msgid "Reset" msgid "Reset"
msgstr "Réinitialiser" msgstr "Réinitialiser"
#: assets/components/tracking/ConnectorForm.tsx:19 #: assets/components/tracking/ConnectorForm.tsx:37
msgid "Application key"
msgstr "Clé d'application"
#: assets/components/tracking/ConnectorForm.tsx:20
msgid "Application secret"
msgstr "Clé secrète d'application"
#: assets/components/tracking/ConnectorForm.tsx:21
msgid "Consumer key"
msgstr "Clé d'utilisateur"
#: assets/components/tracking/ConnectorForm.tsx:26
msgid "European Region"
msgstr "Continent Européen"
#: assets/components/tracking/ConnectorForm.tsx:31
msgid "Europa"
msgstr "Europe"
#: assets/components/tracking/ConnectorForm.tsx:36
msgid "The domain is free and at the standard price"
msgstr "Le domaine est libre et au prix standard"
#: assets/components/tracking/ConnectorForm.tsx:39
msgid ""
"The domain is free but is a premium. Its price varies from one domain to "
"another"
msgstr ""
"Le domaine est libre mais est un premium. Son prix est variable d'un domaine "
"à l'autre"
#: assets/components/tracking/ConnectorForm.tsx:52
#: assets/components/tracking/ConnectorsList.tsx:21 #: assets/components/tracking/ConnectorsList.tsx:21
msgid "Provider" msgid "Provider"
msgstr "Fournisseur" msgstr "Fournisseur"
#: assets/components/tracking/ConnectorForm.tsx:57 #: assets/components/tracking/ConnectorForm.tsx:44
msgid "Please select a Provider" msgid "Please select a Provider"
msgstr "Veuillez sélectionner un fournisseur" msgstr "Veuillez sélectionner un fournisseur"
#: assets/components/tracking/ConnectorForm.tsx:88 #: assets/components/tracking/ConnectorForm.tsx:72
msgid "OVH Endpoint" msgid "OVH Endpoint"
msgstr "Endpoint OVH" msgstr "Endpoint OVH"
#: assets/components/tracking/ConnectorForm.tsx:95 #: assets/components/tracking/ConnectorForm.tsx:79
msgid "OVH subsidiary" msgid "OVH subsidiary"
msgstr "Filiale d'OVH" msgstr "Filiale OVH"
#: assets/components/tracking/ConnectorForm.tsx:103 #: assets/components/tracking/ConnectorForm.tsx:87
msgid "OVH pricing mode" msgid "OVH pricing mode"
msgstr "Mode de tarification OVH" msgstr "Mode de tarification OVH"
#: assets/components/tracking/ConnectorForm.tsx:95
msgid "API Terms of Service"
msgstr "Conditions d'utilisation de l'API"
#: assets/components/tracking/ConnectorForm.tsx:102
msgid ""
"I certify that I have read and accepted the conditions of use of the "
"Provider API, accessible from this hyperlink"
msgstr ""
"Je certifie avoir lu et accepté les conditions d'utilisation de l'API du "
"Fournisseur, accessibles à partir de ce lien hypertexte"
#: assets/components/tracking/ConnectorForm.tsx:108
msgid "Legal age"
msgstr "Âge minimum légal"
#: assets/components/tracking/ConnectorForm.tsx:113
msgid ""
"I certify on my honor that I am of the minimum age required to consent to "
"these conditions"
msgstr ""
"Je certifie sur l'honneur que j'ai l'âge minimum requis pour consentir à ces "
"conditions"
#: assets/components/tracking/ConnectorForm.tsx:117
msgid "Withdrawal period"
msgstr "Délai de rétractation"
#: assets/components/tracking/ConnectorForm.tsx:122
msgid ""
"I expressly waive my right of withdrawal regarding the purchase of domain "
"names via the Provider's API"
msgstr ""
"Je renonce expressément à mon droit de rétractation concernant l'achat de "
"noms de domaine via l'API du Fournisseur"
#: assets/components/tracking/WatchlistsList.tsx:14 #: assets/components/tracking/WatchlistsList.tsx:14
#, javascript-format #, javascript-format
msgid "Watchlist ${ watchlist.token }" msgid "Watchlist ${ watchlist.token }"
@ -313,8 +316,8 @@ msgstr "Trouvé !"
#: assets/pages/search/DomainSearchPage.tsx:23 #: assets/pages/search/DomainSearchPage.tsx:23
#: assets/pages/tracking/ConnectorsPage.tsx:23 #: assets/pages/tracking/ConnectorsPage.tsx:23
#: assets/pages/tracking/ConnectorsPage.tsx:31 #: assets/pages/tracking/ConnectorsPage.tsx:31
#: assets/pages/tracking/WatchlistPage.tsx:35 #: assets/pages/tracking/WatchlistPage.tsx:36
#: assets/pages/tracking/WatchlistPage.tsx:43 #: assets/pages/tracking/WatchlistPage.tsx:44
msgid "An error occurred" msgid "An error occurred"
msgstr "Une erreur s'est produite" msgstr "Une erreur s'est produite"
@ -454,19 +457,19 @@ msgstr "Connecteur créé !"
msgid "Create a Connector" msgid "Create a Connector"
msgstr "Créer un Connecteur" msgstr "Créer un Connecteur"
#: assets/pages/tracking/ConnectorsPage.tsx:48 #: assets/App.tsx:143 assets/pages/tracking/ConnectorsPage.tsx:48
msgid "My Connectors" msgid "My Connectors"
msgstr "Mes Connecteurs" msgstr "Mes Connecteurs"
#: assets/pages/tracking/WatchlistPage.tsx:32 #: assets/pages/tracking/WatchlistPage.tsx:33
msgid "Watchlist created !" msgid "Watchlist created !"
msgstr "Watchlist créée !" msgstr "Watchlist créée !"
#: assets/pages/tracking/WatchlistPage.tsx:53 #: assets/pages/tracking/WatchlistPage.tsx:54
msgid "Create a Watchlist" msgid "Create a Watchlist"
msgstr "Créer une Watchlist" msgstr "Créer une Watchlist"
#: assets/App.tsx:136 assets/pages/tracking/WatchlistPage.tsx:63 #: assets/App.tsx:136 assets/pages/tracking/WatchlistPage.tsx:64
msgid "My Watchlists" msgid "My Watchlists"
msgstr "Mes Watchlists" msgstr "Mes Watchlists"
@ -494,6 +497,45 @@ msgstr "Se connecter"
msgid "Log in with SSO" msgid "Log in with SSO"
msgstr "Se connecter par SSO" msgstr "Se connecter par SSO"
#: assets/utils/providers/index.tsx:11
msgid ""
"Retrieve a set of tokens from your customer account on the Provider's website"
msgstr ""
"Récupérer un ensemble de jetons à partir de votre compte client sur le site "
"web du Fournisseur"
#: assets/utils/providers/ovh.tsx:5
msgid "Application key"
msgstr "Clé d'application"
#: assets/utils/providers/ovh.tsx:6
msgid "Application secret"
msgstr "Clé secrète d'application"
#: assets/utils/providers/ovh.tsx:7
msgid "Consumer key"
msgstr "Clé d'utilisateur"
#: assets/utils/providers/ovh.tsx:12
msgid "European Region"
msgstr "Continent Européen"
#: assets/utils/providers/ovh.tsx:19
msgid "Europe"
msgstr "Europe"
#: assets/utils/providers/ovh.tsx:22
msgid "The domain is free and at the standard price"
msgstr "Le domaine est libre et au prix standard"
#: assets/utils/providers/ovh.tsx:25
msgid ""
"The domain is free but is a premium. Its price varies from one domain to "
"another"
msgstr ""
"Le domaine est libre mais est un premium. Son prix est variable d'un domaine "
"à l'autre"
#: assets/App.tsx:71 #: assets/App.tsx:71
msgid "Home" msgid "Home"
msgstr "Accueil" msgstr "Accueil"
@ -542,10 +584,6 @@ msgstr "Statistiques"
msgid "Tracking" msgid "Tracking"
msgstr "Suivi" msgstr "Suivi"
#: assets/App.tsx:143
msgid "My connectors"
msgstr "Mes connecteurs"
#: assets/App.tsx:151 #: assets/App.tsx:151
msgid "My Watchdog" msgid "My Watchdog"
msgstr "Mon Watchdog" msgstr "Mon Watchdog"
@ -565,3 +603,9 @@ msgstr "FAQ"
#: assets/App.tsx:196 #: assets/App.tsx:196
msgid "Log out" msgid "Log out"
msgstr "Se déconnecter" msgstr "Se déconnecter"
#~ msgid "Buy the domain if available"
#~ msgstr "Acheter le domaine s'il est disponible"
#~ msgid "My connectors"
#~ msgstr "Mes connecteurs"

View File

@ -48,22 +48,24 @@ msgid "ENUM validation expiration"
msgstr "" msgstr ""
#: assets/components/search/DomainSearchBar.tsx:23 #: assets/components/search/DomainSearchBar.tsx:23
#: assets/components/tracking/ConnectorForm.tsx:54 #: assets/components/tracking/ConnectorForm.tsx:40
#: assets/components/tracking/ConnectorForm.tsx:82 #: assets/components/tracking/ConnectorForm.tsx:66
#: assets/components/tracking/ConnectorForm.tsx:90 #: assets/components/tracking/ConnectorForm.tsx:74
#: assets/components/tracking/ConnectorForm.tsx:81
#: assets/components/tracking/ConnectorForm.tsx:89
#: assets/components/tracking/ConnectorForm.tsx:97 #: assets/components/tracking/ConnectorForm.tsx:97
#: assets/components/tracking/ConnectorForm.tsx:105 #: assets/components/tracking/ConnectorForm.tsx:110
#: assets/components/tracking/WatchlistForm.tsx:115 #: assets/components/tracking/ConnectorForm.tsx:119
#: assets/components/tracking/WatchlistForm.tsx:174 #: assets/components/tracking/WatchlistForm.tsx:110
#: assets/components/tracking/WatchlistForm.tsx:184 #: assets/components/tracking/WatchlistForm.tsx:169
#: assets/components/tracking/WatchlistForm.tsx:196 #: assets/components/tracking/WatchlistForm.tsx:179
#: assets/pages/LoginPage.tsx:53 #: assets/pages/LoginPage.tsx:53
#: assets/pages/LoginPage.tsx:61 #: assets/pages/LoginPage.tsx:61
msgid "Required" msgid "Required"
msgstr "" msgstr ""
#: assets/components/search/DomainSearchBar.tsx:26 #: assets/components/search/DomainSearchBar.tsx:26
#: assets/components/tracking/WatchlistForm.tsx:118 #: assets/components/tracking/WatchlistForm.tsx:113
msgid "This domain name does not appear to be valid" msgid "This domain name does not appear to be valid"
msgstr "" msgstr ""
@ -151,112 +153,109 @@ msgstr ""
msgid "Send me an email" msgid "Send me an email"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:78 #: assets/components/tracking/WatchlistForm.tsx:90
msgid "Buy the domain if available"
msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:95
msgid "At least one domain name" msgid "At least one domain name"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:106 #: assets/components/tracking/WatchlistForm.tsx:101
msgid "Domain names" msgid "Domain names"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:124 #: assets/components/tracking/WatchlistForm.tsx:119
#: assets/components/tracking/WatchlistsList.tsx:22 #: assets/components/tracking/WatchlistsList.tsx:22
msgid "Domain name" msgid "Domain name"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:141 #: assets/components/tracking/WatchlistForm.tsx:136
msgid "Add a Domain name" msgid "Add a Domain name"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:154 #: assets/components/tracking/WatchlistForm.tsx:149
msgid "At least one domain trigger" msgid "At least one domain trigger"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:165 #: assets/components/tracking/WatchlistForm.tsx:160
msgid "Domain trigger" msgid "Domain trigger"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:178 #: assets/components/tracking/WatchlistForm.tsx:173
msgid "If this happens" msgid "If this happens"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:188 #: assets/components/tracking/WatchlistForm.tsx:183
msgid "Then do that" msgid "Then do that"
msgstr "" msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:201 #: assets/components/tracking/WatchlistForm.tsx:203
msgid "Connector"
msgstr ""
#: assets/components/tracking/WatchlistForm.tsx:227
msgid "Add a Trigger" msgid "Add a Trigger"
msgstr "" msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:116 #: assets/components/tracking/WatchlistForm.tsx:210
#: assets/components/tracking/WatchlistForm.tsx:237 #: assets/components/tracking/WatchlistForm.tsx:216
msgid "Connector"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:131
#: assets/components/tracking/WatchlistForm.tsx:228
msgid "Create" msgid "Create"
msgstr "" msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:119 #: assets/components/tracking/ConnectorForm.tsx:134
#: assets/components/tracking/WatchlistForm.tsx:240 #: assets/components/tracking/WatchlistForm.tsx:231
msgid "Reset" msgid "Reset"
msgstr "" msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:19 #: assets/components/tracking/ConnectorForm.tsx:37
msgid "Application key"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:20
msgid "Application secret"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:21
msgid "Consumer key"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:26
msgid "European Region"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:31
msgid "Europa"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:36
msgid "The domain is free and at the standard price"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:39
msgid ""
"The domain is free but is a premium. Its price varies from one domain to "
"another"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:52
#: assets/components/tracking/ConnectorsList.tsx:21 #: assets/components/tracking/ConnectorsList.tsx:21
msgid "Provider" msgid "Provider"
msgstr "" msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:57 #: assets/components/tracking/ConnectorForm.tsx:44
msgid "Please select a Provider" msgid "Please select a Provider"
msgstr "" msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:88 #: assets/components/tracking/ConnectorForm.tsx:72
msgid "OVH Endpoint" msgid "OVH Endpoint"
msgstr "" msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:95 #: assets/components/tracking/ConnectorForm.tsx:79
msgid "OVH subsidiary" msgid "OVH subsidiary"
msgstr "" msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:103 #: assets/components/tracking/ConnectorForm.tsx:87
msgid "OVH pricing mode" msgid "OVH pricing mode"
msgstr "" msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:95
msgid "API Terms of Service"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:102
msgid ""
"I certify that I have read and accepted the conditions of use of the "
"Provider API, accessible from this hyperlink"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:108
msgid "Legal age"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:113
msgid ""
"I certify on my honor that I am of the minimum age required to consent to "
"these conditions"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:117
msgid "Withdrawal period"
msgstr ""
#: assets/components/tracking/ConnectorForm.tsx:122
msgid ""
"I expressly waive my right of withdrawal regarding the purchase of domain "
"names via the Provider's API"
msgstr ""
#: assets/components/tracking/WatchlistsList.tsx:14 #: assets/components/tracking/WatchlistsList.tsx:14
#, javascript-format #, javascript-format
msgid "Watchlist ${ watchlist.token }" msgid "Watchlist ${ watchlist.token }"
@ -304,8 +303,8 @@ msgstr ""
#: assets/pages/search/DomainSearchPage.tsx:23 #: assets/pages/search/DomainSearchPage.tsx:23
#: assets/pages/tracking/ConnectorsPage.tsx:23 #: assets/pages/tracking/ConnectorsPage.tsx:23
#: assets/pages/tracking/ConnectorsPage.tsx:31 #: assets/pages/tracking/ConnectorsPage.tsx:31
#: assets/pages/tracking/WatchlistPage.tsx:35 #: assets/pages/tracking/WatchlistPage.tsx:36
#: assets/pages/tracking/WatchlistPage.tsx:43 #: assets/pages/tracking/WatchlistPage.tsx:44
msgid "An error occurred" msgid "An error occurred"
msgstr "" msgstr ""
@ -427,20 +426,21 @@ msgstr ""
msgid "Create a Connector" msgid "Create a Connector"
msgstr "" msgstr ""
#: assets/App.tsx:143
#: assets/pages/tracking/ConnectorsPage.tsx:48 #: assets/pages/tracking/ConnectorsPage.tsx:48
msgid "My Connectors" msgid "My Connectors"
msgstr "" msgstr ""
#: assets/pages/tracking/WatchlistPage.tsx:32 #: assets/pages/tracking/WatchlistPage.tsx:33
msgid "Watchlist created !" msgid "Watchlist created !"
msgstr "" msgstr ""
#: assets/pages/tracking/WatchlistPage.tsx:53 #: assets/pages/tracking/WatchlistPage.tsx:54
msgid "Create a Watchlist" msgid "Create a Watchlist"
msgstr "" msgstr ""
#: assets/App.tsx:136 #: assets/App.tsx:136
#: assets/pages/tracking/WatchlistPage.tsx:63 #: assets/pages/tracking/WatchlistPage.tsx:64
msgid "My Watchlists" msgid "My Watchlists"
msgstr "" msgstr ""
@ -469,6 +469,42 @@ msgstr ""
msgid "Log in with SSO" msgid "Log in with SSO"
msgstr "" msgstr ""
#: assets/utils/providers/index.tsx:11
msgid ""
"Retrieve a set of tokens from your customer account on the Provider's "
"website"
msgstr ""
#: assets/utils/providers/ovh.tsx:5
msgid "Application key"
msgstr ""
#: assets/utils/providers/ovh.tsx:6
msgid "Application secret"
msgstr ""
#: assets/utils/providers/ovh.tsx:7
msgid "Consumer key"
msgstr ""
#: assets/utils/providers/ovh.tsx:12
msgid "European Region"
msgstr ""
#: assets/utils/providers/ovh.tsx:19
msgid "Europe"
msgstr ""
#: assets/utils/providers/ovh.tsx:22
msgid "The domain is free and at the standard price"
msgstr ""
#: assets/utils/providers/ovh.tsx:25
msgid ""
"The domain is free but is a premium. Its price varies from one domain to "
"another"
msgstr ""
#: assets/App.tsx:71 #: assets/App.tsx:71
msgid "Home" msgid "Home"
msgstr "" msgstr ""
@ -517,10 +553,6 @@ msgstr ""
msgid "Tracking" msgid "Tracking"
msgstr "" msgstr ""
#: assets/App.tsx:143
msgid "My connectors"
msgstr ""
#: assets/App.tsx:151 #: assets/App.tsx:151
msgid "My Watchdog" msgid "My Watchdog"
msgstr "" msgstr ""