diff --git a/embed/src/App.jsx b/embed/src/App.jsx index 4973328b..0ce0a3e0 100644 --- a/embed/src/App.jsx +++ b/embed/src/App.jsx @@ -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 ( + <> + +
+ +
+ + ) + } + + // Renders the default bubble chat if nothing is defined return ( <> @@ -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 && ( { + await ChatService.resetEmbedChatSession(settings, sessionId); + setChatHistory([]); + setShowOptions(false); + }; + + return ( +
+ {iconUrl +
+ {settings.loaded && ( + + )} +
+ +
+ ); +} + function OptionsMenu({ showing, resetChat }) { if (!showing) return null; return ( diff --git a/embed/src/components/ChatWindow/index.jsx b/embed/src/components/ChatWindow/index.jsx index 2e717cac..1e3645b0 100644 --- a/embed/src/components/ChatWindow/index.jsx +++ b/embed/src/components/ChatWindow/index.jsx @@ -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 ( +
+ + {/* + */} +
+ ); +} + + // 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. diff --git a/embed/src/hooks/useScriptAttributes.js b/embed/src/hooks/useScriptAttributes.js index a3e5594b..9807745b 100644 --- a/embed/src/hooks/useScriptAttributes.js +++ b/embed/src/hooks/useScriptAttributes.js @@ -17,6 +17,7 @@ const DEFAULT_SETTINGS = { // behaviors openOnLoad: "off", // or "on" + iframe: null, // 'enabled' is the only valid option }; export default function useGetScriptAttributes() { diff --git a/server/index.js b/server/index.js index 3d613191..52ede021 100644 --- a/server/index.js +++ b/server/index.js @@ -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); }); diff --git a/server/utils/embed/render.js b/server/utils/embed/render.js new file mode 100644 index 00000000..cc651023 --- /dev/null +++ b/server/utils/embed/render.js @@ -0,0 +1,32 @@ +const HTML = ({ scriptLoc }) => ` + + + + +` + + +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 +} \ No newline at end of file diff --git a/server/utils/middleware/embedMiddleware.js b/server/utils/middleware/embedMiddleware.js index c87d8771..5bd9bce3 100644 --- a/server/utils/middleware/embedMiddleware.js +++ b/server/utils/middleware/embedMiddleware.js @@ -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, };