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.

775 lines
21 KiB

11 months ago
  1. const { EventLogs } = require("../../../models/eventLogs");
  2. const { Invite } = require("../../../models/invite");
  3. const { SystemSettings } = require("../../../models/systemSettings");
  4. const { User } = require("../../../models/user");
  5. const { Workspace } = require("../../../models/workspace");
  6. const { WorkspaceChats } = require("../../../models/workspaceChats");
  7. const { WorkspaceUser } = require("../../../models/workspaceUsers");
  8. const { canModifyAdmin } = require("../../../utils/helpers/admin");
  9. const { multiUserMode, reqBody } = require("../../../utils/http");
  10. const { validApiKey } = require("../../../utils/middleware/validApiKey");
  11. function apiAdminEndpoints(app) {
  12. if (!app) return;
  13. app.get("/v1/admin/is-multi-user-mode", [validApiKey], (_, response) => {
  14. /*
  15. #swagger.tags = ['Admin']
  16. #swagger.description = 'Check to see if the instance is in multi-user-mode first. Methods are disabled until multi user mode is enabled via the UI.'
  17. #swagger.responses[200] = {
  18. content: {
  19. "application/json": {
  20. schema: {
  21. type: 'object',
  22. example: {
  23. "isMultiUser": true
  24. }
  25. }
  26. }
  27. }
  28. }
  29. #swagger.responses[403] = {
  30. schema: {
  31. "$ref": "#/definitions/InvalidAPIKey"
  32. }
  33. }
  34. */
  35. const isMultiUser = multiUserMode(response);
  36. response.status(200).json({ isMultiUser });
  37. });
  38. app.get("/v1/admin/users", [validApiKey], async (request, response) => {
  39. /*
  40. #swagger.tags = ['Admin']
  41. #swagger.description = 'Check to see if the instance is in multi-user-mode first. Methods are disabled until multi user mode is enabled via the UI.'
  42. #swagger.responses[200] = {
  43. content: {
  44. "application/json": {
  45. schema: {
  46. type: 'object',
  47. example: {
  48. "users": [
  49. {
  50. username: "sample-sam",
  51. role: 'default',
  52. }
  53. ]
  54. }
  55. }
  56. }
  57. }
  58. }
  59. #swagger.responses[403] = {
  60. schema: {
  61. "$ref": "#/definitions/InvalidAPIKey"
  62. }
  63. }
  64. #swagger.responses[401] = {
  65. description: "Instance is not in Multi-User mode. Method denied",
  66. }
  67. */
  68. try {
  69. if (!multiUserMode(response)) {
  70. response.sendStatus(401).end();
  71. return;
  72. }
  73. const users = await User.where();
  74. response.status(200).json({ users });
  75. } catch (e) {
  76. console.error(e);
  77. response.sendStatus(500).end();
  78. }
  79. });
  80. app.post("/v1/admin/users/new", [validApiKey], async (request, response) => {
  81. /*
  82. #swagger.tags = ['Admin']
  83. #swagger.description = 'Create a new user with username and password. Methods are disabled until multi user mode is enabled via the UI.'
  84. #swagger.requestBody = {
  85. description: 'Key pair object that will define the new user to add to the system.',
  86. required: true,
  87. content: {
  88. "application/json": {
  89. example: {
  90. username: "sample-sam",
  91. password: 'hunter2',
  92. role: 'default | admin'
  93. }
  94. }
  95. }
  96. }
  97. #swagger.responses[200] = {
  98. content: {
  99. "application/json": {
  100. schema: {
  101. type: 'object',
  102. example: {
  103. user: {
  104. id: 1,
  105. username: 'sample-sam',
  106. role: 'default',
  107. },
  108. error: null,
  109. }
  110. }
  111. }
  112. }
  113. }
  114. #swagger.responses[403] = {
  115. schema: {
  116. "$ref": "#/definitions/InvalidAPIKey"
  117. }
  118. }
  119. #swagger.responses[401] = {
  120. description: "Instance is not in Multi-User mode. Method denied",
  121. }
  122. */
  123. try {
  124. if (!multiUserMode(response)) {
  125. response.sendStatus(401).end();
  126. return;
  127. }
  128. const newUserParams = reqBody(request);
  129. const { user: newUser, error } = await User.create(newUserParams);
  130. response.status(newUser ? 200 : 400).json({ user: newUser, error });
  131. } catch (e) {
  132. console.error(e);
  133. response.sendStatus(500).end();
  134. }
  135. });
  136. app.post("/v1/admin/users/:id", [validApiKey], async (request, response) => {
  137. /*
  138. #swagger.tags = ['Admin']
  139. #swagger.parameters['id'] = {
  140. in: 'path',
  141. description: 'id of the user in the database.',
  142. required: true,
  143. type: 'string'
  144. }
  145. #swagger.description = 'Update existing user settings. Methods are disabled until multi user mode is enabled via the UI.'
  146. #swagger.requestBody = {
  147. description: 'Key pair object that will update the found user. All fields are optional and will not update unless specified.',
  148. required: true,
  149. content: {
  150. "application/json": {
  151. example: {
  152. username: "sample-sam",
  153. password: 'hunter2',
  154. role: 'default | admin',
  155. suspended: 0,
  156. }
  157. }
  158. }
  159. }
  160. #swagger.responses[200] = {
  161. content: {
  162. "application/json": {
  163. schema: {
  164. type: 'object',
  165. example: {
  166. success: true,
  167. error: null,
  168. }
  169. }
  170. }
  171. }
  172. }
  173. #swagger.responses[403] = {
  174. schema: {
  175. "$ref": "#/definitions/InvalidAPIKey"
  176. }
  177. }
  178. #swagger.responses[401] = {
  179. description: "Instance is not in Multi-User mode. Method denied",
  180. }
  181. */
  182. try {
  183. if (!multiUserMode(response)) {
  184. response.sendStatus(401).end();
  185. return;
  186. }
  187. const { id } = request.params;
  188. const updates = reqBody(request);
  189. const user = await User.get({ id: Number(id) });
  190. const validAdminRoleModification = await canModifyAdmin(user, updates);
  191. if (!validAdminRoleModification.valid) {
  192. response
  193. .status(200)
  194. .json({ success: false, error: validAdminRoleModification.error });
  195. return;
  196. }
  197. const { success, error } = await User.update(id, updates);
  198. response.status(200).json({ success, error });
  199. } catch (e) {
  200. console.error(e);
  201. response.sendStatus(500).end();
  202. }
  203. });
  204. app.delete(
  205. "/v1/admin/users/:id",
  206. [validApiKey],
  207. async (request, response) => {
  208. /*
  209. #swagger.tags = ['Admin']
  210. #swagger.description = 'Delete existing user by id. Methods are disabled until multi user mode is enabled via the UI.'
  211. #swagger.parameters['id'] = {
  212. in: 'path',
  213. description: 'id of the user in the database.',
  214. required: true,
  215. type: 'string'
  216. }
  217. #swagger.responses[200] = {
  218. content: {
  219. "application/json": {
  220. schema: {
  221. type: 'object',
  222. example: {
  223. success: true,
  224. error: null,
  225. }
  226. }
  227. }
  228. }
  229. }
  230. #swagger.responses[403] = {
  231. schema: {
  232. "$ref": "#/definitions/InvalidAPIKey"
  233. }
  234. }
  235. #swagger.responses[401] = {
  236. description: "Instance is not in Multi-User mode. Method denied",
  237. }
  238. */
  239. try {
  240. if (!multiUserMode(response)) {
  241. response.sendStatus(401).end();
  242. return;
  243. }
  244. const { id } = request.params;
  245. const user = await User.get({ id: Number(id) });
  246. await User.delete({ id: user.id });
  247. await EventLogs.logEvent("api_user_deleted", {
  248. userName: user.username,
  249. });
  250. response.status(200).json({ success: true, error: null });
  251. } catch (e) {
  252. console.error(e);
  253. response.sendStatus(500).end();
  254. }
  255. }
  256. );
  257. app.get("/v1/admin/invites", [validApiKey], async (request, response) => {
  258. /*
  259. #swagger.tags = ['Admin']
  260. #swagger.description = 'List all existing invitations to instance regardless of status. Methods are disabled until multi user mode is enabled via the UI.'
  261. #swagger.responses[200] = {
  262. content: {
  263. "application/json": {
  264. schema: {
  265. type: 'object',
  266. example: {
  267. "invites": [
  268. {
  269. id: 1,
  270. status: "pending",
  271. code: 'abc-123',
  272. claimedBy: null
  273. }
  274. ]
  275. }
  276. }
  277. }
  278. }
  279. }
  280. #swagger.responses[403] = {
  281. schema: {
  282. "$ref": "#/definitions/InvalidAPIKey"
  283. }
  284. }
  285. #swagger.responses[401] = {
  286. description: "Instance is not in Multi-User mode. Method denied",
  287. }
  288. */
  289. try {
  290. if (!multiUserMode(response)) {
  291. response.sendStatus(401).end();
  292. return;
  293. }
  294. const invites = await Invite.whereWithUsers();
  295. response.status(200).json({ invites });
  296. } catch (e) {
  297. console.error(e);
  298. response.sendStatus(500).end();
  299. }
  300. });
  301. app.post("/v1/admin/invite/new", [validApiKey], async (request, response) => {
  302. /*
  303. #swagger.tags = ['Admin']
  304. #swagger.description = 'Create a new invite code for someone to use to register with instance. Methods are disabled until multi user mode is enabled via the UI.'
  305. #swagger.requestBody = {
  306. description: 'Request body for creation parameters of the invitation',
  307. required: false,
  308. content: {
  309. "application/json": {
  310. example: {
  311. workspaceIds: [1,2,45],
  312. }
  313. }
  314. }
  315. }
  316. #swagger.responses[200] = {
  317. content: {
  318. "application/json": {
  319. schema: {
  320. type: 'object',
  321. example: {
  322. invite: {
  323. id: 1,
  324. status: "pending",
  325. code: 'abc-123',
  326. },
  327. error: null,
  328. }
  329. }
  330. }
  331. }
  332. }
  333. #swagger.responses[403] = {
  334. schema: {
  335. "$ref": "#/definitions/InvalidAPIKey"
  336. }
  337. }
  338. #swagger.responses[401] = {
  339. description: "Instance is not in Multi-User mode. Method denied",
  340. }
  341. */
  342. try {
  343. if (!multiUserMode(response)) {
  344. response.sendStatus(401).end();
  345. return;
  346. }
  347. const body = reqBody(request);
  348. const { invite, error } = await Invite.create({
  349. workspaceIds: body?.workspaceIds ?? [],
  350. });
  351. response.status(200).json({ invite, error });
  352. } catch (e) {
  353. console.error(e);
  354. response.sendStatus(500).end();
  355. }
  356. });
  357. app.delete(
  358. "/v1/admin/invite/:id",
  359. [validApiKey],
  360. async (request, response) => {
  361. /*
  362. #swagger.tags = ['Admin']
  363. #swagger.description = 'Deactivates (soft-delete) invite by id. Methods are disabled until multi user mode is enabled via the UI.'
  364. #swagger.parameters['id'] = {
  365. in: 'path',
  366. description: 'id of the invite in the database.',
  367. required: true,
  368. type: 'string'
  369. }
  370. #swagger.responses[200] = {
  371. content: {
  372. "application/json": {
  373. schema: {
  374. type: 'object',
  375. example: {
  376. success: true,
  377. error: null,
  378. }
  379. }
  380. }
  381. }
  382. }
  383. #swagger.responses[403] = {
  384. schema: {
  385. "$ref": "#/definitions/InvalidAPIKey"
  386. }
  387. }
  388. #swagger.responses[401] = {
  389. description: "Instance is not in Multi-User mode. Method denied",
  390. }
  391. */
  392. try {
  393. if (!multiUserMode(response)) {
  394. response.sendStatus(401).end();
  395. return;
  396. }
  397. const { id } = request.params;
  398. const { success, error } = await Invite.deactivate(id);
  399. response.status(200).json({ success, error });
  400. } catch (e) {
  401. console.error(e);
  402. response.sendStatus(500).end();
  403. }
  404. }
  405. );
  406. app.get(
  407. "/v1/admin/workspaces/:workspaceId/users",
  408. [validApiKey],
  409. async (request, response) => {
  410. /*
  411. #swagger.tags = ['Admin']
  412. #swagger.parameters['workspaceId'] = {
  413. in: 'path',
  414. description: 'id of the workspace.',
  415. required: true,
  416. type: 'string'
  417. }
  418. #swagger.description = 'Retrieve a list of users with permissions to access the specified workspace.'
  419. #swagger.responses[200] = {
  420. content: {
  421. "application/json": {
  422. schema: {
  423. type: 'object',
  424. example: {
  425. users: [
  426. {"userId": 1, "role": "admin"},
  427. {"userId": 2, "role": "member"}
  428. ]
  429. }
  430. }
  431. }
  432. }
  433. }
  434. #swagger.responses[403] = {
  435. schema: {
  436. "$ref": "#/definitions/InvalidAPIKey"
  437. }
  438. }
  439. #swagger.responses[401] = {
  440. description: "Instance is not in Multi-User mode. Method denied",
  441. }
  442. */
  443. try {
  444. if (!multiUserMode(response)) {
  445. response.sendStatus(401).end();
  446. return;
  447. }
  448. const workspaceId = request.params.workspaceId;
  449. const users = await Workspace.workspaceUsers(workspaceId);
  450. response.status(200).json({ users });
  451. } catch (e) {
  452. console.error(e);
  453. response.sendStatus(500).end();
  454. }
  455. }
  456. );
  457. app.post(
  458. "/v1/admin/workspaces/:workspaceId/update-users",
  459. [validApiKey],
  460. async (request, response) => {
  461. /*
  462. #swagger.tags = ['Admin']
  463. #swagger.deprecated = true
  464. #swagger.parameters['workspaceId'] = {
  465. in: 'path',
  466. description: 'id of the workspace in the database.',
  467. required: true,
  468. type: 'string'
  469. }
  470. #swagger.description = 'Overwrite workspace permissions to only be accessible by the given user ids and admins. Methods are disabled until multi user mode is enabled via the UI.'
  471. #swagger.requestBody = {
  472. description: 'Entire array of user ids who can access the workspace. All fields are optional and will not update unless specified.',
  473. required: true,
  474. content: {
  475. "application/json": {
  476. example: {
  477. userIds: [1,2,4,12],
  478. }
  479. }
  480. }
  481. }
  482. #swagger.responses[200] = {
  483. content: {
  484. "application/json": {
  485. schema: {
  486. type: 'object',
  487. example: {
  488. success: true,
  489. error: null,
  490. }
  491. }
  492. }
  493. }
  494. }
  495. #swagger.responses[403] = {
  496. schema: {
  497. "$ref": "#/definitions/InvalidAPIKey"
  498. }
  499. }
  500. #swagger.responses[401] = {
  501. description: "Instance is not in Multi-User mode. Method denied",
  502. }
  503. */
  504. try {
  505. if (!multiUserMode(response)) {
  506. response.sendStatus(401).end();
  507. return;
  508. }
  509. const { workspaceId } = request.params;
  510. const { userIds } = reqBody(request);
  511. const { success, error } = await Workspace.updateUsers(
  512. workspaceId,
  513. userIds
  514. );
  515. response.status(200).json({ success, error });
  516. } catch (e) {
  517. console.error(e);
  518. response.sendStatus(500).end();
  519. }
  520. }
  521. );
  522. app.post(
  523. "/v1/admin/workspaces/:workspaceSlug/manage-users",
  524. [validApiKey],
  525. async (request, response) => {
  526. /*
  527. #swagger.tags = ['Admin']
  528. #swagger.parameters['workspaceSlug'] = {
  529. in: 'path',
  530. description: 'slug of the workspace in the database',
  531. required: true,
  532. type: 'string'
  533. }
  534. #swagger.description = 'Set workspace permissions to be accessible by the given user ids and admins. Methods are disabled until multi user mode is enabled via the UI.'
  535. #swagger.requestBody = {
  536. description: 'Array of user ids who will be given access to the target workspace. <code>reset</code> will remove all existing users from the workspace and only add the new users - default <code>false</code>.',
  537. required: true,
  538. content: {
  539. "application/json": {
  540. example: {
  541. userIds: [1,2,4,12],
  542. reset: false
  543. }
  544. }
  545. }
  546. }
  547. #swagger.responses[200] = {
  548. content: {
  549. "application/json": {
  550. schema: {
  551. type: 'object',
  552. example: {
  553. success: true,
  554. error: null,
  555. users: [
  556. {"userId": 1, "username": "main-admin", "role": "admin"},
  557. {"userId": 2, "username": "sample-sam", "role": "default"}
  558. ]
  559. }
  560. }
  561. }
  562. }
  563. }
  564. #swagger.responses[403] = {
  565. schema: {
  566. "$ref": "#/definitions/InvalidAPIKey"
  567. }
  568. }
  569. #swagger.responses[401] = {
  570. description: "Instance is not in Multi-User mode. Method denied",
  571. }
  572. */
  573. try {
  574. if (!multiUserMode(response)) {
  575. response.sendStatus(401).end();
  576. return;
  577. }
  578. const { workspaceSlug } = request.params;
  579. const { userIds: _uids, reset = false } = reqBody(request);
  580. const userIds = (
  581. await User.where({ id: { in: _uids.map(Number) } })
  582. ).map((user) => user.id);
  583. const workspace = await Workspace.get({ slug: String(workspaceSlug) });
  584. const workspaceUsers = await Workspace.workspaceUsers(workspace.id);
  585. if (!workspace) {
  586. response.status(404).json({
  587. success: false,
  588. error: `Workspace ${workspaceSlug} not found`,
  589. users: workspaceUsers,
  590. });
  591. return;
  592. }
  593. if (userIds.length === 0) {
  594. response.status(404).json({
  595. success: false,
  596. error: `No valid user IDs provided.`,
  597. users: workspaceUsers,
  598. });
  599. return;
  600. }
  601. // Reset all users in the workspace and add the new users as the only users in the workspace
  602. if (reset) {
  603. const { success, error } = await Workspace.updateUsers(
  604. workspace.id,
  605. userIds
  606. );
  607. return response.status(200).json({
  608. success,
  609. error,
  610. users: await Workspace.workspaceUsers(workspace.id),
  611. });
  612. }
  613. // Add new users to the workspace if they are not already in the workspace
  614. const existingUserIds = workspaceUsers.map((user) => user.userId);
  615. const usersToAdd = userIds.filter(
  616. (userId) => !existingUserIds.includes(userId)
  617. );
  618. if (usersToAdd.length > 0)
  619. await WorkspaceUser.createManyUsers(usersToAdd, workspace.id);
  620. response.status(200).json({
  621. success: true,
  622. error: null,
  623. users: await Workspace.workspaceUsers(workspace.id),
  624. });
  625. } catch (e) {
  626. console.error(e);
  627. response.sendStatus(500).end();
  628. }
  629. }
  630. );
  631. app.post(
  632. "/v1/admin/workspace-chats",
  633. [validApiKey],
  634. async (request, response) => {
  635. /*
  636. #swagger.tags = ['Admin']
  637. #swagger.description = 'All chats in the system ordered by most recent. Methods are disabled until multi user mode is enabled via the UI.'
  638. #swagger.requestBody = {
  639. description: 'Page offset to show of workspace chats. All fields are optional and will not update unless specified.',
  640. required: false,
  641. content: {
  642. "application/json": {
  643. example: {
  644. offset: 2,
  645. }
  646. }
  647. }
  648. }
  649. #swagger.responses[200] = {
  650. content: {
  651. "application/json": {
  652. schema: {
  653. type: 'object',
  654. example: {
  655. success: true,
  656. error: null,
  657. }
  658. }
  659. }
  660. }
  661. }
  662. #swagger.responses[403] = {
  663. schema: {
  664. "$ref": "#/definitions/InvalidAPIKey"
  665. }
  666. }
  667. */
  668. try {
  669. const pgSize = 20;
  670. const { offset = 0 } = reqBody(request);
  671. const chats = await WorkspaceChats.whereWithData(
  672. {},
  673. pgSize,
  674. offset * pgSize,
  675. { id: "desc" }
  676. );
  677. const hasPages = (await WorkspaceChats.count()) > (offset + 1) * pgSize;
  678. response.status(200).json({ chats: chats, hasPages });
  679. } catch (e) {
  680. console.error(e);
  681. response.sendStatus(500).end();
  682. }
  683. }
  684. );
  685. app.post(
  686. "/v1/admin/preferences",
  687. [validApiKey],
  688. async (request, response) => {
  689. /*
  690. #swagger.tags = ['Admin']
  691. #swagger.description = 'Update multi-user preferences for instance. Methods are disabled until multi user mode is enabled via the UI.'
  692. #swagger.requestBody = {
  693. description: 'Object with setting key and new value to set. All keys are optional and will not update unless specified.',
  694. required: true,
  695. content: {
  696. "application/json": {
  697. example: {
  698. support_email: "support@example.com",
  699. }
  700. }
  701. }
  702. }
  703. #swagger.responses[200] = {
  704. content: {
  705. "application/json": {
  706. schema: {
  707. type: 'object',
  708. example: {
  709. success: true,
  710. error: null,
  711. }
  712. }
  713. }
  714. }
  715. }
  716. #swagger.responses[403] = {
  717. schema: {
  718. "$ref": "#/definitions/InvalidAPIKey"
  719. }
  720. }
  721. #swagger.responses[401] = {
  722. description: "Instance is not in Multi-User mode. Method denied",
  723. }
  724. */
  725. try {
  726. if (!multiUserMode(response)) {
  727. response.sendStatus(401).end();
  728. return;
  729. }
  730. const updates = reqBody(request);
  731. await SystemSettings.updateSettings(updates);
  732. response.status(200).json({ success: true, error: null });
  733. } catch (e) {
  734. console.error(e);
  735. response.sendStatus(500).end();
  736. }
  737. }
  738. );
  739. }
  740. module.exports = { apiAdminEndpoints };