import { Button, Divider, Input, Switch, Textarea, Tooltip } from "@mui/joy"; import { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { Link } from "react-router-dom"; import * as api from "@/helpers/api"; import { formatBytes } from "@/helpers/utils"; import { useGlobalStore } from "@/store/module"; import { useTranslate } from "@/utils/i18n"; import { showCommonDialog } from "../Dialog/CommonDialog"; import showDisablePasswordLoginDialog from "../DisablePasswordLoginDialog"; import Icon from "../Icon"; import LearnMore from "../LearnMore"; import showUpdateCustomizedProfileDialog from "../UpdateCustomizedProfileDialog"; import "@/less/settings/system-section.less"; interface State { dbSize: number; allowSignUp: boolean; disablePasswordLogin: boolean; disablePublicMemos: boolean; additionalStyle: string; additionalScript: string; maxUploadSizeMiB: number; autoBackupInterval: number; memoDisplayWithUpdatedTs: boolean; } const SystemSection = () => { const t = useTranslate(); const globalStore = useGlobalStore(); const systemStatus = globalStore.state.systemStatus; const [state, setState] = useState({ dbSize: systemStatus.dbSize, allowSignUp: systemStatus.allowSignUp, disablePasswordLogin: systemStatus.disablePasswordLogin, additionalStyle: systemStatus.additionalStyle, additionalScript: systemStatus.additionalScript, disablePublicMemos: systemStatus.disablePublicMemos, maxUploadSizeMiB: systemStatus.maxUploadSizeMiB, autoBackupInterval: systemStatus.autoBackupInterval, memoDisplayWithUpdatedTs: systemStatus.memoDisplayWithUpdatedTs, }); const [telegramBotToken, setTelegramBotToken] = useState(""); useEffect(() => { globalStore.fetchSystemStatus(); }, []); useEffect(() => { api.getSystemSetting().then(({ data: systemSettings }) => { const telegramBotSetting = systemSettings.find((setting) => setting.name === "telegram-bot-token"); if (telegramBotSetting) { setTelegramBotToken(telegramBotSetting.value); } }); }, []); useEffect(() => { setState({ ...state, dbSize: systemStatus.dbSize, allowSignUp: systemStatus.allowSignUp, disablePasswordLogin: systemStatus.disablePasswordLogin, additionalStyle: systemStatus.additionalStyle, additionalScript: systemStatus.additionalScript, disablePublicMemos: systemStatus.disablePublicMemos, maxUploadSizeMiB: systemStatus.maxUploadSizeMiB, autoBackupInterval: systemStatus.autoBackupInterval, memoDisplayWithUpdatedTs: systemStatus.memoDisplayWithUpdatedTs, }); }, [systemStatus]); const handleAllowSignUpChanged = async (value: boolean) => { setState({ ...state, allowSignUp: value, }); globalStore.setSystemStatus({ allowSignUp: value }); await api.upsertSystemSetting({ name: "allow-signup", value: JSON.stringify(value), }); }; const handleDisablePasswordLoginChanged = async (value: boolean) => { if (value) { showDisablePasswordLoginDialog(); } else { showCommonDialog({ title: t("setting.system-section.enable-password-login"), content: t("setting.system-section.enable-password-login-warning"), style: "danger", dialogName: "enable-password-login-dialog", onConfirm: async () => { setState({ ...state, disablePasswordLogin: value }); globalStore.setSystemStatus({ disablePasswordLogin: value }); await api.upsertSystemSetting({ name: "disable-password-login", value: JSON.stringify(value), }); }, }); } }; const handleUpdateCustomizedProfileButtonClick = () => { showUpdateCustomizedProfileDialog(); }; const handleVacuumBtnClick = async () => { try { await api.vacuumDatabase(); await globalStore.fetchSystemStatus(); } catch (error) { console.error(error); return; } toast.success(t("message.succeed-vacuum-database")); }; const handleTelegramBotTokenChanged = (value: string) => { setTelegramBotToken(value); }; const handleSaveTelegramBotToken = async () => { try { await api.upsertSystemSetting({ name: "telegram-bot-token", value: telegramBotToken, }); } catch (error: any) { console.error(error); toast.error(error.response.data.message); return; } toast.success("Telegram Bot Token updated"); }; const handleAdditionalStyleChanged = (value: string) => { setState({ ...state, additionalStyle: value, }); }; const handleSaveAdditionalStyle = async () => { try { await api.upsertSystemSetting({ name: "additional-style", value: JSON.stringify(state.additionalStyle), }); } catch (error) { console.error(error); return; } toast.success(t("message.succeed-update-additional-style")); }; const handleAdditionalScriptChanged = (value: string) => { setState({ ...state, additionalScript: value, }); }; const handleSaveAdditionalScript = async () => { try { await api.upsertSystemSetting({ name: "additional-script", value: JSON.stringify(state.additionalScript), }); } catch (error) { console.error(error); return; } toast.success(t("message.succeed-update-additional-script")); }; const handleDisablePublicMemosChanged = async (value: boolean) => { setState({ ...state, disablePublicMemos: value, }); globalStore.setSystemStatus({ disablePublicMemos: value }); await api.upsertSystemSetting({ name: "disable-public-memos", value: JSON.stringify(value), }); }; const handleMemoDisplayWithUpdatedTs = async (value: boolean) => { setState({ ...state, memoDisplayWithUpdatedTs: value, }); globalStore.setSystemStatus({ memoDisplayWithUpdatedTs: value }); await api.upsertSystemSetting({ name: "memo-display-with-updated-ts", value: JSON.stringify(value), }); }; const handleMaxUploadSizeChanged = async (event: React.FocusEvent) => { // fixes cursor skipping position on mobile event.target.selectionEnd = event.target.value.length; let num = parseInt(event.target.value); if (Number.isNaN(num)) { num = 0; } setState({ ...state, maxUploadSizeMiB: num, }); event.target.value = num.toString(); globalStore.setSystemStatus({ maxUploadSizeMiB: num }); await api.upsertSystemSetting({ name: "max-upload-size-mib", value: JSON.stringify(num), }); }; const handleMaxUploadSizeFocus = (event: React.FocusEvent) => { event.target.select(); }; const handleAutoBackupIntervalChanged = async (event: React.FocusEvent) => { // fixes cursor skipping position on mobile event.target.selectionEnd = event.target.value.length; let num = parseInt(event.target.value); if (Number.isNaN(num)) { num = 0; } setState({ ...state, autoBackupInterval: num, }); event.target.value = num.toString(); globalStore.setSystemStatus({ autoBackupInterval: num }); await api.upsertSystemSetting({ name: "auto-backup-interval", value: JSON.stringify(num), }); }; const handleAutoBackupIntervalFocus = (event: React.FocusEvent) => { event.target.select(); }; return (

{t("common.basic")}

{t("setting.system-section.server-name")}: {systemStatus.customizedProfile.name}
{t("setting.system-section.database-file-size")}: {formatBytes(state.dbSize)}

{t("common.settings")}

{t("setting.system-section.allow-user-signup")} handleAllowSignUpChanged(event.target.checked)} />
{t("setting.system-section.disable-password-login")} handleDisablePasswordLoginChanged(event.target.checked)} />
{t("setting.system-section.disable-public-memos")} handleDisablePublicMemosChanged(event.target.checked)} />
{t("setting.system-section.display-with-updated-time")} handleMemoDisplayWithUpdatedTs(event.target.checked)} />
{t("setting.system-section.max-upload-size")}
{t("setting.system-section.auto-backup-interval")}
{t("setting.system-section.telegram-bot-token")}
handleTelegramBotTokenChanged(event.target.value)} />
{t("setting.system-section.additional-style")}