feature: modularized express routes for reusability, testability, composability and performance (#2030)

* transition GET /api/v1/apikey

* transition POST /api/v1/apikey

* transition PUT /api/v1/apikey/:id

* transition DELETE /api/v1/apikey/:id

* Enable e2e tests for api/v1/apikey routes

* remove unused addChatflowsCount

* Enable e2e tests for api/v1/variables routes

* Enable Cypress in GitHub Action

* Update main.yml

* Update main.yml

* Transition GET /api/v1/variables

* Enable cypress on github workflow

* Transition POST /api/v1/variables

* Transition PUT /api/v1/variables

* Transition DELETE /api/v1/variables

* Transition GET /api/v1/variables

* Transition GET /api/v1/chatflows

* Transition GET /api/v1/chatflows/:id

* Transition POST /api/v1/chatflows

* Transition DELETE /api/v1/chatflows/:id

* Transition PUT /api/v1/chatflows/:id

* Transition GET /api/v1/chatflows/apikey/:apiKey

* Transition GET /api/v1/credentials

* Transition POST /api/v1/credentials

* Transition GET /api/v1/credentials/:id

* Transition PUT /api/v1/credentials/:id

* Transition DELETE /api/v1/credentials/:id

* Transition GET /api/v1/tools

* Transition GET /api/v1/tools/:id

* Transition POST /api/v1/tools

* Transition PUT & DELETE /api/v1/tools/:id

* Transition /api/v1/assistants routes

* Transition /api/v1/nodes routes

* Transition GET /api/v1/chatflows-streaming/:id & GET /api/v1/chatflows-uploads/:id

* wip-all-routes

* Transition GET /api/v1/public-chatflows/:id & /api/v1/public-chatbotConfig/:id

* Remove ts-ignore annotations

* Transition GET /api/v1/chatmessage/:id

* Transition POST /api/v1/chatmessage/:id

* delete /api/v1/chatmessage/:id

* transition /api/v1/feedback/:id routes

* transition /api/v1/stats/:id

* Transition GET /api/v1/openai-assistants/:id

* Transition GET /api/v1/openai-assistants

* Transition POST /api/v1/openai-assistants-file

* transition GET /api/v1/get-upload-path

* transition GET /api/v1/get-upload-file

* transition GET /api/v1/flow-config/:id

* transition POST /api/v1/node-config

* transition GET /api/v1/version

* transition GET /api/v1/fetch-links

* transition POST /api/v1/vector/upsert/:id

* transition POST /api/v1/vector/internal-upsert/:id

* transition POST /api/v1/load-prompt

* Update index.ts

* transition POST /api/v1/prompts-list

* transition predictions

* Update index.ts

* transition GET /api/v1/marketplaces/templates

* Router update modularity cleanup

* extend request interface - express namespace

* Update index.ts

* add errorMiddleware

* Add custom application error handler

* Fix pnpm lock file

* prediction return and vector upsert

* Move the getUploadsConfig into its own file

* Remove lint warnings

* fix undefined variable value

* Fix node-load-method api call

* standardize the error message display

* Apply review comment bugfixes

* Update index.ts

* standardize error message display  in snack notifications

* Error message standard in the UI

* Rename flowXpressApp to appServer

* Upload middleware fix and axios update

* fix async await

---------

Co-authored-by: Henry <hzj94@hotmail.com>
This commit is contained in:
Octavian FlowiseAI
2024-04-02 17:44:04 +02:00
committed by GitHub
parent ea255db15d
commit 957694a912
136 changed files with 5347 additions and 2380 deletions
@@ -0,0 +1,79 @@
import { Request, Response, NextFunction } from 'express'
import { StatusCodes } from 'http-status-codes'
import { ApiError } from '../../errors/apiError'
import apikeyService from '../../services/apikey'
// Get api keys
const getAllApiKeys = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await apikeyService.getAllApiKeys()
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const createApiKey = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body.keyName === 'undefined' || req.body.keyName === '') {
throw new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.createApiKey - keyName not provided!`)
}
const apiResponse = await apikeyService.createApiKey(req.body.keyName)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
// Update api key
const updateApiKey = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.updateApiKey - id not provided!`)
}
if (typeof req.body.keyName === 'undefined' || req.body.keyName === '') {
new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.updateApiKey - keyName not provided!`)
}
const apiResponse = await apikeyService.updateApiKey(req.params.id, req.body.keyName)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
// Delete api key
const deleteApiKey = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.deleteApiKey - id not provided!`)
}
const apiResponse = await apikeyService.deleteApiKey(req.params.id)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
// Verify api key
const verifyApiKey = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.apiKey === 'undefined' || req.params.apiKey === '') {
new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.verifyApiKey - apiKey not provided!`)
}
const apiResponse = await apikeyService.verifyApiKey(req.params.apiKey)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
createApiKey,
deleteApiKey,
getAllApiKeys,
updateApiKey,
verifyApiKey
}
@@ -0,0 +1,85 @@
import { Request, Response, NextFunction } from 'express'
import assistantsService from '../../services/assistants'
const creatAssistant = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: assistantsController.creatAssistant - body not provided!`)
}
const apiResponse = await assistantsService.creatAssistant(req.body)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const deleteAssistant = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: assistantsController.deleteAssistant - id not provided!`)
}
const apiResponse = await assistantsService.deleteAssistant(req.params.id, req.query.isDeleteBoth)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getAllAssistants = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await assistantsService.getAllAssistants()
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getAssistantById = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: assistantsController.getAssistantById - id not provided!`)
}
const apiResponse = await assistantsService.getAssistantById(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const updateAssistant = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: assistantsController.updateAssistant - id not provided!`)
}
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: assistantsController.updateAssistant - body not provided!`)
}
const apiResponse = await assistantsService.updateAssistant(req.params.id, req.body)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
creatAssistant,
deleteAssistant,
getAllAssistants,
getAssistantById,
updateAssistant
}
@@ -0,0 +1,156 @@
import { Request, Response, NextFunction } from 'express'
import { chatType, IReactFlowObject } from '../../Interface'
import chatflowsService from '../../services/chatflows'
import chatMessagesService from '../../services/chat-messages'
import { clearSessionMemory } from '../../utils'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { FindOptionsWhere } from 'typeorm'
import { ChatMessage } from '../../database/entities/ChatMessage'
const createChatMessage = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error('Error: chatMessagesController.createChatMessage - request body not provided!')
}
const apiResponse = await chatMessagesService.createChatMessage(req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getAllChatMessages = async (req: Request, res: Response, next: NextFunction) => {
try {
let chatTypeFilter = req.query?.chatType as chatType | undefined
if (chatTypeFilter) {
try {
const chatTypeFilterArray = JSON.parse(chatTypeFilter)
if (chatTypeFilterArray.includes(chatType.EXTERNAL) && chatTypeFilterArray.includes(chatType.INTERNAL)) {
chatTypeFilter = undefined
} else if (chatTypeFilterArray.includes(chatType.EXTERNAL)) {
chatTypeFilter = chatType.EXTERNAL
} else if (chatTypeFilterArray.includes(chatType.INTERNAL)) {
chatTypeFilter = chatType.INTERNAL
}
} catch (e) {
return res.status(500).send(e)
}
}
const sortOrder = req.query?.order as string | undefined
const chatId = req.query?.chatId as string | undefined
const memoryType = req.query?.memoryType as string | undefined
const sessionId = req.query?.sessionId as string | undefined
const messageId = req.query?.messageId as string | undefined
const startDate = req.query?.startDate as string | undefined
const endDate = req.query?.endDate as string | undefined
const feedback = req.query?.feedback as boolean | undefined
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: chatMessageController.getAllChatMessages - id not provided!`)
}
const apiResponse = await chatMessagesService.getAllChatMessages(
req.params.id,
chatTypeFilter,
sortOrder,
chatId,
memoryType,
sessionId,
startDate,
endDate,
messageId,
feedback
)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getAllInternalChatMessages = async (req: Request, res: Response, next: NextFunction) => {
try {
const sortOrder = req.query?.order as string | undefined
const chatId = req.query?.chatId as string | undefined
const memoryType = req.query?.memoryType as string | undefined
const sessionId = req.query?.sessionId as string | undefined
const messageId = req.query?.messageId as string | undefined
const startDate = req.query?.startDate as string | undefined
const endDate = req.query?.endDate as string | undefined
const feedback = req.query?.feedback as boolean | undefined
const apiResponse = await chatMessagesService.getAllInternalChatMessages(
req.params.id,
chatType.INTERNAL,
sortOrder,
chatId,
memoryType,
sessionId,
startDate,
endDate,
messageId,
feedback
)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
//Delete all chatmessages from chatId
const removeAllChatMessages = async (req: Request, res: Response, next: NextFunction) => {
try {
const appServer = getRunningExpressApp()
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error('Error: chatMessagesController.removeAllChatMessages - id not provided!')
}
const chatflowid = req.params.id
const chatflow = await chatflowsService.getChatflowById(req.params.id)
if (!chatflow) {
return res.status(404).send(`Chatflow ${req.params.id} not found`)
}
const chatId = req.query?.chatId as string
const memoryType = req.query?.memoryType as string | undefined
const sessionId = req.query?.sessionId as string | undefined
const chatType = req.query?.chatType as string | undefined
const isClearFromViewMessageDialog = req.query?.isClearFromViewMessageDialog as string | undefined
const flowData = chatflow.flowData
const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
const nodes = parsedFlowData.nodes
try {
await clearSessionMemory(
nodes,
appServer.nodesPool.componentNodes,
chatId,
appServer.AppDataSource,
sessionId,
memoryType,
isClearFromViewMessageDialog
)
} catch (e) {
return res.status(500).send('Error clearing chat messages')
}
const deleteOptions: FindOptionsWhere<ChatMessage> = { chatflowid }
if (chatId) deleteOptions.chatId = chatId
if (memoryType) deleteOptions.memoryType = memoryType
if (sessionId) deleteOptions.sessionId = sessionId
if (chatType) deleteOptions.chatType = chatType
const apiResponse = await chatMessagesService.removeAllChatMessages(chatId, chatflowid, deleteOptions)
if (apiResponse.executionError) {
res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
createChatMessage,
getAllChatMessages,
getAllInternalChatMessages,
removeAllChatMessages
}
@@ -0,0 +1,170 @@
import { Request, Response, NextFunction } from 'express'
import chatflowsService from '../../services/chatflows'
import { ChatFlow } from '../../database/entities/ChatFlow'
import { createRateLimiter } from '../../utils/rateLimit'
import { getApiKey } from '../../utils/apiKey'
const checkIfChatflowIsValidForStreaming = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: chatflowsRouter.checkIfChatflowIsValidForStreaming - id not provided!`)
}
const apiResponse = await chatflowsService.checkIfChatflowIsValidForStreaming(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const checkIfChatflowIsValidForUploads = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: chatflowsRouter.checkIfChatflowIsValidForUploads - id not provided!`)
}
const apiResponse = await chatflowsService.checkIfChatflowIsValidForUploads(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const deleteChatflow = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: chatflowsRouter.deleteChatflow - id not provided!`)
}
const apiResponse = await chatflowsService.deleteChatflow(req.params.id)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getAllChatflows = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await chatflowsService.getAllChatflows()
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
// Get specific chatflow via api key
const getChatflowByApiKey = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.apiKey === 'undefined' || req.params.apiKey === '') {
throw new Error(`Error: chatflowsRouter.getChatflowById - apiKey not provided!`)
}
const apiKey = await getApiKey(req.params.apiKey)
if (!apiKey) {
return res.status(401).send('Unauthorized')
}
const apiResponse = await chatflowsService.getChatflowByApiKey(apiKey.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getChatflowById = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: chatflowsRouter.getChatflowById - id not provided!`)
}
const apiResponse = await chatflowsService.getChatflowById(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const saveChatflow = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: chatflowsRouter.saveChatflow - body not provided!`)
}
const body = req.body
const newChatFlow = new ChatFlow()
Object.assign(newChatFlow, body)
const apiResponse = await chatflowsService.saveChatflow(newChatFlow)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const updateChatflow = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: chatflowsRouter.updateChatflow - id not provided!`)
}
const chatflow = await chatflowsService.getChatflowById(req.params.id)
if (!chatflow) {
return res.status(404).send(`Chatflow ${req.params.id} not found`)
}
const body = req.body
const updateChatFlow = new ChatFlow()
Object.assign(updateChatFlow, body)
updateChatFlow.id = chatflow.id
createRateLimiter(updateChatFlow)
const apiResponse = await chatflowsService.updateChatflow(chatflow, updateChatFlow)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getSinglePublicChatflow = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: chatflowsRouter.updateChatflow - id not provided!`)
}
const apiResponse = await chatflowsService.getSinglePublicChatflow(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getSinglePublicChatbotConfig = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: chatflowsRouter.getSinglePublicChatbotConfig - id not provided!`)
}
const apiResponse = await chatflowsService.getSinglePublicChatbotConfig(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
checkIfChatflowIsValidForStreaming,
checkIfChatflowIsValidForUploads,
deleteChatflow,
getAllChatflows,
getChatflowByApiKey,
getChatflowById,
saveChatflow,
updateChatflow,
getSinglePublicChatflow,
getSinglePublicChatbotConfig
}
@@ -0,0 +1,44 @@
import { Request, Response, NextFunction } from 'express'
import componentsCredentialsService from '../../services/components-credentials'
// Get all component credentials
const getAllComponentsCredentials = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await componentsCredentialsService.getAllComponentsCredentials()
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
// Get component credential via name
const getComponentByName = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.name === 'undefined' || req.params.name === '') {
throw new Error(`Error: componentsCredentialsController.getComponentByName - name not provided!`)
}
const apiResponse = await componentsCredentialsService.getComponentByName(req.params.name)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
// Returns specific component credential icon via name
const getSingleComponentsCredentialIcon = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.name === 'undefined' || req.params.name === '') {
throw new Error(`Error: componentsCredentialsController.getSingleComponentsCredentialIcon - name not provided!`)
}
const apiResponse = await componentsCredentialsService.getSingleComponentsCredentialIcon(req.params.name)
return res.sendFile(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getAllComponentsCredentials,
getComponentByName,
getSingleComponentsCredentialIcon
}
@@ -0,0 +1,79 @@
import { Request, Response, NextFunction } from 'express'
import credentialsService from '../../services/credentials'
const createCredential = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: credentialsController.createCredential - body not provided!`)
}
const apiResponse = await credentialsService.createCredential(req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const deleteCredentials = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: credentialsController.deleteCredentials - id not provided!`)
}
const apiResponse = await credentialsService.deleteCredentials(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getAllCredentials = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await credentialsService.getAllCredentials(req.query.credentialName)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getCredentialById = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: credentialsController.getCredentialById - id not provided!`)
}
const apiResponse = await credentialsService.getCredentialById(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const updateCredential = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: credentialsController.updateCredential - id not provided!`)
}
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: credentialsController.updateCredential - body not provided!`)
}
const apiResponse = await credentialsService.updateCredential(req.params.id, req.body)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
createCredential,
deleteCredentials,
getAllCredentials,
getCredentialById,
updateCredential
}
@@ -0,0 +1,52 @@
import { Request, Response, NextFunction } from 'express'
import feedbackService from '../../services/feedback'
const getAllChatMessageFeedback = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: feedbackController.getAllChatMessageFeedback - id not provided!`)
}
const chatflowid = req.params.id
const chatId = req.query?.chatId as string | undefined
const sortOrder = req.query?.order as string | undefined
const startDate = req.query?.startDate as string | undefined
const endDate = req.query?.endDate as string | undefined
const apiResponse = await feedbackService.getAllChatMessageFeedback(chatflowid, chatId, sortOrder, startDate, endDate)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const createChatMessageFeedbackForChatflow = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: feedbackController.createChatMessageFeedbackForChatflow - body not provided!`)
}
const apiResponse = await feedbackService.createChatMessageFeedbackForChatflow(req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const updateChatMessageFeedbackForChatflow = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: feedbackController.updateChatMessageFeedbackForChatflow - body not provided!`)
}
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: feedbackController.updateChatMessageFeedbackForChatflow - id not provided!`)
}
const apiResponse = await feedbackService.updateChatMessageFeedbackForChatflow(req.params.id, req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getAllChatMessageFeedback,
createChatMessageFeedbackForChatflow,
updateChatMessageFeedbackForChatflow
}
@@ -0,0 +1,31 @@
import { Request, Response, NextFunction } from 'express'
import fetchLinksService from '../../services/fetch-links'
const getAllLinks = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.query.url === 'undefined' || req.query.url === '') {
throw new Error(`Error: fetchLinksController.getAllLinks - url not provided!`)
}
if (typeof req.query.relativeLinksMethod === 'undefined' || req.query.relativeLinksMethod === '') {
throw new Error(`Error: fetchLinksController.getAllLinks - relativeLinksMethod not provided!`)
}
if (typeof req.query.limit === 'undefined' || req.query.limit === '') {
throw new Error(`Error: fetchLinksController.getAllLinks - limit not provided!`)
}
const apiResponse = await fetchLinksService.getAllLinks(
req.query.url as string,
req.query.relativeLinksMethod as string,
req.query.limit as string
)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getAllLinks
}
@@ -0,0 +1,21 @@
import { Request, Response, NextFunction } from 'express'
import flowConfigsService from '../../services/flow-configs'
const getSingleFlowConfig = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: flowConfigsController.getSingleFlowConfig - id not provided!`)
}
const apiResponse = await flowConfigsService.getSingleFlowConfig(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getSingleFlowConfig
}
@@ -0,0 +1,37 @@
import { Request, Response, NextFunction } from 'express'
import path from 'path'
import contentDisposition from 'content-disposition'
import { getStoragePath } from 'flowise-components'
import * as fs from 'fs'
const streamUploadedImage = async (req: Request, res: Response, next: NextFunction) => {
try {
if (!req.query.chatflowId || !req.query.chatId || !req.query.fileName) {
return res.status(500).send(`Invalid file path`)
}
const chatflowId = req.query.chatflowId as string
const chatId = req.query.chatId as string
const fileName = req.query.fileName as string
const filePath = path.join(getStoragePath(), chatflowId, chatId, fileName)
//raise error if file path is not absolute
if (!path.isAbsolute(filePath)) return res.status(500).send(`Invalid file path`)
//raise error if file path contains '..'
if (filePath.includes('..')) return res.status(500).send(`Invalid file path`)
//only return from the storage folder
if (!filePath.startsWith(getStoragePath())) return res.status(500).send(`Invalid file path`)
if (fs.existsSync(filePath)) {
res.setHeader('Content-Disposition', contentDisposition(path.basename(filePath)))
const fileStream = fs.createReadStream(filePath)
fileStream.pipe(res)
} else {
return res.status(404).send(`File ${fileName} not found`)
}
} catch (error) {
next(error)
}
}
export default {
streamUploadedImage
}
@@ -0,0 +1,17 @@
import { Request, Response, NextFunction } from 'express'
import { getStoragePath } from 'flowise-components'
const getPathForUploads = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = {
storagePath: getStoragePath()
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getPathForUploads
}
@@ -0,0 +1,16 @@
import { Request, Response, NextFunction } from 'express'
import { utilBuildChatflow } from '../../utils/buildChatflow'
// Send input message and get prediction result (Internal)
const createInternalPrediction = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await utilBuildChatflow(req, req.io, true)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
createInternalPrediction
}
@@ -0,0 +1,21 @@
import { Request, Response, NextFunction } from 'express'
// Configure number of proxies in Host Environment
const configureProxyNrInHostEnv = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.ip === 'undefined' || req.ip) {
throw new Error(`Error: ipController.configureProxyNrInHostEnv - ip not provided!`)
}
const apiResponse = {
ip: req.ip,
msg: `Check returned IP address in the response. If it matches your current IP address ( which you can get by going to http://ip.nfriedly.com/ or https://api.ipify.org/ ), then the number of proxies is correct and the rate limiter should now work correctly. If not, increase the number of proxies by 1 and restart Cloud-Hosted Flowise until the IP address matches your own. Visit https://docs.flowiseai.com/configuration/rate-limit#cloud-hosted-rate-limit-setup-guide for more information.`
}
return res.send(apiResponse)
} catch (error) {
next(error)
}
}
export default {
configureProxyNrInHostEnv
}
@@ -0,0 +1,21 @@
import { Request, Response, NextFunction } from 'express'
import loadPromptsService from '../../services/load-prompts'
const createPrompt = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || typeof req.body.promptName === 'undefined' || req.body.promptName === '') {
throw new Error(`Error: loadPromptsController.createPrompt - promptName not provided!`)
}
const apiResponse = await loadPromptsService.createPrompt(req.body.promptName as string)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
createPrompt
}
@@ -0,0 +1,16 @@
import { Request, Response, NextFunction } from 'express'
import marketplacesService from '../../services/marketplaces'
// Get all templates for marketplaces
const getAllTemplates = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await marketplacesService.getAllTemplates()
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getAllTemplates
}
@@ -0,0 +1,18 @@
import { Request, Response, NextFunction } from 'express'
import nodeConfigsService from '../../services/node-configs'
const getAllNodeConfigs = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: nodeConfigsController.getAllNodeConfigs - body not provided!`)
}
const apiResponse = await nodeConfigsService.getAllNodeConfigs(req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getAllNodeConfigs
}
@@ -0,0 +1,30 @@
import { Request, Response, NextFunction } from 'express'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
// Returns specific component node icon via name
const getSingleNodeIcon = async (req: Request, res: Response, next: NextFunction) => {
try {
const appServer = getRunningExpressApp()
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentNodes, req.params.name)) {
const nodeInstance = appServer.nodesPool.componentNodes[req.params.name]
if (nodeInstance.icon === undefined) {
throw new Error(`Error: nodeIconController.getSingleNodeIcon - Node ${req.params.name} icon not found`)
}
if (nodeInstance.icon.endsWith('.svg') || nodeInstance.icon.endsWith('.png') || nodeInstance.icon.endsWith('.jpg')) {
const filepath = nodeInstance.icon
res.sendFile(filepath)
} else {
throw new Error(`Error: nodeIconController.getSingleNodeIcon - Node ${req.params.name} icon is missing icon`)
}
} else {
throw new Error(`Error: nodeIconController.getSingleNodeIcon - Node ${req.params.name} not found`)
}
} catch (error) {
next(error)
}
}
export default {
getSingleNodeIcon
}
@@ -0,0 +1,76 @@
import { Request, Response, NextFunction } from 'express'
import nodesService from '../../services/nodes'
const getAllNodes = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await nodesService.getAllNodes()
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getNodeByName = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.name === 'undefined' || req.params.name === '') {
throw new Error(`Error: nodesController.getNodeByName - name not provided!`)
}
const apiResponse = await nodesService.getNodeByName(req.params.name)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getSingleNodeIcon = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.name === 'undefined' || req.params.name === '') {
throw new Error(`Error: nodesController.getSingleNodeIcon - name not provided!`)
}
const apiResponse = await nodesService.getSingleNodeIcon(req.params.name)
return res.sendFile(apiResponse)
} catch (error) {
next(error)
}
}
const getSingleNodeAsyncOptions = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: nodesController.getSingleNodeAsyncOptions - body not provided!`)
}
if (typeof req.params.name === 'undefined' || req.params.name === '') {
throw new Error(`Error: nodesController.getSingleNodeAsyncOptions - name not provided!`)
}
const apiResponse = await nodesService.getSingleNodeAsyncOptions(req.params.name, req.body)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const executeCustomFunction = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: nodesController.executeCustomFunction - body not provided!`)
}
const apiResponse = await nodesService.executeCustomFunction(req.body)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getAllNodes,
getNodeByName,
getSingleNodeIcon,
getSingleNodeAsyncOptions,
executeCustomFunction
}
@@ -0,0 +1,69 @@
import { Request, Response, NextFunction } from 'express'
import path from 'path'
import * as fs from 'fs'
import openaiAssistantsService from '../../services/openai-assistants'
import { getUserHome } from '../../utils'
import contentDisposition from 'content-disposition'
// List available assistants
const getAllOpenaiAssistants = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.query.credential === 'undefined' || req.query.credential === '') {
throw new Error(`Error: openaiAssistantsController.getAllOpenaiAssistants - credential not provided!`)
}
const apiResponse = await openaiAssistantsService.getAllOpenaiAssistants(req.query.credential as string)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
// Get assistant object
const getSingleOpenaiAssistant = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: openaiAssistantsController.getSingleOpenaiAssistant - id not provided!`)
}
if (typeof req.query.credential === 'undefined' || req.query.credential === '') {
throw new Error(`Error: openaiAssistantsController.getSingleOpenaiAssistant - credential not provided!`)
}
const apiResponse = await openaiAssistantsService.getSingleOpenaiAssistant(req.query.credential as string, req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
// Download file from assistant
const getFileFromAssistant = async (req: Request, res: Response, next: NextFunction) => {
try {
const filePath = path.join(getUserHome(), '.flowise', 'openai-assistant', req.body.fileName)
//raise error if file path is not absolute
if (!path.isAbsolute(filePath)) return res.status(500).send(`Invalid file path`)
//raise error if file path contains '..'
if (filePath.includes('..')) return res.status(500).send(`Invalid file path`)
//only return from the .flowise openai-assistant folder
if (!(filePath.includes('.flowise') && filePath.includes('openai-assistant'))) return res.status(500).send(`Invalid file path`)
if (fs.existsSync(filePath)) {
res.setHeader('Content-Disposition', contentDisposition(path.basename(filePath)))
const fileStream = fs.createReadStream(filePath)
fileStream.pipe(res)
} else {
return res.status(404).send(`File ${req.body.fileName} not found`)
}
} catch (error) {
next(error)
}
}
export default {
getAllOpenaiAssistants,
getSingleOpenaiAssistant,
getFileFromAssistant
}
@@ -0,0 +1,66 @@
import { Request, Response, NextFunction } from 'express'
import { getRateLimiter } from '../../utils/rateLimit'
import chatflowsService from '../../services/chatflows'
import logger from '../../utils/logger'
import { utilBuildChatflow } from '../../utils/buildChatflow'
// Send input message and get prediction result (External)
const createPrediction = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: predictionsController.createPrediction - id not provided!`)
}
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: predictionsController.createPrediction - body not provided!`)
}
const chatflow = await chatflowsService.getChatflowById(req.params.id)
if (!chatflow) {
return res.status(404).send(`Chatflow ${req.params.id} not found`)
}
let isDomainAllowed = true
logger.info(`[server]: Request originated from ${req.headers.origin}`)
if (chatflow.chatbotConfig) {
const parsedConfig = JSON.parse(chatflow.chatbotConfig)
// check whether the first one is not empty. if it is empty that means the user set a value and then removed it.
const isValidAllowedOrigins = parsedConfig.allowedOrigins?.length && parsedConfig.allowedOrigins[0] !== ''
if (isValidAllowedOrigins) {
const originHeader = req.headers.origin as string
const origin = new URL(originHeader).host
isDomainAllowed =
parsedConfig.allowedOrigins.filter((domain: string) => {
try {
const allowedOrigin = new URL(domain).host
return origin === allowedOrigin
} catch (e) {
return false
}
}).length > 0
}
}
if (isDomainAllowed) {
const apiResponse = await utilBuildChatflow(req, req.io)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} else {
return res.status(401).send(`This site is not allowed to access this chatbot`)
}
} catch (error) {
next(error)
}
}
const getRateLimiterMiddleware = async (req: Request, res: Response, next: NextFunction) => {
try {
return getRateLimiter(req, res, next)
} catch (error) {
next(error)
}
}
export default {
createPrediction,
getRateLimiterMiddleware
}
@@ -0,0 +1,16 @@
import { Request, Response, NextFunction } from 'express'
import promptsListsService from '../../services/prompts-lists'
// Prompt from Hub
const createPromptsList = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await promptsListsService.createPromptsList(req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
createPromptsList
}
@@ -0,0 +1,40 @@
import { Request, Response, NextFunction } from 'express'
import statsService from '../../services/stats'
import { chatType } from '../../Interface'
const getChatflowStats = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: statsController.getChatflowStats - id not provided!`)
}
const chatflowid = req.params.id
let chatTypeFilter = req.query?.chatType as chatType | undefined
const startDate = req.query?.startDate as string | undefined
const endDate = req.query?.endDate as string | undefined
if (chatTypeFilter) {
try {
const chatTypeFilterArray = JSON.parse(chatTypeFilter)
if (chatTypeFilterArray.includes(chatType.EXTERNAL) && chatTypeFilterArray.includes(chatType.INTERNAL)) {
chatTypeFilter = undefined
} else if (chatTypeFilterArray.includes(chatType.EXTERNAL)) {
chatTypeFilter = chatType.EXTERNAL
} else if (chatTypeFilterArray.includes(chatType.INTERNAL)) {
chatTypeFilter = chatType.INTERNAL
}
} catch (e) {
return res.status(500).send(e)
}
}
const apiResponse = await statsService.getChatflowStats(chatflowid, chatTypeFilter, startDate, endDate, '', true)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getChatflowStats
}
@@ -0,0 +1,85 @@
import { Request, Response, NextFunction } from 'express'
import toolsService from '../../services/tools'
const creatTool = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: toolsController.creatTool - body not provided!`)
}
const apiResponse = await toolsService.creatTool(req.body)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const deleteTool = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: toolsController.updateTool - id not provided!`)
}
const apiResponse = await toolsService.deleteTool(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getAllTools = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await toolsService.getAllTools()
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getToolById = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: toolsController.getToolById - id not provided!`)
}
const apiResponse = await toolsService.getToolById(req.params.id)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const updateTool = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: toolsController.updateTool - id not provided!`)
}
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: toolsController.deleteTool - body not provided!`)
}
const apiResponse = await toolsService.updateTool(req.params.id, req.body)
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
creatTool,
deleteTool,
getAllTools,
getToolById,
updateTool
}
@@ -0,0 +1,68 @@
import { Request, Response, NextFunction } from 'express'
import variablesService from '../../services/variables'
import { Variable } from '../../database/entities/Variable'
const createVariable = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined') {
throw new Error(`Error: variablesController.createVariable - body not provided!`)
}
const body = req.body
const newVariable = new Variable()
Object.assign(newVariable, body)
const apiResponse = await variablesService.createVariable(newVariable)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const deleteVariable = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error('Error: variablesController.deleteVariable - id not provided!')
}
const apiResponse = await variablesService.deleteVariable(req.params.id)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const getAllVariables = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await variablesService.getAllVariables()
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const updateVariable = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error('Error: variablesController.updateVariable - id not provided!')
}
if (typeof req.body === 'undefined') {
throw new Error('Error: variablesController.updateVariable - body not provided!')
}
const variable = await variablesService.getVariableById(req.params.id)
if (!variable) {
return res.status(404).send(`Variable ${req.params.id} not found in the database`)
}
const body = req.body
const updatedVariable = new Variable()
Object.assign(updatedVariable, body)
const apiResponse = await variablesService.updateVariable(variable, updatedVariable)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
createVariable,
deleteVariable,
getAllVariables,
updateVariable
}
@@ -0,0 +1,33 @@
import { Request, Response, NextFunction } from 'express'
import vectorsService from '../../services/vectors'
import { getRateLimiter } from '../../utils/rateLimit'
const getRateLimiterMiddleware = async (req: Request, res: Response, next: NextFunction) => {
try {
return getRateLimiter(req, res, next)
} catch (error) {
next(error)
}
}
const upsertVectorMiddleware = async (req: Request, res: Response, next: NextFunction) => {
try {
return await vectorsService.upsertVectorMiddleware(req, res)
} catch (error) {
next(error)
}
}
const createInternalUpsert = async (req: Request, res: Response, next: NextFunction) => {
try {
return await vectorsService.upsertVectorMiddleware(req, res, true)
} catch (error) {
next(error)
}
}
export default {
upsertVectorMiddleware,
createInternalUpsert,
getRateLimiterMiddleware
}
@@ -0,0 +1,18 @@
import { Request, Response, NextFunction } from 'express'
import versionsService from '../../services/versions'
const getVersion = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await versionsService.getVersion()
if (apiResponse.executionError) {
return res.status(apiResponse.status).send(apiResponse.msg)
}
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getVersion
}