mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-13 02:00:10 +01:00
WIP on iframe embed support
This commit is contained in:
parent
27381a612c
commit
083dc5bf1c
@ -3,7 +3,7 @@ import useSessionId from "@/hooks/useSessionId";
|
||||
import useOpenChat from "@/hooks/useOpen";
|
||||
import Head from "@/components/Head";
|
||||
import OpenButton from "@/components/OpenButton";
|
||||
import ChatWindow from "./components/ChatWindow";
|
||||
import ChatWindow, { ChatWindowFull } from "./components/ChatWindow";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function App() {
|
||||
@ -16,6 +16,22 @@ export default function App() {
|
||||
}, [embedSettings.loaded]);
|
||||
|
||||
if (!embedSettings.loaded) return null;
|
||||
|
||||
if (embedSettings.iframe === 'enabled') {
|
||||
return (
|
||||
<>
|
||||
<Head />
|
||||
<div className={`w-screen h-screen bg-red-500`}>
|
||||
<ChatWindowFull
|
||||
settings={embedSettings}
|
||||
sessionId={sessionId}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Renders the default bubble chat if nothing is defined
|
||||
return (
|
||||
<>
|
||||
<Head />
|
||||
@ -25,11 +41,10 @@ export default function App() {
|
||||
width: isChatOpen ? 320 : "auto",
|
||||
height: isChatOpen ? "93vh" : "auto",
|
||||
}}
|
||||
className={`transition-all duration-300 ease-in-out ${
|
||||
isChatOpen
|
||||
? "max-w-md p-4 bg-white rounded-lg border shadow-lg w-72"
|
||||
: "w-16 h-16 rounded-full"
|
||||
}`}
|
||||
className={`transition-all duration-300 ease-in-out ${isChatOpen
|
||||
? "max-w-md p-4 bg-white rounded-lg border shadow-lg w-72"
|
||||
: "w-16 h-16 rounded-full"
|
||||
}`}
|
||||
>
|
||||
{isChatOpen && (
|
||||
<ChatWindow
|
||||
|
@ -51,6 +51,46 @@ export default function ChatWindowHeader({
|
||||
);
|
||||
}
|
||||
|
||||
export function ChatFullWindowHeader({
|
||||
sessionId,
|
||||
settings = {},
|
||||
iconUrl = null,
|
||||
setChatHistory,
|
||||
}) {
|
||||
const [showingOptions, setShowOptions] = useState(false);
|
||||
|
||||
const handleChatReset = async () => {
|
||||
await ChatService.resetEmbedChatSession(settings, sessionId);
|
||||
setChatHistory([]);
|
||||
setShowOptions(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex justify-between items-center relative">
|
||||
<img
|
||||
style={{ maxWidth: 100, maxHeight: 20 }}
|
||||
src={iconUrl ?? AnythingLLMLogo}
|
||||
alt={iconUrl ? "Brand" : "AnythingLLM Logo"}
|
||||
/>
|
||||
<div className="flex gap-x-1 items-center">
|
||||
{settings.loaded && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowOptions(!showingOptions)}
|
||||
className="hover:bg-gray-100 rounded-sm text-slate-800"
|
||||
>
|
||||
<DotsThreeOutlineVertical
|
||||
size={18}
|
||||
weight={!showingOptions ? "regular" : "fill"}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<OptionsMenu showing={showingOptions} resetChat={handleChatReset} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OptionsMenu({ showing, resetChat }) {
|
||||
if (!showing) return null;
|
||||
return (
|
||||
|
@ -1,4 +1,4 @@
|
||||
import ChatWindowHeader from "./Header";
|
||||
import ChatWindowHeader, { ChatFullWindowHeader } from "./Header";
|
||||
import SessionId from "../SessionId";
|
||||
import useChatHistory from "@/hooks/chat/useChatHistory";
|
||||
import ChatContainer from "./ChatContainer";
|
||||
@ -30,6 +30,33 @@ export default function ChatWindow({ closeChat, settings, sessionId }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function ChatWindowFull({ settings, sessionId }) {
|
||||
const { chatHistory, setChatHistory, loading } = useChatHistory(
|
||||
settings,
|
||||
sessionId
|
||||
);
|
||||
|
||||
if (loading) return null;
|
||||
setEventDelegatorForCodeSnippets();
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<ChatFullWindowHeader
|
||||
sessionId={sessionId}
|
||||
settings={settings}
|
||||
iconUrl={settings.brandImageUrl}
|
||||
setChatHistory={setChatHistory}
|
||||
/>
|
||||
{/* <ChatContainer
|
||||
sessionId={sessionId}
|
||||
settings={settings}
|
||||
knownHistory={chatHistory}
|
||||
/>
|
||||
<SessionId /> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Enables us to safely markdown and sanitize all responses without risk of injection
|
||||
// but still be able to attach a handler to copy code snippets on all elements
|
||||
// that are code snippets.
|
||||
|
@ -17,6 +17,7 @@ const DEFAULT_SETTINGS = {
|
||||
|
||||
// behaviors
|
||||
openOnLoad: "off", // or "on"
|
||||
iframe: null, // 'enabled' is the only valid option
|
||||
};
|
||||
|
||||
export default function useGetScriptAttributes() {
|
||||
|
@ -19,6 +19,8 @@ const { utilEndpoints } = require("./endpoints/utils");
|
||||
const { developerEndpoints } = require("./endpoints/api");
|
||||
const { extensionEndpoints } = require("./endpoints/extensions");
|
||||
const { bootHTTP, bootSSL } = require("./utils/boot");
|
||||
const { renderIFrameResponse } = require("./utils/embed/render");
|
||||
const { validEmbedConfigUUID } = require("./utils/middleware/embedMiddleware");
|
||||
const app = express();
|
||||
const apiRouter = express.Router();
|
||||
const FILE_LIMIT = "3GB";
|
||||
@ -90,6 +92,10 @@ if (process.env.NODE_ENV !== "development") {
|
||||
});
|
||||
}
|
||||
|
||||
// Named embedded to not collide with public folder /embed/ directory
|
||||
// when in production
|
||||
app.get("/embedded/:embedId", [validEmbedConfigUUID], renderIFrameResponse);
|
||||
|
||||
app.all("*", function (_, response) {
|
||||
response.sendStatus(404);
|
||||
});
|
||||
|
32
server/utils/embed/render.js
Normal file
32
server/utils/embed/render.js
Normal file
@ -0,0 +1,32 @@
|
||||
const HTML = ({ scriptLoc }) => `<!doctype html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<script
|
||||
data-embed-id="example-uuid"
|
||||
data-base-api-url='/api/embed'
|
||||
data-iframe='enabled'
|
||||
src="${scriptLoc}">
|
||||
</script>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
|
||||
function renderIFrameResponse(request, response) {
|
||||
const embed = response.locals.embedConfig;
|
||||
const settings = {
|
||||
scriptLoc:
|
||||
process.env.NODE_ENV === 'production' ?
|
||||
`/embed/anythingllm-chat-widget.min.js` :
|
||||
'http://localhost:3080/dist/anythingllm-chat-widget.js', // Running yarn dev in embed folder
|
||||
embedId: embed.id,
|
||||
}
|
||||
response.type('text/html');
|
||||
response.header('Access-Control-Allow-Origin', '*');
|
||||
response.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
|
||||
response.header('Access-Control-Allow-Headers', 'Content-Type, X-Requested-With, Authorization');
|
||||
response.send(HTML(settings)).end();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
renderIFrameResponse
|
||||
}
|
@ -27,6 +27,19 @@ function setConnectionMeta(request, response, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
async function validEmbedConfigUUID(request, response, next) {
|
||||
const { embedId } = request.params;
|
||||
|
||||
const embed = await EmbedConfig.getWithWorkspace({ uuid: embedId });
|
||||
if (!embed) {
|
||||
response.sendStatus(404).end();
|
||||
return;
|
||||
}
|
||||
|
||||
response.locals.embedConfig = embed;
|
||||
next();
|
||||
}
|
||||
|
||||
async function validEmbedConfigId(request, response, next) {
|
||||
const { embedId } = request.params;
|
||||
|
||||
@ -147,5 +160,6 @@ module.exports = {
|
||||
setConnectionMeta,
|
||||
validEmbedConfig,
|
||||
validEmbedConfigId,
|
||||
validEmbedConfigUUID,
|
||||
canRespond,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user