const prisma = require("../utils/prisma"); const uuidAPIKey = require("uuid-apikey"); const slugify = require("slugify"); const { WorkspaceDocument } = require("./workspaceDocument"); const { selectConnector } = require("../utils/vectordatabases/providers"); const OrganizationWorkspace = { makeKey: () => { return `ws-${uuidAPIKey.create().apiKey}`; }, // Will check the relevant connector to make sure the slug and namespace/collection map safeCreate: async function ( workspaceName = "", organizationId = 0, dbConnectorRecord ) { try { if (!workspaceName) return { workspace: null, message: "No Workspace name provided." }; const connector = selectConnector(dbConnectorRecord); var slug = slugify(workspaceName, { lower: true }); const existingDbBySlug = await this.get({ slug }); const existingConnectorNamespace = await connector.namespace(slug); // If there was a name collision in the DB or the vectorstore collection - make a unique slug. // as the namespace/collection will always be the slug. if (!!existingDbBySlug || !!existingConnectorNamespace) { const slugSeed = Math.floor(10000000 + Math.random() * 90000000); slug = slugify(`${workspaceName}-${slugSeed}`, { lower: true }); } const workspace = await prisma.organization_workspaces.create({ data: { name: workspaceName, slug, fname: slug, uuid: this.makeKey(), organization_id: Number(organizationId), }, }); if (!workspace) { await db.close(); console.error("FAILED TO CREATE WORKSPACE."); return { workspace: null, message: "Could not create workspace" }; } return { workspace, message: null }; } catch (e) { console.error(e.message); return null; } }, create: async function ( workspaceName = "", organizationId = 0, presetFName = null ) { try { if (!workspaceName) return { workspace: null, message: "No Workspace name provided." }; var slug = slugify(workspaceName, { lower: true }); const existingBySlug = await this.get({ slug }); if (!!existingBySlug) { const slugSeed = Math.floor(10000000 + Math.random() * 90000000); slug = slugify(`${workspaceName}-${slugSeed}`, { lower: true }); } const workspace = await prisma.organization_workspaces.create({ data: { name: workspaceName, slug, fname: presetFName ?? workspaceName, uuid: this.makeKey(), organization_id: Number(organizationId), }, }); if (!workspace) { await db.close(); console.error("FAILED TO CREATE WORKSPACE."); return { workspace: null, message: "Could not create workspace" }; } return { workspace, message: null }; } catch (e) { console.error(e.message); return null; } }, get: async function (clause = {}) { try { const workspace = await prisma.organization_workspaces.findFirst({ where: clause, }); return workspace ? { ...workspace } : null; } catch (e) { console.error(e.message); return null; } }, forOrganization: async function ( organizationId, page = 1, pageSize = 10, includeSlugs = [], searchTerm = "" ) { try { const offset = (page - 1) * pageSize; const orgWorkspaces = [ ...(includeSlugs?.length > 0 ? await this.where({ organization_id: Number(organizationId), ...(includeSlugs.length > 0 ? { slug: { in: includeSlugs } } : {}), }) : []), ...(await this.likeWhere(organizationId, searchTerm, pageSize, offset)), ]; const slugs = new Set(); const workspaces = []; for (const workspace of orgWorkspaces) { if (slugs.has(workspace.slug)) continue; workspaces.push({ ...workspace, documentCount: await WorkspaceDocument.count({ workspace_id: Number(workspace.id), }), }); slugs.add(workspace.slug); } return workspaces; } catch (e) { console.error(e.message); return []; } }, bySlugAndOrg: async function (wsSlug, organizationId = null) { return await this.get({ slug: wsSlug, organization_id: Number(organizationId), }); }, where: async function ( clause = {}, limit = null, offset = null, orderBy = null ) { try { const workspaces = await prisma.organization_workspaces.findMany({ where: clause, ...(limit !== null ? { take: limit } : {}), ...(offset !== null ? { skip: offset } : {}), ...(orderBy !== null ? { orderBy } : {}), }); return workspaces; } catch (e) { console.error(e.message); return 0; } }, // We have to break this out separately so Prisma can find it as it does not // support % searching. likeWhere: async function ( organization_id = 0, searchTerm = "", limit = 1, offset = 0 ) { return await prisma.$queryRaw`SELECT * FROM organization_workspaces WHERE organization_id = ${organization_id} AND name LIKE ${ "%" + searchTerm + "%" } LIMIT ${limit} OFFSET ${offset}`; }, count: async function (clause = {}) { try { const count = await prisma.organization_workspaces.count({ where: clause, }); return count; } catch (e) { console.error(e.message); return 0; } }, delete: async function (clause = {}) { try { await prisma.organization_workspaces.deleteMany({ where: clause }); return true; } catch (e) { console.error(e.message); return false; } }, }; module.exports.OrganizationWorkspace = OrganizationWorkspace;