mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-18 20:20:11 +01:00
Replace custom sqlite dbms with prisma (#239)
* WIP converted all sqlite models into prisma calls * modify db setup and fix ApiKey model calls in admin.js * renaming function params to be consistent * converted adminEndpoints to utilize prisma orm * converted chatEndpoints to utilize prisma orm * converted inviteEndpoints to utilize prisma orm * converted systemEndpoints to utilize prisma orm * converted workspaceEndpoints to utilize prisma orm * converting sql queries to prisma calls * fixed default param bug for orderBy and limit * fixed typo for workspace chats * fixed order of deletion to account for sql relations * fix invite CRUD and workspace management CRUD * fixed CRUD for api keys * created prisma setup scripts/docs for understanding how to use prisma * prisma dependency change * removing unneeded console.logs * removing unneeded sql escape function * linting and creating migration script * migration from depreciated sqlite script update * removing unneeded migrations in prisma folder * create backup of old sqlite db and use transactions to ensure all operations complete successfully * adding migrations to gitignore * updated PRISMA.md docs for info on how to use sqlite migration script * comment changes * adding back migrations folder to repo * Reviewing SQL and prisma integraiton on fresh repo * update inline key replacement * ensure migration script executes and maps foreign_keys regardless of db ordering * run migration endpoint * support new prisma backend * bump version * change migration call --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
parent
e4a5fe5971
commit
a126b5f5aa
@ -77,6 +77,10 @@ RUN cd /app/collector && \
|
|||||||
. v-env/bin/activate && \
|
. v-env/bin/activate && \
|
||||||
pip install --no-cache-dir -r requirements.txt
|
pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Migrate and Run Prisma against known schema
|
||||||
|
RUN cd ./server && npx prisma generate --schema=./prisma/schema.prisma
|
||||||
|
RUN cd ./server && npx prisma migrate deploy --schema=./prisma/schema.prisma
|
||||||
|
|
||||||
# Setup the environment
|
# Setup the environment
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV PATH=/app/collector/v-env/bin:$PATH
|
ENV PATH=/app/collector/v-env/bin:$PATH
|
||||||
|
@ -22,7 +22,7 @@ export default function DocumentSettings({ workspace }) {
|
|||||||
async function fetchKeys(refetchWorkspace = false) {
|
async function fetchKeys(refetchWorkspace = false) {
|
||||||
const localFiles = await System.localFiles();
|
const localFiles = await System.localFiles();
|
||||||
const currentWorkspace = refetchWorkspace
|
const currentWorkspace = refetchWorkspace
|
||||||
? await Workspace.bySlug(slug)
|
? await Workspace.bySlug(slug ?? workspace.slug)
|
||||||
: workspace;
|
: workspace;
|
||||||
const originalDocs =
|
const originalDocs =
|
||||||
currentWorkspace.documents.map((doc) => doc.docpath) || [];
|
currentWorkspace.documents.map((doc) => doc.docpath) || [];
|
||||||
|
@ -3,6 +3,22 @@ import Workspace from "../../../../models/workspace";
|
|||||||
import paths from "../../../../utils/paths";
|
import paths from "../../../../utils/paths";
|
||||||
import { chatPrompt } from "../../../../utils/chat";
|
import { chatPrompt } from "../../../../utils/chat";
|
||||||
|
|
||||||
|
// Ensure that a type is correct before sending the body
|
||||||
|
// to the backend.
|
||||||
|
function castToType(key, value) {
|
||||||
|
const definitions = {
|
||||||
|
openAiTemp: {
|
||||||
|
cast: (value) => Number(value),
|
||||||
|
},
|
||||||
|
openAiHistory: {
|
||||||
|
cast: (value) => Number(value),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!definitions.hasOwnProperty(key)) return value;
|
||||||
|
return definitions[key].cast(value);
|
||||||
|
}
|
||||||
|
|
||||||
export default function WorkspaceSettings({ workspace }) {
|
export default function WorkspaceSettings({ workspace }) {
|
||||||
const formEl = useRef(null);
|
const formEl = useRef(null);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
@ -34,7 +50,7 @@ export default function WorkspaceSettings({ workspace }) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const data = {};
|
const data = {};
|
||||||
const form = new FormData(formEl.current);
|
const form = new FormData(formEl.current);
|
||||||
for (var [key, value] of form.entries()) data[key] = value;
|
for (var [key, value] of form.entries()) data[key] = castToType(key, value);
|
||||||
const { workspace: updatedWorkspace, message } = await Workspace.update(
|
const { workspace: updatedWorkspace, message } = await Workspace.update(
|
||||||
workspace.slug,
|
workspace.slug,
|
||||||
data
|
data
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "anything-llm",
|
"name": "anything-llm",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"description": "The best solution for turning private documents into a chat bot using off-the-shelf tools and commercially viable AI technologies.",
|
"description": "The best solution for turning private documents into a chat bot using off-the-shelf tools and commercially viable AI technologies.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "Timothy Carambat (Mintplex Labs)",
|
"author": "Timothy Carambat (Mintplex Labs)",
|
||||||
@ -10,10 +10,14 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "cd server && yarn lint && cd .. && cd frontend && yarn lint",
|
"lint": "cd server && yarn lint && cd .. && cd frontend && yarn lint",
|
||||||
"setup": "cd server && yarn && cd ../frontend && yarn && cd .. && yarn setup:envs && echo \"Please run yarn dev:server and yarn dev:frontend in separate terminal tabs.\"",
|
"setup": "cd server && yarn && cd ../frontend && yarn && cd .. && yarn setup:envs && yarn prisma:setup && echo \"Please run yarn dev:server and yarn dev:frontend in separate terminal tabs.\"",
|
||||||
"setup:envs": "cp -n ./frontend/.env.example ./frontend/.env && cp -n ./server/.env.example ./server/.env.development && cp -n ./collector/.env.example ./collector/.env && cp -n ./docker/.env.example ./docker/.env && echo \"All ENV files copied!\n\"",
|
"setup:envs": "cp -n ./frontend/.env.example ./frontend/.env && cp -n ./server/.env.example ./server/.env.development && cp -n ./collector/.env.example ./collector/.env && cp -n ./docker/.env.example ./docker/.env && echo \"All ENV files copied!\n\"",
|
||||||
"dev:server": "cd server && yarn dev",
|
"dev:server": "cd server && yarn dev",
|
||||||
"dev:frontend": "cd frontend && yarn start",
|
"dev:frontend": "cd frontend && yarn start",
|
||||||
|
"prisma:generate": "cd server && npx prisma generate",
|
||||||
|
"prisma:migrate": "cd server && npx prisma migrate dev --name init",
|
||||||
|
"prisma:seed": "cd server && npx prisma db seed",
|
||||||
|
"prisma:setup": "yarn prisma:generate && yarn prisma:migrate && yarn prisma:seed",
|
||||||
"prod:server": "cd server && yarn start",
|
"prod:server": "cd server && yarn start",
|
||||||
"prod:frontend": "cd frontend && yarn build",
|
"prod:frontend": "cd frontend && yarn build",
|
||||||
"generate:cloudformation": "node cloud-deployments/aws/cloudformation/generate.mjs",
|
"generate:cloudformation": "node cloud-deployments/aws/cloudformation/generate.mjs",
|
||||||
|
1
server/.gitignore
vendored
1
server/.gitignore
vendored
@ -16,3 +16,4 @@ public/
|
|||||||
# For legacy copies of repo
|
# For legacy copies of repo
|
||||||
documents
|
documents
|
||||||
vector-cache
|
vector-cache
|
||||||
|
yarn-error.log
|
@ -1,4 +1,3 @@
|
|||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
const { ApiKey } = require("../models/apiKeys");
|
const { ApiKey } = require("../models/apiKeys");
|
||||||
const { Document } = require("../models/documents");
|
const { Document } = require("../models/documents");
|
||||||
const { Invite } = require("../models/invite");
|
const { Invite } = require("../models/invite");
|
||||||
@ -82,7 +81,7 @@ function adminEndpoints(app) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await User.delete(`id = ${id}`);
|
await User.delete({ id: Number(id) });
|
||||||
response.status(200).json({ success: true, error: null });
|
response.status(200).json({ success: true, error: null });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -204,7 +203,7 @@ function adminEndpoints(app) {
|
|||||||
const { workspaceId } = request.params;
|
const { workspaceId } = request.params;
|
||||||
const { userIds } = reqBody(request);
|
const { userIds } = reqBody(request);
|
||||||
const { success, error } = await Workspace.updateUsers(
|
const { success, error } = await Workspace.updateUsers(
|
||||||
escape(workspaceId),
|
workspaceId,
|
||||||
userIds
|
userIds
|
||||||
);
|
);
|
||||||
response.status(200).json({ success, error });
|
response.status(200).json({ success, error });
|
||||||
@ -228,23 +227,23 @@ function adminEndpoints(app) {
|
|||||||
|
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
const VectorDb = getVectorDbClass();
|
const VectorDb = getVectorDbClass();
|
||||||
const workspace = Workspace.get(`id = ${escape(id)}`);
|
const workspace = await Workspace.get({ id: Number(id) });
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
response.sendStatus(404).end();
|
response.sendStatus(404).end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Workspace.delete(`id = ${workspace.id}`);
|
await WorkspaceChats.delete({ workspaceId: Number(workspace.id) });
|
||||||
await DocumentVectors.deleteForWorkspace(workspace.id);
|
await DocumentVectors.deleteForWorkspace(Number(workspace.id));
|
||||||
await Document.delete(`workspaceId = ${Number(workspace.id)}`);
|
await Document.delete({ workspaceId: Number(workspace.id) });
|
||||||
await WorkspaceChats.delete(`workspaceId = ${Number(workspace.id)}`);
|
await Workspace.delete({ id: Number(workspace.id) });
|
||||||
try {
|
try {
|
||||||
await VectorDb["delete-namespace"]({ namespace: workspace.slug });
|
await VectorDb["delete-namespace"]({ namespace: workspace.slug });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e.message);
|
console.error(e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.status(200).json({ success, error });
|
response.status(200).json({ success: true, error: null });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
response.sendStatus(500).end();
|
response.sendStatus(500).end();
|
||||||
@ -262,13 +261,18 @@ function adminEndpoints(app) {
|
|||||||
response.sendStatus(401).end();
|
response.sendStatus(401).end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { offset = 0 } = reqBody(request);
|
|
||||||
|
const { offset = 0, limit = 20 } = reqBody(request);
|
||||||
const chats = await WorkspaceChats.whereWithData(
|
const chats = await WorkspaceChats.whereWithData(
|
||||||
`id >= ${escape(offset)}`,
|
{ id: { gte: offset } },
|
||||||
20
|
limit
|
||||||
);
|
);
|
||||||
const hasPages = (await WorkspaceChats.count()) > 20;
|
const totalChats = await WorkspaceChats.count();
|
||||||
response.status(200).json({ chats: chats.reverse(), hasPages });
|
const hasPages = totalChats > offset + limit;
|
||||||
|
|
||||||
|
response
|
||||||
|
.status(200)
|
||||||
|
.json({ chats: chats.reverse(), hasPages, totalChats });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
response.sendStatus(500).end();
|
response.sendStatus(500).end();
|
||||||
@ -288,7 +292,7 @@ function adminEndpoints(app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await WorkspaceChats.delete(`id = ${id}`);
|
await WorkspaceChats.delete({ id: Number(id) });
|
||||||
response.status(200).json({ success, error });
|
response.status(200).json({ success, error });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -310,14 +314,14 @@ function adminEndpoints(app) {
|
|||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
users_can_delete_workspaces:
|
users_can_delete_workspaces:
|
||||||
(await SystemSettings.get(`label = 'users_can_delete_workspaces'`))
|
(await SystemSettings.get({ label: "users_can_delete_workspaces" }))
|
||||||
?.value === "true",
|
?.value === "true",
|
||||||
limit_user_messages:
|
limit_user_messages:
|
||||||
(await SystemSettings.get(`label = 'limit_user_messages'`))
|
(await SystemSettings.get({ label: "limit_user_messages" }))
|
||||||
?.value === "true",
|
?.value === "true",
|
||||||
message_limit:
|
message_limit:
|
||||||
Number(
|
Number(
|
||||||
(await SystemSettings.get(`label = 'message_limit'`))?.value
|
(await SystemSettings.get({ label: "message_limit" }))?.value
|
||||||
) || 10,
|
) || 10,
|
||||||
};
|
};
|
||||||
response.status(200).json({ settings });
|
response.status(200).json({ settings });
|
||||||
@ -357,7 +361,7 @@ function adminEndpoints(app) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiKeys = await ApiKey.whereWithUser("id IS NOT NULL");
|
const apiKeys = await ApiKey.whereWithUser({});
|
||||||
return response.status(200).json({
|
return response.status(200).json({
|
||||||
apiKeys,
|
apiKeys,
|
||||||
error: null,
|
error: null,
|
||||||
@ -405,8 +409,7 @@ function adminEndpoints(app) {
|
|||||||
response.sendStatus(401).end();
|
response.sendStatus(401).end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await ApiKey.delete({ id: Number(id) });
|
||||||
await ApiKey.delete(`id = ${id}`);
|
|
||||||
return response.status(200).end();
|
return response.status(200).end();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
const { Invite } = require("../../../models/invite");
|
const { Invite } = require("../../../models/invite");
|
||||||
const { SystemSettings } = require("../../../models/systemSettings");
|
const { SystemSettings } = require("../../../models/systemSettings");
|
||||||
const { User } = require("../../../models/user");
|
const { User } = require("../../../models/user");
|
||||||
@ -249,7 +248,7 @@ function apiAdminEndpoints(app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await User.delete(`id = ${id}`);
|
await User.delete({ id });
|
||||||
response.status(200).json({ success: true, error: null });
|
response.status(200).json({ success: true, error: null });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -457,7 +456,7 @@ function apiAdminEndpoints(app) {
|
|||||||
const { workspaceId } = request.params;
|
const { workspaceId } = request.params;
|
||||||
const { userIds } = reqBody(request);
|
const { userIds } = reqBody(request);
|
||||||
const { success, error } = await Workspace.updateUsers(
|
const { success, error } = await Workspace.updateUsers(
|
||||||
escape(workspaceId),
|
workspaceId,
|
||||||
userIds
|
userIds
|
||||||
);
|
);
|
||||||
response.status(200).json({ success, error });
|
response.status(200).json({ success, error });
|
||||||
@ -517,7 +516,7 @@ function apiAdminEndpoints(app) {
|
|||||||
|
|
||||||
const { offset = 0 } = reqBody(request);
|
const { offset = 0 } = reqBody(request);
|
||||||
const chats = await WorkspaceChats.whereWithData(
|
const chats = await WorkspaceChats.whereWithData(
|
||||||
`id >= ${escape(offset)}`,
|
{ id: { gte: offset } },
|
||||||
20
|
20
|
||||||
);
|
);
|
||||||
const hasPages = (await WorkspaceChats.count()) > 20;
|
const hasPages = (await WorkspaceChats.count()) > 20;
|
||||||
@ -566,14 +565,14 @@ function apiAdminEndpoints(app) {
|
|||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
users_can_delete_workspaces:
|
users_can_delete_workspaces:
|
||||||
(await SystemSettings.get(`label = 'users_can_delete_workspaces'`))
|
(await SystemSettings.get({ label: "users_can_delete_workspaces" }))
|
||||||
?.value === "true",
|
?.value === "true",
|
||||||
limit_user_messages:
|
limit_user_messages:
|
||||||
(await SystemSettings.get(`label = 'limit_user_messages'`))?.value ===
|
(await SystemSettings.get({ label: "limit_user_messages" }))
|
||||||
"true",
|
?.value === "true",
|
||||||
message_limit:
|
message_limit:
|
||||||
Number(
|
Number(
|
||||||
(await SystemSettings.get(`label = 'message_limit'`))?.value
|
(await SystemSettings.get({ label: "message_limit" }))?.value
|
||||||
) || 10,
|
) || 10,
|
||||||
};
|
};
|
||||||
response.status(200).json({ settings });
|
response.status(200).json({ settings });
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
const { Document } = require("../../../models/documents");
|
const { Document } = require("../../../models/documents");
|
||||||
const { Telemetry } = require("../../../models/telemetry");
|
const { Telemetry } = require("../../../models/telemetry");
|
||||||
const { DocumentVectors } = require("../../../models/vectors");
|
const { DocumentVectors } = require("../../../models/vectors");
|
||||||
@ -154,7 +153,7 @@ function apiWorkspaceEndpoints(app) {
|
|||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
const { slug } = request.params;
|
const { slug } = request.params;
|
||||||
const workspace = await Workspace.get(`slug = ${escape(slug)}`);
|
const workspace = await Workspace.get({ slug });
|
||||||
response.status(200).json({ workspace });
|
response.status(200).json({ workspace });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e.message, e);
|
console.log(e.message, e);
|
||||||
@ -185,17 +184,17 @@ function apiWorkspaceEndpoints(app) {
|
|||||||
try {
|
try {
|
||||||
const { slug = "" } = request.params;
|
const { slug = "" } = request.params;
|
||||||
const VectorDb = getVectorDbClass();
|
const VectorDb = getVectorDbClass();
|
||||||
const workspace = await Workspace.get(`slug = ${escape(slug)}`);
|
const workspace = await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Workspace.delete(`id = ${Number(workspace.id)}`);
|
await WorkspaceChats.delete({ workspaceId: Number(workspace.id) });
|
||||||
await DocumentVectors.deleteForWorkspace(workspace.id);
|
await DocumentVectors.deleteForWorkspace(Number(workspace.id));
|
||||||
await Document.delete(`workspaceId = ${Number(workspace.id)}`);
|
await Document.delete({ workspaceId: Number(workspace.id) });
|
||||||
await WorkspaceChats.delete(`workspaceId = ${Number(workspace.id)}`);
|
await Workspace.delete({ id: Number(workspace.id) });
|
||||||
try {
|
try {
|
||||||
await VectorDb["delete-namespace"]({ namespace: slug });
|
await VectorDb["delete-namespace"]({ namespace: slug });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -270,7 +269,7 @@ function apiWorkspaceEndpoints(app) {
|
|||||||
try {
|
try {
|
||||||
const { slug = null } = request.params;
|
const { slug = null } = request.params;
|
||||||
const data = reqBody(request);
|
const data = reqBody(request);
|
||||||
const currWorkspace = await Workspace.get(`slug = ${escape(slug)}`);
|
const currWorkspace = await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!currWorkspace) {
|
if (!currWorkspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
@ -334,7 +333,7 @@ function apiWorkspaceEndpoints(app) {
|
|||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
const { slug } = request.params;
|
const { slug } = request.params;
|
||||||
const workspace = await Workspace.get(`slug = ${escape(slug)}`);
|
const workspace = await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
@ -409,7 +408,7 @@ function apiWorkspaceEndpoints(app) {
|
|||||||
try {
|
try {
|
||||||
const { slug = null } = request.params;
|
const { slug = null } = request.params;
|
||||||
const { adds = [], deletes = [] } = reqBody(request);
|
const { adds = [], deletes = [] } = reqBody(request);
|
||||||
const currWorkspace = await Workspace.get(`slug = ${escape(slug)}`);
|
const currWorkspace = await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!currWorkspace) {
|
if (!currWorkspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
|
@ -6,7 +6,6 @@ const { validatedRequest } = require("../utils/middleware/validatedRequest");
|
|||||||
const { WorkspaceChats } = require("../models/workspaceChats");
|
const { WorkspaceChats } = require("../models/workspaceChats");
|
||||||
const { SystemSettings } = require("../models/systemSettings");
|
const { SystemSettings } = require("../models/systemSettings");
|
||||||
const { Telemetry } = require("../models/telemetry");
|
const { Telemetry } = require("../models/telemetry");
|
||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
|
|
||||||
function chatEndpoints(app) {
|
function chatEndpoints(app) {
|
||||||
if (!app) return;
|
if (!app) return;
|
||||||
@ -19,9 +18,10 @@ function chatEndpoints(app) {
|
|||||||
const user = await userFromSession(request, response);
|
const user = await userFromSession(request, response);
|
||||||
const { slug } = request.params;
|
const { slug } = request.params;
|
||||||
const { message, mode = "query" } = reqBody(request);
|
const { message, mode = "query" } = reqBody(request);
|
||||||
|
|
||||||
const workspace = multiUserMode(response)
|
const workspace = multiUserMode(response)
|
||||||
? await Workspace.getWithUser(user, `slug = ${escape(slug)}`)
|
? await Workspace.getWithUser(user, { slug })
|
||||||
: await Workspace.get(`slug = ${escape(slug)}`);
|
: await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
@ -29,18 +29,25 @@ function chatEndpoints(app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (multiUserMode(response) && user.role !== "admin") {
|
if (multiUserMode(response) && user.role !== "admin") {
|
||||||
const limitMessages =
|
const limitMessagesSetting = await SystemSettings.get({
|
||||||
(await SystemSettings.get(`label = 'limit_user_messages'`))
|
label: "limit_user_messages",
|
||||||
?.value === "true";
|
});
|
||||||
|
const limitMessages = limitMessagesSetting?.value === "true";
|
||||||
|
|
||||||
if (limitMessages) {
|
if (limitMessages) {
|
||||||
const systemLimit = Number(
|
const messageLimitSetting = await SystemSettings.get({
|
||||||
(await SystemSettings.get(`label = 'message_limit'`))?.value
|
label: "message_limit",
|
||||||
);
|
});
|
||||||
|
const systemLimit = Number(messageLimitSetting?.value);
|
||||||
|
|
||||||
if (!!systemLimit) {
|
if (!!systemLimit) {
|
||||||
const currentChatCount = await WorkspaceChats.count(
|
const currentChatCount = await WorkspaceChats.count({
|
||||||
`user_id = ${user.id} AND createdAt > datetime(CURRENT_TIMESTAMP, '-1 days')`
|
user_id: user.id,
|
||||||
);
|
createdAt: {
|
||||||
|
gte: new Date(new Date() - 24 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (currentChatCount >= systemLimit) {
|
if (currentChatCount >= systemLimit) {
|
||||||
response.status(500).json({
|
response.status(500).json({
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
const { Invite } = require("../models/invite");
|
const { Invite } = require("../models/invite");
|
||||||
const { User } = require("../models/user");
|
const { User } = require("../models/user");
|
||||||
const { reqBody } = require("../utils/http");
|
const { reqBody } = require("../utils/http");
|
||||||
@ -9,7 +8,7 @@ function inviteEndpoints(app) {
|
|||||||
app.get("/invite/:code", async (request, response) => {
|
app.get("/invite/:code", async (request, response) => {
|
||||||
try {
|
try {
|
||||||
const { code } = request.params;
|
const { code } = request.params;
|
||||||
const invite = await Invite.get(`code = ${escape(code)}`);
|
const invite = await Invite.get({ code });
|
||||||
if (!invite) {
|
if (!invite) {
|
||||||
response.status(200).json({ invite: null, error: "Invite not found." });
|
response.status(200).json({ invite: null, error: "Invite not found." });
|
||||||
return;
|
return;
|
||||||
@ -35,7 +34,7 @@ function inviteEndpoints(app) {
|
|||||||
try {
|
try {
|
||||||
const { code } = request.params;
|
const { code } = request.params;
|
||||||
const userParams = reqBody(request);
|
const userParams = reqBody(request);
|
||||||
const invite = await Invite.get(`code = ${escape(code)}`);
|
const invite = await Invite.get({ code });
|
||||||
if (!invite || invite.status !== "pending") {
|
if (!invite || invite.status !== "pending") {
|
||||||
response
|
response
|
||||||
.status(200)
|
.status(200)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
process.env.NODE_ENV === "development"
|
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 { validateTablePragmas } = require("../utils/database");
|
|
||||||
const { viewLocalFiles } = require("../utils/files");
|
const { viewLocalFiles } = require("../utils/files");
|
||||||
const { exportData, unpackAndOverwriteImport } = require("../utils/files/data");
|
const { exportData, unpackAndOverwriteImport } = require("../utils/files/data");
|
||||||
const {
|
const {
|
||||||
@ -38,7 +37,6 @@ const {
|
|||||||
const { Telemetry } = require("../models/telemetry");
|
const { Telemetry } = require("../models/telemetry");
|
||||||
const { WelcomeMessages } = require("../models/welcomeMessages");
|
const { WelcomeMessages } = require("../models/welcomeMessages");
|
||||||
const { ApiKey } = require("../models/apiKeys");
|
const { ApiKey } = require("../models/apiKeys");
|
||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
|
|
||||||
function systemEndpoints(app) {
|
function systemEndpoints(app) {
|
||||||
if (!app) return;
|
if (!app) return;
|
||||||
@ -48,7 +46,10 @@ function systemEndpoints(app) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.get("/migrate", async (_, response) => {
|
app.get("/migrate", async (_, response) => {
|
||||||
await validateTablePragmas(true);
|
const execSync = require("child_process").execSync;
|
||||||
|
execSync("npx prisma migrate deploy --schema=./prisma/schema.prisma", {
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
response.sendStatus(200);
|
response.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -97,7 +98,7 @@ function systemEndpoints(app) {
|
|||||||
try {
|
try {
|
||||||
if (await SystemSettings.isMultiUserMode()) {
|
if (await SystemSettings.isMultiUserMode()) {
|
||||||
const { username, password } = reqBody(request);
|
const { username, password } = reqBody(request);
|
||||||
const existingUser = await User.get(`username = ${escape(username)}`);
|
const existingUser = await User.get({ username });
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
response.status(200).json({
|
response.status(200).json({
|
||||||
@ -524,7 +525,7 @@ function systemEndpoints(app) {
|
|||||||
return response.sendStatus(401).end();
|
return response.sendStatus(401).end();
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiKey = await ApiKey.get("id IS NOT NULL");
|
const apiKey = await ApiKey.get({});
|
||||||
return response.status(200).json({
|
return response.status(200).json({
|
||||||
apiKey,
|
apiKey,
|
||||||
error: null,
|
error: null,
|
||||||
|
@ -13,7 +13,6 @@ const {
|
|||||||
const { validatedRequest } = require("../utils/middleware/validatedRequest");
|
const { validatedRequest } = require("../utils/middleware/validatedRequest");
|
||||||
const { SystemSettings } = require("../models/systemSettings");
|
const { SystemSettings } = require("../models/systemSettings");
|
||||||
const { Telemetry } = require("../models/telemetry");
|
const { Telemetry } = require("../models/telemetry");
|
||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
const { handleUploads } = setupMulter();
|
const { handleUploads } = setupMulter();
|
||||||
|
|
||||||
function workspaceEndpoints(app) {
|
function workspaceEndpoints(app) {
|
||||||
@ -45,8 +44,8 @@ function workspaceEndpoints(app) {
|
|||||||
const { slug = null } = request.params;
|
const { slug = null } = request.params;
|
||||||
const data = reqBody(request);
|
const data = reqBody(request);
|
||||||
const currWorkspace = multiUserMode(response)
|
const currWorkspace = multiUserMode(response)
|
||||||
? await Workspace.getWithUser(user, `slug = ${escape(slug)}`)
|
? await Workspace.getWithUser(user, { slug })
|
||||||
: await Workspace.get(`slug = ${escape(slug)}`);
|
: await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!currWorkspace) {
|
if (!currWorkspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
@ -106,8 +105,8 @@ function workspaceEndpoints(app) {
|
|||||||
const { slug = null } = request.params;
|
const { slug = null } = request.params;
|
||||||
const { adds = [], deletes = [] } = reqBody(request);
|
const { adds = [], deletes = [] } = reqBody(request);
|
||||||
const currWorkspace = multiUserMode(response)
|
const currWorkspace = multiUserMode(response)
|
||||||
? await Workspace.getWithUser(user, `slug = ${escape(slug)}`)
|
? await Workspace.getWithUser(user, { slug })
|
||||||
: await Workspace.get(`slug = ${escape(slug)}`);
|
: await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!currWorkspace) {
|
if (!currWorkspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
@ -116,9 +115,7 @@ function workspaceEndpoints(app) {
|
|||||||
|
|
||||||
await Document.removeDocuments(currWorkspace, deletes);
|
await Document.removeDocuments(currWorkspace, deletes);
|
||||||
await Document.addDocuments(currWorkspace, adds);
|
await Document.addDocuments(currWorkspace, adds);
|
||||||
const updatedWorkspace = await Workspace.get(
|
const updatedWorkspace = await Workspace.get({ id: currWorkspace.id });
|
||||||
`id = ${currWorkspace.id}`
|
|
||||||
);
|
|
||||||
response.status(200).json({ workspace: updatedWorkspace });
|
response.status(200).json({ workspace: updatedWorkspace });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e.message, e);
|
console.log(e.message, e);
|
||||||
@ -136,8 +133,8 @@ function workspaceEndpoints(app) {
|
|||||||
const user = await userFromSession(request, response);
|
const user = await userFromSession(request, response);
|
||||||
const VectorDb = getVectorDbClass();
|
const VectorDb = getVectorDbClass();
|
||||||
const workspace = multiUserMode(response)
|
const workspace = multiUserMode(response)
|
||||||
? await Workspace.getWithUser(user, `slug = ${escape(slug)}`)
|
? await Workspace.getWithUser(user, { slug })
|
||||||
: await Workspace.get(`slug = ${escape(slug)}`);
|
: await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
@ -146,7 +143,7 @@ function workspaceEndpoints(app) {
|
|||||||
|
|
||||||
if (multiUserMode(response) && user.role !== "admin") {
|
if (multiUserMode(response) && user.role !== "admin") {
|
||||||
const canDelete =
|
const canDelete =
|
||||||
(await SystemSettings.get(`label = 'users_can_delete_workspaces'`))
|
(await SystemSettings.get({ label: "users_can_delete_workspaces" }))
|
||||||
?.value === "true";
|
?.value === "true";
|
||||||
if (!canDelete) {
|
if (!canDelete) {
|
||||||
response.sendStatus(500).end();
|
response.sendStatus(500).end();
|
||||||
@ -154,10 +151,11 @@ function workspaceEndpoints(app) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Workspace.delete(`id = ${Number(workspace.id)}`);
|
await WorkspaceChats.delete({ workspaceId: Number(workspace.id) });
|
||||||
await DocumentVectors.deleteForWorkspace(workspace.id);
|
await DocumentVectors.deleteForWorkspace(workspace.id);
|
||||||
await Document.delete(`workspaceId = ${Number(workspace.id)}`);
|
await Document.delete({ workspaceId: Number(workspace.id) });
|
||||||
await WorkspaceChats.delete(`workspaceId = ${Number(workspace.id)}`);
|
await Workspace.delete({ id: Number(workspace.id) });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await VectorDb["delete-namespace"]({ namespace: slug });
|
await VectorDb["delete-namespace"]({ namespace: slug });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -190,8 +188,8 @@ function workspaceEndpoints(app) {
|
|||||||
const { slug } = request.params;
|
const { slug } = request.params;
|
||||||
const user = await userFromSession(request, response);
|
const user = await userFromSession(request, response);
|
||||||
const workspace = multiUserMode(response)
|
const workspace = multiUserMode(response)
|
||||||
? await Workspace.getWithUser(user, `slug = ${escape(slug)}`)
|
? await Workspace.getWithUser(user, { slug })
|
||||||
: await Workspace.get(`slug = ${escape(slug)}`);
|
: await Workspace.get({ slug });
|
||||||
|
|
||||||
response.status(200).json({ workspace });
|
response.status(200).json({ workspace });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -208,8 +206,8 @@ function workspaceEndpoints(app) {
|
|||||||
const { slug } = request.params;
|
const { slug } = request.params;
|
||||||
const user = await userFromSession(request, response);
|
const user = await userFromSession(request, response);
|
||||||
const workspace = multiUserMode(response)
|
const workspace = multiUserMode(response)
|
||||||
? await Workspace.getWithUser(user, `slug = ${escape(slug)}`)
|
? await Workspace.getWithUser(user, { slug })
|
||||||
: await Workspace.get(`slug = ${escape(slug)}`);
|
: await Workspace.get({ slug });
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
response.sendStatus(400).end();
|
response.sendStatus(400).end();
|
||||||
|
@ -12,12 +12,12 @@ const { systemEndpoints } = require("./endpoints/system");
|
|||||||
const { workspaceEndpoints } = require("./endpoints/workspaces");
|
const { workspaceEndpoints } = require("./endpoints/workspaces");
|
||||||
const { chatEndpoints } = require("./endpoints/chat");
|
const { chatEndpoints } = require("./endpoints/chat");
|
||||||
const { getVectorDbClass } = require("./utils/helpers");
|
const { getVectorDbClass } = require("./utils/helpers");
|
||||||
const { validateTablePragmas, setupTelemetry } = require("./utils/database");
|
|
||||||
const { adminEndpoints } = require("./endpoints/admin");
|
const { adminEndpoints } = require("./endpoints/admin");
|
||||||
const { inviteEndpoints } = require("./endpoints/invite");
|
const { inviteEndpoints } = require("./endpoints/invite");
|
||||||
const { utilEndpoints } = require("./endpoints/utils");
|
const { utilEndpoints } = require("./endpoints/utils");
|
||||||
const { Telemetry } = require("./models/telemetry");
|
const { Telemetry } = require("./models/telemetry");
|
||||||
const { developerEndpoints } = require("./endpoints/api");
|
const { developerEndpoints } = require("./endpoints/api");
|
||||||
|
const setupTelemetry = require("./utils/telemetry");
|
||||||
const app = express();
|
const app = express();
|
||||||
const apiRouter = express.Router();
|
const apiRouter = express.Router();
|
||||||
const FILE_LIMIT = "3GB";
|
const FILE_LIMIT = "3GB";
|
||||||
@ -90,7 +90,6 @@ app.all("*", function (_, response) {
|
|||||||
|
|
||||||
app
|
app
|
||||||
.listen(process.env.SERVER_PORT || 3001, async () => {
|
.listen(process.env.SERVER_PORT || 3001, async () => {
|
||||||
await validateTablePragmas();
|
|
||||||
await setupTelemetry();
|
await setupTelemetry();
|
||||||
console.log(
|
console.log(
|
||||||
`Example app listening on port ${process.env.SERVER_PORT || 3001}`
|
`Example app listening on port ${process.env.SERVER_PORT || 3001}`
|
||||||
|
@ -1,122 +1,83 @@
|
|||||||
const { Telemetry } = require("./telemetry");
|
const { Telemetry } = require("./telemetry");
|
||||||
|
const prisma = require("../utils/prisma");
|
||||||
|
|
||||||
const ApiKey = {
|
const ApiKey = {
|
||||||
tablename: "api_keys",
|
tablename: "api_keys",
|
||||||
writable: [],
|
writable: [],
|
||||||
colsInit: `
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
secret TEXT UNIQUE,
|
|
||||||
createdBy INTEGER DEFAULT NULL,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
const { checkForMigrations } = require("../utils/database");
|
|
||||||
console.log(`\x1b[34m[MIGRATING]\x1b[0m Checking for ApiKey migrations`);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
|
||||||
migrations: function () {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
makeSecret: () => {
|
makeSecret: () => {
|
||||||
const uuidAPIKey = require("uuid-apikey");
|
const uuidAPIKey = require("uuid-apikey");
|
||||||
return uuidAPIKey.create().apiKey;
|
return uuidAPIKey.create().apiKey;
|
||||||
},
|
},
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
create: async function (createdByUserId = null) {
|
create: async function (createdByUserId = null) {
|
||||||
const db = await this.db();
|
try {
|
||||||
const { id, success, message } = await db
|
const apiKey = await prisma.api_keys.create({
|
||||||
.run(`INSERT INTO ${this.tablename} (secret, createdBy) VALUES(?, ?)`, [
|
data: {
|
||||||
this.makeSecret(),
|
secret: this.makeSecret(),
|
||||||
createdByUserId,
|
createdBy: createdByUserId,
|
||||||
])
|
},
|
||||||
.then((res) => {
|
|
||||||
return { id: res.lastID, success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { id: null, success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
db.close();
|
|
||||||
console.error("FAILED TO CREATE API KEY.", message);
|
|
||||||
return { apiKey: null, error: message };
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKey = await db.get(
|
|
||||||
`SELECT * FROM ${this.tablename} WHERE id = ${id} `
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
await Telemetry.sendTelemetry("api_key_created");
|
await Telemetry.sendTelemetry("api_key_created");
|
||||||
return { apiKey, error: null };
|
return { apiKey, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO CREATE API KEY.", error.message);
|
||||||
|
return { apiKey: null, error: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
get: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
const result = await db
|
|
||||||
.get(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : clause}`
|
|
||||||
)
|
|
||||||
.then((res) => res || null);
|
|
||||||
if (!result) return null;
|
|
||||||
db.close();
|
|
||||||
return { ...result };
|
|
||||||
},
|
|
||||||
count: async function (clause = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const { count } = await db.get(
|
|
||||||
`SELECT COUNT(*) as count FROM ${this.tablename} ${
|
|
||||||
clause ? `WHERE ${clause}` : ""
|
|
||||||
} `
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
get: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const apiKey = await prisma.api_keys.findFirst({ where: clause });
|
||||||
|
return apiKey;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO GET API KEY.", error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
count: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const count = await prisma.api_keys.count({ where: clause });
|
||||||
return count;
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO COUNT API KEYS.", error.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
delete: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
await db.get(
|
|
||||||
`DELETE FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
delete: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
await prisma.api_keys.deleteMany({ where: clause });
|
||||||
return true;
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO DELETE API KEY.", error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
where: async function (clause = "", limit = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return results;
|
where: async function (clause = {}, limit) {
|
||||||
|
try {
|
||||||
|
const apiKeys = await prisma.api_keys.findMany({
|
||||||
|
where: clause,
|
||||||
|
take: limit,
|
||||||
|
});
|
||||||
|
return apiKeys;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO GET API KEYS.", error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
whereWithUser: async function (clause = "", limit = null) {
|
|
||||||
|
whereWithUser: async function (clause = {}, limit) {
|
||||||
|
try {
|
||||||
const { User } = require("./user");
|
const { User } = require("./user");
|
||||||
const apiKeys = await this.where(clause, limit);
|
const apiKeys = await this.where(clause, limit);
|
||||||
|
|
||||||
for (const apiKey of apiKeys) {
|
for (const apiKey of apiKeys) {
|
||||||
if (!apiKey.createdBy) continue;
|
if (!apiKey.createdBy) continue;
|
||||||
const user = await User.get(`id = ${apiKey.createdBy}`);
|
const user = await User.get({ id: apiKey.createdBy });
|
||||||
if (!user) continue;
|
if (!user) continue;
|
||||||
|
|
||||||
apiKey.createdBy = {
|
apiKey.createdBy = {
|
||||||
@ -127,6 +88,10 @@ const ApiKey = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return apiKeys;
|
return apiKeys;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO GET API KEYS WITH USER.", error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,76 +1,42 @@
|
|||||||
const { fileData } = require("../utils/files");
|
const { fileData } = require("../utils/files");
|
||||||
const { v4: uuidv4 } = require("uuid");
|
const { v4: uuidv4 } = require("uuid");
|
||||||
const { getVectorDbClass } = require("../utils/helpers");
|
const { getVectorDbClass } = require("../utils/helpers");
|
||||||
const { checkForMigrations } = require("../utils/database");
|
const prisma = require("../utils/prisma");
|
||||||
const { Telemetry } = require("./telemetry");
|
const { Telemetry } = require("./telemetry");
|
||||||
|
|
||||||
const Document = {
|
const Document = {
|
||||||
tablename: "workspace_documents",
|
|
||||||
colsInit: `
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
docId TEXT NOT NULL UNIQUE,
|
|
||||||
filename TEXT NOT NULL,
|
|
||||||
docpath TEXT NOT NULL,
|
|
||||||
workspaceId INTEGER NOT NULL,
|
|
||||||
metadata TEXT NULL,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
console.log(`\x1b[34m[MIGRATING]\x1b[0m Checking for Document migrations`);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
|
||||||
migrations: function () {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
forWorkspace: async function (workspaceId = null) {
|
forWorkspace: async function (workspaceId = null) {
|
||||||
if (!workspaceId) return [];
|
if (!workspaceId) return [];
|
||||||
return await this.where(`workspaceId = ${workspaceId}`);
|
return await prisma.workspace_documents.findMany({
|
||||||
|
where: { workspaceId },
|
||||||
|
});
|
||||||
},
|
},
|
||||||
delete: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
await db.get(`DELETE FROM ${this.tablename} WHERE ${clause}`);
|
|
||||||
db.close();
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
where: async function (clause = "", limit = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
|
|
||||||
db.close();
|
delete: async function (clause = {}) {
|
||||||
return results;
|
try {
|
||||||
|
await prisma.workspace_documents.deleteMany({ where: clause });
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
firstWhere: async function (clause = "") {
|
|
||||||
const results = await this.where(clause);
|
firstWhere: async function (clause = {}) {
|
||||||
return results.length > 0 ? results[0] : null;
|
try {
|
||||||
|
const document = await prisma.workspace_documents.findFirst({
|
||||||
|
where: clause,
|
||||||
|
});
|
||||||
|
return document || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addDocuments: async function (workspace, additions = []) {
|
addDocuments: async function (workspace, additions = []) {
|
||||||
const VectorDb = getVectorDbClass();
|
const VectorDb = getVectorDbClass();
|
||||||
if (additions.length === 0) return;
|
if (additions.length === 0) return;
|
||||||
const insertParams = [];
|
|
||||||
|
|
||||||
for (const path of additions) {
|
for (const path of additions) {
|
||||||
const data = await fileData(path);
|
const data = await fileData(path);
|
||||||
@ -82,7 +48,7 @@ const Document = {
|
|||||||
docId,
|
docId,
|
||||||
filename: path.split("/")[1],
|
filename: path.split("/")[1],
|
||||||
docpath: path,
|
docpath: path,
|
||||||
workspaceId: Number(workspace.id),
|
workspaceId: workspace.id,
|
||||||
metadata: JSON.stringify(metadata),
|
metadata: JSON.stringify(metadata),
|
||||||
};
|
};
|
||||||
const vectorized = await VectorDb.addDocumentToNamespace(
|
const vectorized = await VectorDb.addDocumentToNamespace(
|
||||||
@ -95,72 +61,44 @@ const Document = {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
insertParams.push([
|
|
||||||
docId,
|
|
||||||
newDoc.filename,
|
|
||||||
newDoc.docpath,
|
|
||||||
newDoc.workspaceId,
|
|
||||||
newDoc.metadata,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const db = await this.db();
|
|
||||||
const stmt = await db.prepare(
|
|
||||||
`INSERT INTO ${this.tablename} (docId, filename, docpath, workspaceId, metadata) VALUES (?,?,?,?,?)`
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.exec("BEGIN TRANSACTION");
|
|
||||||
try {
|
try {
|
||||||
for (const params of insertParams) {
|
await prisma.workspace_documents.create({ data: newDoc });
|
||||||
await stmt.run(params);
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
}
|
}
|
||||||
await db.exec("COMMIT");
|
|
||||||
} catch {
|
|
||||||
await db.exec("ROLLBACK");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.finalize();
|
|
||||||
db.close();
|
|
||||||
await Telemetry.sendTelemetry("documents_embedded_in_workspace", {
|
await Telemetry.sendTelemetry("documents_embedded_in_workspace", {
|
||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
|
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
removeDocuments: async function (workspace, removals = []) {
|
removeDocuments: async function (workspace, removals = []) {
|
||||||
const VectorDb = getVectorDbClass();
|
const VectorDb = getVectorDbClass();
|
||||||
const deleteParams = [];
|
|
||||||
if (removals.length === 0) return;
|
if (removals.length === 0) return;
|
||||||
|
|
||||||
for (const path of removals) {
|
for (const path of removals) {
|
||||||
const document = await this.firstWhere(
|
const document = await this.firstWhere({
|
||||||
`docPath = '${path}' AND workspaceId = ${workspace.id}`
|
docpath: path,
|
||||||
);
|
workspaceId: workspace.id,
|
||||||
|
});
|
||||||
if (!document) continue;
|
if (!document) continue;
|
||||||
await VectorDb.deleteDocumentFromNamespace(
|
await VectorDb.deleteDocumentFromNamespace(
|
||||||
workspace.slug,
|
workspace.slug,
|
||||||
document.docId
|
document.docId
|
||||||
);
|
);
|
||||||
deleteParams.push([path, workspace.id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const db = await this.db();
|
|
||||||
const stmt = await db.prepare(
|
|
||||||
`DELETE FROM ${this.tablename} WHERE docpath = ? AND workspaceId = ?`
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.exec("BEGIN TRANSACTION");
|
|
||||||
try {
|
try {
|
||||||
for (const params of deleteParams) {
|
await prisma.workspace_documents.delete({
|
||||||
await stmt.run(params);
|
where: { id: document.id, workspaceId: workspace.id },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
}
|
}
|
||||||
await db.exec("COMMIT");
|
|
||||||
} catch {
|
|
||||||
await db.exec("ROLLBACK");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.finalize();
|
|
||||||
db.close();
|
|
||||||
await Telemetry.sendTelemetry("documents_removed_in_workspace", {
|
await Telemetry.sendTelemetry("documents_removed_in_workspace", {
|
||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
|
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
|
||||||
|
@ -1,192 +1,122 @@
|
|||||||
const { escape } = require("sqlstring-sqlite");
|
const prisma = require("../utils/prisma");
|
||||||
|
|
||||||
const Invite = {
|
const Invite = {
|
||||||
tablename: "invites",
|
|
||||||
writable: [],
|
|
||||||
colsInit: `
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
code TEXT UNIQUE NOT NULL,
|
|
||||||
status TEXT NOT NULL DEFAULT "pending",
|
|
||||||
claimedBy INTEGER DEFAULT NULL,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
createdBy INTEGER NOT NULL,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
const { checkForMigrations } = require("../utils/database");
|
|
||||||
console.log(`\x1b[34m[MIGRATING]\x1b[0m Checking for Invites migrations`);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
|
||||||
migrations: function () {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
makeCode: () => {
|
makeCode: () => {
|
||||||
const uuidAPIKey = require("uuid-apikey");
|
const uuidAPIKey = require("uuid-apikey");
|
||||||
return uuidAPIKey.create().apiKey;
|
return uuidAPIKey.create().apiKey;
|
||||||
},
|
},
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
create: async function (createdByUserId = 0) {
|
create: async function (createdByUserId = 0) {
|
||||||
const db = await this.db();
|
try {
|
||||||
const { id, success, message } = await db
|
const invite = await prisma.invites.create({
|
||||||
.run(`INSERT INTO ${this.tablename} (code, createdBy) VALUES(?, ?)`, [
|
data: {
|
||||||
this.makeCode(),
|
code: this.makeCode(),
|
||||||
createdByUserId,
|
createdBy: createdByUserId,
|
||||||
])
|
},
|
||||||
.then((res) => {
|
|
||||||
return { id: res.lastID, success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { id: null, success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
db.close();
|
|
||||||
console.error("FAILED TO CREATE USER.", message);
|
|
||||||
return { invite: null, error: message };
|
|
||||||
}
|
|
||||||
|
|
||||||
const invite = await db.get(
|
|
||||||
`SELECT * FROM ${this.tablename} WHERE id = ${id} `
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return { invite, error: null };
|
return { invite, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO CREATE INVITE.", error.message);
|
||||||
|
return { invite: null, error: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivate: async function (inviteId = null) {
|
deactivate: async function (inviteId = null) {
|
||||||
const invite = await this.get(`id = ${escape(inviteId)}`);
|
try {
|
||||||
if (!invite) return { success: false, error: "Invite does not exist." };
|
const invite = await prisma.invites.update({
|
||||||
if (invite.status !== "pending")
|
where: { id: Number(inviteId) },
|
||||||
return { success: false, error: "Invite is not in pending status." };
|
data: { status: "disabled" },
|
||||||
|
|
||||||
const db = await this.db();
|
|
||||||
const { success, message } = await db
|
|
||||||
.run(`UPDATE ${this.tablename} SET status=? WHERE id=?`, [
|
|
||||||
"disabled",
|
|
||||||
inviteId,
|
|
||||||
])
|
|
||||||
.then(() => {
|
|
||||||
return { success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
db.close();
|
|
||||||
if (!success) {
|
|
||||||
console.error(message);
|
|
||||||
return { success: false, error: message };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, error: null };
|
return { success: true, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
markClaimed: async function (inviteId = null, user) {
|
markClaimed: async function (inviteId = null, user) {
|
||||||
const invite = await this.get(`id = ${escape(inviteId)}`);
|
try {
|
||||||
if (!invite) return { success: false, error: "Invite does not exist." };
|
const invite = await prisma.invites.update({
|
||||||
if (invite.status !== "pending")
|
where: { id: Number(inviteId) },
|
||||||
return { success: false, error: "Invite is not in pending status." };
|
data: { status: "claimed", claimedBy: user.id },
|
||||||
|
|
||||||
const db = await this.db();
|
|
||||||
const { success, message } = await db
|
|
||||||
.run(`UPDATE ${this.tablename} SET status=?,claimedBy=? WHERE id=?`, [
|
|
||||||
"claimed",
|
|
||||||
user.id,
|
|
||||||
inviteId,
|
|
||||||
])
|
|
||||||
.then(() => {
|
|
||||||
return { success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
db.close();
|
|
||||||
if (!success) {
|
|
||||||
console.error(message);
|
|
||||||
return { success: false, error: message };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, error: null };
|
return { success: true, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
get: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
const result = await db
|
|
||||||
.get(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : clause}`
|
|
||||||
)
|
|
||||||
.then((res) => res || null);
|
|
||||||
if (!result) return null;
|
|
||||||
db.close();
|
|
||||||
return { ...result };
|
|
||||||
},
|
|
||||||
count: async function (clause = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const { count } = await db.get(
|
|
||||||
`SELECT COUNT(*) as count FROM ${this.tablename} ${
|
|
||||||
clause ? `WHERE ${clause}` : ""
|
|
||||||
} `
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
get: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const invite = await prisma.invites.findFirst({ where: clause });
|
||||||
|
return invite || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
count: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const count = await prisma.invites.count({ where: clause });
|
||||||
return count;
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
delete: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
await db.get(`DELETE FROM ${this.tablename} WHERE ${clause}`);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
delete: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
await prisma.invites.deleteMany({ where: clause });
|
||||||
return true;
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
where: async function (clause = "", limit = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return results;
|
where: async function (clause = {}, limit) {
|
||||||
|
try {
|
||||||
|
const invites = await prisma.invites.findMany({
|
||||||
|
where: clause,
|
||||||
|
take: limit || undefined,
|
||||||
|
});
|
||||||
|
return invites;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
whereWithUsers: async function (clause = "", limit = null) {
|
|
||||||
|
whereWithUsers: async function (clause = {}, limit) {
|
||||||
const { User } = require("./user");
|
const { User } = require("./user");
|
||||||
const results = await this.where(clause, limit);
|
try {
|
||||||
for (const invite of results) {
|
const invites = await this.where(clause, limit);
|
||||||
if (!!invite.claimedBy) {
|
for (const invite of invites) {
|
||||||
const acceptedUser = await User.get(`id = ${invite.claimedBy}`);
|
if (invite.claimedBy) {
|
||||||
|
const acceptedUser = await User.get({ id: invite.claimedBy });
|
||||||
invite.claimedBy = {
|
invite.claimedBy = {
|
||||||
id: acceptedUser?.id,
|
id: acceptedUser?.id,
|
||||||
username: acceptedUser?.username,
|
username: acceptedUser?.username,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!!invite.createdBy) {
|
if (invite.createdBy) {
|
||||||
const createdUser = await User.get(`id = ${invite.createdBy}`);
|
const createdUser = await User.get({ id: invite.createdBy });
|
||||||
invite.createdBy = {
|
invite.createdBy = {
|
||||||
id: createdUser?.id,
|
id: createdUser?.id,
|
||||||
username: createdUser?.username,
|
username: createdUser?.username,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results;
|
return invites;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.Invite = Invite;
|
module.exports = { Invite };
|
||||||
|
@ -2,6 +2,8 @@ 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 prisma = require("../utils/prisma");
|
||||||
|
|
||||||
const SystemSettings = {
|
const SystemSettings = {
|
||||||
supportedFields: [
|
supportedFields: [
|
||||||
"multi_user_mode",
|
"multi_user_mode",
|
||||||
@ -11,44 +13,6 @@ const SystemSettings = {
|
|||||||
"logo_filename",
|
"logo_filename",
|
||||||
"telemetry_id",
|
"telemetry_id",
|
||||||
],
|
],
|
||||||
privateField: [],
|
|
||||||
tablename: "system_settings",
|
|
||||||
colsInit: `
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
label TEXT UNIQUE NOT NULL,
|
|
||||||
value TEXT,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
const { checkForMigrations } = require("../utils/database");
|
|
||||||
console.log(
|
|
||||||
`\x1b[34m[MIGRATING]\x1b[0m Checking for System Setting migrations`
|
|
||||||
);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
|
||||||
migrations: function () {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
currentSettings: async function () {
|
currentSettings: async function () {
|
||||||
const llmProvider = process.env.LLM_PROVIDER || "openai";
|
const llmProvider = process.env.LLM_PROVIDER || "openai";
|
||||||
const vectorDB = process.env.VECTOR_DB || "pinecone";
|
const vectorDB = process.env.VECTOR_DB || "pinecone";
|
||||||
@ -102,89 +66,83 @@ const SystemSettings = {
|
|||||||
: {}),
|
: {}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
get: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
const result = await db
|
|
||||||
.get(`SELECT * FROM ${this.tablename} WHERE ${clause}`)
|
|
||||||
.then((res) => res || null);
|
|
||||||
if (!result) return null;
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return result;
|
get: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const setting = await prisma.system_settings.findFirst({ where: clause });
|
||||||
|
return setting || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
where: async function (clause = null, limit = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return results;
|
where: async function (clause = {}, limit) {
|
||||||
|
try {
|
||||||
|
const settings = await prisma.system_settings.findMany({
|
||||||
|
where: clause,
|
||||||
|
take: limit || undefined,
|
||||||
|
});
|
||||||
|
return settings;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateSettings: async function (updates = {}) {
|
updateSettings: async function (updates = {}) {
|
||||||
const validConfigKeys = Object.keys(updates).filter((key) =>
|
try {
|
||||||
this.supportedFields.includes(key)
|
const updatePromises = Object.keys(updates)
|
||||||
);
|
.filter((key) => this.supportedFields.includes(key))
|
||||||
for (const key of validConfigKeys) {
|
.map((key) => {
|
||||||
const existingRecord = await this.get(`label = '${key}'`);
|
return prisma.system_settings.upsert({
|
||||||
if (!existingRecord) {
|
where: { label: key },
|
||||||
const db = await this.db();
|
update: {
|
||||||
const value = updates[key] === null ? null : String(updates[key]);
|
value: updates[key] === null ? null : String(updates[key]),
|
||||||
const { success, message } = await db
|
},
|
||||||
.run(`INSERT INTO ${this.tablename} (label, value) VALUES (?, ?)`, [
|
create: {
|
||||||
key,
|
label: key,
|
||||||
value,
|
value: updates[key] === null ? null : String(updates[key]),
|
||||||
])
|
},
|
||||||
.then((res) => {
|
|
||||||
return { id: res.lastID, success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { id: null, success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
db.close();
|
|
||||||
if (!success) {
|
|
||||||
console.error("FAILED TO ADD SYSTEM CONFIG OPTION", message);
|
|
||||||
return { success: false, error: message };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const db = await this.db();
|
|
||||||
const value = updates[key] === null ? null : String(updates[key]);
|
|
||||||
const { success, message } = await db
|
|
||||||
.run(`UPDATE ${this.tablename} SET label=?,value=? WHERE id = ?`, [
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
existingRecord.id,
|
|
||||||
])
|
|
||||||
.then(() => {
|
|
||||||
return { success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
db.close();
|
await Promise.all(updatePromises);
|
||||||
if (!success) {
|
|
||||||
console.error("FAILED TO UPDATE SYSTEM CONFIG OPTION", message);
|
|
||||||
return { success: false, error: message };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { success: true, error: null };
|
return { success: true, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO UPDATE SYSTEM SETTINGS", error.message);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isMultiUserMode: async function () {
|
isMultiUserMode: async function () {
|
||||||
return (await this.get(`label = 'multi_user_mode'`))?.value === "true";
|
try {
|
||||||
|
const setting = await this.get({ label: "multi_user_mode" });
|
||||||
|
return setting?.value === "true";
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
currentLogoFilename: async function () {
|
currentLogoFilename: async function () {
|
||||||
const result = await this.get(`label = 'logo_filename'`);
|
try {
|
||||||
return result ? result.value : null;
|
const setting = await this.get({ label: "logo_filename" });
|
||||||
|
return setting?.value || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
canDeleteWorkspaces: async function () {
|
canDeleteWorkspaces: async function () {
|
||||||
return (
|
try {
|
||||||
(await this.get(`label = 'users_can_delete_workspaces'`))?.value ===
|
const setting = await this.get({ label: "users_can_delete_workspaces" });
|
||||||
"true"
|
return setting?.value === "true";
|
||||||
);
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,26 +6,28 @@ const Telemetry = {
|
|||||||
pubkey: "phc_9qu7QLpV8L84P3vFmEiZxL020t2EqIubP7HHHxrSsqS",
|
pubkey: "phc_9qu7QLpV8L84P3vFmEiZxL020t2EqIubP7HHHxrSsqS",
|
||||||
stubDevelopmentEvents: true, // [DO NOT TOUCH] Core team only.
|
stubDevelopmentEvents: true, // [DO NOT TOUCH] Core team only.
|
||||||
label: "telemetry_id",
|
label: "telemetry_id",
|
||||||
|
|
||||||
id: async function () {
|
id: async function () {
|
||||||
const result = await SystemSettings.get(`label = '${this.label}'`);
|
const result = await SystemSettings.get({ label: this.label });
|
||||||
if (!!result?.value) return result.value;
|
return result?.value || null;
|
||||||
return result?.value;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
connect: async function () {
|
connect: async function () {
|
||||||
const client = this.client();
|
const client = this.client();
|
||||||
const distinctId = await this.findOrCreateId();
|
const distinctId = await this.findOrCreateId();
|
||||||
return { client, distinctId };
|
return { client, distinctId };
|
||||||
},
|
},
|
||||||
|
|
||||||
isDev: function () {
|
isDev: function () {
|
||||||
if (process.env.NODE_ENV === "development")
|
return process.env.NODE_ENV === "development" && this.stubDevelopmentEvents;
|
||||||
return this.stubDevelopmentEvents;
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
client: function () {
|
client: function () {
|
||||||
if (process.env.DISABLE_TELEMETRY === "true" || this.isDev()) return null;
|
if (process.env.DISABLE_TELEMETRY === "true" || this.isDev()) return null;
|
||||||
const { PostHog } = require("posthog-node");
|
const { PostHog } = require("posthog-node");
|
||||||
return new PostHog(this.pubkey);
|
return new PostHog(this.pubkey);
|
||||||
},
|
},
|
||||||
|
|
||||||
sendTelemetry: async function (event, properties = {}) {
|
sendTelemetry: async function (event, properties = {}) {
|
||||||
try {
|
try {
|
||||||
const { client, distinctId } = await this.connect();
|
const { client, distinctId } = await this.connect();
|
||||||
@ -43,22 +45,25 @@ const Telemetry = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
flush: async function () {
|
flush: async function () {
|
||||||
const { client } = this.client();
|
const client = this.client();
|
||||||
if (!client) return;
|
if (!client) return;
|
||||||
await client.shutdownAsync();
|
await client.shutdownAsync();
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setUid: async function () {
|
setUid: async function () {
|
||||||
const newId = v4();
|
const newId = v4();
|
||||||
await SystemSettings.updateSettings({ [this.label]: newId });
|
await SystemSettings.updateSettings({ [this.label]: newId });
|
||||||
return newId;
|
return newId;
|
||||||
},
|
},
|
||||||
|
|
||||||
findOrCreateId: async function () {
|
findOrCreateId: async function () {
|
||||||
const currentId = await this.id();
|
let currentId = await this.id();
|
||||||
if (!!currentId) return currentId;
|
if (currentId) return currentId;
|
||||||
const newId = await this.setUid();
|
|
||||||
return newId;
|
currentId = await this.setUid();
|
||||||
|
return currentId;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,171 +1,78 @@
|
|||||||
const { escape } = require("sqlstring-sqlite");
|
const prisma = require("../utils/prisma");
|
||||||
|
const bcrypt = require("bcrypt");
|
||||||
|
|
||||||
const User = {
|
const User = {
|
||||||
tablename: "users",
|
create: async function ({ username, password, role = "default" }) {
|
||||||
writable: [],
|
try {
|
||||||
colsInit: `
|
const hashedPassword = bcrypt.hashSync(password, 10);
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
const user = await prisma.users.create({
|
||||||
username TEXT UNIQUE,
|
data: {
|
||||||
password TEXT NOT NULL,
|
username,
|
||||||
role TEXT NOT NULL DEFAULT "default",
|
password: hashedPassword,
|
||||||
suspended INTEGER NOT NULL DEFAULT 0,
|
role,
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
const { checkForMigrations } = require("../utils/database");
|
|
||||||
console.log(`\x1b[34m[MIGRATING]\x1b[0m Checking for User migrations`);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
},
|
||||||
migrations: function () {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
create: async function ({ username, password, role = null }) {
|
|
||||||
const bcrypt = require("bcrypt");
|
|
||||||
const db = await this.db();
|
|
||||||
const { id, success, message } = await db
|
|
||||||
.run(
|
|
||||||
`INSERT INTO ${this.tablename} (username, password, role) VALUES(?, ?, ?)`,
|
|
||||||
[username, bcrypt.hashSync(password, 10), role ?? "default"]
|
|
||||||
)
|
|
||||||
.then((res) => {
|
|
||||||
return { id: res.lastID, success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { id: null, success: false, message: error.message };
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
db.close();
|
|
||||||
console.error("FAILED TO CREATE USER.", message);
|
|
||||||
return { user: null, error: message };
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await db.get(
|
|
||||||
`SELECT * FROM ${this.tablename} WHERE id = ${id} `
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return { user, error: null };
|
return { user, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("FAILED TO CREATE USER.", error.message);
|
||||||
|
return { user: null, error: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
update: async function (userId, updates = {}) {
|
update: async function (userId, updates = {}) {
|
||||||
const user = await this.get(`id = ${escape(userId)}`);
|
try {
|
||||||
if (!user) return { success: false, error: "User does not exist." };
|
const updatedUser = await prisma.users.update({
|
||||||
const { username, password, role, suspended = 0 } = updates;
|
where: { id: parseInt(userId) },
|
||||||
const toUpdate = { suspended };
|
data: updates,
|
||||||
|
|
||||||
if (user.username !== username && username?.length > 0) {
|
|
||||||
const usedUsername = !!(await this.get(`username = ${escape(username)}`));
|
|
||||||
if (usedUsername)
|
|
||||||
return { success: false, error: `${username} is already in use.` };
|
|
||||||
toUpdate.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!!password) {
|
|
||||||
const bcrypt = require("bcrypt");
|
|
||||||
toUpdate.password = bcrypt.hashSync(password, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.role !== role && ["admin", "default"].includes(role)) {
|
|
||||||
// If was existing admin and that has been changed
|
|
||||||
// make sure at least one admin exists
|
|
||||||
if (user.role === "admin") {
|
|
||||||
const validAdminCount = (await this.count(`role = 'admin'`)) > 1;
|
|
||||||
if (!validAdminCount)
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: `There would be no admins if this action was completed. There must be at least one admin.`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toUpdate.role = role;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(toUpdate).length !== 0) {
|
|
||||||
const values = Object.values(toUpdate);
|
|
||||||
const template = `UPDATE ${this.tablename} SET ${Object.keys(
|
|
||||||
toUpdate
|
|
||||||
).map((key) => {
|
|
||||||
return `${key}=?`;
|
|
||||||
})} WHERE id = ?`;
|
|
||||||
|
|
||||||
const db = await this.db();
|
|
||||||
const { success, message } = await db
|
|
||||||
.run(template, [...values, userId])
|
|
||||||
.then(() => {
|
|
||||||
return { success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
db.close();
|
|
||||||
if (!success) {
|
|
||||||
console.error(message);
|
|
||||||
return { success: false, error: message };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, error: null };
|
return { success: true, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
get: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
const result = await db
|
|
||||||
.get(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : clause}`
|
|
||||||
)
|
|
||||||
.then((res) => res || null);
|
|
||||||
if (!result) return null;
|
|
||||||
db.close();
|
|
||||||
return { ...result };
|
|
||||||
},
|
|
||||||
count: async function (clause = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const { count } = await db.get(
|
|
||||||
`SELECT COUNT(*) as count FROM ${this.tablename} ${
|
|
||||||
clause ? `WHERE ${clause}` : ""
|
|
||||||
} `
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
get: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const user = await prisma.users.findFirst({ where: clause });
|
||||||
|
return user ? { ...user } : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
count: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const count = await prisma.users.count({ where: clause });
|
||||||
return count;
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
delete: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
await db.get(`DELETE FROM ${this.tablename} WHERE ${clause}`);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
delete: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
await prisma.users.delete({ where: clause });
|
||||||
return true;
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
where: async function (clause = "", limit = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return results;
|
where: async function (clause = {}, limit = null) {
|
||||||
|
try {
|
||||||
|
const users = await prisma.users.findMany({
|
||||||
|
where: clause,
|
||||||
|
...(limit !== null ? { take: limit } : {}),
|
||||||
|
});
|
||||||
|
return users;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,103 +1,68 @@
|
|||||||
const { checkForMigrations } = require("../utils/database");
|
const prisma = require("../utils/prisma");
|
||||||
const { Document } = require("./documents");
|
const { Document } = require("./documents");
|
||||||
|
|
||||||
// TODO: Do we want to store entire vectorized chunks in here
|
|
||||||
// so that we can easily spin up temp-namespace clones for threading
|
|
||||||
const DocumentVectors = {
|
const DocumentVectors = {
|
||||||
tablename: "document_vectors",
|
|
||||||
colsInit: `
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
docId TEXT NOT NULL,
|
|
||||||
vectorId TEXT NOT NULL,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
console.log(
|
|
||||||
`\x1b[34m[MIGRATING]\x1b[0m Checking for DocumentVector migrations`
|
|
||||||
);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
|
||||||
migrations: function () {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
bulkInsert: async function (vectorRecords = []) {
|
bulkInsert: async function (vectorRecords = []) {
|
||||||
if (vectorRecords.length === 0) return;
|
if (vectorRecords.length === 0) return;
|
||||||
|
|
||||||
const db = await this.db();
|
|
||||||
|
|
||||||
// Build a single query string with multiple placeholders for the INSERT operation
|
|
||||||
const placeholders = vectorRecords.map(() => "(?, ?)").join(", ");
|
|
||||||
|
|
||||||
const stmt = await db.prepare(
|
|
||||||
`INSERT INTO ${this.tablename} (docId, vectorId) VALUES ${placeholders}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Flatten the vectorRecords array to match the order of placeholders
|
|
||||||
const values = vectorRecords.reduce(
|
|
||||||
(arr, record) => arr.concat([record.docId, record.vectorId]),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.exec("BEGIN TRANSACTION");
|
|
||||||
try {
|
try {
|
||||||
await stmt.run(values);
|
const inserts = [];
|
||||||
await db.exec("COMMIT");
|
vectorRecords.forEach((record) => {
|
||||||
} catch {
|
inserts.push(
|
||||||
await db.exec("ROLLBACK");
|
prisma.document_vectors.create({
|
||||||
}
|
data: {
|
||||||
|
docId: record.docId,
|
||||||
stmt.finalize();
|
vectorId: record.vectorId,
|
||||||
db.close();
|
|
||||||
|
|
||||||
return { documentsInserted: vectorRecords.length };
|
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await prisma.$transaction(inserts);
|
||||||
|
return { documentsInserted: inserts.length };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Bulk insert failed", error);
|
||||||
|
return { documentsInserted: 0 };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
deleteForWorkspace: async function (workspaceId) {
|
deleteForWorkspace: async function (workspaceId) {
|
||||||
const documents = await Document.forWorkspace(workspaceId);
|
const documents = await Document.forWorkspace(workspaceId);
|
||||||
const docIds = [...new Set(documents.map((doc) => doc.docId))];
|
const docIds = [...new Set(documents.map((doc) => doc.docId))];
|
||||||
const ids = (
|
|
||||||
await this.where(`docId IN (${docIds.map((id) => `'${id}'`).join(",")})`)
|
|
||||||
).map((doc) => doc.id);
|
|
||||||
await this.deleteIds(ids);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
where: async function (clause = "", limit = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
|
|
||||||
db.close();
|
try {
|
||||||
return results;
|
await prisma.document_vectors.deleteMany({
|
||||||
},
|
where: { docId: { in: docIds } },
|
||||||
deleteIds: async function (ids = []) {
|
});
|
||||||
const db = await this.db();
|
|
||||||
await db.get(
|
|
||||||
`DELETE FROM ${this.tablename} WHERE id IN (${ids.join(", ")}) `
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
return true;
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Delete for workspace failed", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
where: async function (clause = {}, limit) {
|
||||||
|
try {
|
||||||
|
const results = await prisma.document_vectors.findMany({
|
||||||
|
where: clause,
|
||||||
|
take: limit || undefined,
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Where query failed", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteIds: async function (ids = []) {
|
||||||
|
try {
|
||||||
|
await prisma.document_vectors.deleteMany({
|
||||||
|
where: { id: { in: ids } },
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Delete IDs failed", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,88 +1,61 @@
|
|||||||
|
const prisma = require("../utils/prisma");
|
||||||
|
|
||||||
const WelcomeMessages = {
|
const WelcomeMessages = {
|
||||||
tablename: "welcome_messages",
|
get: async function (clause = {}) {
|
||||||
colsInit: `
|
try {
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
const message = await prisma.welcome_messages.findFirst({
|
||||||
user TEXT NOT NULL,
|
where: clause,
|
||||||
response TEXT NOT NULL,
|
|
||||||
orderIndex INTEGER,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
||||||
`,
|
|
||||||
|
|
||||||
migrateTable: async function () {
|
|
||||||
const { checkForMigrations } = require("../utils/database");
|
|
||||||
console.log(
|
|
||||||
`\x1b[34m[MIGRATING]\x1b[0m Checking for Welcome Messages migrations`
|
|
||||||
);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
db.close();
|
|
||||||
},
|
|
||||||
|
|
||||||
migrations: function () {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
|
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
});
|
||||||
|
return message || null;
|
||||||
await db.exec(
|
} catch (error) {
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
console.error(error.message);
|
||||||
);
|
return null;
|
||||||
|
|
||||||
if (tracing) {
|
|
||||||
db.on("trace", (sql) => console.log(sql));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return db;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get: async function (clause = "") {
|
where: async function (clause = {}, limit) {
|
||||||
const db = await this.db();
|
try {
|
||||||
const result = await db
|
const messages = await prisma.welcome_messages.findMany({
|
||||||
.get(`SELECT * FROM ${this.tablename} WHERE ${clause}`)
|
where: clause,
|
||||||
.then((res) => res || null);
|
take: limit || undefined,
|
||||||
db.close();
|
});
|
||||||
return result;
|
return messages;
|
||||||
},
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
where: async function (clause = null, limit = null) {
|
return [];
|
||||||
const db = await this.db();
|
}
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
return results;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
saveAll: async function (messages) {
|
saveAll: async function (messages) {
|
||||||
const db = await this.db();
|
try {
|
||||||
await db.run(`DELETE FROM ${this.tablename}`);
|
await prisma.welcome_messages.deleteMany({}); // Delete all existing messages
|
||||||
|
|
||||||
|
// Create new messages
|
||||||
for (const [index, message] of messages.entries()) {
|
for (const [index, message] of messages.entries()) {
|
||||||
await db.run(
|
await prisma.welcome_messages.create({
|
||||||
`INSERT INTO ${this.tablename} (user, response, orderIndex) VALUES (?, ?, ?)`,
|
data: {
|
||||||
[message.user, message.response, index]
|
user: message.user,
|
||||||
);
|
response: message.response,
|
||||||
|
orderIndex: index,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to save all messages", error.message);
|
||||||
}
|
}
|
||||||
db.close();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getMessages: async function () {
|
getMessages: async function () {
|
||||||
const db = await this.db();
|
try {
|
||||||
const results = await db.all(
|
const messages = await prisma.welcome_messages.findMany({
|
||||||
`SELECT user, response FROM ${this.tablename} ORDER BY orderIndex ASC`
|
orderBy: { orderIndex: "asc" },
|
||||||
);
|
select: { user: true, response: true },
|
||||||
db.close();
|
});
|
||||||
return results;
|
return messages;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get all messages", error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
|
const prisma = require("../utils/prisma");
|
||||||
const slugify = require("slugify");
|
const slugify = require("slugify");
|
||||||
const { Document } = require("./documents");
|
const { Document } = require("./documents");
|
||||||
const { checkForMigrations } = require("../utils/database");
|
|
||||||
const { WorkspaceUser } = require("./workspaceUsers");
|
const { WorkspaceUser } = require("./workspaceUsers");
|
||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
|
|
||||||
const Workspace = {
|
const Workspace = {
|
||||||
tablename: "workspaces",
|
|
||||||
writable: [
|
writable: [
|
||||||
// Used for generic updates so we can validate keys in request body
|
// Used for generic updates so we can validate keys in request body
|
||||||
"name",
|
"name",
|
||||||
@ -16,223 +14,180 @@ const Workspace = {
|
|||||||
"lastUpdatedAt",
|
"lastUpdatedAt",
|
||||||
"openAiPrompt",
|
"openAiPrompt",
|
||||||
],
|
],
|
||||||
colsInit: `
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
slug TEXT NOT NULL UNIQUE,
|
|
||||||
vectorTag TEXT DEFAULT NULL,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
openAiTemp REAL DEFAULT NULL,
|
|
||||||
openAiHistory INTEGER DEFAULT 20,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
openAiPrompt TEXT DEFAULT NULL
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
console.log(`\x1b[34m[MIGRATING]\x1b[0m Checking for Workspace migrations`);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
|
||||||
migrations: function () {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
colName: "openAiTemp",
|
|
||||||
execCmd: `ALTER TABLE ${this.tablename} ADD COLUMN openAiTemp REAL DEFAULT NULL`,
|
|
||||||
doif: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
colName: "openAiPrompt",
|
|
||||||
execCmd: `ALTER TABLE ${this.tablename} ADD COLUMN openAiPrompt TEXT DEFAULT NULL`,
|
|
||||||
doif: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
colName: "id",
|
|
||||||
execCmd: `CREATE TRIGGER IF NOT EXISTS Trg_LastUpdated AFTER UPDATE ON ${this.tablename}
|
|
||||||
FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
UPDATE ${this.tablename} SET lastUpdatedAt = CURRENT_TIMESTAMP WHERE id = old.id;
|
|
||||||
END`,
|
|
||||||
doif: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
colName: "openAiHistory",
|
|
||||||
execCmd: `ALTER TABLE ${this.tablename} ADD COLUMN openAiHistory INTEGER DEFAULT 20`,
|
|
||||||
doif: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
new: async function (name = null, creatorId = null) {
|
new: async function (name = null, creatorId = null) {
|
||||||
if (!name) return { result: null, message: "name cannot be null" };
|
if (!name) return { result: null, message: "name cannot be null" };
|
||||||
var slug = slugify(name, { lower: true });
|
var slug = slugify(name, { lower: true });
|
||||||
|
|
||||||
const existingBySlug = await this.get(`slug = ${escape(slug)}`);
|
const existingBySlug = await this.get({ slug });
|
||||||
if (existingBySlug !== null) {
|
if (existingBySlug !== null) {
|
||||||
const slugSeed = Math.floor(10000000 + Math.random() * 90000000);
|
const slugSeed = Math.floor(10000000 + Math.random() * 90000000);
|
||||||
slug = slugify(`${name}-${slugSeed}`, { lower: true });
|
slug = slugify(`${name}-${slugSeed}`, { lower: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = await this.db();
|
try {
|
||||||
const { id, success, message } = await db
|
const workspace = await prisma.workspaces.create({
|
||||||
.run(`INSERT INTO ${this.tablename} (name, slug) VALUES (?, ?)`, [
|
data: { name, slug },
|
||||||
name,
|
|
||||||
slug,
|
|
||||||
])
|
|
||||||
.then((res) => {
|
|
||||||
return { id: res.lastID, success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { id: null, success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
db.close();
|
|
||||||
return { workspace: null, message };
|
|
||||||
}
|
|
||||||
|
|
||||||
const workspace = await db.get(
|
|
||||||
`SELECT * FROM ${this.tablename} WHERE id = ${id}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
// If created with a user then we need to create the relationship as well.
|
// If created with a user then we need to create the relationship as well.
|
||||||
// If creating with an admin User it wont change anything because admins can
|
// If creating with an admin User it wont change anything because admins can
|
||||||
// view all workspaces anyway.
|
// view all workspaces anyway.
|
||||||
if (!!creatorId) await WorkspaceUser.create(creatorId, workspace.id);
|
if (!!creatorId) await WorkspaceUser.create(creatorId, workspace.id);
|
||||||
return { workspace, message: null };
|
return { workspace, message: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return { workspace: null, message: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
update: async function (id = null, data = {}) {
|
update: async function (id = null, data = {}) {
|
||||||
if (!id) throw new Error("No workspace id provided for update");
|
if (!id) throw new Error("No workspace id provided for update");
|
||||||
|
|
||||||
const validKeys = Object.keys(data).filter((key) =>
|
const validKeys = Object.keys(data).filter((key) =>
|
||||||
this.writable.includes(key)
|
this.writable.includes(key)
|
||||||
);
|
);
|
||||||
const values = Object.values(data);
|
if (validKeys.length === 0)
|
||||||
if (validKeys.length === 0 || validKeys.length !== values.length)
|
|
||||||
return { workspace: { id }, message: "No valid fields to update!" };
|
return { workspace: { id }, message: "No valid fields to update!" };
|
||||||
|
|
||||||
const template = `UPDATE ${this.tablename} SET ${validKeys.map((key) => {
|
try {
|
||||||
return `${key}=?`;
|
const workspace = await prisma.workspaces.update({
|
||||||
})} WHERE id = ?`;
|
where: { id },
|
||||||
const db = await this.db();
|
data,
|
||||||
const { success, message } = await db
|
|
||||||
.run(template, [...values, id])
|
|
||||||
.then(() => {
|
|
||||||
return { success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { success: false, message: error.message };
|
|
||||||
});
|
});
|
||||||
|
return { workspace, message: null };
|
||||||
db.close();
|
} catch (error) {
|
||||||
if (!success) {
|
console.error(error.message);
|
||||||
return { workspace: null, message };
|
return { workspace: null, message: error.message };
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedWorkspace = await this.get(`id = ${id}`);
|
|
||||||
return { workspace: updatedWorkspace, message: null };
|
|
||||||
},
|
},
|
||||||
getWithUser: async function (user = null, clause = "") {
|
|
||||||
|
getWithUser: async function (user = null, clause = {}) {
|
||||||
if (user.role === "admin") return this.get(clause);
|
if (user.role === "admin") return this.get(clause);
|
||||||
|
|
||||||
const db = await this.db();
|
try {
|
||||||
const result = await db
|
const workspace = await prisma.workspaces.findFirst({
|
||||||
.get(
|
where: {
|
||||||
`SELECT * FROM ${this.tablename} as workspace
|
...clause,
|
||||||
LEFT JOIN workspace_users as ws_users
|
workspace_users: {
|
||||||
ON ws_users.workspace_id = workspace.id
|
some: {
|
||||||
WHERE ws_users.user_id = ${user?.id} AND ${clause}`
|
user_id: user?.id,
|
||||||
)
|
|
||||||
.then((res) => res || null);
|
|
||||||
if (!result) return null;
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
const workspace = { ...result, id: result.workspace_id };
|
|
||||||
const documents = await Document.forWorkspace(workspace.id);
|
|
||||||
return { ...workspace, documents };
|
|
||||||
},
|
},
|
||||||
get: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
const result = await db
|
|
||||||
.get(`SELECT * FROM ${this.tablename} WHERE ${clause}`)
|
|
||||||
.then((res) => res || null);
|
|
||||||
if (!result) return null;
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
const documents = await Document.forWorkspace(result.id);
|
|
||||||
return { ...result, documents };
|
|
||||||
},
|
},
|
||||||
delete: async function (clause = "") {
|
},
|
||||||
const db = await this.db();
|
include: {
|
||||||
await db.get(`DELETE FROM ${this.tablename} WHERE ${clause}`);
|
workspace_users: true,
|
||||||
db.close();
|
documents: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!workspace) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...workspace,
|
||||||
|
documents: await Document.forWorkspace(workspace.id),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const workspace = await prisma.workspaces.findFirst({
|
||||||
|
where: clause,
|
||||||
|
include: {
|
||||||
|
documents: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return workspace || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
delete: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
await prisma.workspaces.delete({
|
||||||
|
where: clause,
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
where: async function (clause = "", limit = null, orderBy = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
} ${!!orderBy ? orderBy : ""}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
where: async function (clause = {}, limit = null, orderBy = null) {
|
||||||
|
try {
|
||||||
|
const results = await prisma.workspaces.findMany({
|
||||||
|
where: clause,
|
||||||
|
...(limit !== null ? { take: limit } : {}),
|
||||||
|
...(orderBy !== null ? { orderBy } : {}),
|
||||||
|
});
|
||||||
return results;
|
return results;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
whereWithUser: async function (
|
whereWithUser: async function (
|
||||||
user,
|
user,
|
||||||
clause = null,
|
clause = {},
|
||||||
limit = null,
|
limit = null,
|
||||||
orderBy = null
|
orderBy = null
|
||||||
) {
|
) {
|
||||||
if (user.role === "admin") return await this.where(clause, limit);
|
if (user.role === "admin") return await this.where(clause, limit, orderBy);
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} as workspace
|
|
||||||
LEFT JOIN workspace_users as ws_users
|
|
||||||
ON ws_users.workspace_id = workspace.id
|
|
||||||
WHERE ws_users.user_id = ${user.id} ${clause ? `AND ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
} ${!!orderBy ? orderBy : ""}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
const workspaces = results.map((ws) => {
|
|
||||||
return { ...ws, id: ws.workspace_id };
|
|
||||||
});
|
|
||||||
|
|
||||||
return workspaces;
|
try {
|
||||||
|
const workspaces = await prisma.workspaces.findMany({
|
||||||
|
where: {
|
||||||
|
...clause,
|
||||||
|
workspace_users: {
|
||||||
|
some: {
|
||||||
|
user_id: user.id,
|
||||||
},
|
},
|
||||||
whereWithUsers: async function (clause = "", limit = null, orderBy = null) {
|
},
|
||||||
|
},
|
||||||
|
...(limit !== null ? { take: limit } : {}),
|
||||||
|
...(orderBy !== null ? { orderBy } : {}),
|
||||||
|
});
|
||||||
|
return workspaces;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
whereWithUsers: async function (clause = {}, limit = null, orderBy = null) {
|
||||||
|
try {
|
||||||
const workspaces = await this.where(clause, limit, orderBy);
|
const workspaces = await this.where(clause, limit, orderBy);
|
||||||
for (const workspace of workspaces) {
|
for (const workspace of workspaces) {
|
||||||
const userIds = (
|
const userIds = (
|
||||||
await WorkspaceUser.where(`workspace_id = ${workspace.id}`)
|
await WorkspaceUser.where({ workspace_id: Number(workspace.id) })
|
||||||
).map((rel) => rel.user_id);
|
).map((rel) => rel.user_id);
|
||||||
workspace.userIds = userIds;
|
workspace.userIds = userIds;
|
||||||
}
|
}
|
||||||
return workspaces;
|
return workspaces;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateUsers: async function (workspaceId, userIds = []) {
|
updateUsers: async function (workspaceId, userIds = []) {
|
||||||
await WorkspaceUser.delete(`workspace_id = ${workspaceId}`);
|
try {
|
||||||
|
await WorkspaceUser.delete({ workspace_id: Number(workspaceId) });
|
||||||
await WorkspaceUser.createManyUsers(userIds, workspaceId);
|
await WorkspaceUser.createManyUsers(userIds, workspaceId);
|
||||||
return { success: true, error: null };
|
return { success: true, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,170 +1,160 @@
|
|||||||
const { checkForMigrations } = require("../utils/database");
|
const prisma = require("../utils/prisma");
|
||||||
|
|
||||||
const WorkspaceChats = {
|
const WorkspaceChats = {
|
||||||
tablename: "workspace_chats",
|
|
||||||
colsInit: `
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
workspaceId INTEGER NOT NULL,
|
|
||||||
prompt TEXT NOT NULL,
|
|
||||||
response TEXT NOT NULL,
|
|
||||||
include BOOL DEFAULT true,
|
|
||||||
user_id INTEGER DEFAULT NULL,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
console.log(
|
|
||||||
`\x1b[34m[MIGRATING]\x1b[0m Checking for WorkspaceChats migrations`
|
|
||||||
);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
|
||||||
migrations: function () {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
colName: "user_id",
|
|
||||||
execCmd: `ALTER TABLE ${this.tablename} ADD COLUMN user_id INTEGER DEFAULT NULL`,
|
|
||||||
doif: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
new: async function ({ workspaceId, prompt, response = {}, user = null }) {
|
new: async function ({ workspaceId, prompt, response = {}, user = null }) {
|
||||||
const db = await this.db();
|
try {
|
||||||
const { id, success, message } = await db
|
const chat = await prisma.workspace_chats.create({
|
||||||
.run(
|
data: {
|
||||||
`INSERT INTO ${this.tablename} (workspaceId, prompt, response, user_id) VALUES (?, ?, ?, ?)`,
|
workspaceId,
|
||||||
[workspaceId, prompt, JSON.stringify(response), user?.id || null]
|
prompt,
|
||||||
)
|
response: JSON.stringify(response),
|
||||||
.then((res) => {
|
user_id: user?.id || null,
|
||||||
return { id: res.lastID, success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { id: null, success: false, message: error.message };
|
|
||||||
});
|
|
||||||
if (!success) {
|
|
||||||
db.close();
|
|
||||||
return { chat: null, message };
|
|
||||||
}
|
|
||||||
|
|
||||||
const chat = await db.get(
|
|
||||||
`SELECT * FROM ${this.tablename} WHERE id = ${id}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return { chat, message: null };
|
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
return { chat, message: null };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return { chat: null, message: error.message };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
forWorkspaceByUser: async function (
|
forWorkspaceByUser: async function (
|
||||||
workspaceId = null,
|
workspaceId = null,
|
||||||
userId = null,
|
userId = null,
|
||||||
limit = null
|
limit = null
|
||||||
) {
|
) {
|
||||||
if (!workspaceId || !userId) return [];
|
if (!workspaceId || !userId) return [];
|
||||||
return await this.where(
|
try {
|
||||||
`workspaceId = ${workspaceId} AND include = true AND user_id = ${userId}`,
|
const chats = await prisma.workspace_chats.findMany({
|
||||||
limit,
|
where: {
|
||||||
"ORDER BY id ASC"
|
workspaceId,
|
||||||
);
|
user_id: userId,
|
||||||
},
|
},
|
||||||
|
...(limit !== null ? { take: limit } : {}),
|
||||||
|
orderBy: {
|
||||||
|
id: "asc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return chats;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
forWorkspace: async function (workspaceId = null, limit = null) {
|
forWorkspace: async function (workspaceId = null, limit = null) {
|
||||||
if (!workspaceId) return [];
|
if (!workspaceId) return [];
|
||||||
return await this.where(
|
try {
|
||||||
`workspaceId = ${workspaceId} AND include = true`,
|
const chats = await prisma.workspace_chats.findMany({
|
||||||
limit,
|
where: {
|
||||||
"ORDER BY id ASC"
|
workspaceId,
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
...(limit !== null ? { take: limit } : {}),
|
||||||
|
orderBy: {
|
||||||
|
id: "asc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return chats;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
markHistoryInvalid: async function (workspaceId = null, user = null) {
|
markHistoryInvalid: async function (workspaceId = null, user = null) {
|
||||||
if (!workspaceId) return;
|
if (!workspaceId) return;
|
||||||
const db = await this.db();
|
try {
|
||||||
await db.run(
|
await prisma.workspace_chats.updateMany({
|
||||||
`UPDATE ${this.tablename} SET include = false WHERE workspaceId = ? ${
|
where: {
|
||||||
user ? `AND user_id = ${user.id}` : ""
|
workspaceId,
|
||||||
}`,
|
user_id: user?.id,
|
||||||
[workspaceId]
|
},
|
||||||
);
|
data: {
|
||||||
db.close();
|
include: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
get: async function (clause = "", limit = null, order = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const result = await db
|
|
||||||
.get(
|
|
||||||
`SELECT * FROM ${this.tablename} WHERE ${clause} ${
|
|
||||||
!!order ? order : ""
|
|
||||||
} ${!!limit ? `LIMIT ${limit}` : ""}`
|
|
||||||
)
|
|
||||||
.then((res) => res || null);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
if (!result) return null;
|
get: async function (clause = {}, limit = null, orderBy = null) {
|
||||||
return result;
|
try {
|
||||||
|
const chat = await prisma.workspace_chats.findFirst({
|
||||||
|
where: clause,
|
||||||
|
...(limit !== null ? { take: limit } : {}),
|
||||||
|
...(orderBy !== null ? { orderBy } : {}),
|
||||||
|
});
|
||||||
|
return chat || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
delete: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
await db.get(`DELETE FROM ${this.tablename} WHERE ${clause}`);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
delete: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
await prisma.workspace_chats.deleteMany({
|
||||||
|
where: clause,
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
where: async function (clause = "", limit = null, order = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!order ? order : ""
|
|
||||||
} ${!!limit ? `LIMIT ${limit}` : ""}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return results;
|
where: async function (clause = {}, limit = null, orderBy = null) {
|
||||||
|
try {
|
||||||
|
const chats = await prisma.workspace_chats.findMany({
|
||||||
|
where: clause,
|
||||||
|
...(limit !== null ? { take: limit } : {}),
|
||||||
|
...(orderBy !== null ? { orderBy } : {}),
|
||||||
|
});
|
||||||
|
return chats;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
count: async function (clause = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const { count } = await db.get(
|
|
||||||
`SELECT COUNT(*) as count FROM ${this.tablename} ${
|
|
||||||
clause ? `WHERE ${clause}` : ""
|
|
||||||
} `
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
count: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const count = await prisma.workspace_chats.count({
|
||||||
|
where: clause,
|
||||||
|
});
|
||||||
return count;
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
whereWithData: async function (clause = "", limit = null, order = null) {
|
|
||||||
|
whereWithData: async function (clause = {}, limit = null, orderBy = null) {
|
||||||
const { Workspace } = require("./workspace");
|
const { Workspace } = require("./workspace");
|
||||||
const { User } = require("./user");
|
const { User } = require("./user");
|
||||||
const results = await this.where(clause, limit, order);
|
|
||||||
|
try {
|
||||||
|
const results = await this.where(clause, limit, orderBy);
|
||||||
|
|
||||||
for (const res of results) {
|
for (const res of results) {
|
||||||
const workspace = await Workspace.get(`id = ${res.workspaceId}`);
|
const workspace = await Workspace.get({ id: res.workspaceId });
|
||||||
res.workspace = workspace
|
res.workspace = workspace
|
||||||
? { name: workspace.name, slug: workspace.slug }
|
? { name: workspace.name, slug: workspace.slug }
|
||||||
: { name: "deleted workspace", slug: null };
|
: { name: "deleted workspace", slug: null };
|
||||||
|
|
||||||
const user = await User.get(`id = ${res.user_id}`);
|
const user = await User.get({ id: res.user_id });
|
||||||
res.user = user
|
res.user = user
|
||||||
? { username: user.username }
|
? { username: user.username }
|
||||||
: { username: "deleted user" };
|
: { username: "deleted user" };
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,142 +1,95 @@
|
|||||||
|
const prisma = require("../utils/prisma");
|
||||||
|
|
||||||
const WorkspaceUser = {
|
const WorkspaceUser = {
|
||||||
tablename: "workspace_users",
|
|
||||||
colsInit: `
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
workspace_id INTEGER NOT NULL,
|
|
||||||
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE
|
|
||||||
`,
|
|
||||||
migrateTable: async function () {
|
|
||||||
const { checkForMigrations } = require("../utils/database");
|
|
||||||
console.log(
|
|
||||||
`\x1b[34m[MIGRATING]\x1b[0m Checking for Workspace User migrations`
|
|
||||||
);
|
|
||||||
const db = await this.db(false);
|
|
||||||
await checkForMigrations(this, db);
|
|
||||||
},
|
|
||||||
migrations: function () {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
db: async function (tracing = true) {
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
const { open } = require("sqlite");
|
|
||||||
|
|
||||||
const db = await open({
|
|
||||||
filename: `${
|
|
||||||
!!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
|
|
||||||
}anythingllm.db`,
|
|
||||||
driver: sqlite3.Database,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.exec(
|
|
||||||
`PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tracing) db.on("trace", (sql) => console.log(sql));
|
|
||||||
return db;
|
|
||||||
},
|
|
||||||
createMany: async function (userId, workspaceIds = []) {
|
createMany: async function (userId, workspaceIds = []) {
|
||||||
if (workspaceIds.length === 0) return;
|
if (workspaceIds.length === 0) return;
|
||||||
const db = await this.db();
|
|
||||||
const stmt = await db.prepare(
|
|
||||||
`INSERT INTO ${this.tablename} (user_id, workspace_id) VALUES (?,?)`
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.exec("BEGIN TRANSACTION");
|
|
||||||
try {
|
try {
|
||||||
for (const workspaceId of workspaceIds) {
|
await prisma.$transaction(
|
||||||
await stmt.run([userId, workspaceId]);
|
workspaceIds.map((workspaceId) =>
|
||||||
|
prisma.workspace_users.create({
|
||||||
|
data: { user_id: userId, workspace_id: workspaceId },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
}
|
}
|
||||||
await db.exec("COMMIT");
|
|
||||||
} catch {
|
|
||||||
await db.exec("ROLLBACK");
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt.finalize();
|
|
||||||
db.close();
|
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
createManyUsers: async function (userIds = [], workspaceId) {
|
createManyUsers: async function (userIds = [], workspaceId) {
|
||||||
if (userIds.length === 0) return;
|
if (userIds.length === 0) return;
|
||||||
const db = await this.db();
|
|
||||||
const stmt = await db.prepare(
|
|
||||||
`INSERT INTO ${this.tablename} (user_id, workspace_id) VALUES (?,?)`
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.exec("BEGIN TRANSACTION");
|
|
||||||
try {
|
try {
|
||||||
for (const userId of userIds) {
|
await prisma.$transaction(
|
||||||
await stmt.run([userId, workspaceId]);
|
userIds.map((userId) =>
|
||||||
|
prisma.workspace_users.create({
|
||||||
|
data: {
|
||||||
|
user_id: Number(userId),
|
||||||
|
workspace_id: Number(workspaceId),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
}
|
}
|
||||||
await db.exec("COMMIT");
|
|
||||||
} catch {
|
|
||||||
await db.exec("ROLLBACK");
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt.finalize();
|
|
||||||
db.close();
|
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
create: async function (userId = 0, workspaceId = 0) {
|
|
||||||
const db = await this.db();
|
|
||||||
const { success, message } = await db
|
|
||||||
.run(
|
|
||||||
`INSERT INTO ${this.tablename} (user_id, workspace_id) VALUES (?, ?)`,
|
|
||||||
[userId, workspaceId]
|
|
||||||
)
|
|
||||||
.then((res) => {
|
|
||||||
return { id: res.lastID, success: true, message: null };
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return { id: null, success: false, message: error.message };
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!success) {
|
create: async function (userId = 0, workspaceId = 0) {
|
||||||
db.close();
|
try {
|
||||||
console.error("FAILED TO CREATE WORKSPACE_USER RELATIONSHIP.", message);
|
await prisma.workspace_users.create({
|
||||||
|
data: { user_id: Number(userId), workspace_id: Number(workspaceId) },
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
"FAILED TO CREATE WORKSPACE_USER RELATIONSHIP.",
|
||||||
|
error.message
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
get: async function (clause = "") {
|
|
||||||
const db = await this.db();
|
|
||||||
const result = await db
|
|
||||||
.get(`SELECT * FROM ${this.tablename} WHERE ${clause}`)
|
|
||||||
.then((res) => res || null);
|
|
||||||
if (!result) return null;
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
return result;
|
get: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const result = await prisma.workspace_users.findFirst({ where: clause });
|
||||||
|
return result || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
where: async function (clause = null, limit = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const results = await db.all(
|
|
||||||
`SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
|
|
||||||
!!limit ? `LIMIT ${limit}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
where: async function (clause = {}, limit = null) {
|
||||||
|
try {
|
||||||
|
const results = await prisma.workspace_users.findMany({
|
||||||
|
where: clause,
|
||||||
|
...(limit !== null ? { take: limit } : {}),
|
||||||
|
});
|
||||||
return results;
|
return results;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
count: async function (clause = null) {
|
|
||||||
const db = await this.db();
|
|
||||||
const { count } = await db.get(
|
|
||||||
`SELECT COUNT(*) as count FROM ${this.tablename} ${
|
|
||||||
clause ? `WHERE ${clause}` : ""
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
|
count: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const count = await prisma.workspace_users.count({ where: clause });
|
||||||
return count;
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
delete: async function (clause = null) {
|
|
||||||
const db = await this.db();
|
delete: async function (clause = {}) {
|
||||||
await db.get(`DELETE FROM ${this.tablename} WHERE ${clause}`);
|
try {
|
||||||
|
await prisma.workspace_users.deleteMany({ where: clause });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
7905
server/package-lock.json
generated
Normal file
7905
server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "anything-llm-server",
|
"name": "anything-llm-server",
|
||||||
"version": "0.0.1-beta",
|
"version": "0.2.0",
|
||||||
"description": "Server endpoints to process or create content for chatting",
|
"description": "Server endpoints to process or create content for chatting",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "Timothy Carambat (Mintplex Labs)",
|
"author": "Timothy Carambat (Mintplex Labs)",
|
||||||
@ -13,12 +13,17 @@
|
|||||||
"dev": "NODE_ENV=development nodemon --ignore documents --ignore vector-cache --ignore storage --ignore swagger --trace-warnings index.js",
|
"dev": "NODE_ENV=development nodemon --ignore documents --ignore vector-cache --ignore storage --ignore swagger --trace-warnings index.js",
|
||||||
"start": "NODE_ENV=production node index.js",
|
"start": "NODE_ENV=production node index.js",
|
||||||
"lint": "yarn prettier --write ./endpoints ./models ./utils index.js",
|
"lint": "yarn prettier --write ./endpoints ./models ./utils index.js",
|
||||||
"swagger": "node ./swagger/init.js"
|
"swagger": "node ./swagger/init.js",
|
||||||
|
"sqlite:migrate": "cd ./utils/prisma && node migrateFromSqlite.js"
|
||||||
|
},
|
||||||
|
"prisma": {
|
||||||
|
"seed": "node prisma/seed.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/openai": "^1.0.0-beta.3",
|
"@azure/openai": "^1.0.0-beta.3",
|
||||||
"@googleapis/youtube": "^9.0.0",
|
"@googleapis/youtube": "^9.0.0",
|
||||||
"@pinecone-database/pinecone": "^0.1.6",
|
"@pinecone-database/pinecone": "^0.1.6",
|
||||||
|
"@prisma/client": "5.3.0",
|
||||||
"@qdrant/js-client-rest": "^1.4.0",
|
"@qdrant/js-client-rest": "^1.4.0",
|
||||||
"archiver": "^5.3.1",
|
"archiver": "^5.3.1",
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.0",
|
||||||
@ -38,11 +43,11 @@
|
|||||||
"openai": "^3.2.1",
|
"openai": "^3.2.1",
|
||||||
"pinecone-client": "^1.1.0",
|
"pinecone-client": "^1.1.0",
|
||||||
"posthog-node": "^3.1.1",
|
"posthog-node": "^3.1.1",
|
||||||
|
"prisma": "^5.3.1",
|
||||||
"serve-index": "^1.9.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",
|
||||||
"sqlstring-sqlite": "^0.1.1",
|
|
||||||
"swagger-autogen": "^2.23.5",
|
"swagger-autogen": "^2.23.5",
|
||||||
"swagger-ui-express": "^5.0.0",
|
"swagger-ui-express": "^5.0.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
|
125
server/prisma/migrations/20230921191814_init/migration.sql
Normal file
125
server/prisma/migrations/20230921191814_init/migration.sql
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "api_keys" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"secret" TEXT,
|
||||||
|
"createdBy" INTEGER,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "workspace_documents" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"docId" TEXT NOT NULL,
|
||||||
|
"filename" TEXT NOT NULL,
|
||||||
|
"docpath" TEXT NOT NULL,
|
||||||
|
"workspaceId" INTEGER NOT NULL,
|
||||||
|
"metadata" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT "workspace_documents_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "workspaces" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "invites" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"code" TEXT NOT NULL,
|
||||||
|
"status" TEXT NOT NULL DEFAULT 'pending',
|
||||||
|
"claimedBy" INTEGER,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"createdBy" INTEGER NOT NULL,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "system_settings" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"label" TEXT NOT NULL,
|
||||||
|
"value" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"username" TEXT,
|
||||||
|
"password" TEXT NOT NULL,
|
||||||
|
"role" TEXT NOT NULL DEFAULT 'default',
|
||||||
|
"suspended" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "document_vectors" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"docId" TEXT NOT NULL,
|
||||||
|
"vectorId" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "welcome_messages" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"user" TEXT NOT NULL,
|
||||||
|
"response" TEXT NOT NULL,
|
||||||
|
"orderIndex" INTEGER,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "workspaces" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"slug" TEXT NOT NULL,
|
||||||
|
"vectorTag" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"openAiTemp" REAL,
|
||||||
|
"openAiHistory" INTEGER NOT NULL DEFAULT 20,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"openAiPrompt" TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "workspace_chats" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"workspaceId" INTEGER NOT NULL,
|
||||||
|
"prompt" TEXT NOT NULL,
|
||||||
|
"response" TEXT NOT NULL,
|
||||||
|
"include" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"user_id" INTEGER,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT "workspace_chats_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "workspace_users" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"user_id" INTEGER NOT NULL,
|
||||||
|
"workspace_id" INTEGER NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT "workspace_users_workspace_id_fkey" FOREIGN KEY ("workspace_id") REFERENCES "workspaces" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "workspace_users_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "api_keys_secret_key" ON "api_keys"("secret");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "workspace_documents_docId_key" ON "workspace_documents"("docId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "invites_code_key" ON "invites"("code");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "system_settings_label_key" ON "system_settings"("label");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "workspaces_slug_key" ON "workspaces"("slug");
|
3
server/prisma/migrations/migration_lock.toml
Normal file
3
server/prisma/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (i.e. Git)
|
||||||
|
provider = "sqlite"
|
118
server/prisma/schema.prisma
Normal file
118
server/prisma/schema.prisma
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncomment the following lines and comment out the SQLite datasource block above to use PostgreSQL
|
||||||
|
// Make sure to set the correct DATABASE_URL in your .env file
|
||||||
|
// After swapping run `yarn prisma:setup` from the root directory to migrate the database
|
||||||
|
//
|
||||||
|
// datasource db {
|
||||||
|
// provider = "postgresql"
|
||||||
|
// url = env("DATABASE_URL")
|
||||||
|
// }
|
||||||
|
datasource db {
|
||||||
|
provider = "sqlite"
|
||||||
|
url = "file:../storage/anythingllm.db"
|
||||||
|
}
|
||||||
|
|
||||||
|
model api_keys {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
secret String? @unique
|
||||||
|
createdBy Int?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model workspace_documents {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
docId String @unique
|
||||||
|
filename String
|
||||||
|
docpath String
|
||||||
|
workspaceId Int
|
||||||
|
metadata String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
workspace workspaces @relation(fields: [workspaceId], references: [id])
|
||||||
|
}
|
||||||
|
|
||||||
|
model invites {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
code String @unique
|
||||||
|
status String @default("pending")
|
||||||
|
claimedBy Int?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
createdBy Int
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model system_settings {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
label String @unique
|
||||||
|
value String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model users {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
username String? @unique
|
||||||
|
password String
|
||||||
|
role String @default("default")
|
||||||
|
suspended Int @default(0)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
workspace_chats workspace_chats[]
|
||||||
|
workspace_users workspace_users[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model document_vectors {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
docId String
|
||||||
|
vectorId String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model welcome_messages {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
user String
|
||||||
|
response String
|
||||||
|
orderIndex Int?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model workspaces {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
slug String @unique
|
||||||
|
vectorTag String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
openAiTemp Float?
|
||||||
|
openAiHistory Int @default(20)
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
openAiPrompt String?
|
||||||
|
workspace_users workspace_users[]
|
||||||
|
documents workspace_documents[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model workspace_chats {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
workspaceId Int
|
||||||
|
prompt String
|
||||||
|
response String
|
||||||
|
include Boolean @default(true)
|
||||||
|
user_id Int?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
users users? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||||
|
}
|
||||||
|
|
||||||
|
model workspace_users {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
user_id Int
|
||||||
|
workspace_id Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
workspaces workspaces @relation(fields: [workspace_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||||
|
users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||||
|
}
|
33
server/prisma/seed.js
Normal file
33
server/prisma/seed.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { PrismaClient } = require('@prisma/client');
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const settings = [
|
||||||
|
{ label: 'multi_user_mode', value: 'false' },
|
||||||
|
{ label: 'users_can_delete_workspaces', value: 'false' },
|
||||||
|
{ label: 'limit_user_messages', value: 'false' },
|
||||||
|
{ label: 'message_limit', value: '25' },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let setting of settings) {
|
||||||
|
const existing = await prisma.system_settings.findUnique({
|
||||||
|
where: { label: setting.label },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only create the setting if it doesn't already exist
|
||||||
|
if (!existing) {
|
||||||
|
await prisma.system_settings.create({
|
||||||
|
data: setting,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
});
|
@ -37,7 +37,7 @@ async function userFromSession(request, response = null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await User.get(`id = ${valid.id}`);
|
const user = await User.get({ id: valid.id });
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const { escape } = require("sqlstring-sqlite");
|
|
||||||
const { ApiKey } = require("../../models/apiKeys");
|
const { ApiKey } = require("../../models/apiKeys");
|
||||||
const { SystemSettings } = require("../../models/systemSettings");
|
const { SystemSettings } = require("../../models/systemSettings");
|
||||||
|
|
||||||
@ -15,7 +14,7 @@ async function validApiKey(request, response, next) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await ApiKey.get(`secret = ${escape(bearerKey)}`))) {
|
if (!(await ApiKey.get({ secret: bearerKey }))) {
|
||||||
response.status(403).json({
|
response.status(403).json({
|
||||||
error: "No valid api key found.",
|
error: "No valid api key found.",
|
||||||
});
|
});
|
||||||
|
@ -66,7 +66,7 @@ async function validateMultiUserRequest(request, response, next) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await User.get(`id = ${valid.id}`);
|
const user = await User.get({ id: valid.id });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
response.status(403).json({
|
response.status(403).json({
|
||||||
error: "Invalid auth for user.",
|
error: "Invalid auth for user.",
|
||||||
|
47
server/utils/prisma/PRISMA.md
Normal file
47
server/utils/prisma/PRISMA.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Prisma Setup and Usage Guide
|
||||||
|
|
||||||
|
This guide will help you set up and use Prisma for the project. Prisma is a powerful ORM for Node.js and TypeScript, helping developers build faster and make fewer errors. Follow the guide to understand how to use Prisma and the scripts available in the project to manage the Prisma setup.
|
||||||
|
|
||||||
|
## Setting Up Prisma
|
||||||
|
|
||||||
|
To get started with setting up Prisma, you should run the setup script from the project root directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn setup
|
||||||
|
```
|
||||||
|
|
||||||
|
This script will install the necessary node modules in both the server and frontend directories, set up the environment files, and set up Prisma (generate client, run migrations, and seed the database).
|
||||||
|
|
||||||
|
## Prisma Scripts
|
||||||
|
|
||||||
|
In the project root's `package.json`, there are several scripts set up to help you manage Prisma:
|
||||||
|
|
||||||
|
- **prisma:generate**: Generates the Prisma client.
|
||||||
|
- **prisma:migrate**: Runs the migrations to ensure the database is in sync with the schema.
|
||||||
|
- **prisma:seed**: Seeds the database with initial data.
|
||||||
|
- **prisma:setup**: A convenience script that runs `prisma:generate`, `prisma:migrate`, and `prisma:seed` in sequence.
|
||||||
|
- **sqlite:migrate**: (To be run from the `server` directory) This script is for users transitioning from the old SQLite custom ORM setup to Prisma and will migrate all exisiting data over to Prisma. If you're a new user, your setup will already use Prisma.
|
||||||
|
|
||||||
|
To run any of these scripts, use `yarn` followed by the script name from the project root directory. For example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn prisma:setup
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Prisma Commands
|
||||||
|
|
||||||
|
While the scripts should cover most of your needs, you may sometimes want to run Prisma commands manually. Here are some commands you might find useful, along with their descriptions:
|
||||||
|
|
||||||
|
- `npx prisma introspect`: Introspects the database to update the Prisma schema by reading the schema of the existing database.
|
||||||
|
- `npx prisma generate`: Generates the Prisma client.
|
||||||
|
- `npx prisma migrate dev --name init`: Ensures the database is in sync with the schema, naming the migration 'init'.
|
||||||
|
- `npx prisma migrate reset`: Resets the database, deleting all data and recreating the schema.
|
||||||
|
|
||||||
|
These commands should be run from the `server` directory, where the Prisma schema is located.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Always make sure to run scripts from the root level to avoid path issues.
|
||||||
|
- Before running migrations, ensure that the Prisma schema is correctly defined to prevent data loss or corruption.
|
||||||
|
- If you are adding a new feature or making changes that require a change in the database schema, create a new migration rather than editing existing migrations.
|
||||||
|
- For users transitioning from the old SQLite ORM, navigate to the `server` directory and run the `sqlite:migrate` script to smoothly transition to Prisma. If you're setting up the project fresh, this step is unnecessary as the setup will already be using Prisma.
|
12
server/utils/prisma/index.js
Normal file
12
server/utils/prisma/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const { PrismaClient } = require("@prisma/client");
|
||||||
|
|
||||||
|
// npx prisma introspect
|
||||||
|
// npx prisma generate
|
||||||
|
// npx prisma migrate dev --name init -> ensures that db is in sync with schema
|
||||||
|
// npx prisma migrate reset -> resets the db
|
||||||
|
|
||||||
|
const prisma = new PrismaClient({
|
||||||
|
log: ["query", "info", "warn"],
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = prisma;
|
269
server/utils/prisma/migrateFromSqlite.js
Normal file
269
server/utils/prisma/migrateFromSqlite.js
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
const { PrismaClient } = require("@prisma/client");
|
||||||
|
const execSync = require("child_process").execSync;
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
|
const DATABASE_PATH = process.env.DB_URL || "../../storage/anythingllm.db";
|
||||||
|
const BACKUP_PATH = path.join(
|
||||||
|
path.dirname(DATABASE_PATH),
|
||||||
|
"anythingllm_backup.db"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Backup the database before migrating data
|
||||||
|
function backupDatabase() {
|
||||||
|
try {
|
||||||
|
fs.copyFileSync(DATABASE_PATH, BACKUP_PATH);
|
||||||
|
console.log("Database backup created successfully.");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create database backup:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backupDatabase();
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
// Reset the prisma database and prepare it for migration of data from sqlite
|
||||||
|
function resetAndMigrateDatabase() {
|
||||||
|
try {
|
||||||
|
console.log("Resetting and migrating the database...");
|
||||||
|
execSync("cd ../.. && npx prisma migrate reset --skip-seed --force", {
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
execSync("cd ../.. && npx prisma migrate dev --name init", {
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
console.log("Database reset and initial migration completed successfully");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to reset and migrate the database:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetAndMigrateDatabase();
|
||||||
|
|
||||||
|
// Migrate data from sqlite to prisma
|
||||||
|
async function migrateData() {
|
||||||
|
try {
|
||||||
|
console.log("Starting data migration...");
|
||||||
|
var legacyMap = {
|
||||||
|
users: {
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
workspaces: {
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 1: Migrate system_settings table
|
||||||
|
await migrateTable("system_settings", (row) => {
|
||||||
|
return prisma.system_settings.create({
|
||||||
|
data: {
|
||||||
|
label: row.label,
|
||||||
|
value: row.value,
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 2: Migrate users table
|
||||||
|
await migrateTable("users", (row) => {
|
||||||
|
legacyMap.users[`user_${row.id}`] = legacyMap.users.count + 1;
|
||||||
|
legacyMap.users.count++;
|
||||||
|
|
||||||
|
return prisma.users.create({
|
||||||
|
data: {
|
||||||
|
username: row.username,
|
||||||
|
password: row.password,
|
||||||
|
role: row.role,
|
||||||
|
suspended: row.suspended,
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 3: Migrate workspaces table
|
||||||
|
await migrateTable("workspaces", (row) => {
|
||||||
|
legacyMap.workspaces[`workspace_${row.id}`] =
|
||||||
|
legacyMap.workspaces.count + 1;
|
||||||
|
legacyMap.workspaces.count++;
|
||||||
|
|
||||||
|
return prisma.workspaces.create({
|
||||||
|
data: {
|
||||||
|
name: row.name,
|
||||||
|
slug: row.slug,
|
||||||
|
vectorTag: row.vectorTag,
|
||||||
|
openAiTemp: Number(row.openAiTemp) || 0.7,
|
||||||
|
openAiHistory: Number(row.openAiHistory) || 20,
|
||||||
|
openAiPrompt: row.openAiPrompt,
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 4: Migrate api_keys table
|
||||||
|
await migrateTable("api_keys", async (row) => {
|
||||||
|
const legacyUserId = row.createdBy
|
||||||
|
? legacyMap.users?.[`user_${row.createdBy}`]
|
||||||
|
: null;
|
||||||
|
return prisma.api_keys.create({
|
||||||
|
data: {
|
||||||
|
secret: row.secret,
|
||||||
|
...(legacyUserId
|
||||||
|
? { createdBy: Number(legacyUserId) }
|
||||||
|
: { createdBy: Number(row.createdBy) }),
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 5: Migrate invites table
|
||||||
|
await migrateTable("invites", async (row) => {
|
||||||
|
const legacyCreatedUserId = row.createdBy
|
||||||
|
? legacyMap.users?.[`user_${row.createdBy}`]
|
||||||
|
: null;
|
||||||
|
const legacyClaimedUserId = row.claimedBy
|
||||||
|
? legacyMap.users?.[`user_${row.claimedBy}`]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return prisma.invites.create({
|
||||||
|
data: {
|
||||||
|
code: row.code,
|
||||||
|
status: row.status,
|
||||||
|
...(legacyClaimedUserId
|
||||||
|
? { claimedBy: Number(legacyClaimedUserId) }
|
||||||
|
: { claimedBy: Number(row.claimedBy) }),
|
||||||
|
...(legacyCreatedUserId
|
||||||
|
? { createdBy: Number(legacyCreatedUserId) }
|
||||||
|
: { createdBy: Number(row.createdBy) }),
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 6: Migrate workspace_documents table
|
||||||
|
await migrateTable("workspace_documents", async (row) => {
|
||||||
|
const legacyWorkspaceId = row.workspaceId
|
||||||
|
? legacyMap.workspaces?.[`workspace_${row.workspaceId}`]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return prisma.workspace_documents.create({
|
||||||
|
data: {
|
||||||
|
docId: row.docId,
|
||||||
|
filename: row.filename,
|
||||||
|
docpath: row.docpath,
|
||||||
|
...(legacyWorkspaceId
|
||||||
|
? { workspaceId: Number(legacyWorkspaceId) }
|
||||||
|
: {}),
|
||||||
|
metadata: row.metadata,
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 7: Migrate document_vectors table
|
||||||
|
await migrateTable("document_vectors", (row) => {
|
||||||
|
return prisma.document_vectors.create({
|
||||||
|
data: {
|
||||||
|
docId: row.docId,
|
||||||
|
vectorId: row.vectorId,
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 8: Migrate welcome_messages table
|
||||||
|
await migrateTable("welcome_messages", (row) => {
|
||||||
|
return prisma.welcome_messages.create({
|
||||||
|
data: {
|
||||||
|
user: row.user,
|
||||||
|
response: row.response,
|
||||||
|
orderIndex: row.orderIndex,
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 9: Migrate workspace_users table
|
||||||
|
await migrateTable("workspace_users", async (row) => {
|
||||||
|
const legacyUserId = row.user_id
|
||||||
|
? legacyMap.users?.[`user_${row.user_id}`]
|
||||||
|
: null;
|
||||||
|
const legacyWorkspaceId = row.workspace_id
|
||||||
|
? legacyMap.workspaces?.[`workspace_${row.workspace_id}`]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (!legacyUserId || !legacyWorkspaceId) return;
|
||||||
|
|
||||||
|
return prisma.workspace_users.create({
|
||||||
|
data: {
|
||||||
|
user_id: Number(legacyUserId),
|
||||||
|
workspace_id: Number(legacyWorkspaceId),
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 10: Migrate workspace_chats table
|
||||||
|
await migrateTable("workspace_chats", async (row) => {
|
||||||
|
const legacyUserId = row.user_id
|
||||||
|
? legacyMap.users?.[`user_${row.user_id}`]
|
||||||
|
: null;
|
||||||
|
const legacyWorkspaceId = row.workspaceId
|
||||||
|
? legacyMap.workspaces?.[`workspace_${row.workspaceId}`]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return prisma.workspace_chats.create({
|
||||||
|
data: {
|
||||||
|
workspaceId: Number(legacyWorkspaceId),
|
||||||
|
prompt: row.prompt,
|
||||||
|
response: row.response,
|
||||||
|
include: row.include === 1,
|
||||||
|
...(legacyUserId ? { user_id: Number(legacyUserId) } : {}),
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
lastUpdatedAt: new Date(row.lastUpdatedAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Data migration completed successfully");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Data migration failed:", error);
|
||||||
|
} finally {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function migrateTable(tableName, migrateRowFunc) {
|
||||||
|
const sqlite3 = require("sqlite3").verbose();
|
||||||
|
const { open } = require("sqlite");
|
||||||
|
const db = await open({
|
||||||
|
filename: BACKUP_PATH,
|
||||||
|
driver: sqlite3.Database,
|
||||||
|
});
|
||||||
|
const upserts = [];
|
||||||
|
const rows = await db.all(`SELECT * FROM ${tableName}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const row of rows) {
|
||||||
|
await migrateRowFunc(row);
|
||||||
|
upserts.push(row);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
console.log({ tableName, upserts });
|
||||||
|
} finally {
|
||||||
|
await db.close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
migrateData();
|
33
server/utils/telemetry/index.js
Normal file
33
server/utils/telemetry/index.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { getGitVersion } = require("../../endpoints/utils");
|
||||||
|
const { Telemetry } = require("../../models/telemetry");
|
||||||
|
|
||||||
|
// Telemetry is anonymized and your data is never read. This can be disabled by setting
|
||||||
|
// DISABLE_TELEMETRY=true in the `.env` of however you setup. Telemetry helps us determine use
|
||||||
|
// of how AnythingLLM is used and how to improve this product!
|
||||||
|
// You can see all Telemetry events by ctrl+f `Telemetry.sendEvent` calls to verify this claim.
|
||||||
|
async function setupTelemetry() {
|
||||||
|
if (process.env.DISABLE_TELEMETRY === "true") {
|
||||||
|
console.log(
|
||||||
|
`\x1b[31m[TELEMETRY DISABLED]\x1b[0m Telemetry is marked as disabled - no events will send. Telemetry helps Mintplex Labs Inc improve AnythingLLM.`
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Telemetry.isDev()) {
|
||||||
|
console.log(
|
||||||
|
`\x1b[33m[TELEMETRY STUBBED]\x1b[0m Anonymous Telemetry stubbed in development.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`\x1b[32m[TELEMETRY ENABLED]\x1b[0m Anonymous Telemetry enabled. Telemetry helps Mintplex Labs Inc improve AnythingLLM.`
|
||||||
|
);
|
||||||
|
await Telemetry.findOrCreateId();
|
||||||
|
await Telemetry.sendTelemetry("server_boot", {
|
||||||
|
commit: getGitVersion(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = setupTelemetry;
|
@ -225,7 +225,7 @@ const Chroma = {
|
|||||||
name: namespace,
|
name: namespace,
|
||||||
});
|
});
|
||||||
|
|
||||||
const knownDocuments = await DocumentVectors.where(`docId = '${docId}'`);
|
const knownDocuments = await DocumentVectors.where({ docId });
|
||||||
if (knownDocuments.length === 0) return;
|
if (knownDocuments.length === 0) return;
|
||||||
|
|
||||||
const vectorIds = knownDocuments.map((doc) => doc.vectorId);
|
const vectorIds = knownDocuments.map((doc) => doc.vectorId);
|
||||||
|
@ -118,10 +118,11 @@ const LanceDb = {
|
|||||||
|
|
||||||
const { DocumentVectors } = require("../../../models/vectors");
|
const { DocumentVectors } = require("../../../models/vectors");
|
||||||
const table = await client.openTable(namespace);
|
const table = await client.openTable(namespace);
|
||||||
const vectorIds = (await DocumentVectors.where(`docId = '${docId}'`)).map(
|
const vectorIds = (await DocumentVectors.where({ docId })).map(
|
||||||
(record) => record.vectorId
|
(record) => record.vectorId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (vectorIds.length === 0) return;
|
||||||
await table.delete(`id IN (${vectorIds.map((v) => `'${v}'`).join(",")})`);
|
await table.delete(`id IN (${vectorIds.map((v) => `'${v}'`).join(",")})`);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -182,7 +182,7 @@ const Pinecone = {
|
|||||||
const { pineconeIndex } = await this.connect();
|
const { pineconeIndex } = await this.connect();
|
||||||
if (!(await this.namespaceExists(pineconeIndex, namespace))) return;
|
if (!(await this.namespaceExists(pineconeIndex, namespace))) return;
|
||||||
|
|
||||||
const knownDocuments = await DocumentVectors.where(`docId = '${docId}'`);
|
const knownDocuments = await DocumentVectors.where({ docId });
|
||||||
if (knownDocuments.length === 0) return;
|
if (knownDocuments.length === 0) return;
|
||||||
|
|
||||||
const vectorIds = knownDocuments.map((doc) => doc.vectorId);
|
const vectorIds = knownDocuments.map((doc) => doc.vectorId);
|
||||||
|
@ -246,7 +246,7 @@ const QDrant = {
|
|||||||
const { client } = await this.connect();
|
const { client } = await this.connect();
|
||||||
if (!(await this.namespaceExists(client, namespace))) return;
|
if (!(await this.namespaceExists(client, namespace))) return;
|
||||||
|
|
||||||
const knownDocuments = await DocumentVectors.where(`docId = '${docId}'`);
|
const knownDocuments = await DocumentVectors.where({ docId });
|
||||||
if (knownDocuments.length === 0) return;
|
if (knownDocuments.length === 0) return;
|
||||||
|
|
||||||
const vectorIds = knownDocuments.map((doc) => doc.vectorId);
|
const vectorIds = knownDocuments.map((doc) => doc.vectorId);
|
||||||
|
@ -316,7 +316,7 @@ const Weaviate = {
|
|||||||
const { client } = await this.connect();
|
const { client } = await this.connect();
|
||||||
if (!(await this.namespaceExists(client, namespace))) return;
|
if (!(await this.namespaceExists(client, namespace))) return;
|
||||||
|
|
||||||
const knownDocuments = await DocumentVectors.where(`docId = '${docId}'`);
|
const knownDocuments = await DocumentVectors.where({ docId });
|
||||||
if (knownDocuments.length === 0) return;
|
if (knownDocuments.length === 0) return;
|
||||||
|
|
||||||
for (const doc of knownDocuments) {
|
for (const doc of knownDocuments) {
|
||||||
|
@ -173,6 +173,23 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
cross-fetch "^3.1.5"
|
cross-fetch "^3.1.5"
|
||||||
|
|
||||||
|
"@prisma/client@5.3.0":
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.3.0.tgz#47f07e5639993cffcf1c740a144495410562f279"
|
||||||
|
integrity sha512-cduYBlwj6oBfAUx2OI5i7t3NlpVeOtkN7pAqv0cw0B6gs4y8cY1mr8ZYywja0NUCOCqEWDkcZWBTVBwm6mnRIw==
|
||||||
|
dependencies:
|
||||||
|
"@prisma/engines-version" "5.3.0-36.e90b936d84779543cbe0e494bc8b9d7337fad8e4"
|
||||||
|
|
||||||
|
"@prisma/engines-version@5.3.0-36.e90b936d84779543cbe0e494bc8b9d7337fad8e4":
|
||||||
|
version "5.3.0-36.e90b936d84779543cbe0e494bc8b9d7337fad8e4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.3.0-36.e90b936d84779543cbe0e494bc8b9d7337fad8e4.tgz#46ee2884e04cdba1163461ef856cec882d31c836"
|
||||||
|
integrity sha512-uftIog5FQ/OUR8Vb9TzpNBJ6L+zJnBgmd1A0uPJUzuvGMU32UmeyobpdXVzST5UprKryTdWOYXQFVyiQ2OU4Nw==
|
||||||
|
|
||||||
|
"@prisma/engines@5.3.1":
|
||||||
|
version "5.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.3.1.tgz#53cc72a5ed176dc27d22305fe5569c64cc78b381"
|
||||||
|
integrity sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==
|
||||||
|
|
||||||
"@qdrant/js-client-rest@^1.4.0":
|
"@qdrant/js-client-rest@^1.4.0":
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@qdrant/js-client-rest/-/js-client-rest-1.4.0.tgz#efd341a9a30b241e7e11f773b581b3102db1adc6"
|
resolved "https://registry.yarnpkg.com/@qdrant/js-client-rest/-/js-client-rest-1.4.0.tgz#efd341a9a30b241e7e11f773b581b3102db1adc6"
|
||||||
@ -2081,6 +2098,13 @@ prettier@^2.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
||||||
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
||||||
|
|
||||||
|
prisma@^5.3.1:
|
||||||
|
version "5.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.3.1.tgz#a0932c1c1a5ed4ff449d064b193d9c7e94e8bf77"
|
||||||
|
integrity sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A==
|
||||||
|
dependencies:
|
||||||
|
"@prisma/engines" "5.3.1"
|
||||||
|
|
||||||
process-nextick-args@~2.0.0:
|
process-nextick-args@~2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||||
@ -2385,11 +2409,6 @@ sqlite@^4.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/sqlite/-/sqlite-4.2.1.tgz#d4eedfd1ad702f79110792375f4241a90c75c828"
|
resolved "https://registry.yarnpkg.com/sqlite/-/sqlite-4.2.1.tgz#d4eedfd1ad702f79110792375f4241a90c75c828"
|
||||||
integrity sha512-Tll0Ndvnwkuv5Hn6WIbh26rZiYQORuH1t5m/or9LUpSmDmmyFG89G9fKrSeugMPxwmEIXoVxqTun4LbizTs4uw==
|
integrity sha512-Tll0Ndvnwkuv5Hn6WIbh26rZiYQORuH1t5m/or9LUpSmDmmyFG89G9fKrSeugMPxwmEIXoVxqTun4LbizTs4uw==
|
||||||
|
|
||||||
sqlstring-sqlite@^0.1.1:
|
|
||||||
version "0.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/sqlstring-sqlite/-/sqlstring-sqlite-0.1.1.tgz#c8c61810663f2e59a6b0d737b70a8752bda3a078"
|
|
||||||
integrity sha512-9CAYUJ0lEUPYJrswqiqdINNSfq3jqWo/bFJ7tufdoNeSK0Fy+d1kFTxjqO9PIqza0Kri+ZtYMfPVf1aZaFOvrQ==
|
|
||||||
|
|
||||||
ssri@^8.0.0, ssri@^8.0.1:
|
ssri@^8.0.0, ssri@^8.0.1:
|
||||||
version "8.0.1"
|
version "8.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
|
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
|
||||||
|
Loading…
Reference in New Issue
Block a user