mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 17:01:00 +03:00
XSS Fixes
This commit is contained in:
@@ -54,6 +54,7 @@
|
|||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-basic-auth": "^1.2.1",
|
"express-basic-auth": "^1.2.1",
|
||||||
"express-rate-limit": "^6.9.0",
|
"express-rate-limit": "^6.9.0",
|
||||||
|
"express-validator": "^7.0.1",
|
||||||
"flowise-components": "*",
|
"flowise-components": "*",
|
||||||
"flowise-ui": "*",
|
"flowise-ui": "*",
|
||||||
"moment-timezone": "^0.5.34",
|
"moment-timezone": "^0.5.34",
|
||||||
|
|||||||
+302
-128
@@ -58,6 +58,7 @@ import { CachePool } from './CachePool'
|
|||||||
import { ICommonObject, IMessage, INodeOptionsValue } from 'flowise-components'
|
import { ICommonObject, IMessage, INodeOptionsValue } from 'flowise-components'
|
||||||
import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
|
import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
|
||||||
import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey'
|
import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey'
|
||||||
|
import { body, param, query, validationResult } from 'express-validator'
|
||||||
|
|
||||||
export class App {
|
export class App {
|
||||||
app: express.Application
|
app: express.Application
|
||||||
@@ -115,6 +116,9 @@ export class App {
|
|||||||
// Allow access from *
|
// Allow access from *
|
||||||
this.app.use(cors())
|
this.app.use(cors())
|
||||||
|
|
||||||
|
// Switch off the default 'X-Powered-By: Express' header
|
||||||
|
this.app.disable('x-powered-by')
|
||||||
|
|
||||||
// Add the expressRequestLogger middleware to log all requests
|
// Add the expressRequestLogger middleware to log all requests
|
||||||
this.app.use(expressRequestLogger)
|
this.app.use(expressRequestLogger)
|
||||||
|
|
||||||
@@ -180,17 +184,27 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get specific component node via name
|
// Get specific component node via name
|
||||||
this.app.get('/api/v1/nodes/:name', (req: Request, res: Response) => {
|
this.app.get('/api/v1/nodes/:name', param('name').notEmpty().escape(), (req: Request, res: Response) => {
|
||||||
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentNodes, req.params.name)) {
|
const name = req.params.name
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
throw new Error(`Node ${name} not found`)
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentNodes, name)) {
|
||||||
return res.json(this.nodesPool.componentNodes[req.params.name])
|
return res.json(this.nodesPool.componentNodes[req.params.name])
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Node ${req.params.name} not found`)
|
throw new Error(`Node ${name} not found`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Get component credential via name
|
// Get component credential via name
|
||||||
this.app.get('/api/v1/components-credentials/:name', (req: Request, res: Response) => {
|
this.app.get('/api/v1/components-credentials/:name', param('name').notEmpty().escape(), (req: Request, res: Response) => {
|
||||||
if (!req.params.name.includes('&')) {
|
const name = req.params.name
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
throw new Error(`Credential ${name} not found`)
|
||||||
|
}
|
||||||
|
if (!req.params.name.includes('&')) {
|
||||||
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentCredentials, req.params.name)) {
|
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentCredentials, req.params.name)) {
|
||||||
return res.json(this.nodesPool.componentCredentials[req.params.name])
|
return res.json(this.nodesPool.componentCredentials[req.params.name])
|
||||||
} else {
|
} else {
|
||||||
@@ -198,7 +212,7 @@ export class App {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const returnResponse = []
|
const returnResponse = []
|
||||||
for (const name of req.params.name.split('&')) {
|
for (const name of req.params.name.split('&')) {
|
||||||
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentCredentials, name)) {
|
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentCredentials, name)) {
|
||||||
returnResponse.push(this.nodesPool.componentCredentials[name])
|
returnResponse.push(this.nodesPool.componentCredentials[name])
|
||||||
} else {
|
} else {
|
||||||
@@ -210,9 +224,14 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Returns specific component node icon via name
|
// Returns specific component node icon via name
|
||||||
this.app.get('/api/v1/node-icon/:name', (req: Request, res: Response) => {
|
this.app.get('/api/v1/node-icon/:name', param('name').notEmpty().escape(), (req: Request, res: Response) => {
|
||||||
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentNodes, req.params.name)) {
|
const name = req.params.name
|
||||||
const nodeInstance = this.nodesPool.componentNodes[req.params.name]
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
throw new Error(`Node ${name} not found`)
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentNodes, name)) {
|
||||||
|
const nodeInstance = this.nodesPool.componentNodes[name]
|
||||||
if (nodeInstance.icon === undefined) {
|
if (nodeInstance.icon === undefined) {
|
||||||
throw new Error(`Node ${req.params.name} icon not found`)
|
throw new Error(`Node ${req.params.name} icon not found`)
|
||||||
}
|
}
|
||||||
@@ -221,38 +240,48 @@ export class App {
|
|||||||
const filepath = nodeInstance.icon
|
const filepath = nodeInstance.icon
|
||||||
res.sendFile(filepath)
|
res.sendFile(filepath)
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Node ${req.params.name} icon is missing icon`)
|
throw new Error(`Node ${name} icon is missing icon`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Node ${req.params.name} not found`)
|
throw new Error(`Node ${name} not found`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Returns specific component credential icon via name
|
// Returns specific component credential icon via name
|
||||||
this.app.get('/api/v1/components-credentials-icon/:name', (req: Request, res: Response) => {
|
this.app.get('/api/v1/components-credentials-icon/:name', param('name').notEmpty().escape(), (req: Request, res: Response) => {
|
||||||
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentCredentials, req.params.name)) {
|
const name = req.params.name
|
||||||
const credInstance = this.nodesPool.componentCredentials[req.params.name]
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
throw new Error(`Credential ${name} not found`)
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentCredentials, name)) {
|
||||||
|
const credInstance = this.nodesPool.componentCredentials[name]
|
||||||
if (credInstance.icon === undefined) {
|
if (credInstance.icon === undefined) {
|
||||||
throw new Error(`Credential ${req.params.name} icon not found`)
|
throw new Error(`Credential ${name} icon not found`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (credInstance.icon.endsWith('.svg') || credInstance.icon.endsWith('.png') || credInstance.icon.endsWith('.jpg')) {
|
if (credInstance.icon.endsWith('.svg') || credInstance.icon.endsWith('.png') || credInstance.icon.endsWith('.jpg')) {
|
||||||
const filepath = credInstance.icon
|
const filepath = credInstance.icon
|
||||||
res.sendFile(filepath)
|
res.sendFile(filepath)
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Credential ${req.params.name} icon is missing icon`)
|
throw new Error(`Credential ${name} is missing icon`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Credential ${req.params.name} not found`)
|
throw new Error(`Credential ${name} not found`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// load async options
|
// load async options
|
||||||
this.app.post('/api/v1/node-load-method/:name', async (req: Request, res: Response) => {
|
this.app.post('/api/v1/node-load-method/:name', param('name').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const name = req.params.name
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
throw new Error(`Node ${name} not found`)
|
||||||
|
}
|
||||||
const nodeData: INodeData = req.body
|
const nodeData: INodeData = req.body
|
||||||
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentNodes, req.params.name)) {
|
if (Object.prototype.hasOwnProperty.call(this.nodesPool.componentNodes, name)) {
|
||||||
try {
|
try {
|
||||||
const nodeInstance = this.nodesPool.componentNodes[req.params.name]
|
const nodeInstance = this.nodesPool.componentNodes[name]
|
||||||
const methodName = nodeData.loadMethod || ''
|
const methodName = nodeData.loadMethod || ''
|
||||||
|
|
||||||
const returnOptions: INodeOptionsValue[] = await nodeInstance.loadMethods![methodName]!.call(nodeInstance, nodeData, {
|
const returnOptions: INodeOptionsValue[] = await nodeInstance.loadMethods![methodName]!.call(nodeInstance, nodeData, {
|
||||||
@@ -265,7 +294,7 @@ export class App {
|
|||||||
return res.json([])
|
return res.json([])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.status(404).send(`Node ${req.params.name} not found`)
|
res.status(404).send(`Node ${name} not found`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -281,7 +310,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get specific chatflow via api key
|
// Get specific chatflow via api key
|
||||||
this.app.get('/api/v1/chatflows/apikey/:apiKey', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/chatflows/apikey/:apiKey', param('apiKey').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(401).send('Unauthorized')
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const apiKey = await getApiKey(req.params.apiKey)
|
const apiKey = await getApiKey(req.params.apiKey)
|
||||||
if (!apiKey) return res.status(401).send('Unauthorized')
|
if (!apiKey) return res.status(401).send('Unauthorized')
|
||||||
@@ -293,14 +326,19 @@ export class App {
|
|||||||
.orderBy('cf.name', 'ASC')
|
.orderBy('cf.name', 'ASC')
|
||||||
.getMany()
|
.getMany()
|
||||||
if (chatflows.length >= 1) return res.status(200).send(chatflows)
|
if (chatflows.length >= 1) return res.status(200).send(chatflows)
|
||||||
return res.status(404).send('Chatflow not found')
|
return res.status(404).send('APIKey not found')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return res.status(500).send(err?.message)
|
return res.status(500).send(err?.message)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Get specific chatflow via id
|
// Get specific chatflow via id
|
||||||
this.app.get('/api/v1/chatflows/:id', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/chatflows/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const chatflowId = req.params.id
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatflow ${chatflowId} not found`)
|
||||||
|
}
|
||||||
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -309,7 +347,12 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get specific chatflow via id (PUBLIC endpoint, used when sharing chatbot link)
|
// Get specific chatflow via id (PUBLIC endpoint, used when sharing chatbot link)
|
||||||
this.app.get('/api/v1/public-chatflows/:id', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/public-chatflows/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const chatflowId = req.params.id
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatflow ${chatflowId} not found`)
|
||||||
|
}
|
||||||
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -331,48 +374,69 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Update chatflow
|
// Update chatflow
|
||||||
this.app.put('/api/v1/chatflows/:id', async (req: Request, res: Response) => {
|
this.app.put(
|
||||||
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
'/api/v1/chatflows/:id',
|
||||||
id: req.params.id
|
body('chatflow.id').notEmpty(),
|
||||||
})
|
param('id').notEmpty().escape(),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const chatflowId = req.params.id
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatflow ${chatflowId} not found`)
|
||||||
|
}
|
||||||
|
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
|
id: chatflowId
|
||||||
|
})
|
||||||
|
|
||||||
if (!chatflow) {
|
if (!chatflow) {
|
||||||
res.status(404).send(`Chatflow ${req.params.id} not found`)
|
res.status(404).send(`Chatflow ${chatflowId} not found`)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = req.body
|
||||||
|
const updateChatFlow = new ChatFlow()
|
||||||
|
Object.assign(updateChatFlow, body)
|
||||||
|
|
||||||
|
updateChatFlow.id = chatflow.id
|
||||||
|
createRateLimiter(updateChatFlow)
|
||||||
|
|
||||||
|
this.AppDataSource.getRepository(ChatFlow).merge(chatflow, updateChatFlow)
|
||||||
|
const result = await this.AppDataSource.getRepository(ChatFlow).save(chatflow)
|
||||||
|
|
||||||
|
// chatFlowPool is initialized only when a flow is opened
|
||||||
|
// if the user attempts to rename/update category without opening any flow, chatFlowPool will be undefined
|
||||||
|
if (this.chatflowPool) {
|
||||||
|
// Update chatflowpool inSync to false, to build Langchain again because data has been changed
|
||||||
|
this.chatflowPool.updateInSync(chatflow.id, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json(result)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
const body = req.body
|
|
||||||
const updateChatFlow = new ChatFlow()
|
|
||||||
Object.assign(updateChatFlow, body)
|
|
||||||
|
|
||||||
updateChatFlow.id = chatflow.id
|
|
||||||
createRateLimiter(updateChatFlow)
|
|
||||||
|
|
||||||
this.AppDataSource.getRepository(ChatFlow).merge(chatflow, updateChatFlow)
|
|
||||||
const result = await this.AppDataSource.getRepository(ChatFlow).save(chatflow)
|
|
||||||
|
|
||||||
// chatFlowPool is initialized only when a flow is opened
|
|
||||||
// if the user attempts to rename/update category without opening any flow, chatFlowPool will be undefined
|
|
||||||
if (this.chatflowPool) {
|
|
||||||
// Update chatflowpool inSync to false, to build Langchain again because data has been changed
|
|
||||||
this.chatflowPool.updateInSync(chatflow.id, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.json(result)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Delete chatflow via id
|
// Delete chatflow via id
|
||||||
this.app.delete('/api/v1/chatflows/:id', async (req: Request, res: Response) => {
|
this.app.delete('/api/v1/chatflows/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const chatflowId = req.params.id
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatflow ${chatflowId} not found`)
|
||||||
|
}
|
||||||
const results = await this.AppDataSource.getRepository(ChatFlow).delete({ id: req.params.id })
|
const results = await this.AppDataSource.getRepository(ChatFlow).delete({ id: req.params.id })
|
||||||
return res.json(results)
|
return res.json(results)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Check if chatflow valid for streaming
|
// Check if chatflow valid for streaming
|
||||||
this.app.get('/api/v1/chatflows-streaming/:id', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/chatflows-streaming/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const chatflowId = req.params.id
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatflow ${chatflowId} not found`)
|
||||||
|
}
|
||||||
|
|
||||||
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
id: req.params.id
|
id: chatflowId
|
||||||
})
|
})
|
||||||
if (!chatflow) return res.status(404).send(`Chatflow ${req.params.id} not found`)
|
if (!chatflow) return res.status(404).send(`Chatflow ${chatflowId} not found`)
|
||||||
|
|
||||||
/*** Get Ending Node with Directed Graph ***/
|
/*** Get Ending Node with Directed Graph ***/
|
||||||
const flowData = chatflow.flowData
|
const flowData = chatflow.flowData
|
||||||
@@ -402,58 +466,84 @@ export class App {
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
// Get all chatmessages from chatflowid
|
// Get all chatmessages from chatflowid
|
||||||
this.app.get('/api/v1/chatmessage/:id', async (req: Request, res: Response) => {
|
this.app.get(
|
||||||
const sortOrder = req.query?.order as string | undefined
|
'/api/v1/chatmessage/:id',
|
||||||
const chatId = req.query?.chatId as string | undefined
|
query('chatId').notEmpty().escape(),
|
||||||
const memoryType = req.query?.memoryType as string | undefined
|
query('sortOrder').notEmpty().escape(),
|
||||||
const sessionId = req.query?.sessionId as string | undefined
|
query('memoryType').notEmpty().escape(),
|
||||||
const startDate = req.query?.startDate as string | undefined
|
query('sessionId').notEmpty().escape(),
|
||||||
const endDate = req.query?.endDate as string | undefined
|
query('startDate').notEmpty().escape(),
|
||||||
let chatTypeFilter = req.query?.chatType as chatType | undefined
|
query('endDate').notEmpty().escape(),
|
||||||
|
query('chatTypeFilter').notEmpty().escape(),
|
||||||
if (chatTypeFilter) {
|
async (req: Request, res: Response) => {
|
||||||
try {
|
const result = validationResult(req)
|
||||||
const chatTypeFilterArray = JSON.parse(chatTypeFilter)
|
if (!result.isEmpty()) {
|
||||||
if (chatTypeFilterArray.includes(chatType.EXTERNAL) && chatTypeFilterArray.includes(chatType.INTERNAL)) {
|
return res.status(404).send(`Chatmessage not found`)
|
||||||
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 startDate = req.query?.startDate as string | undefined
|
||||||
|
const endDate = req.query?.endDate as string | undefined
|
||||||
|
let chatTypeFilter = req.query?.chatType as chatType | undefined
|
||||||
|
|
||||||
const chatmessages = await this.getChatMessage(
|
if (chatTypeFilter) {
|
||||||
req.params.id,
|
try {
|
||||||
chatTypeFilter,
|
const chatTypeFilterArray = JSON.parse(chatTypeFilter)
|
||||||
sortOrder,
|
if (chatTypeFilterArray.includes(chatType.EXTERNAL) && chatTypeFilterArray.includes(chatType.INTERNAL)) {
|
||||||
chatId,
|
chatTypeFilter = undefined
|
||||||
memoryType,
|
} else if (chatTypeFilterArray.includes(chatType.EXTERNAL)) {
|
||||||
sessionId,
|
chatTypeFilter = chatType.EXTERNAL
|
||||||
startDate,
|
} else if (chatTypeFilterArray.includes(chatType.INTERNAL)) {
|
||||||
endDate
|
chatTypeFilter = chatType.INTERNAL
|
||||||
)
|
}
|
||||||
return res.json(chatmessages)
|
} catch (e) {
|
||||||
})
|
return res.status(500).send(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatmessages = await this.getChatMessage(
|
||||||
|
req.params.id,
|
||||||
|
chatTypeFilter,
|
||||||
|
sortOrder,
|
||||||
|
chatId,
|
||||||
|
memoryType,
|
||||||
|
sessionId,
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
|
)
|
||||||
|
return res.json(chatmessages)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Get internal chatmessages from chatflowid
|
// Get internal chatmessages from chatflowid
|
||||||
this.app.get('/api/v1/internal-chatmessage/:id', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/internal-chatmessage/:id', param('chatId').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatmessage not found`)
|
||||||
|
}
|
||||||
const chatmessages = await this.getChatMessage(req.params.id, chatType.INTERNAL)
|
const chatmessages = await this.getChatMessage(req.params.id, chatType.INTERNAL)
|
||||||
return res.json(chatmessages)
|
return res.json(chatmessages)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add chatmessages for chatflowid
|
// Add chatmessages for chatflowid
|
||||||
this.app.post('/api/v1/chatmessage/:id', async (req: Request, res: Response) => {
|
this.app.post('/api/v1/chatmessage/:id', param('chatId').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatmessage not found`)
|
||||||
|
}
|
||||||
const body = req.body
|
const body = req.body
|
||||||
const results = await this.addChatMessage(body)
|
const results = await this.addChatMessage(body)
|
||||||
return res.json(results)
|
return res.json(results)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Delete all chatmessages from chatId
|
// Delete all chatmessages from chatId
|
||||||
this.app.delete('/api/v1/chatmessage/:id', async (req: Request, res: Response) => {
|
this.app.delete('/api/v1/chatmessage/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatmessage not found`)
|
||||||
|
}
|
||||||
const chatflowid = req.params.id
|
const chatflowid = req.params.id
|
||||||
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
id: chatflowid
|
id: chatflowid
|
||||||
@@ -537,7 +627,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get specific credential
|
// Get specific credential
|
||||||
this.app.get('/api/v1/credentials/:id', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/credentials/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const result = validationResult(req)
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return res.status(404).send(`Credential ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -558,7 +652,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Update credential
|
// Update credential
|
||||||
this.app.put('/api/v1/credentials/:id', async (req: Request, res: Response) => {
|
this.app.put('/api/v1/credentials/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Credential ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -574,7 +672,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Delete all chatmessages from chatflowid
|
// Delete all chatmessages from chatflowid
|
||||||
this.app.delete('/api/v1/credentials/:id', async (req: Request, res: Response) => {
|
this.app.delete('/api/v1/credentials/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Credential ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const results = await this.AppDataSource.getRepository(Credential).delete({ id: req.params.id })
|
const results = await this.AppDataSource.getRepository(Credential).delete({ id: req.params.id })
|
||||||
return res.json(results)
|
return res.json(results)
|
||||||
})
|
})
|
||||||
@@ -590,7 +692,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get specific tool
|
// Get specific tool
|
||||||
this.app.get('/api/v1/tools/:id', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/tools/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Tool ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const tool = await this.AppDataSource.getRepository(Tool).findOneBy({
|
const tool = await this.AppDataSource.getRepository(Tool).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -610,7 +716,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Update tool
|
// Update tool
|
||||||
this.app.put('/api/v1/tools/:id', async (req: Request, res: Response) => {
|
this.app.put('/api/v1/tools/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Tool ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const tool = await this.AppDataSource.getRepository(Tool).findOneBy({
|
const tool = await this.AppDataSource.getRepository(Tool).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -631,7 +741,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Delete tool
|
// Delete tool
|
||||||
this.app.delete('/api/v1/tools/:id', async (req: Request, res: Response) => {
|
this.app.delete('/api/v1/tools/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Tool ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const results = await this.AppDataSource.getRepository(Tool).delete({ id: req.params.id })
|
const results = await this.AppDataSource.getRepository(Tool).delete({ id: req.params.id })
|
||||||
return res.json(results)
|
return res.json(results)
|
||||||
})
|
})
|
||||||
@@ -647,7 +761,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get specific assistant
|
// Get specific assistant
|
||||||
this.app.get('/api/v1/assistants/:id', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/assistants/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Assistant ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const assistant = await this.AppDataSource.getRepository(Assistant).findOneBy({
|
const assistant = await this.AppDataSource.getRepository(Assistant).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -655,33 +773,46 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get assistant object
|
// Get assistant object
|
||||||
this.app.get('/api/v1/openai-assistants/:id', async (req: Request, res: Response) => {
|
this.app.get(
|
||||||
const credentialId = req.query.credential as string
|
'/api/v1/openai-assistants/:id',
|
||||||
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
param('id').notEmpty().escape(),
|
||||||
id: credentialId
|
query('credential').notEmpty().escape(),
|
||||||
})
|
async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Assistant or Credential not found`)
|
||||||
|
}
|
||||||
|
const credentialId = req.query.credential as string
|
||||||
|
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: credentialId
|
||||||
|
})
|
||||||
|
|
||||||
if (!credential) return res.status(404).send(`Credential ${credentialId} not found`)
|
if (!credential) return res.status(404).send(`Credential ${credentialId} not found`)
|
||||||
|
|
||||||
// Decrpyt credentialData
|
// Decrpyt credentialData
|
||||||
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||||
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
||||||
if (!openAIApiKey) return res.status(404).send(`OpenAI ApiKey not found`)
|
if (!openAIApiKey) return res.status(404).send(`OpenAI ApiKey not found`)
|
||||||
|
|
||||||
const openai = new OpenAI({ apiKey: openAIApiKey })
|
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||||
const retrievedAssistant = await openai.beta.assistants.retrieve(req.params.id)
|
const retrievedAssistant = await openai.beta.assistants.retrieve(req.params.id)
|
||||||
const resp = await openai.files.list()
|
const resp = await openai.files.list()
|
||||||
const existingFiles = resp.data ?? []
|
const existingFiles = resp.data ?? []
|
||||||
|
|
||||||
if (retrievedAssistant.file_ids && retrievedAssistant.file_ids.length) {
|
if (retrievedAssistant.file_ids && retrievedAssistant.file_ids.length) {
|
||||||
;(retrievedAssistant as any).files = existingFiles.filter((file) => retrievedAssistant.file_ids.includes(file.id))
|
;(retrievedAssistant as any).files = existingFiles.filter((file) => retrievedAssistant.file_ids.includes(file.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json(retrievedAssistant)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
return res.json(retrievedAssistant)
|
|
||||||
})
|
|
||||||
|
|
||||||
// List available assistants
|
// List available assistants
|
||||||
this.app.get('/api/v1/openai-assistants', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/openai-assistants', query('credential').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Assistant or Credential not found`)
|
||||||
|
}
|
||||||
const credentialId = req.query.credential as string
|
const credentialId = req.query.credential as string
|
||||||
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
id: credentialId
|
id: credentialId
|
||||||
@@ -816,7 +947,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Update assistant
|
// Update assistant
|
||||||
this.app.put('/api/v1/assistants/:id', async (req: Request, res: Response) => {
|
this.app.put('/api/v1/assistants/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Assistant ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const assistant = await this.AppDataSource.getRepository(Assistant).findOneBy({
|
const assistant = await this.AppDataSource.getRepository(Assistant).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -924,7 +1059,11 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Delete assistant
|
// Delete assistant
|
||||||
this.app.delete('/api/v1/assistants/:id', async (req: Request, res: Response) => {
|
this.app.delete('/api/v1/assistants/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Assistant ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const assistant = await this.AppDataSource.getRepository(Assistant).findOneBy({
|
const assistant = await this.AppDataSource.getRepository(Assistant).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -964,6 +1103,12 @@ export class App {
|
|||||||
// Download file from assistant
|
// Download file from assistant
|
||||||
this.app.post('/api/v1/openai-assistants-file', async (req: Request, res: Response) => {
|
this.app.post('/api/v1/openai-assistants-file', async (req: Request, res: Response) => {
|
||||||
const filePath = path.join(getUserHome(), '.flowise', 'openai-assistant', req.body.fileName)
|
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`)
|
||||||
res.setHeader('Content-Disposition', 'attachment; filename=' + path.basename(filePath))
|
res.setHeader('Content-Disposition', 'attachment; filename=' + path.basename(filePath))
|
||||||
const fileStream = fs.createReadStream(filePath)
|
const fileStream = fs.createReadStream(filePath)
|
||||||
fileStream.pipe(res)
|
fileStream.pipe(res)
|
||||||
@@ -973,7 +1118,11 @@ export class App {
|
|||||||
// Configuration
|
// Configuration
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
this.app.get('/api/v1/flow-config/:id', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/flow-config/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Chatflow ${req.params.id} not found`)
|
||||||
|
}
|
||||||
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
})
|
})
|
||||||
@@ -1032,7 +1181,11 @@ export class App {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
this.app.post('/api/v1/vector/internal-upsert/:id', async (req: Request, res: Response) => {
|
this.app.post('/api/v1/vector/internal-upsert/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Upsert ${req.params.id} not found`)
|
||||||
|
}
|
||||||
await this.buildChatflow(req, res, undefined, true, true)
|
await this.buildChatflow(req, res, undefined, true, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1043,15 +1196,24 @@ export class App {
|
|||||||
// Send input message and get prediction result (External)
|
// Send input message and get prediction result (External)
|
||||||
this.app.post(
|
this.app.post(
|
||||||
'/api/v1/prediction/:id',
|
'/api/v1/prediction/:id',
|
||||||
|
param('id').notEmpty().escape(),
|
||||||
upload.array('files'),
|
upload.array('files'),
|
||||||
(req: Request, res: Response, next: NextFunction) => getRateLimiter(req, res, next),
|
(req: Request, res: Response, next: NextFunction) => getRateLimiter(req, res, next),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Error Processing Prediction`)
|
||||||
|
}
|
||||||
await this.buildChatflow(req, res, socketIO)
|
await this.buildChatflow(req, res, socketIO)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Send input message and get prediction result (Internal)
|
// Send input message and get prediction result (Internal)
|
||||||
this.app.post('/api/v1/internal-prediction/:id', async (req: Request, res: Response) => {
|
this.app.post('/api/v1/internal-prediction/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Error Processing Prediction`)
|
||||||
|
}
|
||||||
await this.buildChatflow(req, res, socketIO, true)
|
await this.buildChatflow(req, res, socketIO, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1146,19 +1308,31 @@ export class App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Update api key
|
// Update api key
|
||||||
this.app.put('/api/v1/apikey/:id', async (req: Request, res: Response) => {
|
this.app.put('/api/v1/apikey/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Error Processing Update API Key`)
|
||||||
|
}
|
||||||
const keys = await updateAPIKey(req.params.id, req.body.keyName)
|
const keys = await updateAPIKey(req.params.id, req.body.keyName)
|
||||||
return addChatflowsCount(keys, res)
|
return addChatflowsCount(keys, res)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Delete new api key
|
// Delete new api key
|
||||||
this.app.delete('/api/v1/apikey/:id', async (req: Request, res: Response) => {
|
this.app.delete('/api/v1/apikey/:id', param('id').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Error Processing Update API Key`)
|
||||||
|
}
|
||||||
const keys = await deleteAPIKey(req.params.id)
|
const keys = await deleteAPIKey(req.params.id)
|
||||||
return addChatflowsCount(keys, res)
|
return addChatflowsCount(keys, res)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Verify api key
|
// Verify api key
|
||||||
this.app.get('/api/v1/verify/apikey/:apiKey', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/verify/apikey/:apiKey', param('apikey').notEmpty().escape(), async (req: Request, res: Response) => {
|
||||||
|
const valResult = validationResult(req)
|
||||||
|
if (!valResult.isEmpty()) {
|
||||||
|
return res.status(404).send(`Error Processing API Key`)
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const apiKey = await getApiKey(req.params.apiKey)
|
const apiKey = await getApiKey(req.params.apiKey)
|
||||||
if (!apiKey) return res.status(401).send('Unauthorized')
|
if (!apiKey) return res.status(401).send('Unauthorized')
|
||||||
|
|||||||
Reference in New Issue
Block a user