Feature/export import stage 2 (#3063)
* add export all function * modify exportAll to reuse existing code from other services * modify routes of export-import * add exportAll function into UI * add errorhandler * add importAll Function into UI for ChatFlow * modify importAll Function to import tools * remove appServer variable * modify exportAll to exportData for new requirement in backend * chore modify type camelCase to PascalCase in exportImportService * add import export for variables, assistants, and checkboxes for UI --------- Co-authored-by: Henry <hzj94@hotmail.com>
This commit is contained in:
parent
40a1064a8f
commit
56f9208d7c
|
|
@ -1,5 +1,4 @@
|
||||||
import { IAction } from 'flowise-components'
|
import { IAction, ICommonObject, IFileUpload, INode, INodeData as INodeDataFromComponent, INodeParams } from 'flowise-components'
|
||||||
import { ICommonObject, IFileUpload, INode, INodeData as INodeDataFromComponent, INodeParams } from 'flowise-components'
|
|
||||||
|
|
||||||
export type MessageType = 'apiMessage' | 'userMessage'
|
export type MessageType = 'apiMessage' | 'userMessage'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { NextFunction, Request, Response } from 'express'
|
||||||
|
import exportImportService from '../../services/export-import'
|
||||||
|
|
||||||
|
const exportData = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await exportImportService.exportData(exportImportService.convertExportInput(req.body))
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const importData = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const importData = req.body
|
||||||
|
await exportImportService.importData(importData)
|
||||||
|
return res.json({ message: 'success' })
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
exportData,
|
||||||
|
importData
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import express from 'express'
|
||||||
|
import exportImportController from '../../controllers/export-import'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
router.post('/export', exportImportController.exportData)
|
||||||
|
|
||||||
|
router.post('/import', exportImportController.importData)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -9,6 +9,7 @@ import componentsCredentialsRouter from './components-credentials'
|
||||||
import componentsCredentialsIconRouter from './components-credentials-icon'
|
import componentsCredentialsIconRouter from './components-credentials-icon'
|
||||||
import credentialsRouter from './credentials'
|
import credentialsRouter from './credentials'
|
||||||
import documentStoreRouter from './documentstore'
|
import documentStoreRouter from './documentstore'
|
||||||
|
import exportImportRouter from './export-import'
|
||||||
import feedbackRouter from './feedback'
|
import feedbackRouter from './feedback'
|
||||||
import fetchLinksRouter from './fetch-links'
|
import fetchLinksRouter from './fetch-links'
|
||||||
import flowConfigRouter from './flow-config'
|
import flowConfigRouter from './flow-config'
|
||||||
|
|
@ -53,6 +54,7 @@ router.use('/components-credentials-icon', componentsCredentialsIconRouter)
|
||||||
router.use('/chatflows-uploads', chatflowsUploadsRouter)
|
router.use('/chatflows-uploads', chatflowsUploadsRouter)
|
||||||
router.use('/credentials', credentialsRouter)
|
router.use('/credentials', credentialsRouter)
|
||||||
router.use('/document-store', documentStoreRouter)
|
router.use('/document-store', documentStoreRouter)
|
||||||
|
router.use('/export-import', exportImportRouter)
|
||||||
router.use('/feedback', feedbackRouter)
|
router.use('/feedback', feedbackRouter)
|
||||||
router.use('/fetch-links', fetchLinksRouter)
|
router.use('/fetch-links', fetchLinksRouter)
|
||||||
router.use('/flow-config', flowConfigRouter)
|
router.use('/flow-config', flowConfigRouter)
|
||||||
|
|
|
||||||
|
|
@ -289,10 +289,60 @@ const updateAssistant = async (assistantId: string, requestBody: any): Promise<A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const importAssistants = async (newAssistants: Partial<Assistant>[]): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
|
||||||
|
// step 1 - check whether array is zero
|
||||||
|
if (newAssistants.length == 0) return
|
||||||
|
|
||||||
|
// step 2 - check whether ids are duplicate in database
|
||||||
|
let ids = '('
|
||||||
|
let count: number = 0
|
||||||
|
const lastCount = newAssistants.length - 1
|
||||||
|
newAssistants.forEach((newAssistant) => {
|
||||||
|
ids += `'${newAssistant.id}'`
|
||||||
|
if (lastCount != count) ids += ','
|
||||||
|
if (lastCount == count) ids += ')'
|
||||||
|
count += 1
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectResponse = await appServer.AppDataSource.getRepository(Assistant)
|
||||||
|
.createQueryBuilder('assistant')
|
||||||
|
.select('assistant.id')
|
||||||
|
.where(`assistant.id IN ${ids}`)
|
||||||
|
.getMany()
|
||||||
|
const foundIds = selectResponse.map((response) => {
|
||||||
|
return response.id
|
||||||
|
})
|
||||||
|
|
||||||
|
// step 3 - remove ids that are only duplicate
|
||||||
|
const prepVariables: Partial<Assistant>[] = newAssistants.map((newAssistant) => {
|
||||||
|
let id: string = ''
|
||||||
|
if (newAssistant.id) id = newAssistant.id
|
||||||
|
if (foundIds.includes(id)) {
|
||||||
|
newAssistant.id = undefined
|
||||||
|
}
|
||||||
|
return newAssistant
|
||||||
|
})
|
||||||
|
|
||||||
|
// step 4 - transactional insert array of entities
|
||||||
|
const insertResponse = await appServer.AppDataSource.getRepository(Assistant).insert(prepVariables)
|
||||||
|
|
||||||
|
return insertResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalFlowiseError(
|
||||||
|
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
`Error: variableService.importVariables - ${getErrorMessage(error)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createAssistant,
|
createAssistant,
|
||||||
deleteAssistant,
|
deleteAssistant,
|
||||||
getAllAssistants,
|
getAllAssistants,
|
||||||
getAssistantById,
|
getAssistantById,
|
||||||
updateAssistant
|
updateAssistant,
|
||||||
|
importAssistants
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { removeFolderFromStorage } from 'flowise-components'
|
import { removeFolderFromStorage } from 'flowise-components'
|
||||||
import { StatusCodes } from 'http-status-codes'
|
import { StatusCodes } from 'http-status-codes'
|
||||||
import { ChatflowType, IChatFlow, IReactFlowObject } from '../../Interface'
|
import { ChatflowType, IReactFlowObject } from '../../Interface'
|
||||||
import { ChatFlow } from '../../database/entities/ChatFlow'
|
import { ChatFlow } from '../../database/entities/ChatFlow'
|
||||||
import { ChatMessage } from '../../database/entities/ChatMessage'
|
import { ChatMessage } from '../../database/entities/ChatMessage'
|
||||||
import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback'
|
import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback'
|
||||||
|
|
@ -103,14 +103,17 @@ const deleteChatflow = async (chatflowId: string): Promise<any> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllChatflows = async (type?: ChatflowType): Promise<IChatFlow[]> => {
|
const getAllChatflows = async (type?: ChatflowType): Promise<ChatFlow[]> => {
|
||||||
try {
|
try {
|
||||||
const appServer = getRunningExpressApp()
|
const appServer = getRunningExpressApp()
|
||||||
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).find()
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).find()
|
||||||
if (type === 'MULTIAGENT') {
|
if (type === 'MULTIAGENT') {
|
||||||
return dbResponse.filter((chatflow) => chatflow.type === type)
|
return dbResponse.filter((chatflow) => chatflow.type === 'MULTIAGENT')
|
||||||
|
} else if (type === 'CHATFLOW') {
|
||||||
|
// fetch all chatflows that are not agentflow
|
||||||
|
return dbResponse.filter((chatflow) => chatflow.type === 'CHATFLOW' || !chatflow.type)
|
||||||
}
|
}
|
||||||
return dbResponse.filter((chatflow) => chatflow.type === 'CHATFLOW' || !chatflow.type)
|
return dbResponse
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new InternalFlowiseError(
|
throw new InternalFlowiseError(
|
||||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
|
@ -202,7 +205,7 @@ const importChatflows = async (newChatflows: Partial<ChatFlow>[]): Promise<any>
|
||||||
const appServer = getRunningExpressApp()
|
const appServer = getRunningExpressApp()
|
||||||
|
|
||||||
// step 1 - check whether file chatflows array is zero
|
// step 1 - check whether file chatflows array is zero
|
||||||
if (newChatflows.length == 0) throw new Error('No chatflows in this file.')
|
if (newChatflows.length == 0) return
|
||||||
|
|
||||||
// step 2 - check whether ids are duplicate in database
|
// step 2 - check whether ids are duplicate in database
|
||||||
let ids = '('
|
let ids = '('
|
||||||
|
|
@ -232,9 +235,8 @@ const importChatflows = async (newChatflows: Partial<ChatFlow>[]): Promise<any>
|
||||||
if (newChatflow.flowData) flowData = newChatflow.flowData
|
if (newChatflow.flowData) flowData = newChatflow.flowData
|
||||||
if (foundIds.includes(id)) {
|
if (foundIds.includes(id)) {
|
||||||
newChatflow.id = undefined
|
newChatflow.id = undefined
|
||||||
newChatflow.name += ' with new id'
|
newChatflow.name += ' (1)'
|
||||||
}
|
}
|
||||||
newChatflow.type = 'CHATFLOW'
|
|
||||||
newChatflow.flowData = JSON.stringify(JSON.parse(flowData))
|
newChatflow.flowData = JSON.stringify(JSON.parse(flowData))
|
||||||
return newChatflow
|
return newChatflow
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,19 @@ const getAllDocumentStores = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAllDocumentFileChunks = async () => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const entities = await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).find()
|
||||||
|
return entities
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalFlowiseError(
|
||||||
|
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
`Error: documentStoreServices.getAllDocumentFileChunks - ${getErrorMessage(error)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const deleteLoaderFromDocumentStore = async (storeId: string, loaderId: string) => {
|
const deleteLoaderFromDocumentStore = async (storeId: string, loaderId: string) => {
|
||||||
try {
|
try {
|
||||||
const appServer = getRunningExpressApp()
|
const appServer = getRunningExpressApp()
|
||||||
|
|
@ -1225,6 +1238,7 @@ export default {
|
||||||
createDocumentStore,
|
createDocumentStore,
|
||||||
deleteLoaderFromDocumentStore,
|
deleteLoaderFromDocumentStore,
|
||||||
getAllDocumentStores,
|
getAllDocumentStores,
|
||||||
|
getAllDocumentFileChunks,
|
||||||
getDocumentStoreById,
|
getDocumentStoreById,
|
||||||
getUsedChatflowNames,
|
getUsedChatflowNames,
|
||||||
getDocumentStoreFileChunks,
|
getDocumentStoreFileChunks,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
import { StatusCodes } from 'http-status-codes'
|
||||||
|
import { ChatFlow } from '../../database/entities/ChatFlow'
|
||||||
|
import { Tool } from '../../database/entities/Tool'
|
||||||
|
import { Variable } from '../../database/entities/Variable'
|
||||||
|
import { Assistant } from '../../database/entities/Assistant'
|
||||||
|
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||||
|
import { getErrorMessage } from '../../errors/utils'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import chatflowService from '../chatflows'
|
||||||
|
import toolsService from '../tools'
|
||||||
|
import variableService from '../variables'
|
||||||
|
import assistantService from '../assistants'
|
||||||
|
|
||||||
|
type ExportInput = {
|
||||||
|
tool: boolean
|
||||||
|
chatflow: boolean
|
||||||
|
agentflow: boolean
|
||||||
|
variable: boolean
|
||||||
|
assistant: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExportData = {
|
||||||
|
Tool: Tool[]
|
||||||
|
ChatFlow: ChatFlow[]
|
||||||
|
AgentFlow: ChatFlow[]
|
||||||
|
Variable: Variable[]
|
||||||
|
Assistant: Assistant[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertExportInput = (body: any): ExportInput => {
|
||||||
|
try {
|
||||||
|
if (!body || typeof body !== 'object') throw new Error('Invalid ExportInput object in request body')
|
||||||
|
if (body.tool && typeof body.tool !== 'boolean') throw new Error('Invalid tool property in ExportInput object')
|
||||||
|
if (body.chatflow && typeof body.chatflow !== 'boolean') throw new Error('Invalid chatflow property in ExportInput object')
|
||||||
|
if (body.agentflow && typeof body.agentflow !== 'boolean') throw new Error('Invalid agentflow property in ExportInput object')
|
||||||
|
if (body.variable && typeof body.variable !== 'boolean') throw new Error('Invalid variable property in ExportInput object')
|
||||||
|
if (body.assistant && typeof body.assistant !== 'boolean') throw new Error('Invalid assistant property in ExportInput object')
|
||||||
|
return body as ExportInput
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalFlowiseError(
|
||||||
|
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
`Error: exportImportService.convertExportInput - ${getErrorMessage(error)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileDefaultName = 'ExportData.json'
|
||||||
|
const exportData = async (exportInput: ExportInput): Promise<{ FileDefaultName: string } & ExportData> => {
|
||||||
|
try {
|
||||||
|
// step 1 - get all Tool
|
||||||
|
let allTool: Tool[] = []
|
||||||
|
if (exportInput.tool === true) allTool = await toolsService.getAllTools()
|
||||||
|
|
||||||
|
// step 2 - get all ChatFlow
|
||||||
|
let allChatflow: ChatFlow[] = []
|
||||||
|
if (exportInput.chatflow === true) allChatflow = await chatflowService.getAllChatflows('CHATFLOW')
|
||||||
|
|
||||||
|
// step 3 - get all MultiAgent
|
||||||
|
let allMultiAgent: ChatFlow[] = []
|
||||||
|
if (exportInput.agentflow === true) allMultiAgent = await chatflowService.getAllChatflows('MULTIAGENT')
|
||||||
|
|
||||||
|
let allVars: Variable[] = []
|
||||||
|
if (exportInput.variable === true) allVars = await variableService.getAllVariables()
|
||||||
|
|
||||||
|
let allAssistants: Assistant[] = []
|
||||||
|
if (exportInput.assistant === true) allAssistants = await assistantService.getAllAssistants()
|
||||||
|
|
||||||
|
return {
|
||||||
|
FileDefaultName,
|
||||||
|
Tool: allTool,
|
||||||
|
ChatFlow: allChatflow,
|
||||||
|
AgentFlow: allMultiAgent,
|
||||||
|
Variable: allVars,
|
||||||
|
Assistant: allAssistants
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalFlowiseError(
|
||||||
|
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
`Error: exportImportService.exportData - ${getErrorMessage(error)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const importData = async (importData: ExportData) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const queryRunner = appServer.AppDataSource.createQueryRunner()
|
||||||
|
|
||||||
|
try {
|
||||||
|
queryRunner.startTransaction()
|
||||||
|
|
||||||
|
// step 1 - importTools
|
||||||
|
if (importData.Tool.length > 0) await toolsService.importTools(importData.Tool)
|
||||||
|
// step 2 - importChatflows
|
||||||
|
if (importData.ChatFlow.length > 0) await chatflowService.importChatflows(importData.ChatFlow)
|
||||||
|
// step 3 - importAgentlows
|
||||||
|
if (importData.AgentFlow.length > 0) await chatflowService.importChatflows(importData.AgentFlow)
|
||||||
|
if (importData.Variable.length > 0) await variableService.importVariables(importData.Variable)
|
||||||
|
if (importData.Assistant.length > 0) await assistantService.importAssistants(importData.Assistant)
|
||||||
|
queryRunner.commitTransaction()
|
||||||
|
} catch (error) {
|
||||||
|
queryRunner.rollbackTransaction()
|
||||||
|
throw error
|
||||||
|
} finally {
|
||||||
|
queryRunner.release()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalFlowiseError(
|
||||||
|
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
`Error: exportImportService.importAll - ${getErrorMessage(error)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
convertExportInput,
|
||||||
|
exportData,
|
||||||
|
importData
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { StatusCodes } from 'http-status-codes'
|
import { StatusCodes } from 'http-status-codes'
|
||||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
|
||||||
import { Tool } from '../../database/entities/Tool'
|
import { Tool } from '../../database/entities/Tool'
|
||||||
import { getAppVersion } from '../../utils'
|
|
||||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||||
import { getErrorMessage } from '../../errors/utils'
|
import { getErrorMessage } from '../../errors/utils'
|
||||||
|
import { getAppVersion } from '../../utils'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
|
||||||
const createTool = async (requestBody: any): Promise<any> => {
|
const createTool = async (requestBody: any): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -35,7 +35,7 @@ const deleteTool = async (toolId: string): Promise<any> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllTools = async (): Promise<any> => {
|
const getAllTools = async (): Promise<Tool[]> => {
|
||||||
try {
|
try {
|
||||||
const appServer = getRunningExpressApp()
|
const appServer = getRunningExpressApp()
|
||||||
const dbResponse = await appServer.AppDataSource.getRepository(Tool).find()
|
const dbResponse = await appServer.AppDataSource.getRepository(Tool).find()
|
||||||
|
|
@ -79,10 +79,58 @@ const updateTool = async (toolId: string, toolBody: any): Promise<any> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const importTools = async (newTools: Partial<Tool>[]) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
|
||||||
|
// step 1 - check whether file tools array is zero
|
||||||
|
if (newTools.length == 0) return
|
||||||
|
|
||||||
|
// step 2 - check whether ids are duplicate in database
|
||||||
|
let ids = '('
|
||||||
|
let count: number = 0
|
||||||
|
const lastCount = newTools.length - 1
|
||||||
|
newTools.forEach((newTools) => {
|
||||||
|
ids += `'${newTools.id}'`
|
||||||
|
if (lastCount != count) ids += ','
|
||||||
|
if (lastCount == count) ids += ')'
|
||||||
|
count += 1
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectResponse = await appServer.AppDataSource.getRepository(Tool)
|
||||||
|
.createQueryBuilder('t')
|
||||||
|
.select('t.id')
|
||||||
|
.where(`t.id IN ${ids}`)
|
||||||
|
.getMany()
|
||||||
|
const foundIds = selectResponse.map((response) => {
|
||||||
|
return response.id
|
||||||
|
})
|
||||||
|
|
||||||
|
// step 3 - remove ids that are only duplicate
|
||||||
|
const prepTools: Partial<Tool>[] = newTools.map((newTool) => {
|
||||||
|
let id: string = ''
|
||||||
|
if (newTool.id) id = newTool.id
|
||||||
|
if (foundIds.includes(id)) {
|
||||||
|
newTool.id = undefined
|
||||||
|
newTool.name += ' (1)'
|
||||||
|
}
|
||||||
|
return newTool
|
||||||
|
})
|
||||||
|
|
||||||
|
// step 4 - transactional insert array of entities
|
||||||
|
const insertResponse = await appServer.AppDataSource.getRepository(Tool).insert(prepTools)
|
||||||
|
|
||||||
|
return insertResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: toolsService.importTools - ${getErrorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createTool,
|
createTool,
|
||||||
deleteTool,
|
deleteTool,
|
||||||
getAllTools,
|
getAllTools,
|
||||||
getToolById,
|
getToolById,
|
||||||
updateTool
|
updateTool,
|
||||||
|
importTools
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,61 @@ const updateVariable = async (variable: Variable, updatedVariable: Variable) =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const importVariables = async (newVariables: Partial<Variable>[]): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
|
||||||
|
// step 1 - check whether array is zero
|
||||||
|
if (newVariables.length == 0) return
|
||||||
|
|
||||||
|
// step 2 - check whether ids are duplicate in database
|
||||||
|
let ids = '('
|
||||||
|
let count: number = 0
|
||||||
|
const lastCount = newVariables.length - 1
|
||||||
|
newVariables.forEach((newVariable) => {
|
||||||
|
ids += `'${newVariable.id}'`
|
||||||
|
if (lastCount != count) ids += ','
|
||||||
|
if (lastCount == count) ids += ')'
|
||||||
|
count += 1
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectResponse = await appServer.AppDataSource.getRepository(Variable)
|
||||||
|
.createQueryBuilder('v')
|
||||||
|
.select('v.id')
|
||||||
|
.where(`v.id IN ${ids}`)
|
||||||
|
.getMany()
|
||||||
|
const foundIds = selectResponse.map((response) => {
|
||||||
|
return response.id
|
||||||
|
})
|
||||||
|
|
||||||
|
// step 3 - remove ids that are only duplicate
|
||||||
|
const prepVariables: Partial<Variable>[] = newVariables.map((newVariable) => {
|
||||||
|
let id: string = ''
|
||||||
|
if (newVariable.id) id = newVariable.id
|
||||||
|
if (foundIds.includes(id)) {
|
||||||
|
newVariable.id = undefined
|
||||||
|
newVariable.name += ' (1)'
|
||||||
|
}
|
||||||
|
return newVariable
|
||||||
|
})
|
||||||
|
|
||||||
|
// step 4 - transactional insert array of entities
|
||||||
|
const insertResponse = await appServer.AppDataSource.getRepository(Variable).insert(prepVariables)
|
||||||
|
|
||||||
|
return insertResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalFlowiseError(
|
||||||
|
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
`Error: variableService.importVariables - ${getErrorMessage(error)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createVariable,
|
createVariable,
|
||||||
deleteVariable,
|
deleteVariable,
|
||||||
getAllVariables,
|
getAllVariables,
|
||||||
getVariableById,
|
getVariableById,
|
||||||
updateVariable
|
updateVariable,
|
||||||
|
importVariables
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import client from './client'
|
import client from './client'
|
||||||
|
|
||||||
const getAllChatflows = () => client.get('/chatflows')
|
const getAllChatflows = () => client.get('/chatflows?type=CHATFLOW')
|
||||||
|
|
||||||
const getAllAgentflows = () => client.get('/chatflows?type=MULTIAGENT')
|
const getAllAgentflows = () => client.get('/chatflows?type=MULTIAGENT')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import client from './client'
|
||||||
|
|
||||||
|
const exportData = (body) => client.post('/export-import/export', body)
|
||||||
|
const importData = (body) => client.post('/export-import/import', body)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
exportData,
|
||||||
|
importData
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 799 KiB |
|
|
@ -1,9 +1,11 @@
|
||||||
import { closeSnackbar as closeSnackbarAction, enqueueSnackbar as enqueueSnackbarAction, MENU_OPEN, REMOVE_DIRTY } from '@/store/actions'
|
import { closeSnackbar as closeSnackbarAction, enqueueSnackbar as enqueueSnackbarAction, REMOVE_DIRTY } from '@/store/actions'
|
||||||
import { sanitizeChatflows } from '@/utils/genericHelper'
|
import { exportData, stringify } from '@/utils/exportImport'
|
||||||
import useNotifier from '@/utils/useNotifier'
|
import useNotifier from '@/utils/useNotifier'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
|
|
||||||
// material-ui
|
// material-ui
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
|
|
@ -18,7 +20,14 @@ import {
|
||||||
ListItemText,
|
ListItemText,
|
||||||
Paper,
|
Paper,
|
||||||
Popper,
|
Popper,
|
||||||
Typography
|
Typography,
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
Stack,
|
||||||
|
FormControlLabel,
|
||||||
|
Checkbox,
|
||||||
|
DialogActions
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { useTheme } from '@mui/material/styles'
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
|
@ -33,13 +42,114 @@ import Transitions from '@/ui-component/extended/Transitions'
|
||||||
// assets
|
// assets
|
||||||
import { IconFileExport, IconFileUpload, IconInfoCircle, IconLogout, IconSettings, IconX } from '@tabler/icons-react'
|
import { IconFileExport, IconFileUpload, IconInfoCircle, IconLogout, IconSettings, IconX } from '@tabler/icons-react'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
import ExportingGIF from '@/assets/images/Exporting.gif'
|
||||||
|
|
||||||
//API
|
//API
|
||||||
import chatFlowsApi from '@/api/chatflows'
|
import exportImportApi from '@/api/exportimport'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import useApi from '@/hooks/useApi'
|
import useApi from '@/hooks/useApi'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { getErrorMessage } from '@/utils/errorHandler'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
|
const dataToExport = ['Chatflows', 'Agentflows', 'Tools', 'Variables', 'Assistants']
|
||||||
|
|
||||||
|
const ExportDialog = ({ show, onCancel, onExport }) => {
|
||||||
|
const portalElement = document.getElementById('portal')
|
||||||
|
|
||||||
|
const [selectedData, setSelectedData] = useState(['Chatflows', 'Agentflows', 'Tools', 'Variables', 'Assistants'])
|
||||||
|
const [isExporting, setIsExporting] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show) setIsExporting(false)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
setIsExporting(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [show])
|
||||||
|
|
||||||
|
const component = show ? (
|
||||||
|
<Dialog
|
||||||
|
onClose={!isExporting ? onCancel : undefined}
|
||||||
|
open={show}
|
||||||
|
fullWidth
|
||||||
|
maxWidth='sm'
|
||||||
|
aria-labelledby='export-dialog-title'
|
||||||
|
aria-describedby='export-dialog-description'
|
||||||
|
>
|
||||||
|
<DialogTitle sx={{ fontSize: '1rem' }} id='export-dialog-title'>
|
||||||
|
{!isExporting ? 'Select Data to Export' : 'Exporting..'}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
{!isExporting && (
|
||||||
|
<Stack direction='row' sx={{ gap: 1, flexWrap: 'wrap' }}>
|
||||||
|
{dataToExport.map((data, index) => (
|
||||||
|
<FormControlLabel
|
||||||
|
key={index}
|
||||||
|
size='small'
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
color='success'
|
||||||
|
checked={selectedData.includes(data)}
|
||||||
|
onChange={(event) => {
|
||||||
|
setSelectedData(
|
||||||
|
event.target.checked
|
||||||
|
? [...selectedData, data]
|
||||||
|
: selectedData.filter((item) => item !== data)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={data}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{isExporting && (
|
||||||
|
<Box sx={{ height: 'auto', display: 'flex', justifyContent: 'center', mb: 3 }}>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
objectFit: 'cover',
|
||||||
|
height: 'auto',
|
||||||
|
width: 'auto'
|
||||||
|
}}
|
||||||
|
src={ExportingGIF}
|
||||||
|
alt='ExportingGIF'
|
||||||
|
/>
|
||||||
|
<span>Exporting data might takes a while</span>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
{!isExporting && (
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onCancel}>Cancel</Button>
|
||||||
|
<Button
|
||||||
|
disabled={selectedData.length === 0}
|
||||||
|
variant='contained'
|
||||||
|
onClick={() => {
|
||||||
|
setIsExporting(true)
|
||||||
|
onExport(selectedData)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
)}
|
||||||
|
</Dialog>
|
||||||
|
) : null
|
||||||
|
|
||||||
|
return createPortal(component, portalElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportDialog.propTypes = {
|
||||||
|
show: PropTypes.bool,
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
onExport: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
// ==============================|| PROFILE MENU ||============================== //
|
// ==============================|| PROFILE MENU ||============================== //
|
||||||
|
|
||||||
|
|
@ -50,12 +160,16 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||||
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [aboutDialogOpen, setAboutDialogOpen] = useState(false)
|
const [aboutDialogOpen, setAboutDialogOpen] = useState(false)
|
||||||
|
const [exportDialogOpen, setExportDialogOpen] = useState(false)
|
||||||
|
|
||||||
const anchorRef = useRef(null)
|
const anchorRef = useRef(null)
|
||||||
const inputRef = useRef()
|
const inputRef = useRef()
|
||||||
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
|
||||||
|
const importAllApi = useApi(exportImportApi.importData)
|
||||||
|
const exportAllApi = useApi(exportImportApi.exportData)
|
||||||
|
const prevOpen = useRef(open)
|
||||||
|
|
||||||
// ==============================|| Snackbar ||============================== //
|
// ==============================|| Snackbar ||============================== //
|
||||||
|
|
||||||
|
|
@ -90,7 +204,7 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const importChatflowsApi = useApi(chatFlowsApi.importChatflows)
|
|
||||||
const fileChange = (e) => {
|
const fileChange = (e) => {
|
||||||
if (!e.target.files) return
|
if (!e.target.files) return
|
||||||
|
|
||||||
|
|
@ -101,16 +215,16 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||||
if (!evt?.target?.result) {
|
if (!evt?.target?.result) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const chatflows = JSON.parse(evt.target.result)
|
const body = JSON.parse(evt.target.result)
|
||||||
importChatflowsApi.request(chatflows)
|
importAllApi.request(body)
|
||||||
}
|
}
|
||||||
reader.readAsText(file)
|
reader.readAsText(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
const importChatflowsSuccess = () => {
|
const importAllSuccess = () => {
|
||||||
dispatch({ type: REMOVE_DIRTY })
|
dispatch({ type: REMOVE_DIRTY })
|
||||||
enqueueSnackbar({
|
enqueueSnackbar({
|
||||||
message: `Import chatflows successful`,
|
message: `Import All successful`,
|
||||||
options: {
|
options: {
|
||||||
key: new Date().getTime() + Math.random(),
|
key: new Date().getTime() + Math.random(),
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
|
|
@ -122,60 +236,75 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
|
||||||
if (importChatflowsApi.error) errorFailed(`Failed to import chatflows: ${importChatflowsApi.error.response.data.message}`)
|
const importAll = () => {
|
||||||
if (importChatflowsApi.data) {
|
|
||||||
importChatflowsSuccess()
|
|
||||||
// if current location is /chatflows, refresh the page
|
|
||||||
if (location.pathname === '/chatflows') navigate(0)
|
|
||||||
else {
|
|
||||||
// if not redirect to /chatflows
|
|
||||||
dispatch({ type: MENU_OPEN, id: 'chatflows' })
|
|
||||||
navigate('/chatflows')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [importChatflowsApi.error, importChatflowsApi.data])
|
|
||||||
const importAllChatflows = () => {
|
|
||||||
inputRef.current.click()
|
inputRef.current.click()
|
||||||
}
|
}
|
||||||
const getAllChatflowsApi = useApi(chatFlowsApi.getAllChatflows)
|
|
||||||
|
|
||||||
const exportChatflowsSuccess = () => {
|
const onExport = (data) => {
|
||||||
dispatch({ type: REMOVE_DIRTY })
|
const body = {}
|
||||||
enqueueSnackbar({
|
if (data.includes('Chatflows')) body.chatflow = true
|
||||||
message: `Export chatflows successful`,
|
if (data.includes('Agentflows')) body.agentflow = true
|
||||||
options: {
|
if (data.includes('Tools')) body.tool = true
|
||||||
key: new Date().getTime() + Math.random(),
|
if (data.includes('Variables')) body.variable = true
|
||||||
variant: 'success',
|
if (data.includes('Assistants')) body.assistant = true
|
||||||
action: (key) => (
|
|
||||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
exportAllApi.request(body)
|
||||||
<IconX />
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (getAllChatflowsApi.error) errorFailed(`Failed to export Chatflows: ${getAllChatflowsApi.error.response.data.message}`)
|
if (importAllApi.data) {
|
||||||
if (getAllChatflowsApi.data) {
|
importAllSuccess()
|
||||||
const sanitizedChatflows = sanitizeChatflows(getAllChatflowsApi.data)
|
navigate(0)
|
||||||
const dataStr = JSON.stringify({ Chatflows: sanitizedChatflows }, null, 2)
|
|
||||||
const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr)
|
|
||||||
|
|
||||||
const exportFileDefaultName = 'AllChatflows.json'
|
|
||||||
|
|
||||||
const linkElement = document.createElement('a')
|
|
||||||
linkElement.setAttribute('href', dataUri)
|
|
||||||
linkElement.setAttribute('download', exportFileDefaultName)
|
|
||||||
linkElement.click()
|
|
||||||
exportChatflowsSuccess()
|
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [getAllChatflowsApi.error, getAllChatflowsApi.data])
|
}, [importAllApi.data])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (importAllApi.error) {
|
||||||
|
let errMsg = 'Invalid Imported File'
|
||||||
|
let error = importAllApi.error
|
||||||
|
if (error?.response?.data) {
|
||||||
|
errMsg = typeof error.response.data === 'object' ? error.response.data.message : error.response.data
|
||||||
|
}
|
||||||
|
errorFailed(`Failed to import: ${errMsg}`)
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [importAllApi.error])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (exportAllApi.data) {
|
||||||
|
setExportDialogOpen(false)
|
||||||
|
try {
|
||||||
|
const dataStr = stringify(exportData(exportAllApi.data))
|
||||||
|
//const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr)
|
||||||
|
const blob = new Blob([dataStr], { type: 'application/json' })
|
||||||
|
const dataUri = URL.createObjectURL(blob)
|
||||||
|
|
||||||
|
const linkElement = document.createElement('a')
|
||||||
|
linkElement.setAttribute('href', dataUri)
|
||||||
|
linkElement.setAttribute('download', exportAllApi.data.FileDefaultName)
|
||||||
|
linkElement.click()
|
||||||
|
} catch (error) {
|
||||||
|
errorFailed(`Failed to export all: ${getErrorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [exportAllApi.data])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (exportAllApi.error) {
|
||||||
|
setExportDialogOpen(false)
|
||||||
|
let errMsg = 'Internal Server Error'
|
||||||
|
let error = exportAllApi.error
|
||||||
|
if (error?.response?.data) {
|
||||||
|
errMsg = typeof error.response.data === 'object' ? error.response.data.message : error.response.data
|
||||||
|
}
|
||||||
|
errorFailed(`Failed to export: ${errMsg}`)
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [exportAllApi.error])
|
||||||
|
|
||||||
const prevOpen = useRef(open)
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (prevOpen.current === true && open === false) {
|
if (prevOpen.current === true && open === false) {
|
||||||
anchorRef.current.focus()
|
anchorRef.current.focus()
|
||||||
|
|
@ -258,26 +387,26 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
getAllChatflowsApi.request()
|
setExportDialogOpen(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<IconFileExport stroke={1.5} size='1.3rem' />
|
<IconFileExport stroke={1.5} size='1.3rem' />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={<Typography variant='body2'>Export Chatflows</Typography>} />
|
<ListItemText primary={<Typography variant='body2'>Export</Typography>} />
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
importAllChatflows()
|
importAll()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<IconFileUpload stroke={1.5} size='1.3rem' />
|
<IconFileUpload stroke={1.5} size='1.3rem' />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={<Typography variant='body2'>Import Chatflows</Typography>} />
|
<ListItemText primary={<Typography variant='body2'>Import</Typography>} />
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
<input ref={inputRef} type='file' hidden onChange={fileChange} />
|
<input ref={inputRef} type='file' hidden onChange={fileChange} accept='.json' />
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -311,6 +440,7 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||||
)}
|
)}
|
||||||
</Popper>
|
</Popper>
|
||||||
<AboutDialog show={aboutDialogOpen} onCancel={() => setAboutDialogOpen(false)} />
|
<AboutDialog show={aboutDialogOpen} onCancel={() => setAboutDialogOpen(false)} />
|
||||||
|
<ExportDialog show={exportDialogOpen} onCancel={() => setExportDialogOpen(false)} onExport={(data) => onExport(data)} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
const isErrorWithMessage = (error) => {
|
||||||
|
return typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
const toErrorWithMessage = (maybeError) => {
|
||||||
|
if (isErrorWithMessage(maybeError)) return maybeError
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new Error(JSON.stringify(maybeError))
|
||||||
|
} catch {
|
||||||
|
// fallback in case there's an error stringifying the maybeError
|
||||||
|
// like with circular references for example.
|
||||||
|
return new Error(String(maybeError))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getErrorMessage = (error) => {
|
||||||
|
return toErrorWithMessage(error).message
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { getErrorMessage } from './errorHandler'
|
||||||
|
import { generateExportFlowData } from './genericHelper'
|
||||||
|
|
||||||
|
const sanitizeTool = (Tool) => {
|
||||||
|
try {
|
||||||
|
return Tool.map((tool) => {
|
||||||
|
return {
|
||||||
|
id: tool.id,
|
||||||
|
name: tool.name,
|
||||||
|
description: tool.description,
|
||||||
|
color: tool.color,
|
||||||
|
iconSrc: tool.iconSrc,
|
||||||
|
schema: tool.schema,
|
||||||
|
func: tool.func
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`exportImport.sanitizeTool ${getErrorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanitizeChatflow = (ChatFlow) => {
|
||||||
|
try {
|
||||||
|
return ChatFlow.map((chatFlow) => {
|
||||||
|
const sanitizeFlowData = generateExportFlowData(JSON.parse(chatFlow.flowData))
|
||||||
|
return {
|
||||||
|
id: chatFlow.id,
|
||||||
|
name: chatFlow.name,
|
||||||
|
flowData: stringify(sanitizeFlowData),
|
||||||
|
type: chatFlow.type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`exportImport.sanitizeChatflow ${getErrorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanitizeVariable = (Variable) => {
|
||||||
|
try {
|
||||||
|
return Variable.map((variable) => {
|
||||||
|
return {
|
||||||
|
id: variable.id,
|
||||||
|
name: variable.name,
|
||||||
|
value: variable.value,
|
||||||
|
type: variable.type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`exportImport.sanitizeVariable ${getErrorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanitizeAssistant = (Assistant) => {
|
||||||
|
try {
|
||||||
|
return Assistant.map((assistant) => {
|
||||||
|
return {
|
||||||
|
id: assistant.id,
|
||||||
|
details: assistant.details,
|
||||||
|
credential: assistant.credential,
|
||||||
|
iconSrc: assistant.iconSrc
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`exportImport.sanitizeAssistant ${getErrorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stringify = (object) => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(object, null, 2)
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`exportImport.stringify ${getErrorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const exportData = (exportAllData) => {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
Tool: sanitizeTool(exportAllData.Tool),
|
||||||
|
ChatFlow: sanitizeChatflow(exportAllData.ChatFlow),
|
||||||
|
AgentFlow: sanitizeChatflow(exportAllData.AgentFlow),
|
||||||
|
Variable: sanitizeVariable(exportAllData.Variable),
|
||||||
|
Assistant: sanitizeAssistant(exportAllData.Assistant)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`exportImport.exportData ${getErrorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import moment from 'moment'
|
|
||||||
import { uniq } from 'lodash'
|
import { uniq } from 'lodash'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
export const getUniqueNodeId = (nodeData, nodes) => {
|
export const getUniqueNodeId = (nodeData, nodes) => {
|
||||||
// Get amount of same nodes
|
// Get amount of same nodes
|
||||||
|
|
@ -373,18 +373,6 @@ export const getFolderName = (base64ArrayStr) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sanitizeChatflows = (arrayChatflows) => {
|
|
||||||
const sanitizedChatflows = arrayChatflows.map((chatFlow) => {
|
|
||||||
const sanitizeFlowData = generateExportFlowData(JSON.parse(chatFlow.flowData))
|
|
||||||
return {
|
|
||||||
id: chatFlow.id,
|
|
||||||
name: chatFlow.name,
|
|
||||||
flowData: JSON.stringify(sanitizeFlowData, null, 2)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return sanitizedChatflows
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateExportFlowData = (flowData) => {
|
export const generateExportFlowData = (flowData) => {
|
||||||
const nodes = flowData.nodes
|
const nodes = flowData.nodes
|
||||||
const edges = flowData.edges
|
const edges = flowData.edges
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,8 @@ const AssistantDialog = ({ show, dialogProps, onCancel, onConfirm, setError }) =
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (getAssistantObjApi.error) {
|
if (getAssistantObjApi.error) {
|
||||||
let errMsg = ''
|
let errMsg = 'Internal Server Error'
|
||||||
|
let error = getAssistantObjApi.error
|
||||||
if (error?.response?.data) {
|
if (error?.response?.data) {
|
||||||
errMsg = typeof error.response.data === 'object' ? error.response.data.message : error.response.data
|
errMsg = typeof error.response.data === 'object' ? error.response.data.message : error.response.data
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue