diff --git a/.vscode/settings.json b/.vscode/settings.json index ac8c9472..096f1c9f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "hljs", "Langchain", "Milvus", + "Mintplex", "Ollama", "openai", "Qdrant", diff --git a/frontend/src/components/UserMenu/index.jsx b/frontend/src/components/UserMenu/index.jsx index e6f3c7cf..85446a21 100644 --- a/frontend/src/components/UserMenu/index.jsx +++ b/frontend/src/components/UserMenu/index.jsx @@ -52,6 +52,7 @@ function UserButton() { const { user } = useUser(); const [showMenu, setShowMenu] = useState(false); const [showAccountSettings, setShowAccountSettings] = useState(false); + const [supportEmail, setSupportEmail] = useState(""); const mode = useLoginMode(); const menuRef = useRef(); const buttonRef = useRef(); @@ -77,6 +78,18 @@ function UserButton() { return () => document.removeEventListener("mousedown", handleClose); }, [showMenu]); + useEffect(() => { + const fetchSupportEmail = async () => { + const supportEmail = await System.fetchSupportEmail(); + if (supportEmail.email) { + setSupportEmail(`mailto:${supportEmail.email}`); + } else { + setSupportEmail(paths.mailToMintplex()); + } + }; + fetchSupportEmail(); + }, []); + if (mode === null) return null; return ( @@ -105,7 +118,7 @@ function UserButton() { )} Support diff --git a/frontend/src/models/system.js b/frontend/src/models/system.js index 65c9e26e..70e75685 100644 --- a/frontend/src/models/system.js +++ b/frontend/src/models/system.js @@ -5,6 +5,7 @@ import DataConnector from "./dataConnector"; const System = { cacheKeys: { footerIcons: "anythingllm_footer_links", + supportEmail: "anythingllm_support_email", }, ping: async function () { return await fetch(`${API_BASE}/ping`) @@ -225,6 +226,36 @@ const System = { ); return { footerData: newData, error: null }; }, + fetchSupportEmail: async function () { + const cache = window.localStorage.getItem(this.cacheKeys.supportEmail); + const { email, lastFetched } = cache + ? safeJsonParse(cache, { email: "", lastFetched: 0 }) + : { email: "", lastFetched: 0 }; + + if (!!email && Date.now() - lastFetched < 3_600_000) + return { email: email, error: null }; + + const { supportEmail, error } = await fetch( + `${API_BASE}/system/support-email`, + { + method: "GET", + cache: "no-cache", + headers: baseHeaders(), + } + ) + .then((res) => res.json()) + .catch((e) => { + console.log(e); + return { email: "", error: e.message }; + }); + + if (!supportEmail || !!error) return { email: "", error: null }; + window.localStorage.setItem( + this.cacheKeys.supportEmail, + JSON.stringify({ email: supportEmail, lastFetched: Date.now() }) + ); + return { email: supportEmail, error: null }; + }, fetchLogo: async function () { return await fetch(`${API_BASE}/system/logo`, { method: "GET", diff --git a/frontend/src/pages/GeneralSettings/Appearance/SupportEmail/index.jsx b/frontend/src/pages/GeneralSettings/Appearance/SupportEmail/index.jsx new file mode 100644 index 00000000..548001e2 --- /dev/null +++ b/frontend/src/pages/GeneralSettings/Appearance/SupportEmail/index.jsx @@ -0,0 +1,94 @@ +import useUser from "@/hooks/useUser"; +import Admin from "@/models/admin"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import { useEffect, useState } from "react"; + +export default function SupportEmail() { + const { user } = useUser(); + const [loading, setLoading] = useState(true); + const [hasChanges, setHasChanges] = useState(false); + const [supportEmail, setSupportEmail] = useState(""); + const [originalEmail, setOriginalEmail] = useState(""); + + useEffect(() => { + const fetchSupportEmail = async () => { + const supportEmail = await System.fetchSupportEmail(); + setSupportEmail(supportEmail.email || ""); + setOriginalEmail(supportEmail.email || ""); + setLoading(false); + }; + fetchSupportEmail(); + }, []); + + const updateSupportEmail = async (e, newValue = null) => { + e.preventDefault(); + let support_email = newValue; + if (newValue === null) { + const form = new FormData(e.target); + support_email = form.get("supportEmail"); + } + + const { success, error } = await Admin.updateSystemPreferences({ + support_email, + }); + + if (!success) { + showToast(`Failed to update support email: ${error}`, "error"); + return; + } else { + showToast("Successfully updated support email.", "success"); + window.localStorage.removeItem(System.cacheKeys.supportEmail); + setSupportEmail(support_email); + setOriginalEmail(support_email); + setHasChanges(false); + } + }; + + const handleChange = (e) => { + setSupportEmail(e.target.value); + setHasChanges(true); + }; + + if (loading || !user?.role) return null; + return ( +
+
+

Support Email

+

+ Set the support email address that shows up in the user menu while + logged into this instance. +

+
+
+ + {originalEmail !== "" && ( + + )} +
+ {hasChanges && ( + + )} +
+ ); +} diff --git a/frontend/src/pages/GeneralSettings/Appearance/index.jsx b/frontend/src/pages/GeneralSettings/Appearance/index.jsx index 99d413a4..2ac04208 100644 --- a/frontend/src/pages/GeneralSettings/Appearance/index.jsx +++ b/frontend/src/pages/GeneralSettings/Appearance/index.jsx @@ -8,6 +8,7 @@ import EditingChatBubble from "@/components/EditingChatBubble"; import showToast from "@/utils/toast"; import { Plus } from "@phosphor-icons/react"; import FooterCustomization from "./FooterCustomization"; +import SupportEmail from "./SupportEmail"; export default function Appearance() { const { logo: _initLogo, setLogo: _setLogo } = useLogo(); @@ -250,6 +251,7 @@ export default function Appearance() { )} + diff --git a/server/endpoints/admin.js b/server/endpoints/admin.js index 885fa998..a54e0abd 100644 --- a/server/endpoints/admin.js +++ b/server/endpoints/admin.js @@ -307,6 +307,9 @@ function adminEndpoints(app) { footer_data: (await SystemSettings.get({ label: "footer_data" }))?.value || JSON.stringify([]), + support_email: + (await SystemSettings.get({ label: "support_email" }))?.value || + null, }; response.status(200).json({ settings }); } catch (e) { diff --git a/server/endpoints/system.js b/server/endpoints/system.js index 02d78617..11aeb8cb 100644 --- a/server/endpoints/system.js +++ b/server/endpoints/system.js @@ -469,6 +469,21 @@ function systemEndpoints(app) { } }); + app.get("/system/support-email", [validatedRequest], async (_, response) => { + try { + const supportEmail = + ( + await SystemSettings.get({ + label: "support_email", + }) + )?.value ?? null; + response.status(200).json({ supportEmail: supportEmail }); + } catch (error) { + console.error("Error fetching support email:", error); + response.status(500).json({ message: "Internal server error" }); + } + }); + app.get( "/system/pfp/:id", [validatedRequest, flexUserRoleValid([ROLES.all])], diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index 8a008d0f..29949d3d 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -13,6 +13,7 @@ const SystemSettings = { "logo_filename", "telemetry_id", "footer_data", + "support_email", ], validations: { footer_data: (updates) => {