mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-17 17:55:42 +00:00
feat: update front layout
This commit is contained in:
parent
465aeb1ec9
commit
08da62c1b5
@ -4,6 +4,7 @@ import {Bookmark, ChevronLeft, Dns, Explore, LocalPolice, MenuBook, People} from
|
|||||||
import {Divider, List, ListItemButton, ListItemIcon, ListItemText, ListSubheader, styled} from "@mui/material";
|
import {Divider, List, ListItemButton, ListItemIcon, ListItemText, ListSubheader, styled} from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import MuiDrawer from "@mui/material/Drawer";
|
import MuiDrawer from "@mui/material/Drawer";
|
||||||
|
import {useNavigate} from "react-router-dom";
|
||||||
|
|
||||||
|
|
||||||
const Drawer = styled(MuiDrawer, {shouldForwardProp: (prop) => prop !== 'open'})(
|
const Drawer = styled(MuiDrawer, {shouldForwardProp: (prop) => prop !== 'open'})(
|
||||||
@ -37,6 +38,8 @@ export default function DrawerBox() {
|
|||||||
const toggleDrawer = () => {
|
const toggleDrawer = () => {
|
||||||
setOpen(!open);
|
setOpen(!open);
|
||||||
};
|
};
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
return <Drawer variant="permanent" open={open}>
|
return <Drawer variant="permanent" open={open}>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
sx={{
|
sx={{
|
||||||
@ -56,19 +59,19 @@ export default function DrawerBox() {
|
|||||||
<ListSubheader component="div" inset>
|
<ListSubheader component="div" inset>
|
||||||
Domain names
|
Domain names
|
||||||
</ListSubheader>
|
</ListSubheader>
|
||||||
<ListItemButton>
|
<ListItemButton onClick={() => navigate('/finder/domain')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Explore/>
|
<Explore/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Domain finder"/>
|
<ListItemText primary="Domain finder"/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
<ListItemButton>
|
<ListItemButton onClick={() => navigate('/tld')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<LocalPolice/>
|
<LocalPolice/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Top Level Domain"/>
|
<ListItemText primary="Top Level Domain"/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
<ListItemButton>
|
<ListItemButton onClick={() => navigate('/finder/nameserver')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Dns/>
|
<Dns/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
@ -79,13 +82,13 @@ export default function DrawerBox() {
|
|||||||
<ListSubheader component="div" inset>
|
<ListSubheader component="div" inset>
|
||||||
Entities
|
Entities
|
||||||
</ListSubheader>
|
</ListSubheader>
|
||||||
<ListItemButton>
|
<ListItemButton onClick={() => navigate('/finder/entity')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<People/>
|
<People/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Entity finder"/>
|
<ListItemText primary="Entity finder"/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
<ListItemButton>
|
<ListItemButton onClick={() => navigate('/reverse')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<MenuBook/>
|
<MenuBook/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
@ -96,7 +99,7 @@ export default function DrawerBox() {
|
|||||||
<ListSubheader component="div" inset>
|
<ListSubheader component="div" inset>
|
||||||
Tracking
|
Tracking
|
||||||
</ListSubheader>
|
</ListSubheader>
|
||||||
<ListItemButton>
|
<ListItemButton onClick={() => navigate('/watchlist')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Bookmark/>
|
<Bookmark/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
|||||||
@ -6,18 +6,10 @@ interface Column {
|
|||||||
label: string;
|
label: string;
|
||||||
minWidth?: number;
|
minWidth?: number;
|
||||||
align?: 'right';
|
align?: 'right';
|
||||||
format?: (value: number) => string;
|
format?: (value: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Data {
|
export default function HeadTable({columns, rows}: { rows: any[], columns: Column[] }) {
|
||||||
name: string;
|
|
||||||
code: string;
|
|
||||||
population: number;
|
|
||||||
size: number;
|
|
||||||
density: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function StickyHeadTable({columns, rows}: { rows: Data[], columns: Column[] }) {
|
|
||||||
const [page, setPage] = React.useState(0);
|
const [page, setPage] = React.useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = React.useState(10);
|
const [rowsPerPage, setRowsPerPage] = React.useState(10);
|
||||||
|
|
||||||
@ -32,8 +24,8 @@ export default function StickyHeadTable({columns, rows}: { rows: Data[], columns
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper sx={{width: '100%', overflow: 'hidden'}}>
|
<Paper sx={{width: '100%', overflow: 'hidden'}}>
|
||||||
<TableContainer sx={{maxHeight: 440}}>
|
<TableContainer>
|
||||||
<Table stickyHeader aria-label="sticky table">
|
<Table size="small">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{columns.map((column) => (
|
{columns.map((column) => (
|
||||||
@ -50,16 +42,15 @@ export default function StickyHeadTable({columns, rows}: { rows: Data[], columns
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
{rows
|
{rows
|
||||||
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
||||||
.map((row: Data) => {
|
.map((row: any) => {
|
||||||
return (
|
return (
|
||||||
<TableRow hover role="checkbox" tabIndex={-1} key={row.code}>
|
<TableRow hover sx={{'&:last-child td, &:last-child th': {border: 0}}}
|
||||||
|
role="checkbox" tabIndex={-1} key={row.code}>
|
||||||
{columns.map((column) => {
|
{columns.map((column) => {
|
||||||
const value = row[column.id as keyof typeof row]
|
const value = row[column.id as keyof typeof row]
|
||||||
return (
|
return (
|
||||||
<TableCell key={column.id} align={column.align}>
|
<TableCell key={column.id} align={column.align}>
|
||||||
{column.format && typeof value === 'number'
|
{column.format ? column.format(value) : value}
|
||||||
? column.format(value)
|
|
||||||
: value}
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import TextPage from "./pages/TextPage";
|
import TextPage from "./pages/TextPage";
|
||||||
import {HashRouter, Route, Routes} from "react-router-dom";
|
import {HashRouter, Navigate, Route, Routes} from "react-router-dom";
|
||||||
|
|
||||||
import tosContent from "./content/tos.md"
|
import tosContent from "./content/tos.md"
|
||||||
import privacyContent from "./content/privacy.md"
|
import privacyContent from "./content/privacy.md"
|
||||||
@ -9,11 +9,15 @@ import LoginPage from "./pages/LoginPage";
|
|||||||
import {createTheme, PaletteMode, ThemeProvider} from "@mui/material";
|
import {createTheme, PaletteMode, ThemeProvider} from "@mui/material";
|
||||||
import CssBaseline from "@mui/material/CssBaseline";
|
import CssBaseline from "@mui/material/CssBaseline";
|
||||||
import AppAppBar from "./components/AppAppBar";
|
import AppAppBar from "./components/AppAppBar";
|
||||||
import DashboardPage from "./pages/DashboardPage";
|
import {getUser} from "./utils/api/user";
|
||||||
import {getUser} from "./utils/api";
|
|
||||||
import Footer from "./components/Footer";
|
|
||||||
import DrawerBox from "./components/DrawerBox";
|
import DrawerBox from "./components/DrawerBox";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import DomainFinderPage from "./pages/DomainFinderPage";
|
||||||
|
import EntityFinderPage from "./pages/EntityFinderPage";
|
||||||
|
import NameserverFinderPage from "./pages/NameserverFinderPage";
|
||||||
|
import ReverseDirectoryPage from "./pages/ReverseDirectoryPage";
|
||||||
|
import TldPage from "./pages/TldPage";
|
||||||
|
import WatchlistsPage from "./pages/WatchlistsPage";
|
||||||
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||||
@ -41,7 +45,15 @@ function App() {
|
|||||||
<AppAppBar mode={mode} toggleColorMode={toggleColorMode} isAuthenticated={isAuthenticated}/>
|
<AppAppBar mode={mode} toggleColorMode={toggleColorMode} isAuthenticated={isAuthenticated}/>
|
||||||
<Routes>
|
<Routes>
|
||||||
{isAuthenticated ?
|
{isAuthenticated ?
|
||||||
<Route path="/" element={<DashboardPage/>}/>
|
<>
|
||||||
|
<Route path="/" element={<Navigate to="/finder/domain"/>}/>
|
||||||
|
<Route path="/finder/domain" element={<DomainFinderPage/>}/>
|
||||||
|
<Route path="/finder/entity" element={<EntityFinderPage/>}/>
|
||||||
|
<Route path="/finder/nameserver" element={<NameserverFinderPage/>}/>
|
||||||
|
<Route path="/reverse" element={<ReverseDirectoryPage/>}/>
|
||||||
|
<Route path="/tld" element={<TldPage/>}/>
|
||||||
|
<Route path="/watchlist" element={<WatchlistsPage/>}/>
|
||||||
|
</>
|
||||||
:
|
:
|
||||||
<Route path="*" element={<LoginPage setIsAuthenticated={setIsAuthenticated}/>}/>
|
<Route path="*" element={<LoginPage setIsAuthenticated={setIsAuthenticated}/>}/>
|
||||||
}
|
}
|
||||||
@ -50,7 +62,6 @@ function App() {
|
|||||||
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</Box>
|
</Box>
|
||||||
<Footer/>
|
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
|||||||
@ -1,12 +1,25 @@
|
|||||||
import React from 'react';
|
import React, {ChangeEvent, useState} from 'react';
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import {Grid, Paper} from "@mui/material";
|
import {Grid, InputAdornment, Paper} from "@mui/material";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import {Explore} from "@mui/icons-material";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DomainFinderPage() {
|
||||||
|
const [ldhName, setLdhName] = useState("")
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
|
const onChangeDomain = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setLdhName(e.currentTarget.value);
|
||||||
|
const regex = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/
|
||||||
|
setError(!regex.test(e.currentTarget.value))
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Container maxWidth="lg" sx={{mt: 4, mb: 4}}>
|
<Container maxWidth="lg" sx={{mt: 20, mb: 4}}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} md={8} lg={9}>
|
<Grid item xs={12} md={8} lg={9}>
|
||||||
<Paper
|
<Paper
|
||||||
@ -17,10 +30,31 @@ export default function DashboardPage() {
|
|||||||
height: 240,
|
height: 240,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<TextField
|
||||||
|
sx={{mt: 5}} label="Domain name" variant="standard" value={ldhName}
|
||||||
|
onChange={onChangeDomain}
|
||||||
|
helperText={error && "This domain name does not appear to be valid"}
|
||||||
|
error={error}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<Explore/>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Typography variant="subtitle2" sx={{mt: 3}}>
|
||||||
|
This tool allows you to search for a domain name in the database.
|
||||||
|
As a reminder, if a domain name is unknown to Domain Watchdog or if the data is
|
||||||
|
more
|
||||||
|
than a week old, an RDAP search will be performed. The RDAP search is an operation worth
|
||||||
|
a token.
|
||||||
|
</Typography>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Footer/>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,26 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import {Grid, Paper} from "@mui/material";
|
import {Grid} from "@mui/material";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
|
|
||||||
export default function DomainFinderPage() {
|
export default function EntityFinderPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Container maxWidth="lg" sx={{mt: 4, mb: 4}}>
|
<Container maxWidth="lg" sx={{mt: 20, mb: 4}}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} md={8} lg={9}>
|
<Grid item xs={12} md={8} lg={9}>
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
p: 2,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
height: 240,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Footer/>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {login} from "../utils/api";
|
|||||||
import {useNavigate} from "react-router-dom";
|
import {useNavigate} from "react-router-dom";
|
||||||
import {Alert} from "@mui/material";
|
import {Alert} from "@mui/material";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setIsAuthenticated: (val: boolean) => void
|
setIsAuthenticated: (val: boolean) => void
|
||||||
@ -30,7 +31,7 @@ export default function LoginPage({setIsAuthenticated}: Props) {
|
|||||||
navigate('/');
|
navigate('/');
|
||||||
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setCredentials({email: "", password: ""})
|
setCredentials({...credentials, password: ""})
|
||||||
setError(e.response.data.message)
|
setError(e.response.data.message)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -102,6 +103,7 @@ export default function LoginPage({setIsAuthenticated}: Props) {
|
|||||||
Single Sign-On
|
Single Sign-On
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Footer/>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,26 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import {Grid, Paper} from "@mui/material";
|
import {Grid} from "@mui/material";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
|
|
||||||
export default function NameserverPage() {
|
export default function NameserverFinderPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Container maxWidth="lg" sx={{mt: 4, mb: 4}}>
|
<Container maxWidth="lg" sx={{mt: 20, mb: 4}}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} md={8} lg={9}>
|
<Grid item xs={12} md={8} lg={9}>
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
p: 2,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
height: 240,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Footer/>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Container from "@mui/material/Container";
|
|
||||||
import {Grid, Paper} from "@mui/material";
|
|
||||||
|
|
||||||
|
|
||||||
export default function DomainFinderPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Container maxWidth="lg" sx={{mt: 4, mb: 4}}>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12} md={8} lg={9}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
p: 2,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
height: 240,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Container>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,26 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import {Grid, Paper} from "@mui/material";
|
import {Grid} from "@mui/material";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
|
|
||||||
export default function DomainFinderPage() {
|
export default function ReverseDirectoryPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Container maxWidth="lg" sx={{mt: 4, mb: 4}}>
|
<Container maxWidth="lg" sx={{mt: 20, mb: 4}}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} md={8} lg={9}>
|
<Grid item xs={12} md={8} lg={9}>
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
p: 2,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
height: 240,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Footer/>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
content: string
|
content: string
|
||||||
@ -30,6 +31,7 @@ export default function Index({content}: Props) {
|
|||||||
>
|
>
|
||||||
<div dangerouslySetInnerHTML={{__html: content}}></div>
|
<div dangerouslySetInnerHTML={{__html: content}}></div>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Footer/>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,27 +1,110 @@
|
|||||||
import React from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import {Grid, Paper} from "@mui/material";
|
import {Accordion, AccordionDetails, AccordionSummary, Grid, Typography} from "@mui/material";
|
||||||
|
import {ExpandMore} from "@mui/icons-material";
|
||||||
|
import HeadTable from "../components/HeadTable";
|
||||||
|
import {getTldList} from "../utils/api";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
|
const gTldColumns = [
|
||||||
|
{id: 'tld', label: 'TLD'},
|
||||||
|
{id: 'registryOperator', label: 'Operator'}
|
||||||
|
]
|
||||||
|
|
||||||
|
const sTldColumns = [
|
||||||
|
{id: 'tld', label: 'TLD'}
|
||||||
|
]
|
||||||
|
|
||||||
|
const toEmoji = (tld: string) => String.fromCodePoint(
|
||||||
|
...getCountryCode(tld)
|
||||||
|
.toUpperCase()
|
||||||
|
.split('')
|
||||||
|
.map((char) => 127397 + char.charCodeAt(0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const getCountryCode = (tld: string): string => {
|
||||||
|
const exceptions = {uk: 'gb', su: 'ru', tp: 'tl'}
|
||||||
|
if (tld in exceptions) return exceptions[tld as keyof typeof exceptions]
|
||||||
|
return tld
|
||||||
|
}
|
||||||
|
|
||||||
|
const regionNames = new Intl.DisplayNames(['en'], {type: 'region'})
|
||||||
|
|
||||||
|
const ccTldColumns = [
|
||||||
|
{id: 'tld', label: 'TLD'},
|
||||||
|
{
|
||||||
|
id: 'tld',
|
||||||
|
label: 'Flag',
|
||||||
|
format: (tld: string) => toEmoji(tld)
|
||||||
|
},
|
||||||
|
{id: 'tld', label: 'Country name', format: (tld: string) => regionNames.of(getCountryCode(tld)) ?? '-'},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function TldPage() {
|
||||||
|
const [sTld, setSTld] = useState<any>([])
|
||||||
|
const [gTld, setGTld] = useState<any>([])
|
||||||
|
const [ccTld, setCcTld] = useState<any>([])
|
||||||
|
const [brandGTld, setBrandGTld] = useState<any>([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getTldList({type: 'sTLD'}).then(setSTld)
|
||||||
|
getTldList({type: 'gTLD', contractTerminated: 0, specification13: 0}).then(setGTld)
|
||||||
|
getTldList({type: 'gTLD', contractTerminated: 0, specification13: 1}).then(setBrandGTld)
|
||||||
|
getTldList({type: 'ccTLD'}).then(setCcTld)
|
||||||
|
}, [])
|
||||||
|
|
||||||
export default function DomainFinderPage() {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container maxWidth="lg" sx={{mt: 20, mb: 4}}>
|
||||||
<Container maxWidth="lg" sx={{mt: 4, mb: 4}}>
|
<Grid container spacing={3}>
|
||||||
<Grid container spacing={3}>
|
<Grid item xs={12} md={8} lg={9}>
|
||||||
<Grid item xs={12} md={8} lg={9}>
|
<Accordion>
|
||||||
<Paper
|
<AccordionSummary expandIcon={<ExpandMore/>}>
|
||||||
sx={{
|
<Typography sx={{width: '33%', flexShrink: 0}}>
|
||||||
p: 2,
|
sTLD
|
||||||
display: 'flex',
|
</Typography>
|
||||||
flexDirection: 'column',
|
<Typography sx={{color: 'text.secondary'}}>Sponsored Top-Level Domains</Typography>
|
||||||
height: 240,
|
</AccordionSummary>
|
||||||
}}
|
<AccordionDetails>
|
||||||
>
|
<HeadTable rows={sTld} columns={sTldColumns}/>
|
||||||
|
</AccordionDetails>
|
||||||
</Paper>
|
</Accordion>
|
||||||
</Grid>
|
<Accordion>
|
||||||
|
<AccordionSummary expandIcon={<ExpandMore/>}>
|
||||||
|
<Typography sx={{width: '33%', flexShrink: 0}}>
|
||||||
|
gTLD
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{color: 'text.secondary'}}>Generic Top-Level Domains</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<HeadTable rows={gTld} columns={gTldColumns}/>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary expandIcon={<ExpandMore/>}>
|
||||||
|
<Typography sx={{width: '33%', flexShrink: 0}}>
|
||||||
|
Brand gTLD
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{color: 'text.secondary'}}>Brand Generic Top-Level Domains</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<HeadTable rows={brandGTld} columns={gTldColumns}/>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary expandIcon={<ExpandMore/>}>
|
||||||
|
<Typography sx={{width: '33%', flexShrink: 0}}>
|
||||||
|
ccTLD
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{color: 'text.secondary'}}>Country-Code Top-Level Domains</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<HeadTable rows={ccTld} columns={ccTldColumns}/>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Grid>
|
||||||
</>
|
<Footer/>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,27 +1,41 @@
|
|||||||
import React from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import {Grid, Paper} from "@mui/material";
|
import {Grid, List, ListItem, ListItemText} from "@mui/material";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
import {deleteWatchlist, getWatchlists, Watchlist} from "../utils/api";
|
||||||
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import {DeleteForever} from '@mui/icons-material'
|
||||||
|
|
||||||
|
export default function WatchlistsPage() {
|
||||||
|
const [watchlists, setWatchlists] = useState<(Partial<Watchlist> & { token: string })[]>([])
|
||||||
|
const [refreshKey, setRefreshKey] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getWatchlists().then(setWatchlists)
|
||||||
|
}, [refreshKey])
|
||||||
|
|
||||||
export default function DomainFinderPage() {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container maxWidth="lg" sx={{mt: 20, mb: 4}}>
|
||||||
<Container maxWidth="lg" sx={{mt: 4, mb: 4}}>
|
<Grid container spacing={3}>
|
||||||
<Grid container spacing={3}>
|
<Grid item xs={12} md={8} lg={9}>
|
||||||
<Grid item xs={12} md={8} lg={9}>
|
<List sx={{width: '100%', bgcolor: 'background.paper'}}>
|
||||||
<Paper
|
{watchlists.map((w) => (
|
||||||
sx={{
|
<ListItem
|
||||||
p: 2,
|
key={w.token}
|
||||||
display: 'flex',
|
secondaryAction={
|
||||||
flexDirection: 'column',
|
<IconButton aria-label="delete"
|
||||||
height: 240,
|
onClick={(e) => deleteWatchlist(w.token).then(() => setRefreshKey(refreshKey + 1))}>
|
||||||
}}
|
<DeleteForever/>
|
||||||
>
|
</IconButton>
|
||||||
|
}
|
||||||
</Paper>
|
>
|
||||||
</Grid>
|
<ListItemText primary={`Token ${w.token}`}/>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Grid>
|
||||||
</>
|
<Footer/>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
import axios, {AxiosRequestConfig, AxiosResponse} from "axios";
|
|
||||||
|
|
||||||
|
|
||||||
export async function login(email: string, password: string): Promise<boolean> {
|
|
||||||
const response = await request({
|
|
||||||
method: 'POST',
|
|
||||||
url: 'login',
|
|
||||||
data: {email, password}
|
|
||||||
})
|
|
||||||
return response.status === 200
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getUser(): Promise<object> {
|
|
||||||
const response = await request({
|
|
||||||
url: 'me'
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig): Promise<R> {
|
|
||||||
const axiosConfig: AxiosRequestConfig = {
|
|
||||||
...config,
|
|
||||||
baseURL: '/api',
|
|
||||||
withCredentials: true,
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/ld+json'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return await axios.request<T, any, D>(axiosConfig)
|
|
||||||
}
|
|
||||||
9
assets/utils/api/domain.ts
Normal file
9
assets/utils/api/domain.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import {Domain, request} from ".";
|
||||||
|
|
||||||
|
|
||||||
|
export async function getDomain(ldhName: string): Promise<Domain> {
|
||||||
|
const response = await request<Domain>({
|
||||||
|
url: 'domains/' + ldhName
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
69
assets/utils/api/index.ts
Normal file
69
assets/utils/api/index.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import axios, {AxiosRequestConfig, AxiosResponse} from "axios";
|
||||||
|
|
||||||
|
|
||||||
|
export interface Event {
|
||||||
|
action: string
|
||||||
|
date: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Entity {
|
||||||
|
handle: string
|
||||||
|
events: Event[]
|
||||||
|
roles: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Nameserver {
|
||||||
|
ldhName: string
|
||||||
|
entities: Entity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tld {
|
||||||
|
tld: string
|
||||||
|
contractTerminated: boolean
|
||||||
|
dateOfContractSignature: string
|
||||||
|
registryOperator: string
|
||||||
|
delegationDate: string
|
||||||
|
removalDate: string
|
||||||
|
specification13: boolean
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Domain {
|
||||||
|
ldhName: string
|
||||||
|
handle: string
|
||||||
|
status: string[]
|
||||||
|
events: Event[]
|
||||||
|
entities: Entity[]
|
||||||
|
nameservers: Nameserver[]
|
||||||
|
tld: Tld
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
email: string
|
||||||
|
roles: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Watchlist {
|
||||||
|
domains: string[]
|
||||||
|
triggers: Event[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig): Promise<R> {
|
||||||
|
const axiosConfig: AxiosRequestConfig = {
|
||||||
|
...config,
|
||||||
|
baseURL: '/api',
|
||||||
|
withCredentials: true,
|
||||||
|
headers: {
|
||||||
|
...config.headers,
|
||||||
|
Accept: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await axios.request<T, R, D>(axiosConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from './domain'
|
||||||
|
export * from './tld'
|
||||||
|
export * from './user'
|
||||||
|
export * from './watchlist'
|
||||||
|
|
||||||
|
|
||||||
28
assets/utils/api/tld.ts
Normal file
28
assets/utils/api/tld.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {request} from "./index";
|
||||||
|
|
||||||
|
interface Tld {
|
||||||
|
tld: string
|
||||||
|
contractTerminated: boolean
|
||||||
|
registryOperator: string
|
||||||
|
specification13: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTldList(params: object): Promise<Tld[]> {
|
||||||
|
let page = 1
|
||||||
|
let response = (await request<Tld[]>({
|
||||||
|
url: 'tld',
|
||||||
|
params: {...params, page},
|
||||||
|
})).data
|
||||||
|
const tldList: Tld[] = response;
|
||||||
|
|
||||||
|
while (response.length !== 0) {
|
||||||
|
page++
|
||||||
|
response = (await request<Tld[]>({
|
||||||
|
url: 'tld',
|
||||||
|
params: {...params, page},
|
||||||
|
})).data
|
||||||
|
|
||||||
|
tldList.push(...response)
|
||||||
|
}
|
||||||
|
return tldList
|
||||||
|
}
|
||||||
18
assets/utils/api/user.ts
Normal file
18
assets/utils/api/user.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {request, User} from "./index";
|
||||||
|
|
||||||
|
|
||||||
|
export async function login(email: string, password: string): Promise<boolean> {
|
||||||
|
const response = await request({
|
||||||
|
method: 'POST',
|
||||||
|
url: 'login',
|
||||||
|
data: {email, password}
|
||||||
|
})
|
||||||
|
return response.status === 200
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUser(): Promise<User> {
|
||||||
|
const response = await request<User>({
|
||||||
|
url: 'me'
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
52
assets/utils/api/watchlist.ts
Normal file
52
assets/utils/api/watchlist.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {Event, request, Watchlist} from "./index";
|
||||||
|
|
||||||
|
export async function getWatchlists() {
|
||||||
|
const response = await request<{ token: string }[]>({
|
||||||
|
url: 'watchlists'
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getWatchlist(token: string) {
|
||||||
|
const response = await request<Watchlist>({
|
||||||
|
url: 'watchlists/' + token
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function postWatchlist(domains: string[], triggers: Event[]) {
|
||||||
|
const response = await request<{ token: string }>({
|
||||||
|
method: 'POST',
|
||||||
|
url: 'watchlists',
|
||||||
|
data: {
|
||||||
|
domains,
|
||||||
|
triggers
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
"Content-Type": 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteWatchlist(token: string): Promise<void> {
|
||||||
|
await request({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: 'watchlists/' + token
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function patchWatchlist(domains: string[], triggers: Event[]) {
|
||||||
|
const response = await request<Watchlist>({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: 'watchlists',
|
||||||
|
data: {
|
||||||
|
domains,
|
||||||
|
triggers
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
"Content-Type": 'application/merge-patch+json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user