feat: data management
This commit is contained in:
@@ -61,5 +61,6 @@
|
||||
"block": "Block",
|
||||
"unblock": "Unblock",
|
||||
"contact_tip": "This user is not in your contact",
|
||||
"contact_block_tip": "This user has been blocked by you"
|
||||
"contact_block_tip": "This user has been blocked by you",
|
||||
"file_expired": "File Expired"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"general": "General",
|
||||
"overview": "Overview",
|
||||
"members": "Members",
|
||||
"data_management": "Data Management",
|
||||
"auto_delete_msg": "Auto-delete Messages",
|
||||
"user": "User",
|
||||
"my_account": "My Account",
|
||||
@@ -95,6 +96,29 @@
|
||||
"disable": "Disable"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"clear_files": {
|
||||
"title": "Files and Images Data",
|
||||
"desc": "Click the button below to remove all files and images data of this server.",
|
||||
"btn": "Clear Files Data"
|
||||
},
|
||||
"clear_msgs": {
|
||||
"title": "All Messages Data",
|
||||
"desc": "Click the button below to remove all the chat data including texts and files of this server.",
|
||||
"btn": "Clear Chats Data"
|
||||
},
|
||||
"auto_delete_file": {
|
||||
"title": "Auto-delete File Data",
|
||||
"desc": "Set expire time for all file data of this server.",
|
||||
"confirm_desc": "All files older than the set time will be deleted.",
|
||||
"off": "Off",
|
||||
"day1": "1 Day",
|
||||
"day7": "7 Days",
|
||||
"day30": "30 Days",
|
||||
"day90": "90 Days",
|
||||
"day180": "180 Days"
|
||||
}
|
||||
},
|
||||
"license": {
|
||||
"signed": "Signed",
|
||||
"domain": "Domains",
|
||||
|
||||
@@ -61,5 +61,6 @@
|
||||
"block": "屏蔽",
|
||||
"unblock": "解除屏蔽",
|
||||
"contact_tip": "该用户不在您的联系人列表中",
|
||||
"contact_block_tip": "该用户已被你屏蔽"
|
||||
"contact_block_tip": "该用户已被你屏蔽",
|
||||
"file_expired": "文件已过期"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"general": "通用",
|
||||
"overview": "概况",
|
||||
"members": "成员",
|
||||
"data_management": "数据管理",
|
||||
"auto_delete_msg": "自动删除消息",
|
||||
"my_account": "我的账号",
|
||||
"config": "配置",
|
||||
@@ -89,6 +90,29 @@
|
||||
"disable": "关闭"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"clear_files": {
|
||||
"title": "文件数据",
|
||||
"desc": "清除所有人的文件图片类型数据",
|
||||
"btn": "一键删除"
|
||||
},
|
||||
"clear_msgs": {
|
||||
"title": "聊天数据",
|
||||
"desc": "清除所有人的聊天数据,即所有文字与文件数据。",
|
||||
"btn": "一键删除"
|
||||
},
|
||||
"auto_delete_file": {
|
||||
"title": "定时删除数据",
|
||||
"desc": "请选择定时消息的时长",
|
||||
"confirm_desc": "所有在此之前的文件数据都将删除",
|
||||
"off": "关闭",
|
||||
"day1": "1天",
|
||||
"day7": "7天",
|
||||
"day30": "30天",
|
||||
"day90": "90天",
|
||||
"day180": "180天"
|
||||
}
|
||||
},
|
||||
"license": {
|
||||
"signed": "签名",
|
||||
"domain": "域名",
|
||||
|
||||
@@ -365,6 +365,18 @@ export const serverApi = createApi({
|
||||
body: { license }
|
||||
})
|
||||
}),
|
||||
clearAllMessages: builder.query<void, void>({
|
||||
query: () => ({
|
||||
url: "/admin/system/message/clear",
|
||||
method: "DELETE"
|
||||
})
|
||||
}),
|
||||
clearAllFiles: builder.query<void, void>({
|
||||
query: () => ({
|
||||
url: "/resource/file/delete",
|
||||
method: "DELETE"
|
||||
})
|
||||
}),
|
||||
getBotRelatedChannels: builder.query<Channel[], { api_key: string; public_only?: boolean }>({
|
||||
query: ({ api_key, public_only = false }) => ({
|
||||
url: public_only ? `/bot?public_only=${public_only}` : `/bot`,
|
||||
@@ -443,5 +455,7 @@ export const {
|
||||
useLazyGetSystemCommonQuery,
|
||||
useGetSystemCommonQuery,
|
||||
useGenerateAgoraTokenMutation,
|
||||
useLazyGetAgoraUsersByChannelQuery
|
||||
useLazyGetAgoraUsersByChannelQuery,
|
||||
useLazyClearAllFilesQuery,
|
||||
useLazyClearAllMessagesQuery
|
||||
} = serverApi;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// import React from 'react'
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useGetSystemCommonQuery, useUpdateSystemCommonMutation } from "@/app/services/server";
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import { MessageExpireMode } from "@/types/server";
|
||||
import Modal from "@/components/Modal";
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import Button from "@/components/styled/Button";
|
||||
import StyledModal from "@/components/styled/Modal";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
|
||||
// type Props = {
|
||||
// updateConfirmModal: (context: VisibleModalType | null) => void;
|
||||
// };
|
||||
|
||||
const AutoDeleteFiles = () => {
|
||||
const currStatus = useAppSelector((store) => store.server.max_file_expiry_mode ?? "Off");
|
||||
const { t } = useTranslation("setting", { keyPrefix: "data.auto_delete_file" });
|
||||
const { t: ct } = useTranslation();
|
||||
const { refetch } = useGetSystemCommonQuery();
|
||||
const [selected, setSelected] = useState(currStatus);
|
||||
const [updateSetting, { isSuccess, isLoading }] = useUpdateSystemCommonMutation();
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
refetch();
|
||||
toast.success(ct("tip.update"));
|
||||
}
|
||||
}, [isSuccess]);
|
||||
const handleChange = (newVal: MessageExpireMode) => {
|
||||
// updateConfirmModal("auto_delete_file");
|
||||
setSelected(newVal);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setSelected(currStatus);
|
||||
};
|
||||
const handleUpdate = () => {
|
||||
// todo
|
||||
updateSetting({ max_file_expiry_mode: selected });
|
||||
};
|
||||
// if (!loadSuccess) return null;
|
||||
return (
|
||||
<>
|
||||
<SettingBlock title={t("title")} desc={t("desc")}>
|
||||
<StyledRadio
|
||||
options={[t("off"), t("day1"), t("day7"), t("day30"), t("day90"), t("day180")]}
|
||||
values={["Off", "Day1", "Day7", "Day30", "Day90", "Day180"]}
|
||||
value={currStatus}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</SettingBlock>
|
||||
{selected !== currStatus && (
|
||||
<Modal id="modal-modal">
|
||||
<StyledModal
|
||||
title={"Are you sure?"}
|
||||
description={selected == "Off" ? "" : t("confirm_desc")}
|
||||
buttons={
|
||||
<>
|
||||
<Button className="cancel" onClick={handleClose}>
|
||||
{ct("action.cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleUpdate} className="danger">
|
||||
{isLoading ? "Updating" : ct("action.yes")}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
></StyledModal>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AutoDeleteFiles;
|
||||
@@ -0,0 +1,66 @@
|
||||
import { FC, useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useLazyClearAllFilesQuery, useLazyClearAllMessagesQuery } from "@/app/services/server";
|
||||
import Modal from "@/components/Modal";
|
||||
import Button from "@/components/styled/Button";
|
||||
import StyledModal from "@/components/styled/Modal";
|
||||
import { VisibleModalType } from "./index";
|
||||
|
||||
interface Props {
|
||||
context: VisibleModalType;
|
||||
title: string;
|
||||
desc: string;
|
||||
closeModal: () => void;
|
||||
}
|
||||
|
||||
const ClearConfirmModal: FC<Props> = ({ context, title, desc, closeModal }) => {
|
||||
// const { t } = useTranslation("auth");
|
||||
const { t: ct } = useTranslation();
|
||||
const [clearFiles, { isLoading: filesClearing, isSuccess: clearFilesSuccess }] =
|
||||
useLazyClearAllFilesQuery();
|
||||
const [clearMessages, { isLoading: msgClearing, isSuccess: clearMsgSuccess }] =
|
||||
useLazyClearAllMessagesQuery();
|
||||
const handleClear = () => {
|
||||
//todo
|
||||
switch (context) {
|
||||
case "chat":
|
||||
clearMessages();
|
||||
break;
|
||||
case "files":
|
||||
clearFiles();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const clearSuccess = clearFilesSuccess || clearMsgSuccess;
|
||||
useEffect(() => {
|
||||
if (clearSuccess) {
|
||||
toast.success("Clear success");
|
||||
closeModal();
|
||||
}
|
||||
}, [clearSuccess]);
|
||||
const clearing = msgClearing || filesClearing;
|
||||
return (
|
||||
<Modal id="modal-modal">
|
||||
<StyledModal
|
||||
title={title}
|
||||
description={desc}
|
||||
buttons={
|
||||
<>
|
||||
<Button className="cancel" onClick={closeModal}>
|
||||
{ct("action.cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleClear} className="danger">
|
||||
{clearing ? "Clearing" : ct("action.remove")}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
></StyledModal>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClearConfirmModal;
|
||||
@@ -0,0 +1,49 @@
|
||||
// import { useState, useEffect, ChangeEvent } from "react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
// import { useAppSelector } from "@/app/store";
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import StyledButton from "@/components/styled/Button";
|
||||
import AutoDeleteFiles from "./AutoDeleteFiles";
|
||||
import ClearConfirmModal from "./ClearConfirmModal";
|
||||
|
||||
export type VisibleModalType = "chat" | "files";
|
||||
export default function DataManagement() {
|
||||
const [visibleModal, setVisibleModal] = useState<VisibleModalType | null>(null);
|
||||
const { t } = useTranslation("setting");
|
||||
const handleModalVisible = (visible: VisibleModalType | null) => {
|
||||
setVisibleModal(visible);
|
||||
};
|
||||
const clearConfirmDesc = {
|
||||
chat: t("data.clear_msgs.desc"),
|
||||
files: t("data.clear_files.desc")
|
||||
};
|
||||
return (
|
||||
<div className="relative w-full md:w-[512px] flex flex-col gap-6">
|
||||
{/* 清除服务器聊天消息 */}
|
||||
<SettingBlock title={t("data.clear_msgs.title")} desc={t("data.clear_msgs.desc")}>
|
||||
<StyledButton onClick={handleModalVisible.bind(null, "chat")} className="danger">
|
||||
{t("data.clear_msgs.btn")}
|
||||
</StyledButton>
|
||||
</SettingBlock>
|
||||
|
||||
{/* 清除服务器文件 */}
|
||||
<SettingBlock title={t("data.clear_files.title")} desc={t("data.clear_files.desc")}>
|
||||
<StyledButton onClick={handleModalVisible.bind(null, "files")} className="danger">
|
||||
{t("data.clear_files.btn")}
|
||||
</StyledButton>
|
||||
</SettingBlock>
|
||||
{/* 自动清除清除服务器数据 */}
|
||||
<AutoDeleteFiles />
|
||||
{visibleModal && (
|
||||
<ClearConfirmModal
|
||||
closeModal={handleModalVisible.bind(null, null)}
|
||||
context={visibleModal}
|
||||
title="Are you sure?"
|
||||
desc={clearConfirmDesc[visibleModal]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { useEffect } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import {
|
||||
useGetSystemCommonQuery,
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
} from "../../../app/services/server";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import { ChatLayout } from "../../../types/server";
|
||||
import SettingBlock from "./SettingBlock";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@ import { useEffect } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import {
|
||||
useGetSystemCommonQuery,
|
||||
useUpdateSystemCommonMutation
|
||||
} from "../../../app/services/server";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import SettingBlock from "./SettingBlock";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import Radio from "../../../components/styled/Radio";
|
||||
import { Theme } from "../../../types/common";
|
||||
import SettingBlock from "./SettingBlock";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ import { ChangeEvent, useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import StyledButton from "@/components/styled/Button";
|
||||
import StyledInput from "@/components/styled/Input";
|
||||
import { useGetFrontendUrlQuery, useUpdateFrontendUrlMutation } from "../../../app/services/server";
|
||||
import SettingBlock from "./SettingBlock";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// import React from 'react'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import SettingBlock from "./SettingBlock";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@ import { useEffect } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import {
|
||||
useGetSystemCommonQuery,
|
||||
useUpdateSystemCommonMutation
|
||||
} from "../../../app/services/server";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import SettingBlock from "./SettingBlock";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import { LoginConfig, WhoCanSignUp } from "@/types/server";
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import useConfig from "@/hooks/useConfig";
|
||||
import ChatLayout from "./ChatLayout";
|
||||
@@ -12,7 +13,6 @@ import FrontendURL from "./FrontendURL";
|
||||
import Language from "./Language";
|
||||
import OnlineStatus from "./OnlineStatus";
|
||||
import Server from "./Server";
|
||||
import SettingBlock from "./SettingBlock";
|
||||
|
||||
export default function Overview() {
|
||||
const { t } = useTranslation("setting");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// import React from 'react'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import SettingBlock from "./SettingBlock";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import ConfigAgora from "./config/Agora";
|
||||
import ConfigFirebase from "./config/Firebase";
|
||||
import Logins from "./config/Logins";
|
||||
import ConfigSMTP from "./config/SMTP";
|
||||
import DataManagement from "./DataManagement";
|
||||
import License from "./License";
|
||||
import MyAccount from "./MyAccount";
|
||||
import Overview from "./Overview";
|
||||
@@ -31,6 +32,11 @@ const navs = [
|
||||
name: "members",
|
||||
component: <ManageMembers />,
|
||||
admin: true
|
||||
},
|
||||
{
|
||||
name: "data_management",
|
||||
component: <DataManagement />,
|
||||
admin: true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
+2
-1
@@ -7,12 +7,13 @@ export interface Server extends SystemCommon {
|
||||
description: string;
|
||||
}
|
||||
export type ChatLayout = "SelfRight" | "Left";
|
||||
export type MessageExpireMode = "Off" | "Day1" | "Day7" | "Day30" | "Day90" | "Day180";
|
||||
export interface SystemCommon {
|
||||
show_user_online_status?: boolean;
|
||||
webclient_auto_update?: boolean;
|
||||
contact_verification_enable?: boolean;
|
||||
chat_layout_mode?: ChatLayout;
|
||||
max_file_expiry_mode?: "Off" | "Day1" | "Day7" | "Day30" | "Day90" | "Day180";
|
||||
max_file_expiry_mode?: MessageExpireMode;
|
||||
}
|
||||
export interface GithubAuthConfig {
|
||||
client_id?: string;
|
||||
|
||||
Reference in New Issue
Block a user