diff --git a/public/locales/en-US/settings-api-keys.json b/public/locales/en-US/settings-api-keys.json
new file mode 100644
index 0000000..fe1a5d7
--- /dev/null
+++ b/public/locales/en-US/settings-api-keys.json
@@ -0,0 +1,10 @@
+{
+ "title": "Manage your API Keys",
+ "create": {
+ "label": "Create a new API Key"
+ },
+ "emptyState": {
+ "title": "Welcome to your API Keys",
+ "description": "Create an API Key to get started with the API."
+ }
+}
diff --git a/public/locales/en-US/settings.json b/public/locales/en-US/settings.json
index 3f70247..756d4aa 100644
--- a/public/locales/en-US/settings.json
+++ b/public/locales/en-US/settings.json
@@ -3,6 +3,6 @@
"actions": {
"enable2fa": "Two-Factor-Authentication",
"aliasPreferences": "Alias Preferences",
- "apiKeys": "Edit API Keys"
+ "apiKeys": "Manage API Keys"
}
}
diff --git a/src/App.tsx b/src/App.tsx
index c54ecea..c58c86c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -28,6 +28,7 @@ import ReservedAliasDetailRoute from "~/routes/ReservedAliasDetailRoute"
import ReservedAliasesRoute from "~/routes/ReservedAliasesRoute"
import RootRoute from "~/routes/Root"
import Settings2FARoute from "~/routes/Settings2FARoute"
+import SettingsAPIKeysRoute from "~/routes/SettingsAPIKeysRoute"
import SettingsAliasPreferencesRoute from "~/routes/SettingsAliasPreferencesRoute"
import SettingsRoute from "~/routes/SettingsRoute"
import SignupRoute from "~/routes/SignupRoute"
@@ -105,6 +106,10 @@ const router = createBrowserRouter([
path: "/settings/2fa",
element: ,
},
+ {
+ path: "/settings/api-keys",
+ element: ,
+ },
{
path: "/reports",
loader: getServerSettings,
diff --git a/src/route-widgets/SettingsAPIKeysRoute/APIKeyListItem.tsx b/src/route-widgets/SettingsAPIKeysRoute/APIKeyListItem.tsx
new file mode 100644
index 0000000..93e9783
--- /dev/null
+++ b/src/route-widgets/SettingsAPIKeysRoute/APIKeyListItem.tsx
@@ -0,0 +1,24 @@
+import {ReactElement} from "react"
+import {APIKey} from "~/server-types"
+import {IconButton, ListItem, ListItemSecondaryAction, ListItemText} from "@mui/material"
+import {useTranslation} from "react-i18next"
+import {MdDelete} from "react-icons/md"
+
+export interface APIKeyListItemProps {
+ apiKey: APIKey
+}
+
+export default function APIKeyListItem({apiKey}: APIKeyListItemProps): ReactElement {
+ const {t} = useTranslation("settings-api-keys")
+
+ return (
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/route-widgets/SettingsAPIKeysRoute/EmptyStateScreen.tsx b/src/route-widgets/SettingsAPIKeysRoute/EmptyStateScreen.tsx
new file mode 100644
index 0000000..196144b
--- /dev/null
+++ b/src/route-widgets/SettingsAPIKeysRoute/EmptyStateScreen.tsx
@@ -0,0 +1,35 @@
+import {ReactElement} from "react"
+import {useTranslation} from "react-i18next"
+
+import {Container, Grid, Typography} from "@mui/material"
+import {MdVpnKey} from "react-icons/md"
+
+export default function EmptyStateScreen(): ReactElement {
+ const {t} = useTranslation("settings-api-keys")
+
+ return (
+
+
+
+
+ {t("emptyState.title")}
+
+
+
+
+
+
+ {t("emptyState.description")}
+
+
+
+ )
+}
diff --git a/src/routes/SettingsAPIKeysRoute.tsx b/src/routes/SettingsAPIKeysRoute.tsx
new file mode 100644
index 0000000..82d9ab8
--- /dev/null
+++ b/src/routes/SettingsAPIKeysRoute.tsx
@@ -0,0 +1,48 @@
+import {ReactElement} from "react"
+import {useTranslation} from "react-i18next"
+import {useQuery} from "@tanstack/react-query"
+import {APIKey, PaginationResult} from "~/server-types"
+import {AxiosError} from "axios"
+import {getAPIKeys} from "~/apis"
+import {QueryResult, SimplePage} from "~/components"
+import {Button, List} from "@mui/material"
+import {Link} from "react-router-dom"
+import APIKeyListItem from "~/route-widgets/SettingsAPIKeysRoute/APIKeyListItem"
+import EmptyStateScreen from "~/route-widgets/SettingsAPIKeysRoute/EmptyStateScreen"
+
+export default function SettingsAPIKeysRoute(): ReactElement {
+ const {t} = useTranslation("settings-api-keys")
+ const query = useQuery, AxiosError>(["get_api_keys"], () =>
+ getAPIKeys(),
+ )
+
+ return (
+
+ {t("create.label")}
+
+ }
+ >
+ , AxiosError> query={query}>
+ {({items: apiKeys}) =>
+ apiKeys.length > 0 ? (
+
+ {apiKeys.map(apiKey => (
+
+ ))}
+
+ ) : (
+
+ )
+ }
+
+
+ )
+}
diff --git a/src/server-types.ts b/src/server-types.ts
index 62642c7..6046098 100644
--- a/src/server-types.ts
+++ b/src/server-types.ts
@@ -141,6 +141,13 @@ export interface AliasList {
type: AliasType
}
+export interface APIKey {
+ id: string
+ label: string
+ expiresAt: Date
+ scopes: string[]
+}
+
export interface Report {
id: string
encryptedContent: string