diff --git a/assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx b/assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx index 94498e2..e724f0d 100644 --- a/assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx +++ b/assets/components/tracking/diagram/ViewDiagramWatchlistButton.tsx @@ -2,72 +2,26 @@ import {Button, Flex, Modal, Space, Typography} from "antd" import {t} from "ttag" import React, {useEffect, useState} from "react" import {ApartmentOutlined} from "@ant-design/icons" -import vCard from "vcf"; import '@xyflow/react/dist/style.css' import {Background, Controls, MiniMap, ReactFlow, useEdgesState, useNodesState} from "@xyflow/react"; -import {getWatchlist, Watchlist} from "../../../utils/api"; -import {translateRoles} from "../../search/EntitiesList"; +import {getWatchlist} from "../../../utils/api"; import {getLayoutedElements} from "./getLayoutedElements"; +import {watchlistToNodes} from "./WatchlistToNodes"; +import {watchlistToEdges} from "./WatchlistToEdges"; - -function watchlistToNodes(watchlist: Watchlist) { - const domains = watchlist.domains.map(d => ({ - id: d.ldhName, - data: {label: {d.ldhName}}, - style: { - width: 200 - } - })) - const entities = [...new Set(watchlist.domains - .map(d => d.entities - .filter(e => !e.roles.includes('registrar')) - .map(e => e.entity - ) - ).flat())].map(e => { - const jCard = vCard.fromJSON(e.jCard) - let label = e.handle - if (jCard.data.fn !== undefined && !Array.isArray(jCard.data.fn)) label = jCard.data.fn.valueOf() - - return { - id: e.handle, - data: {label}, - style: { - width: 200 - } - } - }) - - return [...domains, ...entities] -} - -const rolesToColor = (roles: string[]) => roles.includes('registrant') ? 'green' : - roles.includes('administrative') ? 'blue' : - roles.includes('technical') ? 'orange' : 'violet' - -function watchlistToEdges(watchlist: Watchlist) { - const domainRole = translateRoles() - - return watchlist.domains - .map(d => d.entities - .filter(e => !e.roles.includes('registrar')) - .map(e => ({ - id: `${d.ldhName}-${e.entity.handle}`, - source: e.roles.includes('technical') ? d.ldhName : e.entity.handle, - target: e.roles.includes('technical') ? e.entity.handle : d.ldhName, - style: {stroke: rolesToColor(e.roles), strokeWidth: 3}, - label: e.roles.map(r => Object.keys(domainRole).includes(r) ? domainRole[r as keyof typeof domainRole] : r).join(', '), - animated: e.roles.includes('registrant'), - })) - ).flat(2) +export type DiagramConfig = { + tld?: boolean + nameserver?: boolean + entities?: boolean } export function ViewDiagramWatchlistButton({token}: { token: string }) { const [open, setOpen] = useState(false) const [loading, setLoading] = useState(false) - const [nodes, setNodes, onNodesChange] = useNodesState([]); - const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [nodes, setNodes, onNodesChange] = useNodesState([]) + const [edges, setEdges, onEdgesChange] = useEdgesState([]) useEffect(() => { if (!open) return diff --git a/assets/components/tracking/diagram/WatchlistToEdges.tsx b/assets/components/tracking/diagram/WatchlistToEdges.tsx new file mode 100644 index 0000000..714ed70 --- /dev/null +++ b/assets/components/tracking/diagram/WatchlistToEdges.tsx @@ -0,0 +1,36 @@ +import {Watchlist} from "../../../utils/api"; +import {translateRoles} from "../../search/EntitiesList"; + +const rolesToColor = (roles: string[]) => roles.includes('registrant') ? 'green' : + roles.includes('administrative') ? 'blue' : + roles.includes('technical') ? 'orange' : 'violet' + + +export function watchlistToEdges(watchlist: Watchlist) { + const domainRole = translateRoles() + + const entitiesEdges = watchlist.domains + .map(d => d.entities + .filter(e => !e.roles.includes('registrar')) // + .map(e => ({ + id: `e-${d.ldhName}-${e.entity.handle}`, + source: e.roles.includes('registrant') ? e.entity.handle : d.ldhName, + target: e.roles.includes('registrant') ? d.ldhName : e.entity.handle, + style: {stroke: rolesToColor(e.roles), strokeWidth: 3}, + label: e.roles.map(r => Object.keys(domainRole).includes(r) ? domainRole[r as keyof typeof domainRole] : r).join(', '), + animated: e.roles.includes('registrant'), + })) + ).flat() + + const nameserversEdges = watchlist.domains + .map(d => d.nameservers + .map(ns => ({ + id: `ns-${d.ldhName}-${ns.ldhName}`, + source: d.ldhName, + target: ns.ldhName, + style: {stroke: 'grey', strokeWidth: 3}, + label: 'DNS' + }))).flat() + + return [...entitiesEdges, ...nameserversEdges] +} diff --git a/assets/components/tracking/diagram/WatchlistToNodes.tsx b/assets/components/tracking/diagram/WatchlistToNodes.tsx new file mode 100644 index 0000000..4a2a4d8 --- /dev/null +++ b/assets/components/tracking/diagram/WatchlistToNodes.tsx @@ -0,0 +1,52 @@ +import {Watchlist} from "../../../utils/api"; +import vCard from "vcf"; +import React from "react"; +import {t} from 'ttag' + +export function watchlistToNodes(watchlist: Watchlist) { + const domains = watchlist.domains.map(d => ({ + id: d.ldhName, + data: {label: {d.ldhName}}, + style: { + width: 200 + }, + parentId: d.tld.tld, + extent: 'parent' + })) + const entities = [...new Set(watchlist.domains + .map(d => d.entities + .filter(e => !e.roles.includes('registrar')) // + .map(e => { + const jCard = vCard.fromJSON(e.entity.jCard) + let label = e.entity.handle + if (jCard.data.fn !== undefined && !Array.isArray(jCard.data.fn)) label = jCard.data.fn.valueOf() + + return { + id: e.entity.handle, + data: {label}, + style: { + width: 200 + }, + parentId: d.tld.tld, + extent: 'parent' + } + })).flat())] + + const tlds = [...new Set(watchlist.domains.map(d => d.tld))].map(tld => ({ + id: tld.tld, + data: {label: t`.${tld.tld} Registry`}, + style: { + width: 200 + } + })) + + const nameservers = [...new Set(watchlist.domains.map(d => d.nameservers))].flat().map(ns => ({ + id: ns.ldhName, + data: {label: ns.ldhName}, + style: { + width: 200 + } + })) + + return [...domains, ...entities, ...nameservers] +} \ No newline at end of file diff --git a/src/Service/RDAPService.php b/src/Service/RDAPService.php index c917ff5..ff14969 100644 --- a/src/Service/RDAPService.php +++ b/src/Service/RDAPService.php @@ -278,6 +278,8 @@ readonly class RDAPService } if (array_key_exists('nameservers', $res) && is_array($res['nameservers'])) { + $domain->getNameservers()->clear(); + foreach ($res['nameservers'] as $rdapNameserver) { $nameserver = $this->nameserverRepository->findOneBy([ 'ldhName' => strtolower($rdapNameserver['ldhName']),