From 665553af5a82310d2b63a2574a3fca2dd56d7495 Mon Sep 17 00:00:00 2001 From: Sean Hatfield Date: Wed, 7 Aug 2024 11:36:32 -0700 Subject: [PATCH] Developer API access for embed chat widgets (#1999) * developer api access for embedded chat widgets and embed chats * lint * uncheck files * sanitize embed api inputs --------- Co-authored-by: Timothy Carambat --- server/endpoints/api/embed/index.js | 219 ++++++++++++++++++++++++++++ server/endpoints/api/index.js | 2 + server/swagger/init.js | 1 + server/swagger/openapi.json | 161 ++++++++++++++++++++ 4 files changed, 383 insertions(+) create mode 100644 server/endpoints/api/embed/index.js diff --git a/server/endpoints/api/embed/index.js b/server/endpoints/api/embed/index.js new file mode 100644 index 00000000..d4230b35 --- /dev/null +++ b/server/endpoints/api/embed/index.js @@ -0,0 +1,219 @@ +const { EmbedConfig } = require("../../../models/embedConfig"); +const { EmbedChats } = require("../../../models/embedChats"); +const { validApiKey } = require("../../../utils/middleware/validApiKey"); + +function apiEmbedEndpoints(app) { + if (!app) return; + + app.get("/v1/embed", [validApiKey], async (request, response) => { + /* + #swagger.tags = ['Embed'] + #swagger.description = 'List all active embeds' + #swagger.responses[200] = { + content: { + "application/json": { + schema: { + type: 'object', + example: { + embeds: [ + { + "id": 1, + "uuid": "embed-uuid-1", + "enabled": true, + "chat_mode": "query", + "createdAt": "2023-04-01T12:00:00Z", + "workspace": { + "id": 1, + "name": "Workspace 1" + }, + "chat_count": 10 + }, + { + "id": 2, + "uuid": "embed-uuid-2", + "enabled": false, + "chat_mode": "chat", + "createdAt": "2023-04-02T14:30:00Z", + "workspace": { + #swagger.responses[403] = { + schema: { + "$ref": "#/definitions/InvalidAPIKey" + } + } + */ + try { + const embeds = await EmbedConfig.whereWithWorkspace(); + const filteredEmbeds = embeds.map((embed) => ({ + id: embed.id, + uuid: embed.uuid, + enabled: embed.enabled, + chat_mode: embed.chat_mode, + createdAt: embed.createdAt, + workspace: { + id: embed.workspace.id, + name: embed.workspace.name, + }, + chat_count: embed._count.embed_chats, + })); + response.status(200).json({ embeds: filteredEmbeds }); + } catch (e) { + console.error(e.message, e); + response.sendStatus(500).end(); + } + }); + + app.get( + "/v1/embed/:embedUuid/chats", + [validApiKey], + async (request, response) => { + /* + #swagger.tags = ['Embed'] + #swagger.description = 'Get all chats for a specific embed' + #swagger.parameters['embedUuid'] = { + in: 'path', + description: 'UUID of the embed', + required: true, + type: 'string' + } + #swagger.responses[200] = { + content: { + "application/json": { + schema: { + type: 'object', + example: { + chats: [ + { + "id": 1, + "session_id": "session-uuid-1", + "prompt": "Hello", + "response": "Hi there!", + "createdAt": "2023-04-01T12:00:00Z" + }, + { + "id": 2, + "session_id": "session-uuid-2", + "prompt": "How are you?", + "response": "I'm doing well, thank you!", + "createdAt": "2023-04-02T14:30:00Z" + } + ] + } + } + } + } + } + #swagger.responses[403] = { + schema: { + "$ref": "#/definitions/InvalidAPIKey" + } + } + #swagger.responses[404] = { + description: "Embed not found", + } + */ + try { + const { embedUuid } = request.params; + const embed = await EmbedConfig.get({ uuid: String(embedUuid) }); + if (!embed) { + return response.status(404).json({ error: "Embed not found" }); + } + + const chats = await EmbedChats.where({ embed_id: embed.id }); + const formattedChats = chats.map((chat) => ({ + id: chat.id, + session_id: chat.session_id, + prompt: chat.prompt, + response: chat.response, + createdAt: chat.createdAt, + })); + + response.status(200).json({ chats: formattedChats }); + } catch (e) { + console.error(e.message, e); + response.sendStatus(500).end(); + } + } + ); + + app.get( + "/v1/embed/:embedUuid/chats/:sessionUuid", + [validApiKey], + async (request, response) => { + /* + #swagger.tags = ['Embed'] + #swagger.description = 'Get chats for a specific embed and session' + #swagger.parameters['embedUuid'] = { + in: 'path', + description: 'UUID of the embed', + required: true, + type: 'string' + } + #swagger.parameters['sessionUuid'] = { + in: 'path', + description: 'UUID of the session', + required: true, + type: 'string' + } + #swagger.responses[200] = { + content: { + "application/json": { + schema: { + type: 'object', + example: { + chats: [ + { + "id": 1, + "prompt": "Hello", + "response": "Hi there!", + "createdAt": "2023-04-01T12:00:00Z" + } + ] + } + } + } + } + } + #swagger.responses[403] = { + schema: { + "$ref": "#/definitions/InvalidAPIKey" + } + } + #swagger.responses[404] = { + description: "Embed or session not found", + } + */ + try { + const { embedUuid, sessionUuid } = request.params; + const embed = await EmbedConfig.get({ uuid: String(embedUuid) }); + if (!embed) { + return response.status(404).json({ error: "Embed not found" }); + } + + const chats = await EmbedChats.where({ + embed_id: embed.id, + session_id: String(sessionUuid), + }); + + if (!chats || chats.length === 0) { + return response + .status(404) + .json({ error: "No chats found for this session" }); + } + + const formattedChats = chats.map((chat) => ({ + id: chat.id, + prompt: chat.prompt, + response: chat.response, + createdAt: chat.createdAt, + })); + + response.status(200).json({ chats: formattedChats }); + } catch (e) { + console.error(e.message, e); + response.sendStatus(500).end(); + } + } + ); +} + +module.exports = { apiEmbedEndpoints }; diff --git a/server/endpoints/api/index.js b/server/endpoints/api/index.js index 60e163eb..6879c015 100644 --- a/server/endpoints/api/index.js +++ b/server/endpoints/api/index.js @@ -7,6 +7,7 @@ const { apiWorkspaceEndpoints } = require("./workspace"); const { apiWorkspaceThreadEndpoints } = require("./workspaceThread"); const { apiUserManagementEndpoints } = require("./userManagement"); const { apiOpenAICompatibleEndpoints } = require("./openai"); +const { apiEmbedEndpoints } = require("./embed"); // All endpoints must be documented and pass through the validApiKey Middleware. // How to JSDoc an endpoint @@ -22,6 +23,7 @@ function developerEndpoints(app, router) { apiWorkspaceThreadEndpoints(router); apiUserManagementEndpoints(router); apiOpenAICompatibleEndpoints(router); + apiEmbedEndpoints(router); } module.exports = { developerEndpoints }; diff --git a/server/swagger/init.js b/server/swagger/init.js index d6fdfdc6..0562230a 100644 --- a/server/swagger/init.js +++ b/server/swagger/init.js @@ -38,6 +38,7 @@ const endpointsFiles = [ "../endpoints/api/workspaceThread/index.js", "../endpoints/api/userManagement/index.js", "../endpoints/api/openai/index.js", + "../endpoints/api/embed/index.js", ]; swaggerAutogen(outputFile, endpointsFiles, doc).then(({ data }) => { diff --git a/server/swagger/openapi.json b/server/swagger/openapi.json index 554bd67b..68380196 100644 --- a/server/swagger/openapi.json +++ b/server/swagger/openapi.json @@ -3140,6 +3140,167 @@ } } } + }, + "/v1/embed": { + "get": { + "tags": [ + "Embed" + ], + "description": "List all active embeds", + "parameters": [], + "responses": { + "200": { + "description": "OK" + }, + "403": { + "description": "Forbidden" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/embed/{embedUuid}/chats": { + "get": { + "tags": [ + "Embed" + ], + "description": "Get all chats for a specific embed", + "parameters": [ + { + "name": "embedUuid", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "UUID of the embed" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "example": { + "chats": [ + { + "id": 1, + "session_id": "session-uuid-1", + "prompt": "Hello", + "response": "Hi there!", + "createdAt": "2023-04-01T12:00:00Z" + }, + { + "id": 2, + "session_id": "session-uuid-2", + "prompt": "How are you?", + "response": "I'm doing well, thank you!", + "createdAt": "2023-04-02T14:30:00Z" + } + ] + } + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidAPIKey" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/InvalidAPIKey" + } + } + } + }, + "404": { + "description": "Embed not found" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/embed/{embedUuid}/chats/{sessionUuid}": { + "get": { + "tags": [ + "Embed" + ], + "description": "Get chats for a specific embed and session", + "parameters": [ + { + "name": "embedUuid", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "UUID of the embed" + }, + { + "name": "sessionUuid", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "UUID of the session" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "example": { + "chats": [ + { + "id": 1, + "prompt": "Hello", + "response": "Hi there!", + "createdAt": "2023-04-01T12:00:00Z" + } + ] + } + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidAPIKey" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/InvalidAPIKey" + } + } + } + }, + "404": { + "description": "Embed or session not found" + }, + "500": { + "description": "Internal Server Error" + } + } + } } }, "components": {