diff --git a/src/App.tsx b/src/App.tsx
index 0bba9e4..a9c8b98 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,6 +7,7 @@ import {CssBaseline, ThemeProvider} from "@mui/material"
import {queryClient} from "~/constants/react-query"
import {lightTheme} from "~/constants/themes"
import {getServerSettings} from "~/apis"
+import AliasesRoute from "~/routes/AliasesRoute"
import AuthContextProvider from "~/AuthContext/AuthContextProvider"
import AuthenticateRoute from "~/routes/AuthenticateRoute"
import AuthenticatedRoute from "~/routes/AuthenticatedRoute"
@@ -39,6 +40,12 @@ const router = createBrowserRouter([
{
path: "/",
element: ,
+ children: [
+ {
+ path: "/aliases",
+ element: ,
+ },
+ ],
},
],
},
diff --git a/src/apis/create-alias.ts b/src/apis/create-alias.ts
new file mode 100644
index 0000000..ce18e57
--- /dev/null
+++ b/src/apis/create-alias.ts
@@ -0,0 +1,49 @@
+import axios from "axios"
+
+import {
+ Alias,
+ AliasType,
+ ImageProxyFormatType,
+ ProxyUserAgentType,
+} from "~/server-types"
+import parseAlias from "~/apis/helpers/parse-alias"
+
+interface CreateAliasDataOther {
+ isActive?: boolean
+ encryptedNotes?: string
+ removeTrackers?: boolean
+ createMailReport?: boolean
+ proxyImages?: boolean
+ imageProxyFormat?: ImageProxyFormatType
+ imageProxyUserAgent?: ProxyUserAgentType
+}
+
+interface CreateAliasDataBase extends CreateAliasDataOther {
+ type: AliasType
+ local?: string
+}
+
+interface CreateAliasDataRandomType extends CreateAliasDataBase {
+ type: AliasType.Random
+ local?: undefined
+}
+
+interface CreateAliasDataCustomType extends CreateAliasDataBase {
+ type: AliasType.Custom
+ local: string
+}
+
+export type CreateAliasData =
+ | CreateAliasDataRandomType
+ | CreateAliasDataCustomType
+
+export default async function createAlias(
+ aliasData: CreateAliasData,
+): Promise {
+ const {data} = await axios.post(
+ `${import.meta.env.VITE_SERVER_BASE_URL}/alias`,
+ aliasData,
+ )
+
+ return parseAlias(data)
+}
diff --git a/src/apis/get-aliases.ts b/src/apis/get-aliases.ts
new file mode 100644
index 0000000..2e671b0
--- /dev/null
+++ b/src/apis/get-aliases.ts
@@ -0,0 +1,12 @@
+import axios from "axios"
+
+import {Alias} from "~/server-types"
+import parseAlias from "~/apis/helpers/parse-alias"
+
+export default async function getAliases(): Promise> {
+ const {data} = await axios.get(
+ `${import.meta.env.VITE_SERVER_BASE_URL}/alias`,
+ )
+
+ return data.map(parseAlias)
+}
diff --git a/src/apis/helpers/parse-alias.ts b/src/apis/helpers/parse-alias.ts
new file mode 100644
index 0000000..13a06e4
--- /dev/null
+++ b/src/apis/helpers/parse-alias.ts
@@ -0,0 +1,16 @@
+import {Alias} from "~/server-types"
+
+export default function parseAlias(alias: any): Alias {
+ return {
+ id: alias.id,
+ domain: alias.domain,
+ local: alias.local,
+ isActive: alias.is_active,
+ encryptedNotes: alias.encrypted_notes,
+ removeTrackers: alias.remove_trackers,
+ createMailReport: alias.create_mail_report,
+ proxyImages: alias.proxy_images,
+ imageProxyFormat: alias.image_proxy_format,
+ imageProxyUserAgent: alias.image_proxy_user_agent,
+ }
+}
diff --git a/src/apis/index.ts b/src/apis/index.ts
index f588252..dca566b 100644
--- a/src/apis/index.ts
+++ b/src/apis/index.ts
@@ -12,3 +12,7 @@ export * from "./refresh-token"
export {default as refreshToken} from "./refresh-token"
export * from "./logout"
export {default as logout} from "./logout"
+export * from "./get-aliases"
+export {default as getAliases} from "./get-aliases"
+export * from "./create-alias"
+export {default as createAlias} from "./create-alias"
diff --git a/src/components/LoadingData.tsx b/src/components/LoadingData.tsx
new file mode 100644
index 0000000..d572734
--- /dev/null
+++ b/src/components/LoadingData.tsx
@@ -0,0 +1,38 @@
+import {ReactElement, useEffect, useState} from "react"
+
+import {CircularProgress, Grid, Typography} from "@mui/material"
+
+export interface LoadingDataProps {
+ message?: string
+}
+
+export default function LoadingData({
+ message = "Loading",
+}: LoadingDataProps): ReactElement {
+ const [ellipsis, setEllipsis] = useState("")
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setEllipsis((value: string) => {
+ if (value.length === 3) {
+ return ""
+ }
+
+ return value + "."
+ })
+ }, 300)
+
+ return () => clearInterval(interval)
+ }, [])
+
+ return (
+
+
+
+
+
+ Loading{ellipsis}
+
+
+ )
+}
diff --git a/src/hooks/use-user.ts b/src/hooks/use-user.ts
index fe3dfb6..f0449e2 100644
--- a/src/hooks/use-user.ts
+++ b/src/hooks/use-user.ts
@@ -12,7 +12,7 @@ export default function useUser(): User {
useLayoutEffect(() => {
if (!isAuthenticated) {
- navigate("/login")
+ navigate("/auth/login")
}
}, [isAuthenticated, navigate])
diff --git a/src/route-widgets/AliasRoute/AliasListItem.tsx b/src/route-widgets/AliasRoute/AliasListItem.tsx
new file mode 100644
index 0000000..5ef9350
--- /dev/null
+++ b/src/route-widgets/AliasRoute/AliasListItem.tsx
@@ -0,0 +1,30 @@
+import {ReactElement} from "react"
+import {MdOutlineMoreVert} from "react-icons/md"
+
+import {
+ IconButton,
+ ListItem,
+ ListItemSecondaryAction,
+ ListItemText,
+} from "@mui/material"
+
+import {Alias} from "~/server-types"
+
+export interface AliasListItemProps {
+ alias: Alias
+}
+
+export default function AliasListItem({
+ alias,
+}: AliasListItemProps): ReactElement {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/route-widgets/AliasRoute/CreateRandomAliasButton.tsx b/src/route-widgets/AliasRoute/CreateRandomAliasButton.tsx
new file mode 100644
index 0000000..6a77747
--- /dev/null
+++ b/src/route-widgets/AliasRoute/CreateRandomAliasButton.tsx
@@ -0,0 +1,37 @@
+import {ReactElement} from "react"
+import {BsArrowClockwise} from "react-icons/bs"
+import {AxiosError} from "axios"
+
+import {Button} from "@mui/material"
+import {useMutation} from "@tanstack/react-query"
+
+import {CreateAliasData, createAlias} from "~/apis"
+import {Alias} from "~/server-types"
+
+export interface CreateRandomAliasButtonProps {
+ onCreated: (alias: Alias) => void
+}
+
+export default function CreateRandomAliasButton({
+ onCreated,
+}: CreateRandomAliasButtonProps): ReactElement {
+ const {mutate} = useMutation(
+ createAlias,
+ {
+ onSuccess: onCreated,
+ },
+ )
+
+ return (
+ }
+ onClick={() =>
+ mutate({
+ type: AliasType.Random,
+ })
+ }
+ >
+ Create random alias
+
+ )
+}
diff --git a/src/routes/AliasesRoute.tsx b/src/routes/AliasesRoute.tsx
new file mode 100644
index 0000000..44ce1de
--- /dev/null
+++ b/src/routes/AliasesRoute.tsx
@@ -0,0 +1,32 @@
+import {ReactElement} from "react"
+import {AxiosError} from "axios"
+
+import {List} from "@mui/material"
+import {useQuery} from "@tanstack/react-query"
+
+import {Alias} from "~/server-types"
+import AliasListItem from "~/route-widgets/AliasRoute/AliasListItem"
+import CreateRandomAliasButton from "~/route-widgets/AliasRoute/CreateRandomAliasButton"
+import LoadingData from "~/components/LoadingData"
+import getAliases from "~/apis/get-aliases"
+
+export default function AliasesRoute(): ReactElement {
+ const {
+ data: aliases,
+ isLoading,
+ refetch,
+ } = useQuery, AxiosError>(["get_aliases"], getAliases)
+
+ return (
+
+ {isLoading ? (
+
+ ) : (
+ aliases?.map?.(alias => (
+
+ ))
+ )}
+ refetch()} />
+
+ )
+}
diff --git a/src/server-types.d.ts b/src/server-types.d.ts
index 77b51ac..1184449 100644
--- a/src/server-types.d.ts
+++ b/src/server-types.d.ts
@@ -13,6 +13,11 @@ export enum ProxyUserAgentType {
CHROME = "chrome",
}
+export enum AliasType {
+ Random = "random",
+ Custom = "custom",
+}
+
export interface User {
id: string
createdAt: Date
@@ -50,3 +55,16 @@ export interface ServerSettings {
export interface MinimumServerResponse {
detail?: string
}
+
+export interface Alias {
+ id: string
+ domain: string
+ local: string
+ isActive: boolean
+ encryptedNotes: string
+ removeTrackers: boolean
+ createMailReport: boolean
+ proxyImages: boolean
+ imageProxyFormat: ImageProxyFormatType
+ imageProxyUserAgent: ProxyUserAgentType
+}