You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

556 lines
17 KiB

11 months ago
  1. const { ApiKey } = require("../models/apiKeys");
  2. const { Document } = require("../models/documents");
  3. const { EventLogs } = require("../models/eventLogs");
  4. const { Invite } = require("../models/invite");
  5. const { SystemSettings } = require("../models/systemSettings");
  6. const { Telemetry } = require("../models/telemetry");
  7. const { User } = require("../models/user");
  8. const { DocumentVectors } = require("../models/vectors");
  9. const { Workspace } = require("../models/workspace");
  10. const { WorkspaceChats } = require("../models/workspaceChats");
  11. const {
  12. getVectorDbClass,
  13. getEmbeddingEngineSelection,
  14. } = require("../utils/helpers");
  15. const {
  16. validRoleSelection,
  17. canModifyAdmin,
  18. validCanModify,
  19. } = require("../utils/helpers/admin");
  20. const { reqBody, userFromSession, safeJsonParse } = require("../utils/http");
  21. const {
  22. strictMultiUserRoleValid,
  23. flexUserRoleValid,
  24. ROLES,
  25. } = require("../utils/middleware/multiUserProtected");
  26. const { validatedRequest } = require("../utils/middleware/validatedRequest");
  27. const ImportedPlugin = require("../utils/agents/imported");
  28. function adminEndpoints(app) {
  29. if (!app) return;
  30. app.get(
  31. "/admin/users",
  32. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  33. async (_request, response) => {
  34. try {
  35. const users = await User.where();
  36. response.status(200).json({ users });
  37. } catch (e) {
  38. console.error(e);
  39. response.sendStatus(500).end();
  40. }
  41. }
  42. );
  43. app.post(
  44. "/admin/users/new",
  45. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  46. async (request, response) => {
  47. try {
  48. const currUser = await userFromSession(request, response);
  49. const newUserParams = reqBody(request);
  50. const roleValidation = validRoleSelection(currUser, newUserParams);
  51. if (!roleValidation.valid) {
  52. response
  53. .status(200)
  54. .json({ user: null, error: roleValidation.error });
  55. return;
  56. }
  57. const { user: newUser, error } = await User.create(newUserParams);
  58. if (!!newUser) {
  59. await EventLogs.logEvent(
  60. "user_created",
  61. {
  62. userName: newUser.username,
  63. createdBy: currUser.username,
  64. },
  65. currUser.id
  66. );
  67. }
  68. response.status(200).json({ user: newUser, error });
  69. } catch (e) {
  70. console.error(e);
  71. response.sendStatus(500).end();
  72. }
  73. }
  74. );
  75. app.post(
  76. "/admin/user/:id",
  77. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  78. async (request, response) => {
  79. try {
  80. const currUser = await userFromSession(request, response);
  81. const { id } = request.params;
  82. const updates = reqBody(request);
  83. const user = await User.get({ id: Number(id) });
  84. const canModify = validCanModify(currUser, user);
  85. if (!canModify.valid) {
  86. response.status(200).json({ success: false, error: canModify.error });
  87. return;
  88. }
  89. const roleValidation = validRoleSelection(currUser, updates);
  90. if (!roleValidation.valid) {
  91. response
  92. .status(200)
  93. .json({ success: false, error: roleValidation.error });
  94. return;
  95. }
  96. const validAdminRoleModification = await canModifyAdmin(user, updates);
  97. if (!validAdminRoleModification.valid) {
  98. response
  99. .status(200)
  100. .json({ success: false, error: validAdminRoleModification.error });
  101. return;
  102. }
  103. const { success, error } = await User.update(id, updates);
  104. response.status(200).json({ success, error });
  105. } catch (e) {
  106. console.error(e);
  107. response.sendStatus(500).end();
  108. }
  109. }
  110. );
  111. app.delete(
  112. "/admin/user/:id",
  113. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  114. async (request, response) => {
  115. try {
  116. const currUser = await userFromSession(request, response);
  117. const { id } = request.params;
  118. const user = await User.get({ id: Number(id) });
  119. const canModify = validCanModify(currUser, user);
  120. if (!canModify.valid) {
  121. response.status(200).json({ success: false, error: canModify.error });
  122. return;
  123. }
  124. await User.delete({ id: Number(id) });
  125. await EventLogs.logEvent(
  126. "user_deleted",
  127. {
  128. userName: user.username,
  129. deletedBy: currUser.username,
  130. },
  131. currUser.id
  132. );
  133. response.status(200).json({ success: true, error: null });
  134. } catch (e) {
  135. console.error(e);
  136. response.sendStatus(500).end();
  137. }
  138. }
  139. );
  140. app.get(
  141. "/admin/invites",
  142. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  143. async (_request, response) => {
  144. try {
  145. const invites = await Invite.whereWithUsers();
  146. response.status(200).json({ invites });
  147. } catch (e) {
  148. console.error(e);
  149. response.sendStatus(500).end();
  150. }
  151. }
  152. );
  153. app.post(
  154. "/admin/invite/new",
  155. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  156. async (request, response) => {
  157. try {
  158. const user = await userFromSession(request, response);
  159. const body = reqBody(request);
  160. const { invite, error } = await Invite.create({
  161. createdByUserId: user.id,
  162. workspaceIds: body?.workspaceIds || [],
  163. });
  164. await EventLogs.logEvent(
  165. "invite_created",
  166. {
  167. inviteCode: invite.code,
  168. createdBy: response.locals?.user?.username,
  169. },
  170. response.locals?.user?.id
  171. );
  172. response.status(200).json({ invite, error });
  173. } catch (e) {
  174. console.error(e);
  175. response.sendStatus(500).end();
  176. }
  177. }
  178. );
  179. app.delete(
  180. "/admin/invite/:id",
  181. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  182. async (request, response) => {
  183. try {
  184. const { id } = request.params;
  185. const { success, error } = await Invite.deactivate(id);
  186. await EventLogs.logEvent(
  187. "invite_deleted",
  188. { deletedBy: response.locals?.user?.username },
  189. response.locals?.user?.id
  190. );
  191. response.status(200).json({ success, error });
  192. } catch (e) {
  193. console.error(e);
  194. response.sendStatus(500).end();
  195. }
  196. }
  197. );
  198. app.get(
  199. "/admin/workspaces",
  200. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  201. async (_request, response) => {
  202. try {
  203. const workspaces = await Workspace.whereWithUsers();
  204. response.status(200).json({ workspaces });
  205. } catch (e) {
  206. console.error(e);
  207. response.sendStatus(500).end();
  208. }
  209. }
  210. );
  211. app.get(
  212. "/admin/workspaces/:workspaceId/users",
  213. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  214. async (request, response) => {
  215. try {
  216. const { workspaceId } = request.params;
  217. const users = await Workspace.workspaceUsers(workspaceId);
  218. response.status(200).json({ users });
  219. } catch (e) {
  220. console.error(e);
  221. response.sendStatus(500).end();
  222. }
  223. }
  224. );
  225. app.post(
  226. "/admin/workspaces/new",
  227. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  228. async (request, response) => {
  229. try {
  230. const user = await userFromSession(request, response);
  231. const { name } = reqBody(request);
  232. const { workspace, message: error } = await Workspace.new(
  233. name,
  234. user.id
  235. );
  236. response.status(200).json({ workspace, error });
  237. } catch (e) {
  238. console.error(e);
  239. response.sendStatus(500).end();
  240. }
  241. }
  242. );
  243. app.post(
  244. "/admin/workspaces/:workspaceId/update-users",
  245. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  246. async (request, response) => {
  247. try {
  248. const { workspaceId } = request.params;
  249. const { userIds } = reqBody(request);
  250. const { success, error } = await Workspace.updateUsers(
  251. workspaceId,
  252. userIds
  253. );
  254. response.status(200).json({ success, error });
  255. } catch (e) {
  256. console.error(e);
  257. response.sendStatus(500).end();
  258. }
  259. }
  260. );
  261. app.delete(
  262. "/admin/workspaces/:id",
  263. [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
  264. async (request, response) => {
  265. try {
  266. const { id } = request.params;
  267. const VectorDb = getVectorDbClass();
  268. const workspace = await Workspace.get({ id: Number(id) });
  269. if (!workspace) {
  270. response.sendStatus(404).end();
  271. return;
  272. }
  273. await WorkspaceChats.delete({ workspaceId: Number(workspace.id) });
  274. await DocumentVectors.deleteForWorkspace(Number(workspace.id));
  275. await Document.delete({ workspaceId: Number(workspace.id) });
  276. await Workspace.delete({ id: Number(workspace.id) });
  277. try {
  278. await VectorDb["delete-namespace"]({ namespace: workspace.slug });
  279. } catch (e) {
  280. console.error(e.message);
  281. }
  282. response.status(200).json({ success: true, error: null });
  283. } catch (e) {
  284. console.error(e);
  285. response.sendStatus(500).end();
  286. }
  287. }
  288. );
  289. // System preferences but only by array of labels
  290. app.get(
  291. "/admin/system-preferences-for",
  292. [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
  293. async (request, response) => {
  294. try {
  295. const requestedSettings = {};
  296. const labels = request.query.labels?.split(",") || [];
  297. const needEmbedder = [
  298. "text_splitter_chunk_size",
  299. "max_embed_chunk_size",
  300. ];
  301. const noRecord = [
  302. "max_embed_chunk_size",
  303. "agent_sql_connections",
  304. "imported_agent_skills",
  305. "feature_flags",
  306. "meta_page_title",
  307. "meta_page_favicon",
  308. ];
  309. for (const label of labels) {
  310. // Skip any settings that are not explicitly defined as public
  311. if (!SystemSettings.publicFields.includes(label)) continue;
  312. // Only get the embedder if the setting actually needs it
  313. let embedder = needEmbedder.includes(label)
  314. ? getEmbeddingEngineSelection()
  315. : null;
  316. // Only get the record from db if the setting actually needs it
  317. let setting = noRecord.includes(label)
  318. ? null
  319. : await SystemSettings.get({ label });
  320. switch (label) {
  321. case "footer_data":
  322. requestedSettings[label] = setting?.value ?? JSON.stringify([]);
  323. break;
  324. case "support_email":
  325. requestedSettings[label] = setting?.value || null;
  326. break;
  327. case "text_splitter_chunk_size":
  328. requestedSettings[label] =
  329. setting?.value || embedder?.embeddingMaxChunkLength || null;
  330. break;
  331. case "text_splitter_chunk_overlap":
  332. requestedSettings[label] = setting?.value || null;
  333. break;
  334. case "max_embed_chunk_size":
  335. requestedSettings[label] =
  336. embedder?.embeddingMaxChunkLength || 1000;
  337. break;
  338. case "agent_search_provider":
  339. requestedSettings[label] = setting?.value || null;
  340. break;
  341. case "agent_sql_connections":
  342. requestedSettings[label] =
  343. await SystemSettings.brief.agent_sql_connections();
  344. break;
  345. case "default_agent_skills":
  346. requestedSettings[label] = safeJsonParse(setting?.value, []);
  347. break;
  348. case "disabled_agent_skills":
  349. requestedSettings[label] = safeJsonParse(setting?.value, []);
  350. break;
  351. case "imported_agent_skills":
  352. requestedSettings[label] = ImportedPlugin.listImportedPlugins();
  353. break;
  354. case "custom_app_name":
  355. requestedSettings[label] = setting?.value || null;
  356. break;
  357. case "feature_flags":
  358. requestedSettings[label] =
  359. (await SystemSettings.getFeatureFlags()) || {};
  360. break;
  361. case "meta_page_title":
  362. requestedSettings[label] =
  363. await SystemSettings.getValueOrFallback({ label }, null);
  364. break;
  365. case "meta_page_favicon":
  366. requestedSettings[label] =
  367. await SystemSettings.getValueOrFallback({ label }, null);
  368. break;
  369. default:
  370. break;
  371. }
  372. }
  373. response.status(200).json({ settings: requestedSettings });
  374. } catch (e) {
  375. console.error(e);
  376. response.sendStatus(500).end();
  377. }
  378. }
  379. );
  380. // TODO: Delete this endpoint
  381. // DEPRECATED - use /admin/system-preferences-for instead with ?labels=... comma separated string of labels
  382. app.get(
  383. "/admin/system-preferences",
  384. [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
  385. async (_, response) => {
  386. try {
  387. const embedder = getEmbeddingEngineSelection();
  388. const settings = {
  389. footer_data:
  390. (await SystemSettings.get({ label: "footer_data" }))?.value ||
  391. JSON.stringify([]),
  392. support_email:
  393. (await SystemSettings.get({ label: "support_email" }))?.value ||
  394. null,
  395. text_splitter_chunk_size:
  396. (await SystemSettings.get({ label: "text_splitter_chunk_size" }))
  397. ?.value ||
  398. embedder?.embeddingMaxChunkLength ||
  399. null,
  400. text_splitter_chunk_overlap:
  401. (await SystemSettings.get({ label: "text_splitter_chunk_overlap" }))
  402. ?.value || null,
  403. max_embed_chunk_size: embedder?.embeddingMaxChunkLength || 1000,
  404. agent_search_provider:
  405. (await SystemSettings.get({ label: "agent_search_provider" }))
  406. ?.value || null,
  407. agent_sql_connections:
  408. await SystemSettings.brief.agent_sql_connections(),
  409. default_agent_skills:
  410. safeJsonParse(
  411. (await SystemSettings.get({ label: "default_agent_skills" }))
  412. ?.value,
  413. []
  414. ) || [],
  415. disabled_agent_skills:
  416. safeJsonParse(
  417. (await SystemSettings.get({ label: "disabled_agent_skills" }))
  418. ?.value,
  419. []
  420. ) || [],
  421. imported_agent_skills: ImportedPlugin.listImportedPlugins(),
  422. custom_app_name:
  423. (await SystemSettings.get({ label: "custom_app_name" }))?.value ||
  424. null,
  425. feature_flags: (await SystemSettings.getFeatureFlags()) || {},
  426. meta_page_title: await SystemSettings.getValueOrFallback(
  427. { label: "meta_page_title" },
  428. null
  429. ),
  430. meta_page_favicon: await SystemSettings.getValueOrFallback(
  431. { label: "meta_page_favicon" },
  432. null
  433. ),
  434. };
  435. response.status(200).json({ settings });
  436. } catch (e) {
  437. console.error(e);
  438. response.sendStatus(500).end();
  439. }
  440. }
  441. );
  442. app.post(
  443. "/admin/system-preferences",
  444. [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
  445. async (request, response) => {
  446. try {
  447. const updates = reqBody(request);
  448. await SystemSettings.updateSettings(updates);
  449. response.status(200).json({ success: true, error: null });
  450. } catch (e) {
  451. console.error(e);
  452. response.sendStatus(500).end();
  453. }
  454. }
  455. );
  456. app.get(
  457. "/admin/api-keys",
  458. [validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
  459. async (_request, response) => {
  460. try {
  461. const apiKeys = await ApiKey.whereWithUser({});
  462. return response.status(200).json({
  463. apiKeys,
  464. error: null,
  465. });
  466. } catch (error) {
  467. console.error(error);
  468. response.status(500).json({
  469. apiKey: null,
  470. error: "Could not find an API Keys.",
  471. });
  472. }
  473. }
  474. );
  475. app.post(
  476. "/admin/generate-api-key",
  477. [validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
  478. async (request, response) => {
  479. try {
  480. const user = await userFromSession(request, response);
  481. const { apiKey, error } = await ApiKey.create(user.id);
  482. await Telemetry.sendTelemetry("api_key_created");
  483. await EventLogs.logEvent(
  484. "api_key_created",
  485. { createdBy: user?.username },
  486. user?.id
  487. );
  488. return response.status(200).json({
  489. apiKey,
  490. error,
  491. });
  492. } catch (e) {
  493. console.error(e);
  494. response.sendStatus(500).end();
  495. }
  496. }
  497. );
  498. app.delete(
  499. "/admin/delete-api-key/:id",
  500. [validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
  501. async (request, response) => {
  502. try {
  503. const { id } = request.params;
  504. await ApiKey.delete({ id: Number(id) });
  505. await EventLogs.logEvent(
  506. "api_key_deleted",
  507. { deletedBy: response.locals?.user?.username },
  508. response?.locals?.user?.id
  509. );
  510. return response.status(200).end();
  511. } catch (e) {
  512. console.error(e);
  513. response.sendStatus(500).end();
  514. }
  515. }
  516. );
  517. }
  518. module.exports = { adminEndpoints };