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.

224 lines
6.8 KiB

11 months ago
  1. const { Workspace } = require("../models/workspace");
  2. const { BrowserExtensionApiKey } = require("../models/browserExtensionApiKey");
  3. const { Document } = require("../models/documents");
  4. const {
  5. validBrowserExtensionApiKey,
  6. } = require("../utils/middleware/validBrowserExtensionApiKey");
  7. const { CollectorApi } = require("../utils/collectorApi");
  8. const { reqBody, multiUserMode, userFromSession } = require("../utils/http");
  9. const { validatedRequest } = require("../utils/middleware/validatedRequest");
  10. const {
  11. flexUserRoleValid,
  12. ROLES,
  13. } = require("../utils/middleware/multiUserProtected");
  14. const { Telemetry } = require("../models/telemetry");
  15. function browserExtensionEndpoints(app) {
  16. if (!app) return;
  17. app.get(
  18. "/browser-extension/check",
  19. [validBrowserExtensionApiKey],
  20. async (request, response) => {
  21. try {
  22. const user = await userFromSession(request, response);
  23. const workspaces = multiUserMode(response)
  24. ? await Workspace.whereWithUser(user)
  25. : await Workspace.where();
  26. const apiKeyId = response.locals.apiKey.id;
  27. response.status(200).json({
  28. connected: true,
  29. workspaces,
  30. apiKeyId,
  31. });
  32. } catch (error) {
  33. console.error(error);
  34. response
  35. .status(500)
  36. .json({ connected: false, error: "Failed to fetch workspaces" });
  37. }
  38. }
  39. );
  40. app.delete(
  41. "/browser-extension/disconnect",
  42. [validBrowserExtensionApiKey],
  43. async (_request, response) => {
  44. try {
  45. const apiKeyId = response.locals.apiKey.id;
  46. const { success, error } =
  47. await BrowserExtensionApiKey.delete(apiKeyId);
  48. if (!success) throw new Error(error);
  49. response.status(200).json({ success: true });
  50. } catch (error) {
  51. console.error(error);
  52. response
  53. .status(500)
  54. .json({ error: "Failed to disconnect and revoke API key" });
  55. }
  56. }
  57. );
  58. app.get(
  59. "/browser-extension/workspaces",
  60. [validBrowserExtensionApiKey],
  61. async (request, response) => {
  62. try {
  63. const user = await userFromSession(request, response);
  64. const workspaces = multiUserMode(response)
  65. ? await Workspace.whereWithUser(user)
  66. : await Workspace.where();
  67. response.status(200).json({ workspaces });
  68. } catch (error) {
  69. console.error(error);
  70. response.status(500).json({ error: "Failed to fetch workspaces" });
  71. }
  72. }
  73. );
  74. app.post(
  75. "/browser-extension/embed-content",
  76. [validBrowserExtensionApiKey],
  77. async (request, response) => {
  78. try {
  79. const { workspaceId, textContent, metadata } = reqBody(request);
  80. const user = await userFromSession(request, response);
  81. const workspace = multiUserMode(response)
  82. ? await Workspace.getWithUser(user, { id: parseInt(workspaceId) })
  83. : await Workspace.get({ id: parseInt(workspaceId) });
  84. if (!workspace) {
  85. response.status(404).json({ error: "Workspace not found" });
  86. return;
  87. }
  88. const Collector = new CollectorApi();
  89. const { success, reason, documents } = await Collector.processRawText(
  90. textContent,
  91. metadata
  92. );
  93. if (!success) {
  94. response.status(500).json({ success: false, error: reason });
  95. return;
  96. }
  97. const { failedToEmbed = [], errors = [] } = await Document.addDocuments(
  98. workspace,
  99. [documents[0].location],
  100. user?.id
  101. );
  102. if (failedToEmbed.length > 0) {
  103. response.status(500).json({ success: false, error: errors[0] });
  104. return;
  105. }
  106. await Telemetry.sendTelemetry("browser_extension_embed_content");
  107. response.status(200).json({ success: true });
  108. } catch (error) {
  109. console.error(error);
  110. response.status(500).json({ error: "Failed to embed content" });
  111. }
  112. }
  113. );
  114. app.post(
  115. "/browser-extension/upload-content",
  116. [validBrowserExtensionApiKey],
  117. async (request, response) => {
  118. try {
  119. const { textContent, metadata } = reqBody(request);
  120. const Collector = new CollectorApi();
  121. const { success, reason } = await Collector.processRawText(
  122. textContent,
  123. metadata
  124. );
  125. if (!success) {
  126. response.status(500).json({ success: false, error: reason });
  127. return;
  128. }
  129. await Telemetry.sendTelemetry("browser_extension_upload_content");
  130. response.status(200).json({ success: true });
  131. } catch (error) {
  132. console.error(error);
  133. response.status(500).json({ error: "Failed to embed content" });
  134. }
  135. }
  136. );
  137. // Internal endpoints for managing API keys
  138. app.get(
  139. "/browser-extension/api-keys",
  140. [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
  141. async (request, response) => {
  142. try {
  143. const user = await userFromSession(request, response);
  144. const apiKeys = multiUserMode(response)
  145. ? await BrowserExtensionApiKey.whereWithUser(user)
  146. : await BrowserExtensionApiKey.where();
  147. response.status(200).json({ success: true, apiKeys });
  148. } catch (error) {
  149. console.error(error);
  150. response
  151. .status(500)
  152. .json({ success: false, error: "Failed to fetch API keys" });
  153. }
  154. }
  155. );
  156. app.post(
  157. "/browser-extension/api-keys/new",
  158. [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
  159. async (request, response) => {
  160. try {
  161. const user = await userFromSession(request, response);
  162. const { apiKey, error } = await BrowserExtensionApiKey.create(
  163. user?.id || null
  164. );
  165. if (error) throw new Error(error);
  166. response.status(200).json({
  167. apiKey: apiKey.key,
  168. });
  169. } catch (error) {
  170. console.error(error);
  171. response.status(500).json({ error: "Failed to create API key" });
  172. }
  173. }
  174. );
  175. app.delete(
  176. "/browser-extension/api-keys/:id",
  177. [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
  178. async (request, response) => {
  179. try {
  180. const { id } = request.params;
  181. const user = await userFromSession(request, response);
  182. if (multiUserMode(response) && user.role !== ROLES.admin) {
  183. const apiKey = await BrowserExtensionApiKey.get({
  184. id: parseInt(id),
  185. user_id: user?.id,
  186. });
  187. if (!apiKey) {
  188. return response.status(403).json({ error: "Unauthorized" });
  189. }
  190. }
  191. const { success, error } = await BrowserExtensionApiKey.delete(id);
  192. if (!success) throw new Error(error);
  193. response.status(200).json({ success: true });
  194. } catch (error) {
  195. console.error(error);
  196. response.status(500).json({ error: "Failed to revoke API key" });
  197. }
  198. }
  199. );
  200. }
  201. module.exports = { browserExtensionEndpoints };