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.

615 lines
19 KiB

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