diff --git a/.env b/.env index 1ad33af..b46f47f 100644 --- a/.env +++ b/.env @@ -42,18 +42,22 @@ CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' ###> symfony/lock ### # Choose one of the stores below # postgresql+advisory://db_user:db_password@localhost/db_name -LOCK_DSN=flock +LOCK_DSN=redis://localhost:6379 ###< symfony/lock ### ###> symfony/mailer ### MAILER_DSN=null://null ###< symfony/mailer ### +MESSENGER_CONSUMER_NAME=worker + ###> symfony/messenger ### # Choose one of the transports below # MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages # MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages -MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 +MESSENGER_ASYNC_TRANSPORT_DSN=redis://localhost:6379/messages?lazy=1 +MESSENGER_RDAP_LOW_TRANSPORT_DSN=redis://localhost:6379/messages-rdap-low?lazy=1 +MESSENGER_RDAP_HIGH_TRANSPORT_DSN=redis://localhost:6379/messages-rdap-high?lazy=1 ###< symfony/messenger ### diff --git a/.github/workflows/lint-and-tests.yml b/.github/workflows/lint-and-tests.yml index aef8b12..1f2163e 100644 --- a/.github/workflows/lint-and-tests.yml +++ b/.github/workflows/lint-and-tests.yml @@ -2,9 +2,11 @@ name: Lint and Tests on: push: - branches: [ "master", "develop" ] + branches: [ "develop" ] + paths-ignore: [ "docs/**" ] pull_request: - branches: [ "master", "develop" ] + branches: [ "develop" ] + paths-ignore: [ "docs/**" ] permissions: contents: read @@ -121,6 +123,15 @@ jobs: --health-retries 5 ports: - 5432:5432 + valkey: + image: valkey/valkey:latest + options: >- + --health-cmd "valkey-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 0000000..713d342 --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,44 @@ +name: Publish Documentation + +on: + push: + paths: [ 'docs/**' ] + branches: [ develop ] + +jobs: + build: + runs-on: ubuntu-latest + environment: + name: Domain Watchdog Documentation + url: https://domainwatchdog.eu + + steps: + - name: Checkout source + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Install dependencies + run: npm install --global yarn && yarn install + working-directory: docs + + - name: Build Astro site + run: yarn run build + working-directory: docs + + - name: Upload build via SCP + uses: appleboy/scp-action@master + with: + host: dw1.srv.domainwatchdog.eu + port: 2004 + username: deploy + key: ${{ secrets.DEPLOYER_PRIVATE_KEY }} + source: "docs/dist/*" + target: /var/www/domainwatchdog.eu/ + overwrite: true + debug: true diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md index 0ddfa50..42b2ce3 100644 --- a/CODE-OF-CONDUCT.md +++ b/CODE-OF-CONDUCT.md @@ -1,118 +1,87 @@ -# Code of conduct +# Contributor Covenant 3.0 Code of Conduct ## Our Pledge -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. +We pledge to make our community welcoming, safe, and equitable for all. -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. +We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant. -## Our Standards -Examples of behavior that contributes to a positive environment for our -community include: +## Encouraged Behaviors -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community +While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language. -Examples of unacceptable behavior include: +With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including: -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +1. Respecting the **purpose of our community**, our activities, and our ways of gathering. +2. Engaging **kindly and honestly** with others. +3. Respecting **different viewpoints** and experiences. +4. **Taking responsibility** for our actions and contributions. +5. Gracefully giving and accepting **constructive feedback**. +6. Committing to **repairing harm** when it occurs. +7. Behaving in other ways that promote and sustain the **well-being of our community**. -## Enforcement Responsibilities -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. +## Restricted Behaviors + +We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct. + +1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop. +2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people. +3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits. +4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community. +5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission. +6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group. +7. Behaving in other ways that **threaten the well-being** of our community. + +### Other Restrictions + +1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions. +2. **Failing to credit sources.** Not properly crediting the sources of content you contribute. +3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community. +4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors. + + +## Reporting an Issue + +Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm. + +When an incident does occur, it is important to report it promptly. To report a possible violation, please contact the maintainer. + +Community Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution. + + +## Addressing and Repairing Harm + +If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped. + +1) Warning + 1) Event: A violation involving a single incident or series of incidents. + 2) Consequence: A private, written warning from the Community Moderators. + 3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations. +2) Temporarily Limited Activities + 1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation. + 2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members. + 3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over. +3) Temporary Suspension + 1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation. + 2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions. + 3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted. +4) Permanent Ban + 1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member. + 2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior. + 3) Repair: There is no possible repair in cases of this severity. + +This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community. -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. ## Scope -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official email address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at contact@maelgangloff.fr. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. +This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/). + +Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/) diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index c6926e6..0000000 --- a/INSTALL.md +++ /dev/null @@ -1,121 +0,0 @@ -# Installation and Update - -## Installation - -To deploy a Domain Watchdog instance, please refer to the Symfony documentation -on [How to deploy a Symfony application](https://symfony.com/doc/current/deployment.html). - -### Prerequisites - -- PHP 8.2 or higher -- PostgreSQL 16 or higher - -In order to retrieve information about domain names, Domain Watchdog will query the RDAP server responsible for the TLD. -It is crucial that the Domain Watchdog instance is placed in a clean environment from which these servers can be -queried. -In particular, the DNS servers and root certificates of the system must be trusted. - -### Steps - -Clone the repository: - -```shell -git clone https://github.com/maelgangloff/domain-watchdog.git - ``` - -#### Backend - -1. Install dependencies: - ```shell - composer install - ``` -2. Set up your environment variables: - ```shell - cp .env .env.local - ``` -3. Generate the cryptographic key pair for the JWT signature - ```shell - php bin/console lexik:jwt:generate-keypair - ``` -4. Run database migrations: - ```shell - php bin/console doctrine:migrations:migrate - ``` -5. Start the Symfony server: - ```shell - symfony server:start - ``` -6. Build assets: - ```shell - php bin/console assets:install - ``` -7. Don't forget to set up workers to process the [message queue](https://symfony.com/doc/current/messenger.html) - -#### Frontend - -1. Install dependencies: - ```shell - yarn install - ``` -2. Generate language files: - ```shell - yarn run ttag:po2json - ``` -3. Make the final build: - ```shell - yarn build - ``` -4. Add and modify the following files as you wish: - ~~~ - public/content/home.md - public/content/privacy.md - public/content/tos.md - public/content/faq.md - public/images/icons-512.png - public/images/banner.png - public/favicon.ico - ~~~ - -## 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 - ``` -4. Build assets: - ```shell - php bin/console assets:install - ``` - -### Frontend - -1. Install dependencies: - ```shell - yarn install - ``` -2. Generate language files: - ```shell - yarn run ttag:po2json - ``` -3. Make the final build: - ```shell - yarn build - ``` diff --git a/README.md b/README.md index ae76943..3abd205 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,9 @@

Your companion in the quest for domain names 🔍
domainwatchdog.eu »


-Domain Watchdog is an app that uses RDAP to collect publicly available info about domains, track their history, and purchase them. -For more information please check [the wiki](https://github.com/maelgangloff/domain-watchdog/wiki) ! +Domain Watchdog is an app that uses RDAP to collect publicly available info about domains, track their history, and +purchase them. +For more information please check out [the documentation](https://domainwatchdog.eu) ! ## Why use it? @@ -20,73 +21,42 @@ detailed history of events (ownership changes, renewals, etc.) is not feasible w ## Install > [!TIP] -> For more details on the installation procedure, please refer to [INSTALL.md](/INSTALL.md). +> For more details on the installation procedure, please refer to [the documentation](https://domainwatchdog.eu/en/self-hosting/docker-install/). ### Docker Deployment -1. Clone the repository -2. Modify environment variables (.env) and add static files to customize your instance (see [INSTALL.md](/INSTALL.md)) -3. Pull the latest version of the Domain Watchdog image from Docker Hub. - ```shell - docker compose pull - ``` -4. Start the project in production environment. If you want, you can also build the Docker image to use yourself. - ```shell - docker compose up - ``` +1. Download the [docker-compose.yml](https://github.com/maelgangloff/domain-watchdog/blob/develop/docker-compose.yml) + and modify it as needed +2. Download the [.env](https://github.com/maelgangloff/domain-watchdog/blob/develop/.env) and modify it as needed +3. Add static files to customize your instance (under `public/content`) +4. Pull the latest version of the Domain Watchdog image from Docker Hub + +```shell +docker compose pull +``` + +5. Start the project in production environment + +```shell +docker compose up +``` By default, the container listens on http://localhost:8080, but you can configure this in environment variables. -See the [Docker Compose file](./docker-compose.yml). -## Features +## Development and contributions -### Auto-purchase domain +See [the documentation](https://domainwatchdog.eu) for information on setting up a development environment and making +your contributions. +To add a new provider, a [dedicated page](https://domainwatchdog.eu/en/developing/implementing-new-provider/) is available. -A connector is a way to order a domain name. It is important to mention that this project does not act as a payment -intermediary. -Indeed, the user's credentials are directly used to enable the purchase via the provider's API. To this end, the user -gives his consent to define the legal framework in which the use of his account with the provider's API will be made. +## Security -The table below lists the supported API connector providers: +Please see [SECURITY.md](./SECURITY.md). -| Provider | Supported | -|:--------------------------------------------------------------------------:|:----------------:| -| [OVH](https://api.ovh.com) | **Yes** | -| [GANDI](https://api.gandi.net/docs/domains/) | **Yes** | -| [NAMECHEAP](https://www.namecheap.com/support/api/methods/domains/create/) | **Yes** | -| [AUTODNS](https://cloud.autodns.com/) | **Yes** | -| [NAME.COM](https://www.name.com/en-en/api-docs/) | **Yes** | -| Custom EPP Server | **EXPERIMENTAL** | +## License -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. - -Note: If the same domain name is present on several Watchlists, it is not possible to predict in advance which user will -win the domain name. The choice is left to chance. - -### Monitoring - -![Watchlist Diagram](https://github.com/user-attachments/assets/c3454572-3ac5-4b39-bc5e-6b7cf72fab92) - - -A watchlist is a list of domain names, triggers and possibly an API connector. - -They allow you to follow the life of the listed domain names and send you a notification when a change has been -detected. - -A notification to the user is sent when a new event occurs on one of the domain names in the Watchlist. This can be an -email or a chat via Webhook (Slack, Mattermost, Discord, ...). An iCalendar export of domain events is possible. - -### RDAP search - -The latest version of the WHOIS protocol was standardized in 2004 by RFC 3912.[^1] This protocol allows anyone to -retrieve key information concerning a domain name, an IP address, or an entity registered with a registry. - -ICANN launched a global vote in 2023 to propose replacing the WHOIS protocol with RDAP. As a result, registries and -registrars will no longer be required to support WHOIS from 2025 (*WHOIS Sunset Date*).[^2] - -Domain Watchdog uses the RDAP protocol, which will soon be the new standard for retrieving information concerning domain -names. +This source code of this project is licensed under *GNU Affero General Public License v3.0 or later*. +Contributions are welcome as long as they do not contravene the Code of Conduct. ## Disclaimer @@ -96,20 +66,5 @@ names. caching system, etc. > * Please note that this project is NOT affiliated IN ANY WAY with the API Providers used to order domain names. > * The project installers are responsible for the use of their own instance. -> * Under no circumstances will the owner of this project be held responsible for other cases over which he has no control. - -## Useful documentation - -> [!NOTE] -> - [RFC 7482 : Registration Data Access Protocol (RDAP) Query Format](https://datatracker.ietf.org/doc/html/rfc7482) -> - [RFC 7483 : JSON Responses for the Registration Data Access Protocol (RDAP)](https://datatracker.ietf.org/doc/html/rfc7483) -> - [RFC 7484 : Finding the Authoritative Registration Data (RDAP) Service](https://datatracker.ietf.org/doc/html/rfc7484) - -## Licensing - -This source code of this project is licensed under *GNU Affero General Public License v3.0 or later*. -Contributions are welcome as long as they do not contravene the Code of Conduct. - -[^1]: RFC 3912 : WHOIS Protocol Specification. (2004). IETF Datatracker. https://datatracker.ietf.org/doc/html/rfc3912 -[^2]: 2023 Global Amendments to the Base gTLD Registry Agreement (RA), Specification 13, and 2013 Registrar -Accreditation Agreement (RAA) - ICANN. (2023). https://www.icann.org/resources/pages/global-amendment-2023-en +> * Under no circumstances will the owner of this project be held responsible for other cases over which he has no + control. diff --git a/assets/App.tsx b/assets/App.tsx index 3a5fa32..1fa2da7 100644 --- a/assets/App.tsx +++ b/assets/App.tsx @@ -1,4 +1,4 @@ -import {Button, ConfigProvider, FloatButton, Layout, Space, theme, Tooltip, Typography} from 'antd' +import {Button, ConfigProvider, Drawer, Flex, Layout, theme, Typography} from 'antd' import {Link, Navigate, Route, Routes, useLocation, useNavigate} from 'react-router-dom' import TextPage from './pages/TextPage' import DomainSearchPage from './pages/search/DomainSearchPage' @@ -7,7 +7,8 @@ import TldPage from './pages/infrastructure/TldPage' import StatisticsPage from './pages/StatisticsPage' import WatchlistPage from './pages/tracking/WatchlistPage' import UserPage from './pages/UserPage' -import React, {useCallback, useEffect, useMemo, useState} from 'react' +import type {PropsWithChildren} from 'react' +import React, { useCallback, useEffect, useMemo, useState} from 'react' import {getUser} from './utils/api' import LoginPage, {AuthenticatedContext} from './pages/LoginPage' import ConnectorPage from './pages/tracking/ConnectorPage' @@ -15,21 +16,56 @@ import NotFoundPage from './pages/NotFoundPage' import useBreakpoint from './hooks/useBreakpoint' import {Sider} from './components/Sider' import {jt, t} from 'ttag' -import {BugOutlined, InfoCircleOutlined, MergeOutlined} from '@ant-design/icons' +import {MenuOutlined} from '@ant-design/icons' import TrackedDomainPage from './pages/tracking/TrackedDomainPage' import IcannRegistrarPage from "./pages/infrastructure/IcannRegistrarPage" const PROJECT_LINK = 'https://github.com/maelgangloff/domain-watchdog' const LICENSE_LINK = 'https://www.gnu.org/licenses/agpl-3.0.txt' -const ProjectLink = Domain Watchdog -const LicenseLink = AGPL-3.0-or-later +const ProjectLink = Domain Watchdog +const LicenseLink = AGPL-3.0-or-later + +function SiderWrapper(props: PropsWithChildren<{sidebarCollapsed: boolean, setSidebarCollapsed: (collapsed: boolean) => void}>): React.ReactElement { + const {sidebarCollapsed, setSidebarCollapsed, children} = props + const sm = useBreakpoint('sm') + const location = useLocation() + + useEffect(() => { + if (sm) { + setSidebarCollapsed(false) + } + }, [location]) + + if (sm) { + return setSidebarCollapsed(false)} + closeIcon={null} + styles={{body: {padding: 0, height: '100%', background: '#001529'}}} + width='200px'> + {children} + + } else { + return + {children} + + } +} export default function App(): React.ReactElement { const navigate = useNavigate() const location = useLocation() const sm = useBreakpoint('sm') + const [sidebarCollapsed, setSidebarCollapsed] = useState(false) const [isAuthenticated, setIsAuthenticated] = useState(false) const authenticated = useCallback((authenticated: boolean) => { @@ -75,15 +111,20 @@ export default function App(): React.ReactElement { > - {/* Ant will use a break-off tab to toggle the collapse of the sider when collapseWidth = 0 */} - + - + - + + {sm && + + } +
@@ -116,41 +157,40 @@ export default function App(): React.ReactElement {
- + - - + + + + + + + {jt`${ProjectLink} is an open source project distributed under the ${LicenseLink} license.`}
- } - > - - } target='_blank' href={PROJECT_LINK}/> - - - } target='_blank' href={PROJECT_LINK + '/issues'}/> - -
diff --git a/assets/components/LoginForm.tsx b/assets/components/LoginForm.tsx index c0e1b4f..966353f 100644 --- a/assets/components/LoginForm.tsx +++ b/assets/components/LoginForm.tsx @@ -1,6 +1,6 @@ -import {Button, Form, Input, message, Space} from 'antd' +import {Button, Flex, Form, Input, message} from 'antd' import {t} from 'ttag' -import React, {useContext, useEffect} from 'react' +import React, {useContext, useEffect, useState} from 'react' import {getUser, login} from '../utils/api' import {AuthenticatedContext} from '../pages/LoginPage' import {useNavigate} from 'react-router-dom' @@ -16,6 +16,7 @@ export function LoginForm({ssoLogin}: { ssoLogin?: boolean }) { const navigate = useNavigate() const [messageApi, contextHolder] = message.useMessage() const {setIsAuthenticated} = useContext(AuthenticatedContext) + const [loading, setLoading] = useState(false) useEffect(() => { getUser().then(() => { @@ -25,12 +26,15 @@ export function LoginForm({ssoLogin}: { ssoLogin?: boolean }) { }, []) const onFinish = (data: FieldType) => { + setLoading(true) + login(data.username, data.password).then(() => { setIsAuthenticated(true) navigate('/home') }).catch((e) => { setIsAuthenticated(false) showErrorAPI(e, messageApi) + setLoading(false) }) } return ( @@ -43,6 +47,7 @@ export function LoginForm({ssoLogin}: { ssoLogin?: boolean }) { style={{maxWidth: 600}} onFinish={onFinish} autoComplete='off' + disabled={loading} > - - + - - {ssoLogin && + {ssoLogin && - } - + } + ) diff --git a/assets/components/tracking/connector/ConnectorsList.tsx b/assets/components/tracking/connector/ConnectorsList.tsx index 0df8af5..a874d4c 100644 --- a/assets/components/tracking/connector/ConnectorsList.tsx +++ b/assets/components/tracking/connector/ConnectorsList.tsx @@ -22,16 +22,16 @@ export function ConnectorsList({connectors, onDelete}: { connectors: ConnectorEl <> {connectors.map(connector => { - const createdAt = + const createdAt = {new Date(connector.createdAt).toLocaleString()} const {watchlistCount} = connector const connectorName = Object.keys(ConnectorProvider).find(p => ConnectorProvider[p as keyof typeof ConnectorProvider] === connector.provider) - return <> - {contextHolder} - + return {t`Connector ${connectorName}`}{connector.id} } size='small' @@ -45,6 +45,7 @@ export function ConnectorsList({connectors, onDelete}: { connectors: ConnectorEl > } > + {contextHolder} {jt`Creation date: ${createdAt}`} {t`Used in: ${watchlistCount} Watchlist`} }/> - } )} diff --git a/assets/components/tracking/watchlist/CreateWatchlistButton.tsx b/assets/components/tracking/watchlist/CreateWatchlistButton.tsx index 13e6863..312616d 100644 --- a/assets/components/tracking/watchlist/CreateWatchlistButton.tsx +++ b/assets/components/tracking/watchlist/CreateWatchlistButton.tsx @@ -42,7 +42,6 @@ export function CreateWatchlistButton({onUpdateWatchlist, connectors}: { paddingBottom: 80 } }} - extra={} > redemption period @@ -35,6 +37,7 @@ export function TrackedDomainTable() { const PENDING_DELETE_NOTICE = ( pending delete @@ -53,6 +56,7 @@ export function TrackedDomainTable() { const [dataTable, setDataTable] = useState([]) const [total, setTotal] = useState() const [specialNotice, setSpecialNotice] = useState([]) + const sm = useBreakpoint('sm') const rdapStatusCodeDetailTranslated = rdapStatusCodeDetailTranslation() @@ -220,6 +224,7 @@ export function TrackedDomainTable() { text: {s} , @@ -268,7 +273,8 @@ export function TrackedDomainTable() { fetchData({page, itemsPerPage}) } }} - scroll={{y: '50vh'}} + scroll={sm ? {} : {y: '50vh'}} + size={sm ? 'small' : 'large'} /> } diff --git a/assets/components/tracking/watchlist/UpdateWatchlistButton.tsx b/assets/components/tracking/watchlist/UpdateWatchlistButton.tsx index ec090a2..51ba24a 100644 --- a/assets/components/tracking/watchlist/UpdateWatchlistButton.tsx +++ b/assets/components/tracking/watchlist/UpdateWatchlistButton.tsx @@ -1,4 +1,4 @@ -import {Button, Drawer, Form, Typography} from 'antd' +import {Drawer, Form, Typography} from 'antd' import {t} from 'ttag' import {WatchlistForm} from './WatchlistForm' import React, {useState} from 'react' @@ -53,7 +53,6 @@ export function UpdateWatchlistButton({watchlist, onUpdateWatchlist, connectors} paddingBottom: 80 } }} - extra={} > MAX_DOMAIN_TAGS) { + rest = domains.slice(MAX_DOMAIN_TAGS) + domains = domains.slice(0, MAX_DOMAIN_TAGS) + } + + return + {watchlist.name} + + {domains.map(d => )} + {rest + && )}> + } color='processing'> + {t`${rest.length} more`} + + + } + + +} + +interface WatchlistSelectionModalProps { + onFinish: (watchlist: Watchlist) => Promise|void + description?: string + open?: boolean + modalProps?: Partial +} + +export default function WatchlistSelectionModal(props: WatchlistSelectionModalProps) { + const [watchlists, setWatchlists] = useState() + const [selectedWatchlist, setSelectedWatchlist] = useState() + const [validationLoading, setValidationLoading] = useState(false) + + useEffect(() => { + if (props.open && !watchlists) { + getWatchlists().then(list => setWatchlists(list["hydra:member"])) + } + }, [props.open]) + + const onFinish = () => { + const promise = props.onFinish(selectedWatchlist as Watchlist) + + if (promise) { + setValidationLoading(true) + promise.finally(() => { + setSelectedWatchlist(undefined) + setValidationLoading(false) + }) + } else { + setSelectedWatchlist(undefined) + } + } + + return + + + { + props.description + || t`Select one of your available Watchlists` + } + +