|
|
const { ApiKey } = require("../models/apiKeys"); |
|
|
const { Document } = require("../models/documents"); |
|
|
const { EventLogs } = require("../models/eventLogs"); |
|
|
const { Invite } = require("../models/invite"); |
|
|
const { SystemSettings } = require("../models/systemSettings"); |
|
|
const { Telemetry } = require("../models/telemetry"); |
|
|
const { User } = require("../models/user"); |
|
|
const { DocumentVectors } = require("../models/vectors"); |
|
|
const { Workspace } = require("../models/workspace"); |
|
|
const { WorkspaceChats } = require("../models/workspaceChats"); |
|
|
const { |
|
|
getVectorDbClass, |
|
|
getEmbeddingEngineSelection, |
|
|
} = require("../utils/helpers"); |
|
|
const { |
|
|
validRoleSelection, |
|
|
canModifyAdmin, |
|
|
validCanModify, |
|
|
} = require("../utils/helpers/admin"); |
|
|
const { reqBody, userFromSession, safeJsonParse } = require("../utils/http"); |
|
|
const { |
|
|
strictMultiUserRoleValid, |
|
|
flexUserRoleValid, |
|
|
ROLES, |
|
|
} = require("../utils/middleware/multiUserProtected"); |
|
|
const { validatedRequest } = require("../utils/middleware/validatedRequest"); |
|
|
const ImportedPlugin = require("../utils/agents/imported"); |
|
|
const { |
|
|
simpleSSOLoginDisabledMiddleware, |
|
|
} = require("../utils/middleware/simpleSSOEnabled"); |
|
|
|
|
|
function adminEndpoints(app) { |
|
|
if (!app) return; |
|
|
|
|
|
app.get( |
|
|
"/admin/users", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (_request, response) => { |
|
|
try { |
|
|
const users = await User.where(); |
|
|
response.status(200).json({ users }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/admin/users/new", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const currUser = await userFromSession(request, response); |
|
|
const newUserParams = reqBody(request); |
|
|
const roleValidation = validRoleSelection(currUser, newUserParams); |
|
|
|
|
|
if (!roleValidation.valid) { |
|
|
response |
|
|
.status(200) |
|
|
.json({ user: null, error: roleValidation.error }); |
|
|
return; |
|
|
} |
|
|
|
|
|
const { user: newUser, error } = await User.create(newUserParams); |
|
|
if (!!newUser) { |
|
|
await EventLogs.logEvent( |
|
|
"user_created", |
|
|
{ |
|
|
userName: newUser.username, |
|
|
createdBy: currUser.username, |
|
|
}, |
|
|
currUser.id |
|
|
); |
|
|
} |
|
|
|
|
|
response.status(200).json({ user: newUser, error }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/admin/user/:id", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const currUser = await userFromSession(request, response); |
|
|
const { id } = request.params; |
|
|
const updates = reqBody(request); |
|
|
const user = await User.get({ id: Number(id) }); |
|
|
|
|
|
const canModify = validCanModify(currUser, user); |
|
|
if (!canModify.valid) { |
|
|
response.status(200).json({ success: false, error: canModify.error }); |
|
|
return; |
|
|
} |
|
|
|
|
|
const roleValidation = validRoleSelection(currUser, updates); |
|
|
if (!roleValidation.valid) { |
|
|
response |
|
|
.status(200) |
|
|
.json({ success: false, error: roleValidation.error }); |
|
|
return; |
|
|
} |
|
|
|
|
|
const validAdminRoleModification = await canModifyAdmin(user, updates); |
|
|
if (!validAdminRoleModification.valid) { |
|
|
response |
|
|
.status(200) |
|
|
.json({ success: false, error: validAdminRoleModification.error }); |
|
|
return; |
|
|
} |
|
|
|
|
|
const { success, error } = await User.update(id, updates); |
|
|
response.status(200).json({ success, error }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.delete( |
|
|
"/admin/user/:id", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const currUser = await userFromSession(request, response); |
|
|
const { id } = request.params; |
|
|
const user = await User.get({ id: Number(id) }); |
|
|
|
|
|
const canModify = validCanModify(currUser, user); |
|
|
if (!canModify.valid) { |
|
|
response.status(200).json({ success: false, error: canModify.error }); |
|
|
return; |
|
|
} |
|
|
|
|
|
await User.delete({ id: Number(id) }); |
|
|
await EventLogs.logEvent( |
|
|
"user_deleted", |
|
|
{ |
|
|
userName: user.username, |
|
|
deletedBy: currUser.username, |
|
|
}, |
|
|
currUser.id |
|
|
); |
|
|
response.status(200).json({ success: true, error: null }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.get( |
|
|
"/admin/invites", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (_request, response) => { |
|
|
try { |
|
|
const invites = await Invite.whereWithUsers(); |
|
|
response.status(200).json({ invites }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/admin/invite/new", |
|
|
[ |
|
|
validatedRequest, |
|
|
strictMultiUserRoleValid([ROLES.admin, ROLES.manager]), |
|
|
simpleSSOLoginDisabledMiddleware, |
|
|
], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const user = await userFromSession(request, response); |
|
|
const body = reqBody(request); |
|
|
const { invite, error } = await Invite.create({ |
|
|
createdByUserId: user.id, |
|
|
workspaceIds: body?.workspaceIds || [], |
|
|
}); |
|
|
|
|
|
await EventLogs.logEvent( |
|
|
"invite_created", |
|
|
{ |
|
|
inviteCode: invite.code, |
|
|
createdBy: response.locals?.user?.username, |
|
|
}, |
|
|
response.locals?.user?.id |
|
|
); |
|
|
response.status(200).json({ invite, error }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.delete( |
|
|
"/admin/invite/:id", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const { id } = request.params; |
|
|
const { success, error } = await Invite.deactivate(id); |
|
|
await EventLogs.logEvent( |
|
|
"invite_deleted", |
|
|
{ deletedBy: response.locals?.user?.username }, |
|
|
response.locals?.user?.id |
|
|
); |
|
|
response.status(200).json({ success, error }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.get( |
|
|
"/admin/workspaces", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (_request, response) => { |
|
|
try { |
|
|
const workspaces = await Workspace.whereWithUsers(); |
|
|
response.status(200).json({ workspaces }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.get( |
|
|
"/admin/workspaces/:workspaceId/users", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const { workspaceId } = request.params; |
|
|
const users = await Workspace.workspaceUsers(workspaceId); |
|
|
response.status(200).json({ users }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/admin/workspaces/new", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const user = await userFromSession(request, response); |
|
|
const { name } = reqBody(request); |
|
|
const { workspace, message: error } = await Workspace.new( |
|
|
name, |
|
|
user.id |
|
|
); |
|
|
response.status(200).json({ workspace, error }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/admin/workspaces/:workspaceId/update-users", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const { workspaceId } = request.params; |
|
|
const { userIds } = reqBody(request); |
|
|
const { success, error } = await Workspace.updateUsers( |
|
|
workspaceId, |
|
|
userIds |
|
|
); |
|
|
response.status(200).json({ success, error }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.delete( |
|
|
"/admin/workspaces/:id", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const { id } = request.params; |
|
|
const VectorDb = getVectorDbClass(); |
|
|
const workspace = await Workspace.get({ id: Number(id) }); |
|
|
if (!workspace) { |
|
|
response.sendStatus(404).end(); |
|
|
return; |
|
|
} |
|
|
|
|
|
await WorkspaceChats.delete({ workspaceId: Number(workspace.id) }); |
|
|
await DocumentVectors.deleteForWorkspace(Number(workspace.id)); |
|
|
await Document.delete({ workspaceId: Number(workspace.id) }); |
|
|
await Workspace.delete({ id: Number(workspace.id) }); |
|
|
try { |
|
|
await VectorDb["delete-namespace"]({ namespace: workspace.slug }); |
|
|
} catch (e) { |
|
|
console.error(e.message); |
|
|
} |
|
|
|
|
|
response.status(200).json({ success: true, error: null }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
|
|
|
app.get( |
|
|
"/admin/system-preferences-for", |
|
|
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const requestedSettings = {}; |
|
|
const labels = request.query.labels?.split(",") || []; |
|
|
const needEmbedder = [ |
|
|
"text_splitter_chunk_size", |
|
|
"max_embed_chunk_size", |
|
|
]; |
|
|
const noRecord = [ |
|
|
"max_embed_chunk_size", |
|
|
"agent_sql_connections", |
|
|
"imported_agent_skills", |
|
|
"feature_flags", |
|
|
"meta_page_title", |
|
|
"meta_page_favicon", |
|
|
]; |
|
|
|
|
|
for (const label of labels) { |
|
|
|
|
|
if (!SystemSettings.publicFields.includes(label)) continue; |
|
|
|
|
|
|
|
|
let embedder = needEmbedder.includes(label) |
|
|
? getEmbeddingEngineSelection() |
|
|
: null; |
|
|
|
|
|
let setting = noRecord.includes(label) |
|
|
? null |
|
|
: await SystemSettings.get({ label }); |
|
|
|
|
|
switch (label) { |
|
|
case "footer_data": |
|
|
requestedSettings[label] = setting?.value ?? JSON.stringify([]); |
|
|
break; |
|
|
case "support_email": |
|
|
requestedSettings[label] = setting?.value || null; |
|
|
break; |
|
|
case "text_splitter_chunk_size": |
|
|
requestedSettings[label] = |
|
|
setting?.value || embedder?.embeddingMaxChunkLength || null; |
|
|
break; |
|
|
case "text_splitter_chunk_overlap": |
|
|
requestedSettings[label] = setting?.value || null; |
|
|
break; |
|
|
case "max_embed_chunk_size": |
|
|
requestedSettings[label] = |
|
|
embedder?.embeddingMaxChunkLength || 1000; |
|
|
break; |
|
|
case "agent_search_provider": |
|
|
requestedSettings[label] = setting?.value || null; |
|
|
break; |
|
|
case "agent_sql_connections": |
|
|
requestedSettings[label] = |
|
|
await SystemSettings.brief.agent_sql_connections(); |
|
|
break; |
|
|
case "default_agent_skills": |
|
|
requestedSettings[label] = safeJsonParse(setting?.value, []); |
|
|
break; |
|
|
case "disabled_agent_skills": |
|
|
requestedSettings[label] = safeJsonParse(setting?.value, []); |
|
|
break; |
|
|
case "imported_agent_skills": |
|
|
requestedSettings[label] = ImportedPlugin.listImportedPlugins(); |
|
|
break; |
|
|
case "custom_app_name": |
|
|
requestedSettings[label] = setting?.value || null; |
|
|
break; |
|
|
case "feature_flags": |
|
|
requestedSettings[label] = |
|
|
(await SystemSettings.getFeatureFlags()) || {}; |
|
|
break; |
|
|
case "meta_page_title": |
|
|
requestedSettings[label] = |
|
|
await SystemSettings.getValueOrFallback({ label }, null); |
|
|
break; |
|
|
case "meta_page_favicon": |
|
|
requestedSettings[label] = |
|
|
await SystemSettings.getValueOrFallback({ label }, null); |
|
|
break; |
|
|
default: |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
response.status(200).json({ settings: requestedSettings }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
app.get( |
|
|
"/admin/system-preferences", |
|
|
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (_, response) => { |
|
|
try { |
|
|
const embedder = getEmbeddingEngineSelection(); |
|
|
const settings = { |
|
|
footer_data: |
|
|
(await SystemSettings.get({ label: "footer_data" }))?.value || |
|
|
JSON.stringify([]), |
|
|
support_email: |
|
|
(await SystemSettings.get({ label: "support_email" }))?.value || |
|
|
null, |
|
|
text_splitter_chunk_size: |
|
|
(await SystemSettings.get({ label: "text_splitter_chunk_size" })) |
|
|
?.value || |
|
|
embedder?.embeddingMaxChunkLength || |
|
|
null, |
|
|
text_splitter_chunk_overlap: |
|
|
(await SystemSettings.get({ label: "text_splitter_chunk_overlap" })) |
|
|
?.value || null, |
|
|
max_embed_chunk_size: embedder?.embeddingMaxChunkLength || 1000, |
|
|
agent_search_provider: |
|
|
(await SystemSettings.get({ label: "agent_search_provider" })) |
|
|
?.value || null, |
|
|
agent_sql_connections: |
|
|
await SystemSettings.brief.agent_sql_connections(), |
|
|
default_agent_skills: |
|
|
safeJsonParse( |
|
|
(await SystemSettings.get({ label: "default_agent_skills" })) |
|
|
?.value, |
|
|
[] |
|
|
) || [], |
|
|
disabled_agent_skills: |
|
|
safeJsonParse( |
|
|
(await SystemSettings.get({ label: "disabled_agent_skills" })) |
|
|
?.value, |
|
|
[] |
|
|
) || [], |
|
|
imported_agent_skills: ImportedPlugin.listImportedPlugins(), |
|
|
custom_app_name: |
|
|
(await SystemSettings.get({ label: "custom_app_name" }))?.value || |
|
|
null, |
|
|
feature_flags: (await SystemSettings.getFeatureFlags()) || {}, |
|
|
meta_page_title: await SystemSettings.getValueOrFallback( |
|
|
{ label: "meta_page_title" }, |
|
|
null |
|
|
), |
|
|
meta_page_favicon: await SystemSettings.getValueOrFallback( |
|
|
{ label: "meta_page_favicon" }, |
|
|
null |
|
|
), |
|
|
}; |
|
|
response.status(200).json({ settings }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/admin/system-preferences", |
|
|
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const updates = reqBody(request); |
|
|
await SystemSettings.updateSettings(updates); |
|
|
response.status(200).json({ success: true, error: null }); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.get( |
|
|
"/admin/api-keys", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])], |
|
|
async (_request, response) => { |
|
|
try { |
|
|
const apiKeys = await ApiKey.whereWithUser({}); |
|
|
return response.status(200).json({ |
|
|
apiKeys, |
|
|
error: null, |
|
|
}); |
|
|
} catch (error) { |
|
|
console.error(error); |
|
|
response.status(500).json({ |
|
|
apiKey: null, |
|
|
error: "Could not find an API Keys.", |
|
|
}); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/admin/generate-api-key", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const user = await userFromSession(request, response); |
|
|
const { apiKey, error } = await ApiKey.create(user.id); |
|
|
await EventLogs.logEvent( |
|
|
"api_key_created", |
|
|
{ createdBy: user?.username }, |
|
|
user?.id |
|
|
); |
|
|
return response.status(200).json({ |
|
|
apiKey, |
|
|
error, |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.delete( |
|
|
"/admin/delete-api-key/:id", |
|
|
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const { id } = request.params; |
|
|
if (!id || isNaN(Number(id))) return response.sendStatus(400).end(); |
|
|
await ApiKey.delete({ id: Number(id) }); |
|
|
|
|
|
await EventLogs.logEvent( |
|
|
"api_key_deleted", |
|
|
{ deletedBy: response.locals?.user?.username }, |
|
|
response?.locals?.user?.id |
|
|
); |
|
|
return response.status(200).end(); |
|
|
} catch (e) { |
|
|
console.error(e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
} |
|
|
|
|
|
module.exports = { adminEndpoints }; |
|
|
|