mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-17 17:55:42 +00:00
feat: add entities on layout
This commit is contained in:
parent
117a19fa47
commit
68392dd8ec
@ -4,12 +4,13 @@ import {
|
||||
Badge,
|
||||
Card,
|
||||
Divider,
|
||||
Empty,
|
||||
Flex,
|
||||
Form,
|
||||
FormProps,
|
||||
Input,
|
||||
List,
|
||||
message,
|
||||
Segmented,
|
||||
Space,
|
||||
Tag,
|
||||
Timeline,
|
||||
@ -19,6 +20,7 @@ import {
|
||||
BankOutlined,
|
||||
ClockCircleOutlined,
|
||||
DeleteOutlined,
|
||||
IdcardOutlined,
|
||||
SearchOutlined,
|
||||
ShareAltOutlined,
|
||||
SignatureOutlined,
|
||||
@ -28,7 +30,7 @@ import {
|
||||
} from "@ant-design/icons";
|
||||
import {Domain, getDomain} from "../../utils/api";
|
||||
import {AxiosError} from "axios"
|
||||
|
||||
import vCard from 'vcf'
|
||||
|
||||
const {Title} = Typography
|
||||
|
||||
@ -42,10 +44,13 @@ export default function DomainSearchPage() {
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
|
||||
const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
|
||||
getDomain(values.ldhName).then(setDomain).catch((e: AxiosError) => {
|
||||
getDomain(values.ldhName).then(d => {
|
||||
setDomain(d)
|
||||
messageApi.success('Found !')
|
||||
}).catch((e: AxiosError) => {
|
||||
const data = e?.response?.data as { detail: string }
|
||||
messageApi.error(data.detail ?? 'An error occurred')
|
||||
setDomain(null)
|
||||
messageApi.error(data.detail ?? 'An error occurred')
|
||||
})
|
||||
}
|
||||
|
||||
@ -78,87 +83,106 @@ export default function DomainSearchPage() {
|
||||
|
||||
{
|
||||
domain &&
|
||||
<Space direction="vertical" size="middle" style={{width: '100%'}}>
|
||||
<Badge.Ribbon text={`.${domain.tld.tld.toUpperCase()} (${domain.tld.type})`}
|
||||
color={
|
||||
domain.tld.type === 'ccTLD' ? 'purple' :
|
||||
(domain.tld.type === 'gTLD' && domain.tld.specification13) ? "volcano" :
|
||||
domain.tld.type === 'gTLD' ? "green"
|
||||
: "cyan"
|
||||
}>
|
||||
<Card title={`${domain.ldhName}${domain.handle ? ' (' + domain.handle + ')' : ''}`}
|
||||
size="small">
|
||||
{domain.status.length > 0 &&
|
||||
<>
|
||||
<Divider orientation="left">EPP Status Codes</Divider>
|
||||
<Flex gap="4px 0" wrap>
|
||||
{
|
||||
domain.status.map(s =>
|
||||
<Tag color={s === 'active' ? 'green' : 'blue'}>{s}</Tag>
|
||||
)
|
||||
}
|
||||
</Flex>
|
||||
</>
|
||||
}
|
||||
<Divider orientation="left">Timeline</Divider>
|
||||
<Timeline
|
||||
mode="right"
|
||||
items={domain.events
|
||||
.sort((e1, e2) => new Date(e2.date).getTime() - new Date(e1.date).getTime())
|
||||
.map(({action, date}) => {
|
||||
|
||||
let color, dot
|
||||
if (action === 'registration') {
|
||||
color = 'green'
|
||||
dot = <SignatureOutlined style={{fontSize: '16px'}}/>
|
||||
} else if (action === 'expiration') {
|
||||
color = 'red'
|
||||
dot = <ClockCircleOutlined style={{fontSize: '16px'}}/>
|
||||
} else if (action === 'transfer') {
|
||||
color = 'orange'
|
||||
dot = <ShareAltOutlined style={{fontSize: '16px'}}/>
|
||||
} else if (action === 'last changed') {
|
||||
color = 'blue'
|
||||
dot = <SyncOutlined style={{fontSize: '16px'}}/>
|
||||
} else if (action === 'deletion') {
|
||||
color = 'red'
|
||||
dot = <DeleteOutlined style={{fontSize: '16px'}}/>
|
||||
(!domain.deleted ? <Space direction="vertical" size="middle" style={{width: '100%'}}>
|
||||
<Badge.Ribbon text={`.${domain.tld.tld.toUpperCase()} (${domain.tld.type})`}
|
||||
color={
|
||||
domain.tld.type === 'ccTLD' ? 'purple' :
|
||||
(domain.tld.type === 'gTLD' && domain.tld.specification13) ? "volcano" :
|
||||
domain.tld.type === 'gTLD' ? "green"
|
||||
: "cyan"
|
||||
}>
|
||||
<Card title={`${domain.ldhName}${domain.handle ? ' (' + domain.handle + ')' : ''}`}
|
||||
size="small">
|
||||
{domain.status.length > 0 &&
|
||||
<>
|
||||
<Divider orientation="left">EPP Status Codes</Divider>
|
||||
<Flex gap="4px 0" wrap>
|
||||
{
|
||||
domain.status.map(s =>
|
||||
<Tag color={s === 'active' ? 'green' : 'blue'}>{s}</Tag>
|
||||
)
|
||||
}
|
||||
return {
|
||||
label: new Date(date).toUTCString(),
|
||||
children: action,
|
||||
color,
|
||||
dot,
|
||||
pending: new Date(date).getTime() > new Date().getTime()
|
||||
}
|
||||
}
|
||||
)
|
||||
</Flex>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{
|
||||
domain.entities.length > 0 && <>
|
||||
<Divider orientation="left">Entities</Divider>
|
||||
<Segmented
|
||||
options={domain.entities.map(e => ({
|
||||
label: (
|
||||
<div style={{padding: 4}}>
|
||||
<Avatar style={{backgroundColor: '#87d068'}}
|
||||
icon={e.roles.includes('registrant') ?
|
||||
<SignatureOutlined/> : e.roles.includes('registrar') ?
|
||||
<BankOutlined/> :
|
||||
e.roles.includes('technical') ? <ToolOutlined/> :
|
||||
<UserOutlined/>}/>
|
||||
<div>{e.entity.handle}</div>
|
||||
</div>
|
||||
),
|
||||
value: e.entity.handle
|
||||
}))}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
</Card>
|
||||
</Badge.Ribbon>
|
||||
</Space>
|
||||
<Divider orientation="left">Timeline</Divider>
|
||||
<Timeline
|
||||
mode="right"
|
||||
items={domain.events
|
||||
.sort((e1, e2) => new Date(e2.date).getTime() - new Date(e1.date).getTime())
|
||||
.map(({action, date}) => {
|
||||
|
||||
let color, dot
|
||||
if (action === 'registration') {
|
||||
color = 'green'
|
||||
dot = <SignatureOutlined style={{fontSize: '16px'}}/>
|
||||
} else if (action === 'expiration') {
|
||||
color = 'red'
|
||||
dot = <ClockCircleOutlined style={{fontSize: '16px'}}/>
|
||||
} else if (action === 'transfer') {
|
||||
color = 'orange'
|
||||
dot = <ShareAltOutlined style={{fontSize: '16px'}}/>
|
||||
} else if (action === 'last changed') {
|
||||
color = 'blue'
|
||||
dot = <SyncOutlined style={{fontSize: '16px'}}/>
|
||||
} else if (action === 'deletion') {
|
||||
color = 'red'
|
||||
dot = <DeleteOutlined style={{fontSize: '16px'}}/>
|
||||
}
|
||||
return {
|
||||
label: new Date(date).toUTCString(),
|
||||
children: action,
|
||||
color,
|
||||
dot,
|
||||
pending: new Date(date).getTime() > new Date().getTime()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/>
|
||||
{
|
||||
domain.entities.length > 0 &&
|
||||
<>
|
||||
<Divider orientation="left">Entities</Divider>
|
||||
<List
|
||||
className="demo-loadmore-list"
|
||||
itemLayout="horizontal"
|
||||
dataSource={domain.entities}
|
||||
renderItem={(e) => {
|
||||
const jCard = vCard.fromJSON(e.entity.jCard)
|
||||
let name = ''
|
||||
if (jCard.data.fn !== undefined && !Array.isArray(jCard.data.fn)) name = jCard.data.fn.valueOf()
|
||||
|
||||
return <List.Item>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar style={{backgroundColor: '#87d068'}}
|
||||
icon={e.roles.includes('registrant') ?
|
||||
<SignatureOutlined/> : e.roles.includes('registrar') ?
|
||||
<BankOutlined/> :
|
||||
e.roles.includes('technical') ?
|
||||
<ToolOutlined/> :
|
||||
e.roles.includes('administrative') ?
|
||||
<IdcardOutlined/> :
|
||||
<UserOutlined/>}/>}
|
||||
title={e.entity.handle}
|
||||
description={name}
|
||||
/>
|
||||
<div>{e.roles.join(', ')}</div>
|
||||
</List.Item>
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
</Card>
|
||||
</Badge.Ribbon>
|
||||
</Space>
|
||||
: <Empty
|
||||
description={
|
||||
<Typography.Text>
|
||||
Although the domain exists in my database, it has been deleted from the WHOIS by its
|
||||
registrar.
|
||||
</Typography.Text>
|
||||
}/>)
|
||||
}
|
||||
</Card>
|
||||
</Flex>
|
||||
|
||||
@ -23,8 +23,7 @@ export interface Event {
|
||||
|
||||
export interface Entity {
|
||||
handle: string
|
||||
events: Event[]
|
||||
roles: string[]
|
||||
jCard: any
|
||||
}
|
||||
|
||||
export interface Nameserver {
|
||||
@ -55,6 +54,7 @@ export interface Domain {
|
||||
}[]
|
||||
nameservers: Nameserver[]
|
||||
tld: Tld
|
||||
deleted: boolean
|
||||
}
|
||||
|
||||
export interface User {
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
"@types/punycode": "^2.1.4",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/vcf": "^2.0.7",
|
||||
"antd": "^5.19.3",
|
||||
"axios": "^1.7.2",
|
||||
"core-js": "^3.23.0",
|
||||
@ -37,6 +38,7 @@
|
||||
"snarkdown": "^2.0.0",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.5.3",
|
||||
"vcf": "^2.1.2",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-notifier": "^1.15.0"
|
||||
|
||||
@ -42,7 +42,11 @@ class DomainRefreshController extends AbstractController
|
||||
$domain = $this->domainRepository->findOneBy(["ldhName" => $ldhName]);
|
||||
|
||||
// If the domain name exists in the database, recently updated and not important, we return the stored Domain
|
||||
if ($domain !== null && ($domain->getUpdatedAt()->diff(new DateTimeImmutable('now'))->days < 7) && !$this->RDAPService::isToBeWatchClosely($domain, $domain->getUpdatedAt())) return $domain;
|
||||
if ($domain !== null &&
|
||||
!$domain->getDeleted() &&
|
||||
($domain->getUpdatedAt()->diff(new DateTimeImmutable('now'))->days < 7) &&
|
||||
!$this->RDAPService::isToBeWatchClosely($domain, $domain->getUpdatedAt())
|
||||
) return $domain;
|
||||
|
||||
if (false === $kernel->isDebug()) {
|
||||
$limiter = $this->authenticatedApiLimiter->create($this->getUser()->getUserIdentifier());
|
||||
|
||||
@ -33,7 +33,6 @@ use Symfony\Component\Serializer\Attribute\SerializedName;
|
||||
'groups' => [
|
||||
'domain:item',
|
||||
'event:list',
|
||||
'entity:list',
|
||||
'domain-entity:entity',
|
||||
'nameserver-entity:nameserver',
|
||||
'nameserver-entity:entity',
|
||||
|
||||
@ -43,7 +43,7 @@ class Entity
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['entity:list', 'entity:item'])]
|
||||
#[Groups(['entity:list', 'entity:item', 'domain:item'])]
|
||||
private ?string $handle = null;
|
||||
|
||||
/**
|
||||
@ -66,11 +66,11 @@ class Entity
|
||||
* @var Collection<int, EntityEvent>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: EntityEvent::class, mappedBy: 'entity', cascade: ['persist'], orphanRemoval: true)]
|
||||
#[Groups(['entity:list', 'entity:item', 'entity:list'])]
|
||||
#[Groups(['entity:list', 'entity:item', 'entity:list', 'domain:item'])]
|
||||
private Collection $events;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['entity:item'])]
|
||||
#[Groups(['entity:item', 'domain:item'])]
|
||||
private array $jCard = [];
|
||||
|
||||
public function __construct()
|
||||
|
||||
25
yarn.lock
25
yarn.lock
@ -1510,6 +1510,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/vcf@^2.0.7":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/vcf/-/vcf-2.0.7.tgz#3b116db9f67ce9b30ab37f6c6053a37e61443bdb"
|
||||
integrity sha512-PFZg6Ix/hMvURL4JpExCU3jTVEzGrfW+3RYSca+hlGsmW+PQGScc/vH6czR7tAxF01TZTLK8i3tPKE1EYvBOkg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/ws@^8.5.5":
|
||||
version "8.5.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.11.tgz#90ad17b3df7719ce3e6bc32f83ff954d38656508"
|
||||
@ -2051,6 +2058,11 @@ camel-case@^4.1.2:
|
||||
pascal-case "^3.1.2"
|
||||
tslib "^2.0.3"
|
||||
|
||||
camelcase@^5.0.0:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
camelcase@^6.0.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
@ -3024,6 +3036,11 @@ flat@^5.0.2:
|
||||
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
||||
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
||||
|
||||
foldline@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/foldline/-/foldline-1.1.0.tgz#95017b3fc2cff156c167373897e76ceb32ae0964"
|
||||
integrity sha512-9SheyADS50hjvFYjFJ3OB/GlDz2mD1T2CHd7auIk4Uto5YYWPBcw8iYo3F+gENJ+/SOeH9tT0loHZSqlUlumTA==
|
||||
|
||||
follow-redirects@^1.0.0, follow-redirects@^1.15.6:
|
||||
version "1.15.6"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
|
||||
@ -5829,6 +5846,14 @@ vary@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
||||
|
||||
vcf@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vcf/-/vcf-2.1.2.tgz#e21a943fefbc611cdb32daada4f5da1e08a71a64"
|
||||
integrity sha512-oLYtZ+GJPjpKS950fw70+HavdP7ZO2Q+xMCMeCyiUKuXkJJJG1/wUjCKTagPryS1gApYjZOWW/khmdLsch8jxg==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
foldline "^1.1.0"
|
||||
|
||||
watchpack@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user