mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-29 16:15:04 +00:00
feat: improve ui
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import {Badge, Card, Divider, Flex, Space, Tag, Tooltip, Typography} from "antd";
|
import {Badge, Card, Col, Divider, Flex, Row, Space, Tag, Tooltip, Typography} from "antd";
|
||||||
import {t} from "ttag";
|
import {t} from "ttag";
|
||||||
import {EventTimeline} from "./EventTimeline";
|
import {EventTimeline} from "./EventTimeline";
|
||||||
import {EntitiesList} from "./EntitiesList";
|
import {EntitiesList} from "./EntitiesList";
|
||||||
@@ -11,12 +11,14 @@ import {regionNames} from "../../i18n";
|
|||||||
import {getCountryCode} from "../../utils/functions/getCountryCode";
|
import {getCountryCode} from "../../utils/functions/getCountryCode";
|
||||||
import {eppStatusCodeToColor} from "../../utils/functions/eppStatusCodeToColor";
|
import {eppStatusCodeToColor} from "../../utils/functions/eppStatusCodeToColor";
|
||||||
import {DomainLifecycleSteps} from "./DomainLifecycleSteps";
|
import {DomainLifecycleSteps} from "./DomainLifecycleSteps";
|
||||||
|
import useBreakpoint from "../../hooks/useBreakpoint";
|
||||||
|
|
||||||
export function DomainResult({domain}: { domain: Domain }) {
|
export function DomainResult({domain}: { domain: Domain }) {
|
||||||
|
|
||||||
const rdapStatusCodeDetailTranslated = rdapStatusCodeDetailTranslation()
|
const rdapStatusCodeDetailTranslated = rdapStatusCodeDetailTranslation()
|
||||||
const {tld, events} = domain
|
const {tld, events} = domain
|
||||||
const domainEvents = events.sort((e1, e2) => new Date(e2.date).getTime() - new Date(e1.date).getTime())
|
const domainEvents = events.sort((e1, e2) => new Date(e2.date).getTime() - new Date(e1.date).getTime())
|
||||||
|
const xxl = useBreakpoint('xxl')
|
||||||
|
|
||||||
return <Space direction="vertical" size="middle" style={{width: '100%'}}>
|
return <Space direction="vertical" size="middle" style={{width: '100%'}}>
|
||||||
|
|
||||||
@@ -40,37 +42,46 @@ export function DomainResult({domain}: { domain: Domain }) {
|
|||||||
{
|
{
|
||||||
domain.events.length > 0 && <DomainLifecycleSteps status={domain.status}/>
|
domain.events.length > 0 && <DomainLifecycleSteps status={domain.status}/>
|
||||||
}
|
}
|
||||||
{domain.status.length > 0 &&
|
<Row gutter={8}>
|
||||||
<>
|
<Col span={xxl ? 24 : 12}>
|
||||||
<Divider orientation="left">{t`EPP Status Codes`}</Divider>
|
{domain.status.length > 0 &&
|
||||||
<Flex gap="4px 0" wrap>
|
<>
|
||||||
{
|
<Divider orientation="left">{t`EPP Status Codes`}</Divider>
|
||||||
domain.status.map(s =>
|
<Flex gap="4px 0" wrap>
|
||||||
<Tooltip
|
{
|
||||||
placement='bottomLeft'
|
domain.status.map(s =>
|
||||||
title={s in rdapStatusCodeDetailTranslated ? rdapStatusCodeDetailTranslated[s as keyof typeof rdapStatusCodeDetailTranslated] : undefined}>
|
<Tooltip
|
||||||
<Tag color={eppStatusCodeToColor(s)}>{s}</Tag>
|
placement='bottomLeft'
|
||||||
</Tooltip>
|
title={s in rdapStatusCodeDetailTranslated ? rdapStatusCodeDetailTranslated[s as keyof typeof rdapStatusCodeDetailTranslated] : undefined}>
|
||||||
)
|
<Tag color={eppStatusCodeToColor(s)}>{s}</Tag>
|
||||||
}
|
</Tooltip>
|
||||||
</Flex>
|
)
|
||||||
</>
|
}
|
||||||
}
|
</Flex>
|
||||||
{
|
</>
|
||||||
domain.events.length > 0 && <>
|
}
|
||||||
<Divider orientation="left">{t`Timeline`}</Divider>
|
{
|
||||||
<EventTimeline events={domainEvents}/>
|
domain.events.length > 0 && <>
|
||||||
</>
|
<Divider orientation="left">{t`Timeline`}</Divider>
|
||||||
}
|
<EventTimeline events={domainEvents}/>
|
||||||
{
|
</>
|
||||||
domain.entities.length > 0 &&
|
}
|
||||||
<>
|
{
|
||||||
<Divider orientation="left">{t`Entities`}</Divider>
|
domain.entities.length > 0 &&
|
||||||
<EntitiesList domain={domain}/>
|
<>
|
||||||
</>
|
<Divider orientation="left">{t`Entities`}</Divider>
|
||||||
}
|
<EntitiesList domain={domain}/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</Col>
|
||||||
|
{!xxl &&
|
||||||
|
<Col span={12}>
|
||||||
|
<DomainDiagram domain={domain}/>
|
||||||
|
</Col>
|
||||||
|
}
|
||||||
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
</Badge.Ribbon>
|
</Badge.Ribbon>
|
||||||
<DomainDiagram domain={domain}/>
|
{xxl && <DomainDiagram domain={domain}/>}
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
@@ -4,9 +4,8 @@ import {Domain} from "../../utils/api";
|
|||||||
import {rdapRoleDetailTranslation, rdapRoleTranslation} from "../../utils/functions/rdapTranslation";
|
import {rdapRoleDetailTranslation, rdapRoleTranslation} from "../../utils/functions/rdapTranslation";
|
||||||
import {roleToAvatar} from "../../utils/functions/roleToAvatar";
|
import {roleToAvatar} from "../../utils/functions/roleToAvatar";
|
||||||
import {rolesToColor} from "../../utils/functions/rolesToColor";
|
import {rolesToColor} from "../../utils/functions/rolesToColor";
|
||||||
import {entityToName} from "../../utils/functions/entityToName";
|
|
||||||
import {sortDomainEntities} from "../../utils/functions/sortDomainEntities";
|
import {sortDomainEntities} from "../../utils/functions/sortDomainEntities";
|
||||||
|
import {extractDetailsFromJCard} from "../../utils/functions/extractDetailsFromJCard";
|
||||||
|
|
||||||
export function EntitiesList({domain}: { domain: Domain }) {
|
export function EntitiesList({domain}: { domain: Domain }) {
|
||||||
const rdapRoleTranslated = rdapRoleTranslation()
|
const rdapRoleTranslated = rdapRoleTranslation()
|
||||||
@@ -23,15 +22,21 @@ export function EntitiesList({domain}: { domain: Domain }) {
|
|||||||
className="demo-loadmore-list"
|
className="demo-loadmore-list"
|
||||||
itemLayout="horizontal"
|
itemLayout="horizontal"
|
||||||
dataSource={sortDomainEntities(domain)}
|
dataSource={sortDomainEntities(domain)}
|
||||||
renderItem={(e) =>
|
renderItem={(e) => {
|
||||||
<List.Item>
|
const details = extractDetailsFromJCard(e)
|
||||||
|
|
||||||
|
return <List.Item>
|
||||||
<List.Item.Meta
|
<List.Item.Meta
|
||||||
avatar={roleToAvatar(e)}
|
avatar={roleToAvatar(e)}
|
||||||
title={e.entity.handle}
|
title={e.entity.handle}
|
||||||
description={entityToName(e)}
|
description={<>
|
||||||
|
{details.fn && <div>👤 {details.fn}</div>}
|
||||||
|
{details.organization && <div>🏢 {details.organization}</div>}
|
||||||
|
</>}
|
||||||
/>
|
/>
|
||||||
{e.roles.map(roleToTag)}
|
{e.roles.map(roleToTag)}
|
||||||
</List.Item>
|
</List.Item>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -7,5 +7,7 @@ export const entityToName = (e: { entity: Entity }): string => {
|
|||||||
const jCard = vCard.fromJSON(e.entity.jCard)
|
const jCard = vCard.fromJSON(e.entity.jCard)
|
||||||
let name = e.entity.handle
|
let name = e.entity.handle
|
||||||
if (jCard.data.fn && !Array.isArray(jCard.data.fn) && jCard.data.fn.valueOf() !== '') name = jCard.data.fn.valueOf()
|
if (jCard.data.fn && !Array.isArray(jCard.data.fn) && jCard.data.fn.valueOf() !== '') name = jCard.data.fn.valueOf()
|
||||||
|
if (jCard.data.org && !Array.isArray(jCard.data.org) && jCard.data.org.valueOf() !== '') name = jCard.data.org.valueOf()
|
||||||
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
14
assets/utils/functions/extractDetailsFromJCard.tsx
Normal file
14
assets/utils/functions/extractDetailsFromJCard.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import vCard from "vcf";
|
||||||
|
import {Entity} from "../api";
|
||||||
|
|
||||||
|
export const extractDetailsFromJCard = (e: { entity: Entity }): {
|
||||||
|
fn?: string
|
||||||
|
organization?: string;
|
||||||
|
} => {
|
||||||
|
if (e.entity.jCard.length === 0) return {fn: e.entity.handle}
|
||||||
|
const jCard = vCard.fromJSON(e.entity.jCard)
|
||||||
|
const fn = jCard.data.fn && !Array.isArray(jCard.data.fn) ? jCard.data.fn.valueOf() : undefined
|
||||||
|
const organization = jCard.data.org && !Array.isArray(jCard.data.org) ? jCard.data.org.valueOf() : undefined
|
||||||
|
|
||||||
|
return {fn, organization}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace App\Repository;
|
|||||||
|
|
||||||
use App\Entity\Entity;
|
use App\Entity\Entity;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\DBAL\Exception;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,6 +17,17 @@ class EntityRepository extends ServiceEntityRepository
|
|||||||
parent::__construct($registry, Entity::class);
|
parent::__construct($registry, Entity::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function findByjCard(array $vCardArray): array
|
||||||
|
{
|
||||||
|
return $this->getEntityManager()->getConnection()
|
||||||
|
->prepare('SELECT * FROM entity WHERE j_Card @> :data')
|
||||||
|
->executeQuery(['data' => json_encode($vCardArray)])
|
||||||
|
->fetchAllAssociative();
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @return Entity[] Returns an array of Entity objects
|
// * @return Entity[] Returns an array of Entity objects
|
||||||
// */
|
// */
|
||||||
|
|||||||
@@ -252,11 +252,11 @@ readonly class RDAPService
|
|||||||
|
|
||||||
if (array_key_exists('entities', $res) && is_array($res['entities'])) {
|
if (array_key_exists('entities', $res) && is_array($res['entities'])) {
|
||||||
foreach ($res['entities'] as $rdapEntity) {
|
foreach ($res['entities'] as $rdapEntity) {
|
||||||
|
if ((!array_key_exists('handle', $rdapEntity) || '' === $rdapEntity['handle']) && !array_key_exists('vcardArray', $rdapEntity)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$entity = $this->registerEntity($rdapEntity);
|
$entity = $this->registerEntity($rdapEntity);
|
||||||
|
|
||||||
$this->em->persist($entity);
|
|
||||||
$this->em->flush();
|
|
||||||
|
|
||||||
$domainEntity = $this->domainEntityRepository->findOneBy([
|
$domainEntity = $this->domainEntityRepository->findOneBy([
|
||||||
'domain' => $domain,
|
'domain' => $domain,
|
||||||
'entity' => $entity,
|
'entity' => $entity,
|
||||||
@@ -323,14 +323,11 @@ readonly class RDAPService
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($rdapNameserver['entities'] as $rdapEntity) {
|
foreach ($rdapNameserver['entities'] as $rdapEntity) {
|
||||||
if (!array_key_exists('handle', $rdapEntity) || '' === $rdapEntity['handle']) {
|
if ((!array_key_exists('handle', $rdapEntity) || '' === $rdapEntity['handle']) && !array_key_exists('vcardArray', $rdapEntity)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$entity = $this->registerEntity($rdapEntity);
|
$entity = $this->registerEntity($rdapEntity);
|
||||||
|
|
||||||
$this->em->persist($entity);
|
|
||||||
$this->em->flush();
|
|
||||||
|
|
||||||
$nameserverEntity = $this->nameserverEntityRepository->findOneBy([
|
$nameserverEntity = $this->nameserverEntityRepository->findOneBy([
|
||||||
'nameserver' => $nameserver,
|
'nameserver' => $nameserver,
|
||||||
'entity' => $entity,
|
'entity' => $entity,
|
||||||
@@ -391,20 +388,31 @@ readonly class RDAPService
|
|||||||
*/
|
*/
|
||||||
private function registerEntity(array $rdapEntity): Entity
|
private function registerEntity(array $rdapEntity): Entity
|
||||||
{
|
{
|
||||||
$conn = $this->em->getConnection();
|
if (array_key_exists('vcardArray', $rdapEntity)) {
|
||||||
$sql = 'SELECT * FROM entity WHERE j_Card @> :data';
|
$result = $this->entityRepository->findByjCard($rdapEntity['vcardArray']);
|
||||||
$stmt = $conn->prepare($sql)->executeQuery(['data' => json_encode($rdapEntity['vcardArray'])]);
|
|
||||||
|
|
||||||
$result = $stmt->fetchAllAssociative();
|
if (!array_key_exists('handle', $rdapEntity) || '' === $rdapEntity['handle']) {
|
||||||
|
if (count($result) > 0) {
|
||||||
|
$rdapEntity['handle'] = $result[0]['handle'];
|
||||||
|
} else {
|
||||||
|
$rdapEntity['handle'] = 'DW-NOHANDLE-'.hash('md5', json_encode($rdapEntity['vcardArray']));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (count($result) > 0) {
|
||||||
|
$entity = $this->entityRepository->findOneBy(['handle' => $result[0]['handle']]);
|
||||||
|
|
||||||
$rdapEntity['handle'] = array_key_exists('handle', $rdapEntity) && '' !== $rdapEntity['handle']
|
if (null !== $entity) {
|
||||||
? $rdapEntity['handle']
|
$domainEntities = $this->domainEntityRepository->findBy([
|
||||||
: (
|
'entity' => $entity,
|
||||||
count($result) > 0
|
]);
|
||||||
? $result[0]['handle']
|
|
||||||
: 'DW-NOHANDLE-'.hash('md5', json_encode($rdapEntity)
|
$nameserverEntities = $this->nameserverEntityRepository->findBy([
|
||||||
)
|
'entity' => $entity,
|
||||||
);
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$entity = $this->entityRepository->findOneBy([
|
$entity = $this->entityRepository->findOneBy([
|
||||||
'handle' => $rdapEntity['handle'],
|
'handle' => $rdapEntity['handle'],
|
||||||
@@ -468,6 +476,23 @@ readonly class RDAPService
|
|||||||
->setDeleted(false));
|
->setDeleted(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->em->persist($entity);
|
||||||
|
$this->em->flush();
|
||||||
|
|
||||||
|
if (isset($domainEntities)) {
|
||||||
|
/** @var DomainEntity[] $domainEntities */
|
||||||
|
foreach ($domainEntities as $domainEntity) {
|
||||||
|
$domainEntity->setEntity($entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($nameserverEntities)) {
|
||||||
|
/** @var NameserverEntity[] $nameserverEntities */
|
||||||
|
foreach ($nameserverEntities as $nameserverEntity) {
|
||||||
|
$nameserverEntity->setEntity($entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $entity;
|
return $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user