Custom default messages implementation for single and multi-user modes (#193)

* added ui for custom welcome messages and added label for custom logo in admin settings

* linting

* fixing img to use light/dark modes

* converted ChatBubble into component

* implemented backend for welcome messages and admin appearance page

* completed custom welcome messages for admin

* finished custom messages for single user mode

* merged with master and linted

* improved UI for appearance settings pages

* linted and merged with master

* small updates

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
Sean Hatfield 2023-08-16 17:30:46 -07:00 committed by GitHub
parent 882b362213
commit 31fbb0784b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 656 additions and 93 deletions

View File

@ -0,0 +1,29 @@
import React from "react";
export default function ChatBubble({ message, type, popMsg }) {
const isUser = type === "user";
return (
<div
className={`flex w-full mt-2 items-center ${
popMsg ? "chat__message" : ""
} ${isUser ? "justify-end" : "justify-start"}`}
>
<div
className={`p-4 max-w-full md:max-w-[75%] ${
isUser
? "bg-slate-200 dark:bg-amber-800"
: "bg-orange-100 dark:bg-stone-700"
} rounded-b-2xl ${isUser ? "rounded-tl-2xl" : "rounded-tr-2xl"} ${
isUser ? "rounded-tr-sm" : "rounded-tl-sm"
}`}
>
{message && (
<p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base">
{message}
</p>
)}
</div>
</div>
);
}

View File

@ -6,9 +6,12 @@ import NewWorkspaceModal, {
import paths from "../../utils/paths";
import { isMobile } from "react-device-detect";
import { SidebarMobileHeader } from "../Sidebar";
import ChatBubble from "../ChatBubble";
import System from "../../models/system";
export default function DefaultChatContainer() {
const [mockMsgs, setMockMessages] = useState([]);
const [fetchedMessages, setFetchedMessages] = useState([]);
const {
showing: showingNewWsModal,
showModal: showNewWsModal,
@ -16,6 +19,14 @@ export default function DefaultChatContainer() {
} = useNewWorkspaceModal();
const popMsg = !window.localStorage.getItem("anythingllm_intro");
useEffect(() => {
const fetchData = async () => {
const fetchedMessages = await System.getWelcomeMessages();
setFetchedMessages(fetchedMessages);
};
fetchData();
}, []);
const MESSAGES = [
<React.Fragment>
<div
@ -251,9 +262,25 @@ export default function DefaultChatContainer() {
className="transition-all duration-500 relative md:ml-[2px] md:mr-[8px] md:my-[16px] md:rounded-[26px] bg-white dark:bg-black-900 md:min-w-[82%] p-[18px] h-full overflow-y-scroll"
>
{isMobile && <SidebarMobileHeader />}
{mockMsgs.map((content, i) => {
return <React.Fragment key={i}>{content}</React.Fragment>;
})}
{fetchedMessages.length === 0
? mockMsgs.map((content, i) => {
return <React.Fragment key={i}>{content}</React.Fragment>;
})
: fetchedMessages.map((fetchedMessage, i) => {
return (
<React.Fragment key={i}>
<ChatBubble
message={
fetchedMessage.user === ""
? fetchedMessage.response
: fetchedMessage.user
}
type={fetchedMessage.user === "" ? "response" : "user"}
popMsg={popMsg}
/>
</React.Fragment>
);
})}
{showingNewWsModal && <NewWorkspaceModal hideModal={hideNewWsModal} />}
</div>
);

View File

@ -0,0 +1,67 @@
import React, { useState } from "react";
import { X } from "react-feather";
export default function EditingChatBubble({
message,
index,
type,
handleMessageChange,
removeMessage,
}) {
const [isEditing, setIsEditing] = useState(false);
const [tempMessage, setTempMessage] = useState(message[type]);
const isUser = type === "user";
return (
<div
className={`flex w-full mt-2 items-center ${
isUser ? "justify-end" : "justify-start"
}`}
>
{isUser && (
<button
className="flex items-center text-red-500 hover:text-red-700 transition mr-2"
onClick={() => removeMessage(index)}
>
<X className="mr-2" size={20} />
</button>
)}
<div
className={`p-4 max-w-full md:max-w-[75%] ${
isUser
? "bg-slate-200 dark:bg-amber-800"
: "bg-orange-100 dark:bg-stone-700"
} rounded-b-2xl ${isUser ? "rounded-tl-2xl" : "rounded-tr-2xl"} ${
isUser ? "rounded-tr-sm" : "rounded-tl-sm"
}`}
onDoubleClick={() => setIsEditing(true)}
>
{isEditing ? (
<input
value={tempMessage}
onChange={(e) => setTempMessage(e.target.value)}
onBlur={() => {
handleMessageChange(index, type, tempMessage);
setIsEditing(false);
}}
autoFocus
/>
) : (
tempMessage && (
<p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base">
{tempMessage}
</p>
)
)}
</div>
{!isUser && (
<button
className="flex items-center text-red-500 hover:text-red-700 transition ml-2"
onClick={() => removeMessage(index)}
>
<X className="mr-2" size={20} />
</button>
)}
</div>
);
}

View File

@ -216,6 +216,22 @@ const Admin = {
return { success: false, error: e.message };
});
},
setWelcomeMessages: async function (messages) {
return fetch(`${API_BASE}/system/set-welcome-messages`, {
method: "POST",
headers: baseHeaders(),
body: JSON.stringify({ messages }),
})
.then((res) => {
if (!res.ok)
throw new Error(res.statusText || "Error setting welcome messages.");
return res.json();
})
.catch((e) => {
console.error(e);
return { success: false, error: e.message };
});
},
};
export default Admin;

View File

@ -188,6 +188,38 @@ const System = {
return { success: false, error: e.message };
});
},
getWelcomeMessages: async function () {
return await fetch(`${API_BASE}/system/welcome-messages`, {
method: "GET",
cache: "no-cache",
})
.then((res) => {
if (!res.ok) throw new Error("Could not fetch welcome messages.");
return res.json();
})
.then((res) => res.welcomeMessages)
.catch((e) => {
console.error(e);
return null;
});
},
setWelcomeMessages: async function (messages) {
return fetch(`${API_BASE}/system/set-welcome-messages`, {
method: "POST",
headers: baseHeaders(),
body: JSON.stringify({ messages }),
})
.then((res) => {
if (!res.ok) {
throw new Error(res.statusText || "Error setting welcome messages.");
}
return { success: true, ...res.json() };
})
.catch((e) => {
console.error(e);
return { success: false, error: e.message };
});
},
};
export default System;

View File

@ -7,12 +7,16 @@ import AnythingLLMDark from "../../../media/logo/anything-llm-dark.png";
import usePrefersDarkMode from "../../../hooks/usePrefersDarkMode";
import useLogo from "../../../hooks/useLogo";
import System from "../../../models/system";
import EditingChatBubble from "../../../components/EditingChatBubble";
export default function Appearance() {
const { logo: _initLogo } = useLogo();
const [logo, setLogo] = useState("");
const prefersDarkMode = usePrefersDarkMode();
const [errorMsg, setErrorMsg] = useState("");
const [successMsg, setSuccessMsg] = useState("");
const [hasChanges, setHasChanges] = useState(false);
const [messages, setMessages] = useState([]);
useEffect(() => {
async function setInitLogo() {
@ -27,7 +31,21 @@ export default function Appearance() {
setErrorMsg("");
}, 3_500);
}
}, [errorMsg]);
if (!!successMsg) {
setTimeout(() => {
setSuccessMsg("");
}, 3_500);
}
}, [errorMsg, successMsg]);
useEffect(() => {
async function fetchMessages() {
const messages = await System.getWelcomeMessages();
setMessages(messages);
}
fetchMessages();
}, []);
const handleFileUpload = async (event) => {
const file = event.target.files[0];
@ -62,6 +80,42 @@ export default function Appearance() {
window.location.reload();
};
const addMessage = (type) => {
if (type === "user") {
setMessages([
...messages,
{ user: "Double click to edit...", response: "" },
]);
} else {
setMessages([
...messages,
{ user: "", response: "Double click to edit..." },
]);
}
};
const removeMessage = (index) => {
setHasChanges(true);
setMessages(messages.filter((_, i) => i !== index));
};
const handleMessageChange = (index, type, value) => {
setHasChanges(true);
const newMessages = [...messages];
newMessages[index][type] = value;
setMessages(newMessages);
};
const handleMessageSave = async () => {
const { success, error } = await Admin.setWelcomeMessages(messages);
if (!success) {
setErrorMsg(error);
return;
}
setSuccessMsg("Successfully updated welcome messages.");
setHasChanges(false);
};
return (
<div className="w-screen h-screen overflow-hidden bg-orange-100 dark:bg-stone-700 flex">
{!isMobile && <Sidebar />}
@ -79,48 +133,118 @@ export default function Appearance() {
Customize the appearance settings of your platform.
</p>
</div>
<div className="flex items-center">
<img
src={logo}
alt="Uploaded Logo"
className="w-48 h-48 object-contain mr-6"
onError={(e) =>
(e.target.src = prefersDarkMode
? AnythingLLMLight
: AnythingLLMDark)
}
/>
<div className="flex flex-col">
<div className="mb-4">
<label className="cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">
Upload Image
<input
type="file"
accept="image/*"
className="hidden"
onChange={handleFileUpload}
/>
</label>
<button
onClick={handleRemoveLogo}
className="ml-4 cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
Remove Custom Logo
</button>
</div>
<div className="text-sm text-gray-600 dark:text-gray-300">
Upload your logo. Recommended size: 800x200.
<div className="mb-6">
<div className="flex flex-col gap-y-2">
<h2 className="leading-tight font-medium text-black dark:text-white">
Custom Logo
</h2>
<p className="leading-tight text-sm text-gray-500 dark:text-slate-400">
Change the logo that appears in the sidebar.
</p>
</div>
<div className="flex items-center">
<img
src={logo}
alt="Uploaded Logo"
className="w-48 h-48 object-contain mr-6"
onError={(e) =>
(e.target.src = prefersDarkMode
? AnythingLLMLight
: AnythingLLMDark)
}
/>
<div className="flex flex-col">
<div className="mb-4">
<label className="cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">
Upload Image
<input
type="file"
accept="image/*"
className="hidden"
onChange={handleFileUpload}
/>
</label>
<button
onClick={handleRemoveLogo}
className="ml-4 cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
Remove Custom Logo
</button>
</div>
<div className="text-sm text-gray-600 dark:text-gray-300">
Upload your logo. Recommended size: 800x200.
</div>
</div>
</div>
</div>
<div className="mb-6">
<div className="flex flex-col gap-y-2">
<h2 className="leading-tight font-medium text-black dark:text-white">
Custom Messages
</h2>
<p className="leading-tight text-sm text-gray-500 dark:text-slate-400">
Change the default messages that are displayed to the users.
</p>
</div>
<div className="mt-6 flex flex-col gap-y-6">
{messages.map((message, index) => (
<div key={index} className="flex flex-col gap-y-2">
{message.user && (
<EditingChatBubble
message={message}
index={index}
type="user"
handleMessageChange={handleMessageChange}
removeMessage={removeMessage}
/>
)}
{message.response && (
<EditingChatBubble
message={message}
index={index}
type="response"
handleMessageChange={handleMessageChange}
removeMessage={removeMessage}
/>
)}
</div>
))}
<div className="flex gap-4 mt-4 justify-between">
<button
className="self-end text-orange-500 hover:text-orange-700 transition"
onClick={() => addMessage("response")}
>
+ System Message
</button>
<button
className="self-end text-orange-500 hover:text-orange-700 transition"
onClick={() => addMessage("user")}
>
+ User Message
</button>
</div>
</div>
{hasChanges && (
<div className="flex justify-center py-6">
<button
className="ml-4 cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
onClick={handleMessageSave}
>
Save Messages
</button>
</div>
)}
</div>
{errorMsg && (
<div className="mt-4 text-sm text-red-600 dark:text-red-400 text-center">
{errorMsg}
</div>
)}
{successMsg && (
<div className="mt-4 text-sm text-green-600 dark:text-green-400 text-center">
{successMsg}
</div>
)}
</div>
</div>
</div>

View File

@ -4,6 +4,10 @@ import AnythingLLMDark from "../../media/logo/anything-llm-dark.png";
import System from "../../models/system";
import usePrefersDarkMode from "../../hooks/usePrefersDarkMode";
import useLogo from "../../hooks/useLogo";
import EditingChatBubble from "../../components/EditingChatBubble";
import { isMobile } from "react-device-detect";
import { ArrowLeft } from "react-feather";
import paths from "../../utils/paths";
export default function Appearance() {
const { logo: _initLogo } = useLogo();
@ -11,6 +15,16 @@ export default function Appearance() {
const [logo, setLogo] = useState("");
const [errorMsg, setErrorMsg] = useState("");
const [successMsg, setSuccessMsg] = useState("");
const [hasChanges, setHasChanges] = useState(false);
const [messages, setMessages] = useState([]);
useEffect(() => {
async function fetchMessages() {
const messages = await System.getWelcomeMessages();
setMessages(messages);
}
fetchMessages();
}, []);
useEffect(() => {
async function setInitLogo() {
@ -68,66 +82,181 @@ export default function Appearance() {
setErrorMsg("");
};
const addMessage = (type) => {
if (type === "user") {
setMessages([
...messages,
{ user: "Double click to edit...", response: "" },
]);
} else {
setMessages([
...messages,
{ user: "", response: "Double click to edit..." },
]);
}
};
const removeMessage = (index) => {
setHasChanges(true);
setMessages(messages.filter((_, i) => i !== index));
};
const handleMessageChange = (index, type, value) => {
setHasChanges(true);
const newMessages = [...messages];
newMessages[index][type] = value;
setMessages(newMessages);
};
const handleMessageSave = async () => {
const { success, error } = await System.setWelcomeMessages(messages);
if (!success) {
setErrorMsg(error);
return;
}
setSuccessMsg("Successfully updated welcome messages.");
setHasChanges(false);
};
const handleBackNavigation = () => {
window.location = paths.home();
};
return (
<div className="min-h-screen flex items-center justify-center bg-orange-100 dark:bg-black-900">
<div className="p-6 w-full max-w-xl bg-white dark:bg-stone-600 rounded-xl shadow-md space-y-4">
<h2 className="text-2xl font-bold text-center text-black dark:text-white">
Customize Appearance
</h2>
<p className="text-center text-xs font-light text-black dark:text-white">
Customize the logo you see on the sidebar
</p>
<div className="flex flex-col items-center border border-slate-200 dark:border-black-900 p-6 rounded-xl">
<img
src={logo}
alt="Uploaded Logo"
className="w-48 h-48 object-contain"
onError={(e) =>
(e.target.src = prefersDarkMode
? AnythingLLMLight
: AnythingLLMDark)
}
/>
<div className="flex gap-2 p-2 flex-col items-center">
<div className="text-sm text-gray-600 dark:text-gray-300">
Upload your logo
<div className="w-screen h-screen overflow-hidden bg-orange-100 dark:bg-stone-700 flex justify-center py-6">
<div
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
className="transition-all duration-500 relative md:ml-[2px] md:mr-[8px] md:my-[16px] md:rounded-[26px] bg-white dark:bg-black-900 md:min-w-[82%] p-[18px] h-full overflow-y-scroll"
>
<div className="px-1 md:px-8">
<div className="mb-6">
<div
className="cursor-pointer inline-flex items-center gap-3 mb-5 py-2 pl-2 pr-4 text-white rounded-md hover:bg-gray-300 dark:hover:bg-gray-800 transition-all"
onClick={handleBackNavigation}
>
<ArrowLeft />
<span>Back</span>
</div>
<div className="text-sm text-gray-600 dark:text-gray-300">
Recommended size at least 800x200
<p className="text-3xl font-semibold text-slate-600 dark:text-slate-200">
Appearance Settings
</p>
<p className="mt-2 text-sm font-base text-slate-600 dark:text-slate-200">
Customize the appearance settings of your platform.
</p>
</div>
<div className="mb-6">
<div className="flex flex-col gap-y-2">
<h2 className="leading-tight font-medium text-black dark:text-white">
Custom Logo
</h2>
<p className="leading-tight text-sm text-gray-500 dark:text-slate-400">
Change the logo that appears in the sidebar.
</p>
</div>
<div className="flex items-center">
<img
src={logo}
alt="Uploaded Logo"
className="w-48 h-48 object-contain mr-6"
onError={(e) =>
(e.target.src = prefersDarkMode
? AnythingLLMLight
: AnythingLLMDark)
}
/>
<div className="flex flex-col">
<div className="mb-4">
<label className="cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">
Upload Image
<input
type="file"
accept="image/*"
className="hidden"
onChange={handleFileUpload}
/>
</label>
<button
onClick={handleRemoveLogo}
className="ml-4 cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
Remove Custom Logo
</button>
</div>
<div className="text-sm text-gray-600 dark:text-gray-300">
Upload your logo. Recommended size: 800x200.
</div>
</div>
</div>
</div>
</div>
<div className="flex justify-center mt-4 gap-2">
<label className="cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">
Upload Image
<input
type="file"
accept="image/*"
className="hidden"
onChange={handleFileUpload}
/>
</label>
<button
onClick={handleRemoveLogo}
className="cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
Remove Custom Logo
</button>
</div>
{errorMsg && (
<div className="text-sm text-red-600 dark:text-red-400 text-center">
{errorMsg}
<div className="mb-6">
<div className="flex flex-col gap-y-2">
<h2 className="leading-tight font-medium text-black dark:text-white">
Custom Messages
</h2>
<p className="leading-tight text-sm text-gray-500 dark:text-slate-400">
Change the default messages that are displayed to the users.
</p>
</div>
<div className="mt-6 flex flex-col gap-y-6">
{messages.map((message, index) => (
<div key={index} className="flex flex-col gap-y-2">
{message.user && (
<EditingChatBubble
message={message}
index={index}
type="user"
handleMessageChange={handleMessageChange}
removeMessage={removeMessage}
/>
)}
{message.response && (
<EditingChatBubble
message={message}
index={index}
type="response"
handleMessageChange={handleMessageChange}
removeMessage={removeMessage}
/>
)}
</div>
))}
<div className="flex gap-4 mt-4 justify-between">
<button
className="self-end text-orange-500 hover:text-orange-700 transition"
onClick={() => addMessage("response")}
>
+ System Message
</button>
<button
className="self-end text-orange-500 hover:text-orange-700 transition"
onClick={() => addMessage("user")}
>
+ User Message
</button>
</div>
</div>
{hasChanges && (
<div className="flex justify-center py-6">
<button
className="ml-4 cursor-pointer text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
onClick={handleMessageSave}
>
Save Messages
</button>
</div>
)}
</div>
)}
{successMsg && (
<div className="text-sm text-green-600 dark:text-green-400 text-center">
{successMsg}
</div>
)}
{errorMsg && (
<div className="mt-4 text-sm text-red-600 dark:text-red-400 text-center">
{errorMsg}
</div>
)}
{successMsg && (
<div className="mt-4 text-sm text-green-600 dark:text-green-400 text-center">
{successMsg}
</div>
)}
</div>
</div>
</div>
);

View File

@ -35,6 +35,7 @@ const {
DARK_LOGO_FILENAME,
} = require("../utils/files/logo");
const { Telemetry } = require("../models/telemetry");
const { WelcomeMessages } = require("../models/welcomeMessages");
function systemEndpoints(app) {
if (!app) return;
@ -477,6 +478,53 @@ function systemEndpoints(app) {
}
}
);
app.get("/system/welcome-messages", async function (request, response) {
try {
const welcomeMessages = await WelcomeMessages.getMessages();
response.status(200).json({ success: true, welcomeMessages });
} catch (error) {
console.error("Error fetching welcome messages:", error);
response
.status(500)
.json({ success: false, message: "Internal server error" });
}
});
app.post(
"/system/set-welcome-messages",
[validatedRequest],
async (request, response) => {
try {
if (
response.locals.multiUserMode &&
response.locals.user?.role !== "admin"
) {
return response.sendStatus(401).end();
}
const { messages = [] } = reqBody(request);
if (!Array.isArray(messages)) {
return response.status(400).json({
success: false,
message: "Invalid message format. Expected an array of messages.",
});
}
await WelcomeMessages.saveAll(messages);
return response.status(200).json({
success: true,
message: "Welcome messages saved successfully.",
});
} catch (error) {
console.error("Error processing the welcome messages:", error);
response.status(500).json({
success: true,
message: "Error saving the welcome messages.",
});
}
}
);
}
module.exports = { systemEndpoints };

View File

@ -0,0 +1,89 @@
const WelcomeMessages = {
tablename: "welcome_messages",
colsInit: `
id INTEGER PRIMARY KEY AUTOINCREMENT,
user TEXT NOT NULL,
response TEXT NOT NULL,
orderIndex INTEGER,
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
`,
migrateTable: async function () {
const { checkForMigrations } = require("../utils/database");
console.log(
`\x1b[34m[MIGRATING]\x1b[0m Checking for Welcome Messages migrations`
);
const db = await this.db(false);
await checkForMigrations(this, db);
db.close();
},
migrations: function () {
return [];
},
db: async function (tracing = true) {
const sqlite3 = require("sqlite3").verbose();
const { open } = require("sqlite");
const db = await open({
filename: `${
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
}anythingllm.db`,
driver: sqlite3.Database,
});
await db.exec(
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
);
if (tracing) {
db.on("trace", (sql) => console.log(sql));
}
return db;
},
get: async function (clause = "") {
const db = await this.db();
const result = await db
.get(`SELECT * FROM ${this.tablename} WHERE ${clause}`)
.then((res) => res || null);
db.close();
return result;
},
where: async function (clause = null, limit = null) {
const db = await this.db();
const results = await db.all(
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
!!limit ? `LIMIT ${limit}` : ""
}`
);
db.close();
return results;
},
saveAll: async function (messages) {
const db = await this.db();
await db.run(`DELETE FROM ${this.tablename}`);
for (const [index, message] of messages.entries()) {
await db.run(
`INSERT INTO ${this.tablename} (user, response, orderIndex) VALUES (?, ?, ?)`,
[message.user, message.response, index]
);
}
db.close();
},
getMessages: async function () {
const db = await this.db();
const results = await db.all(
`SELECT user, response FROM ${this.tablename} ORDER BY orderIndex ASC`
);
db.close();
return results;
},
};
module.exports.WelcomeMessages = WelcomeMessages;

View File

@ -61,6 +61,7 @@ async function validateTablePragmas(force = false) {
const { DocumentVectors } = require("../../models/vectors");
const { WorkspaceChats } = require("../../models/workspaceChats");
const { Invite } = require("../../models/invite");
const { WelcomeMessages } = require("../../models/welcomeMessages");
await SystemSettings.migrateTable();
await User.migrateTable();
@ -70,6 +71,7 @@ async function validateTablePragmas(force = false) {
await DocumentVectors.migrateTable();
await WorkspaceChats.migrateTable();
await Invite.migrateTable();
await WelcomeMessages.migrateTable();
} catch (e) {
console.error(`validateTablePragmas: Migrations failed`, e);
}