CVE-2024-0765: Exposure of Sensitive Information to an Unauthorized Actor (CWE-200)
value: As a default user on a multi-user instance of AnythingLLM, you could execute a call to the `/export-data` endpoint of the system and then unzip and read that export that would enable you do exfiltrate data of the system at that save state.

This would require the attacked to be granted explicit access to the system, but they can do this at any role. Additionally, post-download, the data is deleted so no evidence would exist that the exfiltration occured.
CWE description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
Published: 2024-03-03T15:15Z
Repository: https://github.com/mintplex-labs/anything-llm
Language: JavaScript
Files changed: frontend/src/App.jsx, frontend/src/components/SettingsSidebar/index.jsx, frontend/src/models/system.js, frontend/src/utils/paths.js, server/endpoints/system.js, server/index.js, server/package.json, server/yarn.lock
Commit date: 2024-01-18 13:57:23 -0800
Commit message: Merge pull request from GHSA-xmj6-g32r-fc5q

* remove support for import export anythingllm data

* remove unused imports
remove unused dep
update lockfile

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
Diff: diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 7c14be4d9..7acd2c534 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -32,13 +32,10 @@ const GeneralEmbeddingPreference = lazy(
   () => import("@/pages/GeneralSettings/EmbeddingPreference")
 );
 const GeneralVectorDatabase = lazy(
   () => import("@/pages/GeneralSettings/VectorDatabase")
 );
-const GeneralExportImport = lazy(
-  () => import("@/pages/GeneralSettings/ExportImport")
-);
 const GeneralSecurity = lazy(() => import("@/pages/GeneralSettings/Security"));
 const DataConnectors = lazy(
   () => import("@/pages/GeneralSettings/DataConnectors")
 );
 const DataConnectorSetup = lazy(
@@ -72,14 +69,10 @@ export default function App() {
               <Route
                 path="/settings/vector-database"
                 element={<AdminRoute Component={GeneralVectorDatabase} />}
               />
               {/* Manager */}
-              <Route
-                path="/settings/export-import"
-                element={<ManagerRoute Component={GeneralExportImport} />}
-              />
               <Route
                 path="/settings/security"
                 element={<ManagerRoute Component={GeneralSecurity} />}
               />
               <Route
diff --git a/frontend/src/components/SettingsSidebar/index.jsx b/frontend/src/components/SettingsSidebar/index.jsx
index f4c9552e3..99cf1eb35 100644
--- a/frontend/src/components/SettingsSidebar/index.jsx
+++ b/frontend/src/components/SettingsSidebar/index.jsx
@@ -1,8 +1,6 @@
 import React, { useEffect, useRef, useState } from "react";
-// import IndexCount from "../Sidebar/IndexCount";
-// import LLMStatus from "../Sidebar/LLMStatus";
 import paths from "@/utils/paths";
 import useLogo from "@/hooks/useLogo";
 import {
   DiscordLogo,
   EnvelopeSimple,
@@ -12,14 +10,12 @@ import {
   ChatCenteredText,
   Eye,
   Key,
   ChatText,
   Database,
-  DownloadSimple,
   Lock,
   GithubLogo,
-  DotsThree,
   House,
   X,
   List,
   FileCode,
   Plugs,
@@ -133,16 +129,10 @@ export default function SettingsSidebar() {
                       btnText="Data Connectors"
                       icon={<Plugs className="h-5 w-5 flex-shrink-0" />}
                     />
                   </>
                 )}
-
-                <Option
-                  href={paths.settings.exportImport()}
-                  btnText="Export or Import"
-                  icon={<DownloadSimple className="h-5 w-5 flex-shrink-0" />}
-                />
                 <Option
                   href={paths.settings.security()}
                   btnText="Security"
                   icon={<Lock className="h-5 w-5 flex-shrink-0" />}
                 />
@@ -326,15 +316,10 @@ export function SidebarMobileHeader() {
                         btnText="Vector Database"
                         icon={<Database className="h-5 w-5 flex-shrink-0" />}
                       />
                     </>
                   )}
-                  <Option
-                    href={paths.settings.exportImport()}
-                    btnText="Export or Import"
-                    icon={<DownloadSimple className="h-5 w-5 flex-shrink-0" />}
-                  />
                   <Option
                     href={paths.settings.security()}
                     btnText="Security"
                     icon={<Lock className="h-5 w-5 flex-shrink-0" />}
                   />
diff --git a/frontend/src/models/system.js b/frontend/src/models/system.js
index f248d4f9a..6dc003123 100644
--- a/frontend/src/models/system.js
+++ b/frontend/src/models/system.js
@@ -158,35 +158,10 @@ const System = {
       .catch((e) => {
         console.error(e);
         return false;
       });
   },
-  dataExport: async () => {
-    return await fetch(`${API_BASE}/system/data-export`, {
-      method: "GET",
-      headers: baseHeaders(),
-    })
-      .then((res) => res.json())
-      .then((res) => res)
-      .catch((e) => {
-        console.error(e);
-        return { filename: null, error: e.message };
-      });
-  },
-  importData: async (formData) => {
-    return await fetch(`${API_BASE}/system/data-import`, {
-      method: "POST",
-      body: formData,
-      headers: baseHeaders(),
-    })
-      .then((res) => res.json())
-      .then((res) => res)
-      .catch((e) => {
-        console.error(e);
-        return { success: false, error: e.message };
-      });
-  },
   uploadPfp: async function (formData) {
     return await fetch(`${API_BASE}/system/upload-pfp`, {
       method: "POST",
       body: formData,
       headers: baseHeaders(),
diff --git a/frontend/src/pages/GeneralSettings/ExportImport/index.jsx b/frontend/src/pages/GeneralSettings/ExportImport/index.jsx
deleted file mode 100644
index b6a9a608c..000000000
--- a/frontend/src/pages/GeneralSettings/ExportImport/index.jsx
+++ /dev/null
@@ -1,226 +0,0 @@
-import { useRef, useState } from "react";
-import Sidebar, { SidebarMobileHeader } from "@/components/SettingsSidebar";
-import { isMobile } from "react-device-detect";
-import showToast from "@/utils/toast";
-import { CloudArrowUp, DownloadSimple } from "@phosphor-icons/react";
-import System from "@/models/system";
-import { API_BASE } from "@/utils/constants";
-import paths from "@/utils/paths";
-
-export default function GeneralExportImport() {
-  const hostname = window?.location?.hostname;
-  const isHosted = hostname.includes(".useanything.com");
-
-  if (isHosted) {
-    return (
-      <div className="w-screen h-screen overflow-hidden bg-sidebar flex">
-        {!isMobile && <Sidebar />}
-        <div
-          style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
-          className="transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
-        >
-          {isMobile && <SidebarMobileHeader />}
-          <div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
-            <div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
-              <div className="items-center flex gap-x-4">
-                <p className="text-2xl font-semibold text-white">
-                  Export or Import
-                </p>
-              </div>
-            </div>
-          </div>
-
-          <div className="w-full items-center justify-center flex flex-col gap-y-4">
-            <p className="text-lg font-base text-white text-opacity-60">
-              This feature is temporarily disabled for hosted AnythingLLM
-              instances.
-            </p>
-            <a
-              href={`${paths.mailToMintplex()}?Subject=Import/Export disabled on hosted AnythingLLM.`}
-              className="text-blue-300 hover:underline"
-            >
-              Contact Mintplex Labs Inc.
-            </a>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-  return (
-    <div className="w-screen h-screen overflow-hidden bg-sidebar flex">
-      {!isMobile && <Sidebar />}
-      <div
-        style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
-        className="transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
-      >
-        {isMobile && <SidebarMobileHeader />}
-        <div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
-          <div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
-            <div className="items-center flex gap-x-4">
-              <p className="text-2xl font-semibold text-white">
-                Export or Import
-              </p>
-            </div>
-            <p className="text-sm font-base text-white text-opacity-60">
-              Have multiple AnythingLLM instances or simply want to backup or
-              re-import data from another instance? You can do so here.
-            </p>
-          </div>
-          <div className="text-white text-sm font-medium py-4">
-            This will not automatically sync your vector database embeddings.
-          </div>
-          <ImportData />
-          <ExportData />
-        </div>
-      </div>
-    </div>
-  );
-}
-
-function ImportData() {
-  const inputRef = useRef(null);
-  const [loading, setLoading] = useState(false);
-  const [file, setFile] = useState(null);
-  const [result, setResult] = useState(null);
-
-  const startInput = () => inputRef?.current?.click();
-  const handleUpload = async (e) => {
-    setLoading(true);
-    e.preventDefault();
-    setFile(null);
-    setResult(null);
-
-    const file = e.target.files?.[0];
-    if (!file) {
-      showToast("Invalid file upload", "error");
-      return false;
-    }
-
-    setFile(file);
-    setLoading(true);
-    const formData = new FormData();
-    formData.append("file", file, file.name);
-    const { success, error } = await System.importData(formData);
-    if (!success) {
-      showToast(`Failed to import data: ${error}`, "error");
-    } else {
-      setResult(true);
-      showToast(`Successfully imported ${file.name}`, "success");
-    }
-
-    setLoading(false);
-    setFile(null);
-  };
-
-  return (
-    <div
-      onClick={startInput}
-      className="max-w-[600px] py-4 bg-zinc-900/50 rounded-2xl border-2 border-dashed border-white border-opacity-60 justify-center items-center inline-flex transition-all duration-300 hover:opacity-60 cursor-pointer"
-    >
-      <div className="flex flex-col items-center justify-center">
-        {loading ? (
-          <div className="flex items-center justify-center gap-2 animate-pulse">
-            <div className="text-white text-opacity-80 text-sm font-semibold py-1">
-              Importing
-            </div>
-            <div className="h-4 w-4 animate-spin rounded-full border-2 border-solid border-white border-t-transparent " />
-          </div>
-        ) : !!result ? (
-          <div className="flex items-center justify-center gap-2">
-            <CloudArrowUp className="w-8 h-8 text-green-400" />
-            <div className="text-green-400 text-opacity-80 text-sm font-semibold py-1">
-              Import Successful
-            </div>
-          </div>
-        ) : (
-          <>
-            <input
-              ref={inputRef}
-              onChange={handleUpload}
-              name="import"
-              type="file"
-              multiple="false"
-              accept=".zip"
-              hidden={true}
-            />
-            <div className="flex flex-col items-center justify-center">
-              <CloudArrowUp className="w-8 h-8 text-white/80" />
-              <div className="text-white text-opacity-80 text-sm font-semibold py-1">
-                Import AnythingLLM Data
-              </div>
-              <div className="text-white text-opacity-60 text-xs font-medium py-1">
-                This must be an export from an AnythingLLM instance.
-              </div>
-            </div>
-          </>
-        )}
-      </div>
-    </div>
-  );
-}
-
-function ExportData() {
-  const [loading, setLoading] = useState(false);
-  const [result, setResult] = useState(null);
-
-  const exportData = async function () {
-    setLoading(true);
-    const { filename, error } = await System.dataExport();
-    setLoading(false);
-
-    if (!filename) {
-      showToast(`Failed to export data: ${error}`, "error");
-    } else {
-      setResult(filename);
-      const link = document.createElement("a");
-      link.href = `${API_BASE}/system/data-exports/${filename}`;
-      link.target = "_blank";
-      document.body.appendChild(link);
-    }
-  };
-
-  if (loading) {
-    return (
-      <button
-        onClick={exportData}
-        className="transition-all max-w-[600px] bg-white rounded-lg justify-center items-center my-8 text-zinc-900 border-transparent border-2 cursor-not-allowed animate-pulse"
-      >
-        <div className="flex items-center justify-center gap-2">
-          <div className="duration-300 text-center text-sm font-bold py-3">
-            Exporting
-          </div>
-          <div className="h-4 w-4 animate-spin rounded-full border-2 border-solid border-zinc-900 border-t-transparent " />
-        </div>
-      </button>
-    );
-  }
-
-  if (!!result) {
-    return (
-      <a
-        target="_blank"
-        href={`${API_BASE}/system/data-exports/${result}`}
-        className="transition-all max-w-[600px] bg-green-100 hover:bg-zinc-900/50 hover:text-white hover:border-white rounded-lg justify-center items-center my-8 text-zinc-900 border-transparent border-2 cursor-pointer"
-      >
-        <div className="flex items-center justify-center gap-2">
-          <div className="duration-300 text-center text-sm font-bold py-3">
-            Download Data Export
-          </div>
-          <DownloadSimple className="w-6 h-6" />
-        </div>
-      </a>
-    );
-  }
-
-  return (
-    <button
-      onClick={exportData}
-      className="transition-all max-w-[600px] bg-white rounded-lg justify-center items-center my-8 cursor-pointer text-zinc-900 border-transparent border-2 hover:bg-zinc-900/50 hover:text-white hover:border-white"
-    >
-      <div className="duration-300 text-center text-sm font-bold py-3">
-        Export AnythingLLM Data
-      </div>
-    </button>
-  );
-}
diff --git a/frontend/src/utils/paths.js b/frontend/src/utils/paths.js
index 547a3b3f3..abba31d4e 100644
--- a/frontend/src/utils/paths.js
+++ b/frontend/src/utils/paths.js
@@ -54,13 +54,10 @@ export default {
   workspace: {
     chat: (slug) => {
       return `/workspace/${slug}`;
     },
   },
-  exports: () => {
-    return `${API_BASE.replace("/api", "")}/system/data-exports`;
-  },
   apiDocs: () => {
     return `${API_BASE}/docs`;
   },
   settings: {
     system: () => {
@@ -85,13 +82,10 @@ export default {
       return "/settings/embedding-preference";
     },
     vectorDatabase: () => {
       return "/settings/vector-database";
     },
-    exportImport: () => {
-      return "/settings/export-import";
-    },
     security: () => {
       return "/settings/security";
     },
     appearance: () => {
       return "/settings/appearance";
diff --git a/server/endpoints/system.js b/server/endpoints/system.js
index e699cf84c..4bed2b160 100644
--- a/server/endpoints/system.js
+++ b/server/endpoints/system.js
@@ -1,10 +1,9 @@
 process.env.NODE_ENV === "development"
   ? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` })
   : require("dotenv").config();
 const { viewLocalFiles, normalizePath } = require("../utils/files");
-const { exportData, unpackAndOverwriteImport } = require("../utils/files/data");
 const {
   checkProcessorAlive,
   acceptedFileTypes,
 } = require("../utils/files/documentProcessor");
 const { purgeDocument, purgeFolder } = require("../utils/files/purgeDocument");
@@ -387,59 +386,10 @@ function systemEndpoints(app) {
       console.log(e.message, e);
       response.sendStatus(500).end();
     }
   });
 
-  app.get(
-    "/system/data-export",
-    [validatedRequest, flexUserRoleValid],
-    async (_, response) => {
-      try {
-        const { filename, error } = await exportData();
-        response.status(200).json({ filename, error });
-      } catch (e) {
-        console.log(e.message, e);
-        response.sendStatus(500).end();
-      }
-    }
-  );
-
-  app.get("/system/data-exports/:filename", (request, response) => {
-    const exportLocation = __dirname + "/../storage/exports/";
-    const sanitized = normalizePath(request.params.filename);
-    const finalDestination = path.join(exportLocation, sanitized);
-
-    if (!fs.existsSync(finalDestination)) {
-      response.status(404).json({
-        error: 404,
-        msg: `File ${request.params.filename} does not exist in exports.`,
-      });
-      return;
-    }
-
-    response.download(finalDestination, request.params.filename, (err) => {
-      if (err) {
-        response.send({
-          error: err,
-          msg: "Problem downloading the file",
-        });
-      }
-      // delete on download because endpoint is not authenticated.
-      fs.rmSync(finalDestination);
-    });
-  });
-
-  app.post(
-    "/system/data-import",
-    handleImports.single("file"),
-    async function (request, response) {
-      const { originalname } = request.file;
-      const { success, error } = await unpackAndOverwriteImport(originalname);
-      response.status(200).json({ success, error });
-    }
-  );
-
   app.get("/system/logo", async function (request, response) {
     try {
       const defaultFilename = getDefaultFilename();
       const logoPath = await determineLogoFilepath(defaultFilename);
       const { found, buffer, size, mime } = fetchLogo(logoPath);
diff --git a/server/index.js b/server/index.js
index d8d3eabb6..dee831e4b 100644
--- a/server/index.js
+++ b/server/index.js
@@ -2,11 +2,10 @@ process.env.NODE_ENV === "development"
   ? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` })
   : require("dotenv").config();
 
 const express = require("express");
 const bodyParser = require("body-parser");
-const serveIndex = require("serve-index");
 const cors = require("cors");
 const path = require("path");
 const { reqBody } = require("./utils/http");
 const { systemEndpoints } = require("./endpoints/system");
 const { workspaceEndpoints } = require("./endpoints/workspaces");
@@ -83,15 +82,10 @@ if (process.env.NODE_ENV !== "development") {
     response.type("text/plain");
     response.send("User-agent: *\nDisallow: /").end();
   });
 }
 
-app.use(
-  "/system/data-exports",
-  serveIndex(__dirname + "/storage/exports", { icons: true })
-);
-
 app.all("*", function (_, response) {
   response.sendStatus(404);
 });
 
 if (!!process.env.ENABLE_HTTPS) {
diff --git a/server/package.json b/server/package.json
index 9761125a4..58913fe35 100644
--- a/server/package.json
+++ b/server/package.json
@@ -50,11 +50,10 @@
     "node-llama-cpp": "^2.8.0",
     "openai": "^3.2.1",
     "pinecone-client": "^1.1.0",
     "posthog-node": "^3.1.1",
     "prisma": "^5.3.1",
-    "serve-index": "^1.9.1",
     "slugify": "^1.6.6",
     "sqlite": "^4.2.1",
     "sqlite3": "^5.1.6",
     "swagger-autogen": "^2.23.5",
     "swagger-ui-express": "^5.0.0",
diff --git a/server/utils/files/data.js b/server/utils/files/data.js
deleted file mode 100644
index d115903db..000000000
--- a/server/utils/files/data.js
+++ /dev/null
@@ -1,197 +0,0 @@
-const fs = require("fs");
-const path = require("path");
-const { v4 } = require("uuid");
-
-async function exportData() {
-  const uid = `anythingllm-export-${new Date()
-    .toJSON()
-    .slice(0, 10)}-${new Date().toJSON().slice(11, 19)}`;
-  const folder =
-    process.env.NODE_ENV === "development"
-      ? path.resolve(__dirname, `../../storage/exports/${uid}`)
-      : path.resolve(process.env.STORAGE_DIR, `exports/${uid}`);
-  const storageBase =
-    process.env.NODE_ENV === "development"
-      ? path.resolve(__dirname, `../../storage`)
-      : path.resolve(process.env.STORAGE_DIR);
-
-  try {
-    fs.mkdirSync(folder, { recursive: true });
-    if (fs.existsSync(path.resolve(storageBase, `documents`))) {
-      console.log("\x1b[34m[EXPORTING DATA]\x1b[0m Copying documents!");
-      fs.cpSync(
-        path.resolve(storageBase, `documents`),
-        path.resolve(folder, "documents"),
-        { recursive: true }
-      );
-    }
-
-    if (fs.existsSync(path.resolve(storageBase, `lancedb`))) {
-      console.log("\x1b[34m[EXPORTING DATA]\x1b[0m Copying LanceDB data!");
-      fs.cpSync(
-        path.resolve(storageBase, `lancedb`),
-        path.resolve(folder, "lancedb"),
-        { recursive: true }
-      );
-    }
-
-    if (fs.existsSync(path.resolve(storageBase, `vector-cache`))) {
-      console.log("\x1b[34m[EXPORTING DATA]\x1b[0m Copying vector cache!");
-      fs.cpSync(
-        path.resolve(storageBase, `vector-cache`),
-        path.resolve(folder, "vector-cache"),
-        { recursive: true }
-      );
-    }
-
-    if (fs.existsSync(path.resolve(storageBase, `anythingllm.db`))) {
-      console.log(
-        "\x1b[34m[EXPORTING DATA]\x1b[0m Copying anythingllm database!"
-      );
-      fs.cpSync(
-        path.resolve(storageBase, `anythingllm.db`),
-        path.resolve(folder, "anythingllm.db")
-      );
-    }
-
-    await zipDirectory(folder, path.resolve(folder, `../${uid}.zip`));
-    fs.rmSync(folder, { recursive: true, force: true });
-    return { filename: `${uid}.zip`, error: null };
-  } catch (e) {
-    // If anything goes wrong - abort and clean up
-    console.error(e);
-    if (fs.existsSync(folder))
-      fs.rmSync(folder, { recursive: true, force: true });
-    return { filename: null, error: e.message };
-  }
-}
-
-async function unpackAndOverwriteImport(importFilename) {
-  const importFilepath =
-    process.env.NODE_ENV === "development"
-      ? path.resolve(__dirname, `../../storage/imports/${importFilename}`)
-      : path.resolve(process.env.STORAGE_DIR, `imports/${importFilename}`);
-  if (!fs.existsSync(importFilepath))
-    return { success: false, error: "Import file does not exist." };
-
-  const uid = v4();
-  const outDir =
-    process.env.NODE_ENV === "development"
-      ? path.resolve(__dirname, `../../storage/imports/${uid}`)
-      : path.resolve(process.env.STORAGE_DIR, `imports/${uid}`);
-
-  const storageBase =
-    process.env.NODE_ENV === "development"
-      ? path.resolve(__dirname, `../../storage`)
-      : path.resolve(process.env.STORAGE_DIR);
-
-  try {
-    console.log(
-      "\x1b[34m[EXTRACTING DATA]\x1b[0m Extracting data from zip into storage!"
-    );
-    const unzipProc = await unzipDirectory(importFilepath, outDir);
-    if (!unzipProc.success) return unzipProc;
-
-    if (fs.existsSync(path.resolve(outDir, `documents`))) {
-      console.log(
-        "\x1b[34m[OVERWRITE & IMPORT DATA]\x1b[0m Importing documents!"
-      );
-      if (fs.existsSync(path.resolve(storageBase, `documents`)))
-        fs.rmSync(path.resolve(storageBase, `documents`), {
-          recursive: true,
-          force: true,
-        });
-      fs.cpSync(
-        path.resolve(outDir, `documents`),
-        path.resolve(storageBase, "documents"),
-        { recursive: true }
-      );
-    }
-
-    if (fs.existsSync(path.resolve(outDir, `lancedb`))) {
-      console.log(
-        "\x1b[34m[OVERWRITE & IMPORT DATA]\x1b[0m Importing LanceDb!"
-      );
-      if (fs.existsSync(path.resolve(storageBase, `lancedb`)))
-        fs.rmSync(path.resolve(storageBase, `lancedb`), {
-          recursive: true,
-          force: true,
-        });
-      fs.cpSync(
-        path.resolve(outDir, `lancedb`),
-        path.resolve(storageBase, "lancedb"),
-        { recursive: true }
-      );
-    }
-
-    if (fs.existsSync(path.resolve(outDir, `vector-cache`))) {
-      console.log(
-        "\x1b[34m[OVERWRITE & IMPORT DATA]\x1b[0m Importing Vector Cache!"
-      );
-      if (fs.existsSync(path.resolve(storageBase, `vector-cache`)))
-        fs.rmSync(path.resolve(storageBase, `vector-cache`), {
-          recursive: true,
-          force: true,
-        });
-      fs.cpSync(
-        path.resolve(outDir, `vector-cache`),
-        path.resolve(storageBase, "vector-cache"),
-        { recursive: true }
-      );
-    }
-
-    if (fs.existsSync(path.resolve(outDir, `anythingllm.db`))) {
-      console.log(
-        "\x1b[34m[OVERWRITE & IMPORT DATA]\x1b[0m Importing AnythingLLM DB!"
-      );
-      if (fs.existsSync(path.resolve(storageBase, `anythingllm.db`)))
-        fs.rmSync(path.resolve(storageBase, `anythingllm.db`), { force: true });
-      fs.cpSync(
-        path.resolve(outDir, `anythingllm.db`),
-        path.resolve(storageBase, "anythingllm.db")
-      );
-    }
-
-    fs.rmSync(outDir, { recursive: true, force: true });
-    fs.rmSync(importFilepath, { force: true });
-    return { success: true, error: null };
-  } catch (e) {
-    console.error(e);
-    if (fs.existsSync(outDir))
-      fs.rmSync(outDir, { recursive: true, force: true });
-    if (fs.existsSync(importFilepath)) fs.rmSync(importFilepath);
-    return { success: false, error: e.message };
-  }
-}
-
-function zipDirectory(sourceDir, outPath) {
-  const archiver = require("archiver");
-  const archive = archiver("zip", { zlib: { level: 9 } });
-  const stream = fs.createWriteStream(outPath);
-
-  return new Promise((resolve, reject) => {
-    archive
-      .directory(sourceDir, false)
-      .on("error", (err) => reject(err))
-      .pipe(stream);
-
-    stream.on("close", () => resolve());
-    archive.finalize();
-  });
-}
-
-async function unzipDirectory(sourcePath, outDir) {
-  const extract = require("extract-zip");
-  try {
-    await extract(sourcePath, { dir: outDir });
-    return { success: true, error: null };
-  } catch (e) {
-    console.error("unzipToDirectory", e);
-    return { success: false, error: e.message };
-  }
-}
-
-module.exports = {
-  exportData,
-  unpackAndOverwriteImport,
-};
diff --git a/server/yarn.lock b/server/yarn.lock
index cc129dfe9..b25104dd1 100644
--- a/server/yarn.lock
+++ b/server/yarn.lock
@@ -889,11 +889,11 @@ abort-controller@^3.0.0:
   resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
   integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
   dependencies:
     event-target-shim "^5.0.0"
 
-accepts@~1.3.4, accepts@~1.3.8:
+accepts@~1.3.8:
   version "1.3.8"
   resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
   integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
   dependencies:
     mime-types "~2.1.34"
@@ -1224,15 +1224,10 @@ base-64@^0.1.0:
 base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-batch@0.6.1:
-  version "0.6.1"
-  resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
-  integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==
-
 bcrypt@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.0.tgz#bbb27665dbc400480a524d8991ac7434e8529e17"
   integrity sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==
   dependencies:
@@ -1853,15 +1848,10 @@ delegates@^1.0.0:
 depd@2.0.0, depd@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
   integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
 
-depd@~1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
-  integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
-
 deprecation@^2.0.0, deprecation@^2.3.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
   integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==
 
@@ -2893,20 +2883,10 @@ http-errors@2.0.0:
     inherits "2.0.4"
     setprototypeof "1.2.0"
     statuses "2.0.1"
     toidentifier "1.0.1"
 
-http-errors@~1.6.2:
-  version "1.6.3"
-  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
-  integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==
-  dependencies:
-    depd "~1.1.2"
-    inherits "2.0.3"
-    setprototypeof "1.1.0"
-    statuses ">= 1.4.0 < 2"
-
 http-proxy-agent@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
   integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==
   dependencies:
@@ -3001,15 +2981,10 @@ inflight@^1.0.4:
 inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
 
-inherits@2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
-  integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
-
 ini@~1.3.0:
   version "1.3.8"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
   integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
 
@@ -3723,11 +3698,11 @@ methods@~1.1.2:
 mime-db@1.52.0:
   version "1.52.0"
   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
   integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
 
-mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
+mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
   version "2.1.35"
   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
   integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
   dependencies:
     mime-db "1.52.0"
@@ -4339,11 +4314,11 @@ parent-module@^1.0.0:
   resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
   integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
   dependencies:
     callsites "^3.0.0"
 
-parseurl@~1.3.2, parseurl@~1.3.3:
+parseurl@~1.3.3:
   version "1.3.3"
   resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
   integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
 
 path-exists@^4.0.0:
@@ -4833,23 +4808,10 @@ send@0.18.0:
     ms "2.1.3"
     on-finished "2.4.1"
     range-parser "~1.2.1"
     statuses "2.0.1"
 
-serve-index@^1.9.1:
-  version "1.9.1"
-  resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
-  integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==
-  dependencies:
-    accepts "~1.3.4"
-    batch "0.6.1"
-    debug "2.6.9"
-    escape-html "~1.0.3"
-    http-errors "~1.6.2"
-    mime-types "~2.1.17"
-    parseurl "~1.3.2"
-
 serve-static@1.15.0:
   version "1.15.0"
   resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
   integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
   dependencies:
@@ -4880,15 +4842,10 @@ set-function-name@^2.0.0, set-function-name@^2.0.1:
   dependencies:
     define-data-property "^1.0.1"
     functions-have-names "^1.2.3"
     has-property-descriptors "^1.0.0"
 
-setprototypeof@1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
-  integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
-
 setprototypeof@1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
   integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
 
@@ -5027,15 +4984,10 @@ stack-trace@0.0.x:
 statuses@2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
   integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
 
-"statuses@>= 1.4.0 < 2":
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
-  integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
-
 stdin-discarder@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21"
   integrity sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==
   dependencies:

Vulnerable code: const GeneralExportImport = lazy(
  () => import("@/pages/GeneralSettings/ExportImport")
);
              <Route
                path="/settings/export-import"
                element={<ManagerRoute Component={GeneralExportImport} />}
              />
// import IndexCount from "../Sidebar/IndexCount";
// import LLMStatus from "../Sidebar/LLMStatus";
  DownloadSimple,
  DotsThree,

                <Option
                  href={paths.settings.exportImport()}
                  btnText="Export or Import"
                  icon={<DownloadSimple className="h-5 w-5 flex-shrink-0" />}
                />
                  <Option
                    href={paths.settings.exportImport()}
                    btnText="Export or Import"
                    icon={<DownloadSimple className="h-5 w-5 flex-shrink-0" />}
                  />
  dataExport: async () => {
    return await fetch(`${API_BASE}/system/data-export`, {
      method: "GET",
      headers: baseHeaders(),
    })
      .then((res) => res.json())
      .then((res) => res)
      .catch((e) => {
        console.error(e);
        return { filename: null, error: e.message };
      });
  },
  importData: async (formData) => {
    return await fetch(`${API_BASE}/system/data-import`, {
      method: "POST",
      body: formData,
      headers: baseHeaders(),
    })
      .then((res) => res.json())
      .then((res) => res)
      .catch((e) => {
        console.error(e);
        return { success: false, error: e.message };
      });
  },
import { useRef, useState } from "react";
import Sidebar, { SidebarMobileHeader } from "@/components/SettingsSidebar";
import { isMobile } from "react-device-detect";
import showToast from "@/utils/toast";
import { CloudArrowUp, DownloadSimple } from "@phosphor-icons/react";
import System from "@/models/system";
import { API_BASE } from "@/utils/constants";
import paths from "@/utils/paths";

export default function GeneralExportImport() {
  const hostname = window?.location?.hostname;
  const isHosted = hostname.includes(".useanything.com");

  if (isHosted) {
    return (
      <div className="w-screen h-screen overflow-hidden bg-sidebar flex">
        {!isMobile && <Sidebar />}
        <div
          style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
          className="transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
        >
          {isMobile && <SidebarMobileHeader />}
          <div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
            <div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
              <div className="items-center flex gap-x-4">
                <p className="text-2xl font-semibold text-white">
                  Export or Import
                </p>
              </div>
            </div>
          </div>

          <div className="w-full items-center justify-center flex flex-col gap-y-4">
            <p className="text-lg font-base text-white text-opacity-60">
              This feature is temporarily disabled for hosted AnythingLLM
              instances.
            </p>
            <a
              href={`${paths.mailToMintplex()}?Subject=Import/Export disabled on hosted AnythingLLM.`}
              className="text-blue-300 hover:underline"
            >
              Contact Mintplex Labs Inc.
            </a>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="w-screen h-screen overflow-hidden bg-sidebar flex">
      {!isMobile && <Sidebar />}
      <div
        style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
        className="transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
      >
        {isMobile && <SidebarMobileHeader />}
        <div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
          <div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
            <div className="items-center flex gap-x-4">
              <p className="text-2xl font-semibold text-white">
                Export or Import
              </p>
            </div>
            <p className="text-sm font-base text-white text-opacity-60">
              Have multiple AnythingLLM instances or simply want to backup or
              re-import data from another instance? You can do so here.
            </p>
          </div>
          <div className="text-white text-sm font-medium py-4">
            This will not automatically sync your vector database embeddings.
          </div>
          <ImportData />
          <ExportData />
        </div>
      </div>
    </div>
  );
}

function ImportData() {
  const inputRef = useRef(null);
  const [loading, setLoading] = useState(false);
  const [file, setFile] = useState(null);
  const [result, setResult] = useState(null);

  const startInput = () => inputRef?.current?.click();
  const handleUpload = async (e) => {
    setLoading(true);
    e.preventDefault();
    setFile(null);
    setResult(null);

    const file = e.target.files?.[0];
    if (!file) {
      showToast("Invalid file upload", "error");
      return false;
    }

    setFile(file);
    setLoading(true);
    const formData = new FormData();
    formData.append("file", file, file.name);
    const { success, error } = await System.importData(formData);
    if (!success) {
      showToast(`Failed to import data: ${error}`, "error");
    } else {
      setResult(true);
      showToast(`Successfully imported ${file.name}`, "success");
    }

    setLoading(false);
    setFile(null);
  };

  return (
    <div
      onClick={startInput}
      className="max-w-[600px] py-4 bg-zinc-900/50 rounded-2xl border-2 border-dashed border-white border-opacity-60 justify-center items-center inline-flex transition-all duration-300 hover:opacity-60 cursor-pointer"
    >
      <div className="flex flex-col items-center justify-center">
        {loading ? (
          <div className="flex items-center justify-center gap-2 animate-pulse">
            <div className="text-white text-opacity-80 text-sm font-semibold py-1">
              Importing
            </div>
            <div className="h-4 w-4 animate-spin rounded-full border-2 border-solid border-white border-t-transparent " />
          </div>
        ) : !!result ? (
          <div className="flex items-center justify-center gap-2">
            <CloudArrowUp className="w-8 h-8 text-green-400" />
            <div className="text-green-400 text-opacity-80 text-sm font-semibold py-1">
              Import Successful
            </div>
          </div>
        ) : (
          <>
            <input
              ref={inputRef}
              onChange={handleUpload}
              name="import"
              type="file"
              multiple="false"
              accept=".zip"
              hidden={true}
            />
            <div className="flex flex-col items-center justify-center">
              <CloudArrowUp className="w-8 h-8 text-white/80" />
              <div className="text-white text-opacity-80 text-sm font-semibold py-1">
                Import AnythingLLM Data
              </div>
              <div className="text-white text-opacity-60 text-xs font-medium py-1">
                This must be an export from an AnythingLLM instance.
              </div>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

function ExportData() {
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState(null);

  const exportData = async function () {
    setLoading(true);
    const { filename, error } = await System.dataExport();
    setLoading(false);

    if (!filename) {
      showToast(`Failed to export data: ${error}`, "error");
    } else {
      setResult(filename);
      const link = document.createElement("a");
      link.href = `${API_BASE}/system/data-exports/${filename}`;
      link.target = "_blank";
      document.body.appendChild(link);
    }
  };

  if (loading) {
    return (
      <button
        onClick={exportData}
        className="transition-all max-w-[600px] bg-white rounded-lg justify-center items-center my-8 text-zinc-900 border-transparent border-2 cursor-not-allowed animate-pulse"
      >
        <div className="flex items-center justify-center gap-2">
          <div className="duration-300 text-center text-sm font-bold py-3">
            Exporting
          </div>
          <div className="h-4 w-4 animate-spin rounded-full border-2 border-solid border-zinc-900 border-t-transparent " />
        </div>
      </button>
    );
  }

  if (!!result) {
    return (
      <a
        target="_blank"
        href={`${API_BASE}/system/data-exports/${result}`}
        className="transition-all max-w-[600px] bg-green-100 hover:bg-zinc-900/50 hover:text-white hover:border-white rounded-lg justify-center items-center my-8 text-zinc-900 border-transparent border-2 cursor-pointer"
      >
        <div className="flex items-center justify-center gap-2">
          <div className="duration-300 text-center text-sm font-bold py-3">
            Download Data Export
          </div>
          <DownloadSimple className="w-6 h-6" />
        </div>
      </a>
    );
  }

  return (
    <button
      onClick={exportData}
      className="transition-all max-w-[600px] bg-white rounded-lg justify-center items-center my-8 cursor-pointer text-zinc-900 border-transparent border-2 hover:bg-zinc-900/50 hover:text-white hover:border-white"
    >
      <div className="duration-300 text-center text-sm font-bold py-3">
        Export AnythingLLM Data
      </div>
    </button>
  );
}
  exports: () => {
    return `${API_BASE.replace("/api", "")}/system/data-exports`;
  },
    exportImport: () => {
      return "/settings/export-import";
    },
const { exportData, unpackAndOverwriteImport } = require("../utils/files/data");
  app.get(
    "/system/data-export",
    [validatedRequest, flexUserRoleValid],
    async (_, response) => {
      try {
        const { filename, error } = await exportData();
        response.status(200).json({ filename, error });
      } catch (e) {
        console.log(e.message, e);
        response.sendStatus(500).end();
      }
    }
  );

  app.get("/system/data-exports/:filename", (request, response) => {
    const exportLocation = __dirname + "/../storage/exports/";
    const sanitized = normalizePath(request.params.filename);
    const finalDestination = path.join(exportLocation, sanitized);

    if (!fs.existsSync(finalDestination)) {
      response.status(404).json({
        error: 404,
        msg: `File ${request.params.filename} does not exist in exports.`,
      });
      return;
    }

    response.download(finalDestination, request.params.filename, (err) => {
      if (err) {
        response.send({
          error: err,
          msg: "Problem downloading the file",
        });
      }
      // delete on download because endpoint is not authenticated.
      fs.rmSync(finalDestination);
    });
  });

  app.post(
    "/system/data-import",
    handleImports.single("file"),
    async function (request, response) {
      const { originalname } = request.file;
      const { success, error } = await unpackAndOverwriteImport(originalname);
      response.status(200).json({ success, error });
    }
  );

const serveIndex = require("serve-index");
app.use(
  "/system/data-exports",
  serveIndex(__dirname + "/storage/exports", { icons: true })
);

    "serve-index": "^1.9.1",
const fs = require("fs");
const path = require("path");
const { v4 } = require("uuid");

async function exportData() {
  const uid = `anythingllm-export-${new Date()
    .toJSON()
    .slice(0, 10)}-${new Date().toJSON().slice(11, 19)}`;
  const folder =
    process.env.NODE_ENV === "development"
      ? path.resolve(__dirname, `../../storage/exports/${uid}`)
      : path.resolve(process.env.STORAGE_DIR, `exports/${uid}`);
  const storageBase =
    process.env.NODE_ENV === "development"
      ? path.resolve(__dirname, `../../storage`)
      : path.resolve(process.env.STORAGE_DIR);

  try {
    fs.mkdirSync(folder, { recursive: true });
    if (fs.existsSync(path.resolve(storageBase, `documents`))) {
      console.log("\x1b[34m[EXPORTING DATA]\x1b[0m Copying documents!");
      fs.cpSync(
        path.resolve(storageBase, `documents`),
        path.resolve(folder, "documents"),
        { recursive: true }
      );
    }

    if (fs.existsSync(path.resolve(storageBase, `lancedb`))) {
      console.log("\x1b[34m[EXPORTING DATA]\x1b[0m Copying LanceDB data!");
      fs.cpSync(
        path.resolve(storageBase, `lancedb`),
        path.resolve(folder, "lancedb"),
        { recursive: true }
      );
    }

    if (fs.existsSync(path.resolve(storageBase, `vector-cache`))) {
      console.log("\x1b[34m[EXPORTING DATA]\x1b[0m Copying vector cache!");
      fs.cpSync(
        path.resolve(storageBase, `vector-cache`),
        path.resolve(folder, "vector-cache"),
        { recursive: true }
      );
    }

    if (fs.existsSync(path.resolve(storageBase, `anythingllm.db`))) {
      console.log(
        "\x1b[34m[EXPORTING DATA]\x1b[0m Copying anythingllm database!"
      );
      fs.cpSync(
        path.resolve(storageBase, `anythingllm.db`),
        path.resolve(folder, "anythingllm.db")
      );
    }

    await zipDirectory(folder, path.resolve(folder, `../${uid}.zip`));
    fs.rmSync(folder, { recursive: true, force: true });
    return { filename: `${uid}.zip`, error: null };
  } catch (e) {
    // If anything goes wrong - abort and clean up
    console.error(e);
    if (fs.existsSync(folder))
      fs.rmSync(folder, { recursive: true, force: true });
    return { filename: null, error: e.message };
  }
}

async function unpackAndOverwriteImport(importFilename) {
  const importFilepath =
    process.env.NODE_ENV === "development"
      ? path.resolve(__dirname, `../../storage/imports/${importFilename}`)
      : path.resolve(process.env.STORAGE_DIR, `imports/${importFilename}`);
  if (!fs.existsSync(importFilepath))
    return { success: false, error: "Import file does not exist." };

  const uid = v4();
  const outDir =
    process.env.NODE_ENV === "development"
      ? path.resolve(__dirname, `../../storage/imports/${uid}`)
      : path.resolve(process.env.STORAGE_DIR, `imports/${uid}`);

  const storageBase =
    process.env.NODE_ENV === "development"
      ? path.resolve(__dirname, `../../storage`)
      : path.resolve(process.env.STORAGE_DIR);

  try {
    console.log(
      "\x1b[34m[EXTRACTING DATA]\x1b[0m Extracting data from zip into storage!"
    );
    const unzipProc = await unzipDirectory(importFilepath, outDir);
    if (!unzipProc.success) return unzipProc;

    if (fs.existsSync(path.resolve(outDir, `documents`))) {
      console.log(
        "\x1b[34m[OVERWRITE & IMPORT DATA]\x1b[0m Importing documents!"
      );
      if (fs.existsSync(path.resolve(storageBase, `documents`)))
        fs.rmSync(path.resolve(storageBase, `documents`), {
          recursive: true,
          force: true,
        });
      fs.cpSync(
        path.resolve(outDir, `documents`),
        path.resolve(storageBase, "documents"),
        { recursive: true }
      );
    }

    if (fs.existsSync(path.resolve(outDir, `lancedb`))) {
      console.log(
        "\x1b[34m[OVERWRITE & IMPORT DATA]\x1b[0m Importing LanceDb!"
      );
      if (fs.existsSync(path.resolve(storageBase, `lancedb`)))
        fs.rmSync(path.resolve(storageBase, `lancedb`), {
          recursive: true,
          force: true,
        });
      fs.cpSync(
        path.resolve(outDir, `lancedb`),
        path.resolve(storageBase, "lancedb"),
        { recursive: true }
      );
    }

    if (fs.existsSync(path.resolve(outDir, `vector-cache`))) {
      console.log(
        "\x1b[34m[OVERWRITE & IMPORT DATA]\x1b[0m Importing Vector Cache!"
      );
      if (fs.existsSync(path.resolve(storageBase, `vector-cache`)))
        fs.rmSync(path.resolve(storageBase, `vector-cache`), {
          recursive: true,
          force: true,
        });
      fs.cpSync(
        path.resolve(outDir, `vector-cache`),
        path.resolve(storageBase, "vector-cache"),
        { recursive: true }
      );
    }

    if (fs.existsSync(path.resolve(outDir, `anythingllm.db`))) {
      console.log(
        "\x1b[34m[OVERWRITE & IMPORT DATA]\x1b[0m Importing AnythingLLM DB!"
      );
      if (fs.existsSync(path.resolve(storageBase, `anythingllm.db`)))
        fs.rmSync(path.resolve(storageBase, `anythingllm.db`), { force: true });
      fs.cpSync(
        path.resolve(outDir, `anythingllm.db`),
        path.resolve(storageBase, "anythingllm.db")
      );
    }

    fs.rmSync(outDir, { recursive: true, force: true });
    fs.rmSync(importFilepath, { force: true });
    return { success: true, error: null };
  } catch (e) {
    console.error(e);
    if (fs.existsSync(outDir))
      fs.rmSync(outDir, { recursive: true, force: true });
    if (fs.existsSync(importFilepath)) fs.rmSync(importFilepath);
    return { success: false, error: e.message };
  }
}

function zipDirectory(sourceDir, outPath) {
  const archiver = require("archiver");
  const archive = archiver("zip", { zlib: { level: 9 } });
  const stream = fs.createWriteStream(outPath);

  return new Promise((resolve, reject) => {
    archive
      .directory(sourceDir, false)
      .on("error", (err) => reject(err))
      .pipe(stream);

    stream.on("close", () => resolve());
    archive.finalize();
  });
}

async function unzipDirectory(sourcePath, outDir) {
  const extract = require("extract-zip");
  try {
    await extract(sourcePath, { dir: outDir });
    return { success: true, error: null };
  } catch (e) {
    console.error("unzipToDirectory", e);
    return { success: false, error: e.message };
  }
}

module.exports = {
  exportData,
  unpackAndOverwriteImport,
};
accepts@~1.3.4, accepts@~1.3.8:
batch@0.6.1:
  version "0.6.1"
  resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
  integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==

depd@~1.1.2:
  version "1.1.2"
  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
  integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==

http-errors@~1.6.2:
  version "1.6.3"
  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
  integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==
  dependencies:
    depd "~1.1.2"
    inherits "2.0.3"
    setprototypeof "1.1.0"
    statuses ">= 1.4.0 < 2"

inherits@2.0.3:
  version "2.0.3"
  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
  integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==

mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
parseurl@~1.3.2, parseurl@~1.3.3:
serve-index@^1.9.1:
  version "1.9.1"
  resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
  integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==
  dependencies:
    accepts "~1.3.4"
    batch "0.6.1"
    debug "2.6.9"
    escape-html "~1.0.3"
    http-errors "~1.6.2"
    mime-types "~2.1.17"
    parseurl "~1.3.2"

setprototypeof@1.1.0:
  version "1.1.0"
  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
  integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==

"statuses@>= 1.4.0 < 2":
  version "1.5.0"
  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
  integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==

Fixed code: accepts@~1.3.8:
mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
parseurl@~1.3.3: