mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-07-04 16:20:12 +02:00
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>
This commit is contained in:
parent
56fa17caf2
commit
08d33cfd8f
|
@ -34,9 +34,6 @@ const GeneralEmbeddingPreference = lazy(
|
||||||
const GeneralVectorDatabase = lazy(
|
const GeneralVectorDatabase = lazy(
|
||||||
() => import("@/pages/GeneralSettings/VectorDatabase")
|
() => import("@/pages/GeneralSettings/VectorDatabase")
|
||||||
);
|
);
|
||||||
const GeneralExportImport = lazy(
|
|
||||||
() => import("@/pages/GeneralSettings/ExportImport")
|
|
||||||
);
|
|
||||||
const GeneralSecurity = lazy(() => import("@/pages/GeneralSettings/Security"));
|
const GeneralSecurity = lazy(() => import("@/pages/GeneralSettings/Security"));
|
||||||
const DataConnectors = lazy(
|
const DataConnectors = lazy(
|
||||||
() => import("@/pages/GeneralSettings/DataConnectors")
|
() => import("@/pages/GeneralSettings/DataConnectors")
|
||||||
|
@ -74,10 +71,6 @@ export default function App() {
|
||||||
element={<AdminRoute Component={GeneralVectorDatabase} />}
|
element={<AdminRoute Component={GeneralVectorDatabase} />}
|
||||||
/>
|
/>
|
||||||
{/* Manager */}
|
{/* Manager */}
|
||||||
<Route
|
|
||||||
path="/settings/export-import"
|
|
||||||
element={<ManagerRoute Component={GeneralExportImport} />}
|
|
||||||
/>
|
|
||||||
<Route
|
<Route
|
||||||
path="/settings/security"
|
path="/settings/security"
|
||||||
element={<ManagerRoute Component={GeneralSecurity} />}
|
element={<ManagerRoute Component={GeneralSecurity} />}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
// import IndexCount from "../Sidebar/IndexCount";
|
|
||||||
// import LLMStatus from "../Sidebar/LLMStatus";
|
|
||||||
import paths from "@/utils/paths";
|
import paths from "@/utils/paths";
|
||||||
import useLogo from "@/hooks/useLogo";
|
import useLogo from "@/hooks/useLogo";
|
||||||
import {
|
import {
|
||||||
|
@ -14,10 +12,8 @@ import {
|
||||||
Key,
|
Key,
|
||||||
ChatText,
|
ChatText,
|
||||||
Database,
|
Database,
|
||||||
DownloadSimple,
|
|
||||||
Lock,
|
Lock,
|
||||||
GithubLogo,
|
GithubLogo,
|
||||||
DotsThree,
|
|
||||||
House,
|
House,
|
||||||
X,
|
X,
|
||||||
List,
|
List,
|
||||||
|
@ -135,12 +131,6 @@ export default function SettingsSidebar() {
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Option
|
|
||||||
href={paths.settings.exportImport()}
|
|
||||||
btnText="Export or Import"
|
|
||||||
icon={<DownloadSimple className="h-5 w-5 flex-shrink-0" />}
|
|
||||||
/>
|
|
||||||
<Option
|
<Option
|
||||||
href={paths.settings.security()}
|
href={paths.settings.security()}
|
||||||
btnText="Security"
|
btnText="Security"
|
||||||
|
@ -328,11 +318,6 @@ export function SidebarMobileHeader() {
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Option
|
|
||||||
href={paths.settings.exportImport()}
|
|
||||||
btnText="Export or Import"
|
|
||||||
icon={<DownloadSimple className="h-5 w-5 flex-shrink-0" />}
|
|
||||||
/>
|
|
||||||
<Option
|
<Option
|
||||||
href={paths.settings.security()}
|
href={paths.settings.security()}
|
||||||
btnText="Security"
|
btnText="Security"
|
||||||
|
|
|
@ -160,31 +160,6 @@ const System = {
|
||||||
return false;
|
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) {
|
uploadPfp: async function (formData) {
|
||||||
return await fetch(`${API_BASE}/system/upload-pfp`, {
|
return await fetch(`${API_BASE}/system/upload-pfp`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -56,9 +56,6 @@ export default {
|
||||||
return `/workspace/${slug}`;
|
return `/workspace/${slug}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
exports: () => {
|
|
||||||
return `${API_BASE.replace("/api", "")}/system/data-exports`;
|
|
||||||
},
|
|
||||||
apiDocs: () => {
|
apiDocs: () => {
|
||||||
return `${API_BASE}/docs`;
|
return `${API_BASE}/docs`;
|
||||||
},
|
},
|
||||||
|
@ -87,9 +84,6 @@ export default {
|
||||||
vectorDatabase: () => {
|
vectorDatabase: () => {
|
||||||
return "/settings/vector-database";
|
return "/settings/vector-database";
|
||||||
},
|
},
|
||||||
exportImport: () => {
|
|
||||||
return "/settings/export-import";
|
|
||||||
},
|
|
||||||
security: () => {
|
security: () => {
|
||||||
return "/settings/security";
|
return "/settings/security";
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,6 @@ process.env.NODE_ENV === "development"
|
||||||
? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` })
|
? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` })
|
||||||
: require("dotenv").config();
|
: require("dotenv").config();
|
||||||
const { viewLocalFiles, normalizePath } = require("../utils/files");
|
const { viewLocalFiles, normalizePath } = require("../utils/files");
|
||||||
const { exportData, unpackAndOverwriteImport } = require("../utils/files/data");
|
|
||||||
const {
|
const {
|
||||||
checkProcessorAlive,
|
checkProcessorAlive,
|
||||||
acceptedFileTypes,
|
acceptedFileTypes,
|
||||||
|
@ -389,55 +388,6 @@ function systemEndpoints(app) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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) {
|
app.get("/system/logo", async function (request, response) {
|
||||||
try {
|
try {
|
||||||
const defaultFilename = getDefaultFilename();
|
const defaultFilename = getDefaultFilename();
|
||||||
|
|
|
@ -4,7 +4,6 @@ process.env.NODE_ENV === "development"
|
||||||
|
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const bodyParser = require("body-parser");
|
const bodyParser = require("body-parser");
|
||||||
const serveIndex = require("serve-index");
|
|
||||||
const cors = require("cors");
|
const cors = require("cors");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { reqBody } = require("./utils/http");
|
const { reqBody } = require("./utils/http");
|
||||||
|
@ -85,11 +84,6 @@ if (process.env.NODE_ENV !== "development") {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use(
|
|
||||||
"/system/data-exports",
|
|
||||||
serveIndex(__dirname + "/storage/exports", { icons: true })
|
|
||||||
);
|
|
||||||
|
|
||||||
app.all("*", function (_, response) {
|
app.all("*", function (_, response) {
|
||||||
response.sendStatus(404);
|
response.sendStatus(404);
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,7 +52,6 @@
|
||||||
"pinecone-client": "^1.1.0",
|
"pinecone-client": "^1.1.0",
|
||||||
"posthog-node": "^3.1.1",
|
"posthog-node": "^3.1.1",
|
||||||
"prisma": "^5.3.1",
|
"prisma": "^5.3.1",
|
||||||
"serve-index": "^1.9.1",
|
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"sqlite": "^4.2.1",
|
"sqlite": "^4.2.1",
|
||||||
"sqlite3": "^5.1.6",
|
"sqlite3": "^5.1.6",
|
||||||
|
|
|
@ -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,
|
|
||||||
};
|
|
|
@ -891,7 +891,7 @@ abort-controller@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
event-target-shim "^5.0.0"
|
event-target-shim "^5.0.0"
|
||||||
|
|
||||||
accepts@~1.3.4, accepts@~1.3.8:
|
accepts@~1.3.8:
|
||||||
version "1.3.8"
|
version "1.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
||||||
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
|
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
|
||||||
|
@ -1226,11 +1226,6 @@ base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1:
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
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:
|
bcrypt@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.0.tgz#bbb27665dbc400480a524d8991ac7434e8529e17"
|
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.0.tgz#bbb27665dbc400480a524d8991ac7434e8529e17"
|
||||||
|
@ -1855,11 +1850,6 @@ depd@2.0.0, depd@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
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:
|
deprecation@^2.0.0, deprecation@^2.3.1:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
|
resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
|
||||||
|
@ -2895,16 +2885,6 @@ http-errors@2.0.0:
|
||||||
statuses "2.0.1"
|
statuses "2.0.1"
|
||||||
toidentifier "1.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:
|
http-proxy-agent@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
|
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
|
||||||
|
@ -3003,11 +2983,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
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:
|
ini@~1.3.0:
|
||||||
version "1.3.8"
|
version "1.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||||
|
@ -3725,7 +3700,7 @@ mime-db@1.52.0:
|
||||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
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"
|
version "2.1.35"
|
||||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||||
|
@ -4341,7 +4316,7 @@ parent-module@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
callsites "^3.0.0"
|
callsites "^3.0.0"
|
||||||
|
|
||||||
parseurl@~1.3.2, parseurl@~1.3.3:
|
parseurl@~1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||||
|
@ -4835,19 +4810,6 @@ send@0.18.0:
|
||||||
range-parser "~1.2.1"
|
range-parser "~1.2.1"
|
||||||
statuses "2.0.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:
|
serve-static@1.15.0:
|
||||||
version "1.15.0"
|
version "1.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
|
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
|
||||||
|
@ -4882,11 +4844,6 @@ set-function-name@^2.0.0, set-function-name@^2.0.1:
|
||||||
functions-have-names "^1.2.3"
|
functions-have-names "^1.2.3"
|
||||||
has-property-descriptors "^1.0.0"
|
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:
|
setprototypeof@1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||||
|
@ -5029,11 +4986,6 @@ statuses@2.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
|
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
|
||||||
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
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:
|
stdin-discarder@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21"
|
resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user