2024-07-27 01:35:00 +02:00
|
|
|
import React, {useState} from "react";
|
2024-07-27 13:42:46 +02:00
|
|
|
import {
|
2024-07-27 15:45:04 +02:00
|
|
|
Avatar,
|
|
|
|
|
Badge,
|
|
|
|
|
Card,
|
|
|
|
|
Divider,
|
2024-07-27 16:45:20 +02:00
|
|
|
Empty,
|
2024-07-27 15:45:04 +02:00
|
|
|
Flex,
|
|
|
|
|
Form,
|
|
|
|
|
FormProps,
|
|
|
|
|
Input,
|
2024-07-27 16:45:20 +02:00
|
|
|
List,
|
2024-07-27 15:45:04 +02:00
|
|
|
message,
|
2024-07-27 17:02:21 +02:00
|
|
|
Skeleton,
|
2024-07-27 15:45:04 +02:00
|
|
|
Space,
|
|
|
|
|
Tag,
|
|
|
|
|
Timeline,
|
|
|
|
|
Typography
|
|
|
|
|
} from "antd";
|
|
|
|
|
import {
|
|
|
|
|
BankOutlined,
|
2024-07-27 13:42:46 +02:00
|
|
|
ClockCircleOutlined,
|
|
|
|
|
DeleteOutlined,
|
2024-07-27 16:45:20 +02:00
|
|
|
IdcardOutlined,
|
2024-07-27 17:02:21 +02:00
|
|
|
ReloadOutlined,
|
2024-07-27 13:42:46 +02:00
|
|
|
SearchOutlined,
|
|
|
|
|
ShareAltOutlined,
|
|
|
|
|
SignatureOutlined,
|
2024-07-27 15:45:04 +02:00
|
|
|
SyncOutlined,
|
|
|
|
|
ToolOutlined,
|
|
|
|
|
UserOutlined
|
2024-07-27 13:42:46 +02:00
|
|
|
} from "@ant-design/icons";
|
2024-07-27 01:35:00 +02:00
|
|
|
import {Domain, getDomain} from "../../utils/api";
|
|
|
|
|
import {AxiosError} from "axios"
|
2024-07-27 16:45:20 +02:00
|
|
|
import vCard from 'vcf'
|
2024-07-28 15:36:22 +02:00
|
|
|
import {t} from 'ttag'
|
2024-07-27 01:35:00 +02:00
|
|
|
|
2024-07-27 14:40:08 +02:00
|
|
|
|
2024-07-27 01:35:00 +02:00
|
|
|
type FieldType = {
|
|
|
|
|
ldhName: string
|
|
|
|
|
}
|
2024-07-26 16:45:10 +02:00
|
|
|
|
2024-07-28 21:35:38 +02:00
|
|
|
|
2024-07-28 22:14:53 +02:00
|
|
|
const locale = navigator.language.split('-')[0]
|
|
|
|
|
|
2024-07-26 16:45:10 +02:00
|
|
|
export default function DomainSearchPage() {
|
2024-07-29 00:04:45 +02:00
|
|
|
const domainRole = {
|
|
|
|
|
registrant: t`Registrant`,
|
|
|
|
|
technical: t`Technical`,
|
|
|
|
|
administrative: t`Administrative`,
|
|
|
|
|
abuse: t`Abuse`,
|
|
|
|
|
billing: t`Billing`,
|
|
|
|
|
registrar: t`Registrar`,
|
|
|
|
|
reseller: t`Reseller`,
|
|
|
|
|
sponsor: t`Sponsor`,
|
|
|
|
|
proxy: t`Proxy`,
|
|
|
|
|
notifications: t`Notifications`,
|
|
|
|
|
noc: t`Noc`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const domainEvent = {
|
|
|
|
|
registration: t`Registration`,
|
|
|
|
|
reregistration: t`Reregistration`,
|
|
|
|
|
'last changed': t`Last changed`,
|
|
|
|
|
expiration: t`Expiration`,
|
|
|
|
|
deletion: t`Deletion`,
|
|
|
|
|
reinstantiation: t`Reinstantiation`,
|
|
|
|
|
transfer: t`Transfer`,
|
|
|
|
|
locked: t`Locked`,
|
|
|
|
|
unlocked: t`Unlocked`,
|
|
|
|
|
'registrar expiration': t`Registrar expiration`,
|
|
|
|
|
'enum validation expiration': t`ENUM validation expiration`
|
|
|
|
|
}
|
2024-07-27 01:35:00 +02:00
|
|
|
|
2024-07-27 17:02:21 +02:00
|
|
|
const [domain, setDomain] = useState<Domain | null>()
|
2024-07-27 01:35:00 +02:00
|
|
|
const [messageApi, contextHolder] = message.useMessage()
|
|
|
|
|
|
|
|
|
|
const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
|
2024-07-27 17:02:21 +02:00
|
|
|
setDomain(null)
|
2024-07-27 16:45:20 +02:00
|
|
|
getDomain(values.ldhName).then(d => {
|
|
|
|
|
setDomain(d)
|
2024-07-28 15:36:22 +02:00
|
|
|
messageApi.success(t`Found !`)
|
2024-07-27 16:45:20 +02:00
|
|
|
}).catch((e: AxiosError) => {
|
2024-07-27 01:35:00 +02:00
|
|
|
const data = e?.response?.data as { detail: string }
|
2024-07-27 17:02:21 +02:00
|
|
|
setDomain(undefined)
|
2024-07-28 15:36:22 +02:00
|
|
|
messageApi.error(data.detail ?? t`An error occurred`)
|
2024-07-27 01:35:00 +02:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <Flex gap="middle" align="center" justify="center" vertical>
|
2024-07-28 15:36:22 +02:00
|
|
|
<Card title={t`Domain finder`} style={{width: '100%'}}>
|
2024-07-27 01:35:00 +02:00
|
|
|
{contextHolder}
|
|
|
|
|
<Form
|
|
|
|
|
name="basic"
|
|
|
|
|
labelCol={{span: 8}}
|
|
|
|
|
wrapperCol={{span: 16}}
|
|
|
|
|
onFinish={onFinish}
|
|
|
|
|
autoComplete="off"
|
|
|
|
|
>
|
|
|
|
|
<Form.Item<FieldType>
|
|
|
|
|
name="ldhName"
|
|
|
|
|
rules={[{
|
|
|
|
|
required: true,
|
2024-07-28 15:36:22 +02:00
|
|
|
message: t`Required`
|
2024-07-27 01:35:00 +02:00
|
|
|
}, {
|
|
|
|
|
pattern: /^(?=.*\.)\S*[^.\s]$/,
|
2024-07-28 15:36:22 +02:00
|
|
|
message: t`This domain name does not appear to be valid`,
|
2024-07-27 01:35:00 +02:00
|
|
|
max: 63,
|
|
|
|
|
min: 2
|
|
|
|
|
}]}
|
|
|
|
|
>
|
2024-07-27 17:02:21 +02:00
|
|
|
<Input size="large" prefix={<SearchOutlined/>} placeholder="example.com" autoFocus={true}
|
|
|
|
|
autoComplete='off'/>
|
2024-07-27 01:35:00 +02:00
|
|
|
</Form.Item>
|
|
|
|
|
</Form>
|
2024-07-27 14:40:08 +02:00
|
|
|
|
2024-07-27 17:02:21 +02:00
|
|
|
<Skeleton loading={domain === null} active>
|
|
|
|
|
{
|
|
|
|
|
domain &&
|
|
|
|
|
(!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 &&
|
|
|
|
|
<>
|
2024-07-28 21:51:37 +02:00
|
|
|
<Divider orientation="left">{t`EPP Status Codes`}</Divider>
|
2024-07-27 17:02:21 +02:00
|
|
|
<Flex gap="4px 0" wrap>
|
|
|
|
|
{
|
|
|
|
|
domain.status.map(s =>
|
|
|
|
|
<Tag color={s === 'active' ? 'green' : 'blue'}>{s}</Tag>
|
|
|
|
|
)
|
2024-07-27 16:45:20 +02:00
|
|
|
}
|
2024-07-27 17:02:21 +02:00
|
|
|
</Flex>
|
|
|
|
|
</>
|
2024-07-27 16:45:20 +02:00
|
|
|
}
|
2024-07-28 21:51:37 +02:00
|
|
|
<Divider orientation="left">{t`Timeline`}</Divider>
|
2024-07-27 17:02:21 +02:00
|
|
|
<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'}}/>
|
|
|
|
|
} else if (action === 'reregistration') {
|
|
|
|
|
color = 'green'
|
|
|
|
|
dot = <ReloadOutlined style={{fontSize: '16px'}}/>
|
|
|
|
|
}
|
2024-07-27 16:45:20 +02:00
|
|
|
|
2024-07-27 17:02:21 +02:00
|
|
|
return {
|
2024-07-28 22:14:53 +02:00
|
|
|
label: new Date(date).toLocaleString(locale),
|
2024-07-29 00:04:45 +02:00
|
|
|
children: Object.keys(domainEvent).includes(action) ? domainEvent[action as keyof typeof domainEvent] : action,
|
2024-07-27 17:02:21 +02:00
|
|
|
color,
|
|
|
|
|
dot,
|
|
|
|
|
pending: new Date(date).getTime() > new Date().getTime()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
{
|
|
|
|
|
domain.entities.length > 0 &&
|
|
|
|
|
<>
|
2024-07-28 21:51:37 +02:00
|
|
|
<Divider orientation="left">{t`Entities`}</Divider>
|
2024-07-27 17:02:21 +02:00
|
|
|
<List
|
|
|
|
|
className="demo-loadmore-list"
|
|
|
|
|
itemLayout="horizontal"
|
2024-07-27 17:19:25 +02:00
|
|
|
dataSource={domain.entities.sort((e1, e2) => {
|
|
|
|
|
const p = (r: string[]) => r.includes('registrant') ? 4 : r.includes('administrative') ? 3 : r.includes('billing') ? 2 : 1
|
|
|
|
|
return p(e2.roles) - p(e1.roles)
|
|
|
|
|
})}
|
2024-07-27 17:02:21 +02:00
|
|
|
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}
|
|
|
|
|
/>
|
2024-07-29 00:04:45 +02:00
|
|
|
<div>{e.roles.map((r) => Object.keys(domainRole).includes(r) ? domainRole[r as keyof typeof domainRole] : r).join(', ')}</div>
|
2024-07-27 17:02:21 +02:00
|
|
|
</List.Item>
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
</Card>
|
|
|
|
|
</Badge.Ribbon>
|
|
|
|
|
</Space>
|
|
|
|
|
: <Empty
|
|
|
|
|
description={
|
|
|
|
|
<Typography.Text>
|
2024-07-28 15:36:22 +02:00
|
|
|
{t`Although the domain exists in my database, it has been deleted from the WHOIS by its registrar.`}
|
2024-07-27 17:02:21 +02:00
|
|
|
</Typography.Text>
|
|
|
|
|
}/>)
|
|
|
|
|
}
|
|
|
|
|
</Skeleton>
|
2024-07-27 01:35:00 +02:00
|
|
|
</Card>
|
|
|
|
|
</Flex>
|
2024-07-26 16:45:10 +02:00
|
|
|
}
|