diff --git a/public/locales/en-US/translation.json b/public/locales/en-US/translation.json
index 75c8a21..6db885a 100644
--- a/public/locales/en-US/translation.json
+++ b/public/locales/en-US/translation.json
@@ -313,7 +313,8 @@
"AdminRoute": {
"title": "Site configuration",
"routes": {
- "reservedAliases": "Reserved Aliases"
+ "reservedAliases": "Reserved Aliases",
+ "settings": "Global Settings"
},
"forms": {
"reservedAliases": {
@@ -334,6 +335,48 @@
"step2": "Sends mail to",
"step4": "KleckRelay forwards to"
}
+ },
+ "settings": {
+ "randomEmailIdMinLength": {
+ "label": "Minimum random alias ID length",
+ "description": "The minimum length for randomly generated emails. The server will automatically increase the length if required so."
+ },
+ "randomEmailIdChars": {
+ "label": "Random alias character pool",
+ "description": "Characters that are used to generate random emails."
+ },
+ "randomEmailIdLengthIncreaseOnPercentage": {
+ "label": "Percentage of used aliases",
+ "description": "If the percentage of used random email IDs is higher than this value, the length of the random email ID will be increased. This is used to prevent spammers from guessing the email ID."
+ },
+ "customEmailSuffixLength": {
+ "label": "Custom email suffix length",
+ "description": "The length of the custom email suffix."
+ },
+ "customEmailSuffixChars": {
+ "label": "Custom email suffix character pool",
+ "description": "Characters that are used to generate custom email suffixes."
+ },
+ "imageProxyStorageLifeTimeInHours": {
+ "label": "Image proxy storage lifetime",
+ "description": "The lifetime of images that are stored on the server. After this time, the image will be deleted."
+ },
+ "enableImageProxy": {
+ "label": "Enable image proxy",
+ "description": "If enabled, images will be stored on the server and forwarded to the user. This is useful if you want to prevent the user from seeing the IP address of the server. This will only affect new images."
+ },
+ "userEmailEnableDisposableEmails": {
+ "label": "Enable disposable emails for new accounts",
+ "description": "If enabled, users will be able to use disposable emails when creating a new account. This will only affect new accounts."
+ },
+ "userEmailEnableOtherRelays": {
+ "label": "Enable other relays for new accounts",
+ "description": "If enabled, users will be able to use other relays (such as SimpleLogin or DuckDuckGo's Email Tracking Protection) when creating a new account. This will only affect new accounts."
+ },
+ "allowStatistics": {
+ "label": "Allow statistics",
+ "description": "If enabled, your instance will collect anonymous statistics and share them. They will only be stored locally on this instance but made public."
+ }
}
}
}
diff --git a/src/App.tsx b/src/App.tsx
index e692d4b..0e4e335 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -16,6 +16,7 @@ import AuthenticatedRoute from "~/routes/AuthenticatedRoute"
import CompleteAccountRoute from "~/routes/CompleteAccountRoute"
import CreateReservedAliasRoute from "~/routes/CreateReservedAliasRoute"
import EnterDecryptionPassword from "~/routes/EnterDecryptionPassword"
+import GlobalSettingsRoute from "~/routes/GlobalSettingsRoute"
import I18nHandler from "./I18nHandler"
import LoginRoute from "~/routes/LoginRoute"
import LogoutRoute from "~/routes/LogoutRoute"
@@ -119,6 +120,10 @@ const router = createBrowserRouter([
loader: getServerSettings,
element: ,
},
+ {
+ path: "/admin/settings",
+ element: ,
+ },
],
},
],
diff --git a/src/apis/get-admin-settings.ts b/src/apis/get-admin-settings.ts
new file mode 100644
index 0000000..b262a91
--- /dev/null
+++ b/src/apis/get-admin-settings.ts
@@ -0,0 +1,10 @@
+import {client} from "~/constants/axios-client"
+import {AdminSettings} from "~/server-types"
+
+export default async function getAdminSettings(): Promise {
+ const {data} = await client.get(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/admin/settings`, {
+ withCredentials: true,
+ })
+
+ return data
+}
diff --git a/src/apis/index.ts b/src/apis/index.ts
index e2ae2ad..bfb40f1 100644
--- a/src/apis/index.ts
+++ b/src/apis/index.ts
@@ -48,3 +48,7 @@ export * from "./get-reserved-alias"
export {default as getReservedAlias} from "./get-reserved-alias"
export * from "./update-reserved-alias"
export {default as updateReservedAlias} from "./update-reserved-alias"
+export * from "./get-admin-settings"
+export {default as getAdminSettings} from "./get-admin-settings"
+export * from "./update-admin-settings"
+export {default as updateAdminSettings} from "./update-admin-settings"
diff --git a/src/apis/update-admin-settings.ts b/src/apis/update-admin-settings.ts
new file mode 100644
index 0000000..5be9fd9
--- /dev/null
+++ b/src/apis/update-admin-settings.ts
@@ -0,0 +1,16 @@
+import {client} from "~/constants/axios-client"
+import {AdminSettings} from "~/server-types"
+
+export default async function updateAdminSettings(
+ settings: Partial,
+): Promise {
+ const {data} = await client.patch(
+ `${import.meta.env.VITE_SERVER_BASE_URL}/v1/admin/settings`,
+ settings,
+ {
+ withCredentials: true,
+ },
+ )
+
+ return data
+}
diff --git a/src/route-widgets/GlobalSettingsRoute/SettingForm.tsx b/src/route-widgets/GlobalSettingsRoute/SettingForm.tsx
new file mode 100644
index 0000000..4b643c1
--- /dev/null
+++ b/src/route-widgets/GlobalSettingsRoute/SettingForm.tsx
@@ -0,0 +1,92 @@
+import * as yup from "yup"
+import {AdminSettings} from "~/server-types"
+import {useTranslation} from "react-i18next"
+import {useFormik} from "formik"
+import {SimpleForm} from "~/components"
+import {TextField} from "@mui/material"
+
+export interface SettingsFormProps {
+ settings: AdminSettings
+}
+
+export default function SettingsForm({settings}: SettingsFormProps) {
+ const {t} = useTranslation()
+
+ const validationSchema = yup.object().shape({
+ randomEmailIdMinLength: yup
+ .number()
+ .min(1)
+ .max(1_023)
+ .label(t("routes.AdminRoute.forms.settings.randomEmailIdMinLength.label")),
+ randomEmailIdChars: yup
+ .string()
+ .label(t("routes.AdminRoute.forms.settings.randomEmailIdChars.label")),
+ randomEmailLengthIncreaseOnPercentage: yup
+ .number()
+ .label(
+ t("routes.AdminRoute.forms.settings.randomEmailLengthIncreaseOnPercentage.label"),
+ ),
+ imageProxyStorageLifeTimeInHours: yup
+ .number()
+ .label(t("routes.AdminRoute.forms.settings.imageProxyStorageLifeTimeInHours.label")),
+ customEmailSuffixLength: yup
+ .number()
+ .min(1)
+ .max(1_023)
+ .label(t("routes.AdminRoute.forms.settings.customEmailSuffixLength-label")),
+ customEmailSuffixChars: yup
+ .string()
+ .label(t("routes.AdminRoute.forms.settings.customEmailSuffixChars.label")),
+ userEmailEnableDisposableEmails: yup
+ .boolean()
+ .label(t("routes.AdminRoute.forms.settings.userEmailEnableDisposableEmails.label")),
+ userEmailEnableOtherRelays: yup
+ .boolean()
+ .label(t("routes.AdminRoute.forms.settings.userEmailEnableOtherRelays.label")),
+ enableImageProxy: yup
+ .boolean()
+ .label(t("routes.AdminRoute.forms.settings.enableImageProxy.label")),
+ allowStatistics: yup
+ .boolean()
+ .label(t("routes.AdminRoute.forms.settings.allowStatistics.label")),
+ })
+
+ const formik = useFormik({
+ validationSchema,
+ onSubmit: console.log,
+ initialValues: settings,
+ })
+
+ return (
+
+ )
+}
diff --git a/src/routes/AdminRoute.tsx b/src/routes/AdminRoute.tsx
index c19b2aa..37ce762 100644
--- a/src/routes/AdminRoute.tsx
+++ b/src/routes/AdminRoute.tsx
@@ -1,6 +1,7 @@
import {ReactElement, useLayoutEffect} from "react"
import {useTranslation} from "react-i18next"
import {BsStarFill} from "react-icons/bs"
+import {AiFillEdit} from "react-icons/ai"
import {Link} from "react-router-dom"
import {List, ListItemButton, ListItemIcon, ListItemText} from "@mui/material"
@@ -13,6 +14,8 @@ export default function AdminRoute(): ReactElement {
const navigateToNext = useNavigateToNext()
const user = useUser()
+ console.log(user)
+
useLayoutEffect(() => {
if (!user.isAdmin) {
navigateToNext()
@@ -28,6 +31,12 @@ export default function AdminRoute(): ReactElement {
+
+
+
+
+
+
)
diff --git a/src/routes/GlobalSettingsRoute.tsx b/src/routes/GlobalSettingsRoute.tsx
new file mode 100644
index 0000000..84b5e57
--- /dev/null
+++ b/src/routes/GlobalSettingsRoute.tsx
@@ -0,0 +1,19 @@
+import {ReactElement} from "react"
+import {useQuery} from "@tanstack/react-query"
+import {AdminSettings} from "~/server-types"
+import {AxiosError} from "axios"
+import {getAdminSettings} from "~/apis"
+import {QueryResult} from "~/components"
+import {useTranslation} from "react-i18next"
+import SettingsForm from "~/route-widgets/GlobalSettingsRoute/SettingForm"
+
+export default function GlobalSettingsRoute(): ReactElement {
+ const {t} = useTranslation()
+ const query = useQuery(["get_admin_settings"], getAdminSettings)
+
+ return (
+ query={query}>
+ {settings => }
+
+ )
+}
diff --git a/src/server-types.ts b/src/server-types.ts
index 42e284e..a0d252d 100644
--- a/src/server-types.ts
+++ b/src/server-types.ts
@@ -198,3 +198,16 @@ export interface GetPageData {
page?: number
size?: number
}
+
+export interface AdminSettings {
+ randomEmailIdMinLength: number
+ randomEmailIdChars: string
+ randomEmailLengthIncreaseOnPercentage: number
+ customEmailSuffixLength: number
+ customEmailSuffixChars: string
+ imageProxyStorageLifeTimeInHours: number
+ enableImageProxy: boolean
+ userEmailEnableDisposableEmails: boolean
+ userEmailEnableOtherRelays: boolean
+ allowStatistics: boolean
+}