diff --git a/frontend/src/components/alert.tsx b/frontend/src/components/alert.tsx index a36277c..0999d4d 100644 --- a/frontend/src/components/alert.tsx +++ b/frontend/src/components/alert.tsx @@ -1,8 +1,17 @@ -export function ErrorAlert({message, className}: {message: string, className?: string}) { +export function ErrorAlert({ message, className }: { message: string, className?: string }) { return
+ d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /> + + {message} +
; +} + +export function InfoAlert({ message, className }: { message: string, className?: string }) { + return
+ + {message}
; diff --git a/frontend/src/components/input.tsx b/frontend/src/components/input.tsx new file mode 100644 index 0000000..45079d8 --- /dev/null +++ b/frontend/src/components/input.tsx @@ -0,0 +1,22 @@ +interface InputProps { + type?: string; + placeholder?: string; + value: string; + onChange: (e: React.ChangeEvent) => void; + label: string; + inlineLabel?: boolean; +} + +export default function Input(props: InputProps) { + if (props.inlineLabel) { + return + } else { + return
+ {props.label} + +
+ } +} \ No newline at end of file diff --git a/frontend/src/network/models.ts b/frontend/src/network/models.ts index 40ee67c..0d1c67a 100644 --- a/frontend/src/network/models.ts +++ b/frontend/src/network/models.ts @@ -108,3 +108,12 @@ export interface CommentWithResource { user: User; resource: Resource; } + +export interface ServerConfig { + max_uploading_size_in_mb: number; + max_file_size_in_mb: number; + max_downloads_per_day_for_single_ip: number; + allow_register: boolean; + cloudflare_turnstile_site_key: string; + cloudflare_turnstile_secret_key: string; +} diff --git a/frontend/src/network/network.ts b/frontend/src/network/network.ts index dcd7819..b9d9ca9 100644 --- a/frontend/src/network/network.ts +++ b/frontend/src/network/network.ts @@ -13,7 +13,8 @@ import { User, UserWithToken, Comment, - CommentWithResource + CommentWithResource, + ServerConfig } from "./models.ts"; class Network { @@ -635,6 +636,32 @@ class Network { return { success: false, message: e.toString() }; } } + + async getServerConfig(): Promise> { + try { + const response = await axios.get(`${this.apiBaseUrl}/config`); + return response.data; + } catch (e: any) { + console.error(e); + return { + success: false, + message: e.toString(), + }; + } + } + + async setServerConfig(config: ServerConfig): Promise> { + try { + const response = await axios.post(`${this.apiBaseUrl}/config`, config); + return response.data; + } catch (e: any) { + console.error(e); + return { + success: false, + message: e.toString(), + }; + } + } } export const network = new Network(); diff --git a/frontend/src/pages/manage_page.tsx b/frontend/src/pages/manage_page.tsx index 1185bc6..1bbc342 100644 --- a/frontend/src/pages/manage_page.tsx +++ b/frontend/src/pages/manage_page.tsx @@ -4,6 +4,7 @@ import StorageView from "./manage_storage_page.tsx"; import UserView from "./manage_user_page.tsx"; import { useTranslation } from "react-i18next"; import { ManageMePage } from "./manage_me_page.tsx"; +import ManageServerConfigPage from "./manage_server_config_page.tsx"; export default function ManagePage() { const { t } = useTranslation(); @@ -43,13 +44,15 @@ export default function ManagePage() { const pageNames = [ t("My Info"), t("Storage"), - t("Users") + t("Users"), + t("Server"), ] const pageComponents = [ , , - + , + , ] return
@@ -80,6 +83,7 @@ export default function ManagePage() { {buildItem(t("My Info"), , 0)} {buildItem(t("Storage"), , 1)} {buildItem(t("Users"), , 2)} + {buildItem(t("Server"), , 3)}
diff --git a/frontend/src/pages/manage_server_config_page.tsx b/frontend/src/pages/manage_server_config_page.tsx new file mode 100644 index 0000000..f5b54f5 --- /dev/null +++ b/frontend/src/pages/manage_server_config_page.tsx @@ -0,0 +1,92 @@ +import { useTranslation } from "react-i18next"; +import { app } from "../app" +import { ErrorAlert, InfoAlert } from "../components/alert" +import { useEffect, useState } from "react"; +import { ServerConfig } from "../network/models"; +import Loading from "../components/loading"; +import Input from "../components/input"; +import { network } from "../network/network"; +import showToast from "../components/toast"; +import Button from "../components/button"; + +export default function ManageServerConfigPage() { + const { t } = useTranslation(); + + const [config, setConfig] = useState(null); + + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + network.getServerConfig().then((res) => { + if (res.success) { + setConfig(res.data!); + } else { + showToast({ + message: res.message, + type: "error", + }) + } + }) + }, []); + + if (!app.user) { + return + } + + if (!app.user?.is_admin) { + return + } + + if (config == null) { + return + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (isLoading) { + return; + } + setIsLoading(true); + const res = await network.setServerConfig(config); + if (res.success) { + showToast({ + message: t("Update server config successfully"), + type: "success", + }); + } else { + showToast({ + message: res.message, + type: "error", + }); + } + setIsLoading(false); + }; + + return
+ { + setConfig({...config, max_uploading_size_in_mb: parseInt(e.target.value) }) + }}> + { + setConfig({...config, max_file_size_in_mb: parseInt(e.target.value) }) + }}> + { + setConfig({...config, max_downloads_per_day_for_single_ip: parseInt(e.target.value) }) + }}> +
+ Allow register + { + setConfig({ ...config, allow_register: e.target.checked }) + }} /> +
+ { + setConfig({...config, cloudflare_turnstile_site_key: e.target.value }) + }}> + { + setConfig({...config, cloudflare_turnstile_secret_key: e.target.value }) + }}> + +
+ +
+ +} \ No newline at end of file diff --git a/frontend/src/pages/resource_details_page.tsx b/frontend/src/pages/resource_details_page.tsx index ad9eeb2..657f7b0 100644 --- a/frontend/src/pages/resource_details_page.tsx +++ b/frontend/src/pages/resource_details_page.tsx @@ -96,7 +96,7 @@ export default function ResourcePage() { }

-
-