Chore/refractor (#4454)

* markdown files and env examples cleanup

* components update

* update jsonlines description

* server refractor

* update telemetry

* add execute custom node

* add ui refractor

* add username and password authenticate

* correctly retrieve past images in agentflowv2

* disable e2e temporarily

* add existing username and password authenticate

* update migration to default workspace

* update todo

* blob storage migrating

* throw error on agent tool call error

* add missing execution import

* add referral

* chore: add error message when importData is undefined

* migrate api keys to db

* fix: data too long for column executionData

* migrate api keys from json to db at init

* add info on account setup

* update docstore missing fields

---------

Co-authored-by: chungyau97 <chungyau97@gmail.com>
This commit is contained in:
Henry Heng
2025-05-27 14:29:42 +08:00
committed by GitHub
parent e35a126b46
commit 5a37227d14
560 changed files with 62127 additions and 4100 deletions
+83 -119
View File
@@ -1,10 +1,14 @@
import { randomBytes, scryptSync, timingSafeEqual } from 'crypto'
import { ICommonObject } from 'flowise-components'
import moment from 'moment'
import fs from 'fs'
import path from 'path'
import logger from './logger'
import { appConfig } from '../AppConfig'
import { DataSource } from 'typeorm'
import { ApiKey } from '../database/entities/ApiKey'
import { Workspace } from '../enterprise/database/entities/workspace.entity'
import { v4 as uuidv4 } from 'uuid'
import { ChatFlow } from '../database/entities/ChatFlow'
import { addChatflowsCount } from './addChatflowsCount'
import { Platform } from '../Interface'
/**
* Returns the api key path
@@ -51,94 +55,14 @@ export const compareKeys = (storedKey: string, suppliedKey: string): boolean =>
* @returns {Promise<ICommonObject[]>}
*/
export const getAPIKeys = async (): Promise<ICommonObject[]> => {
if (appConfig.apiKeys.storageType !== 'json') {
return []
}
try {
const content = await fs.promises.readFile(getAPIKeyPath(), 'utf8')
return JSON.parse(content)
} catch (error) {
const keyName = 'DefaultKey'
const apiKey = generateAPIKey()
const apiSecret = generateSecretHash(apiKey)
const content = [
{
keyName,
apiKey,
apiSecret,
createdAt: moment().format('DD-MMM-YY'),
id: randomBytes(16).toString('hex')
}
]
await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(content), 'utf8')
return content
return []
}
}
/**
* Add new API key
* @param {string} keyName
* @returns {Promise<ICommonObject[]>}
*/
export const addAPIKey = async (keyName: string): Promise<ICommonObject[]> => {
const existingAPIKeys = await getAPIKeys()
const apiKey = generateAPIKey()
const apiSecret = generateSecretHash(apiKey)
const content = [
...existingAPIKeys,
{
keyName,
apiKey,
apiSecret,
createdAt: moment().format('DD-MMM-YY'),
id: randomBytes(16).toString('hex')
}
]
await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(content), 'utf8')
return content
}
/**
* import API keys
* @param {[]} keys
* @returns {Promise<ICommonObject[]>}
*/
export const importKeys = async (keys: any[], importMode: string): Promise<ICommonObject[]> => {
const allApiKeys = await getAPIKeys()
// if importMode is errorIfExist, check for existing keys and raise error before any modification to the file
if (importMode === 'errorIfExist') {
for (const key of keys) {
const keyNameExists = allApiKeys.find((k) => k.keyName === key.keyName)
if (keyNameExists) {
throw new Error(`Key with name ${key.keyName} already exists`)
}
}
}
for (const key of keys) {
// Check if keyName already exists, if overwrite is false, raise an error else overwrite the key
const keyNameExists = allApiKeys.find((k) => k.keyName === key.keyName)
if (keyNameExists) {
const keyIndex = allApiKeys.findIndex((k) => k.keyName === key.keyName)
switch (importMode) {
case 'overwriteIfExist':
allApiKeys[keyIndex] = key
continue
case 'ignoreIfExist':
// ignore this key and continue
continue
case 'errorIfExist':
// should not reach here as we have already checked for existing keys
throw new Error(`Key with name ${key.keyName} already exists`)
default:
throw new Error(`Unknown overwrite option ${importMode}`)
}
}
allApiKeys.push(key)
}
await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(allApiKeys), 'utf8')
return allApiKeys
}
/**
* Get API Key details
* @param {string} apiKey
@@ -151,42 +75,82 @@ export const getApiKey = async (apiKey: string) => {
return existingAPIKeys[keyIndex]
}
/**
* Update existing API key
* @param {string} keyIdToUpdate
* @param {string} newKeyName
* @returns {Promise<ICommonObject[]>}
*/
export const updateAPIKey = async (keyIdToUpdate: string, newKeyName: string): Promise<ICommonObject[]> => {
const existingAPIKeys = await getAPIKeys()
const keyIndex = existingAPIKeys.findIndex((key) => key.id === keyIdToUpdate)
if (keyIndex < 0) return []
existingAPIKeys[keyIndex].keyName = newKeyName
await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(existingAPIKeys), 'utf8')
return existingAPIKeys
}
export const migrateApiKeysFromJsonToDb = async (appDataSource: DataSource, platformType: Platform) => {
if (platformType === Platform.CLOUD) {
return
}
/**
* Delete API key
* @param {string} keyIdToDelete
* @returns {Promise<ICommonObject[]>}
*/
export const deleteAPIKey = async (keyIdToDelete: string): Promise<ICommonObject[]> => {
const existingAPIKeys = await getAPIKeys()
const result = existingAPIKeys.filter((key) => key.id !== keyIdToDelete)
await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(result), 'utf8')
return result
}
if (!process.env.APIKEY_STORAGE_TYPE || process.env.APIKEY_STORAGE_TYPE === 'json') {
const keys = await getAPIKeys()
if (keys.length > 0) {
try {
// Get all available workspaces
const workspaces = await appDataSource.getRepository(Workspace).find()
/**
* Replace all api keys
* @param {ICommonObject[]} content
* @returns {Promise<void>}
*/
export const replaceAllAPIKeys = async (content: ICommonObject[]): Promise<void> => {
try {
await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(content), 'utf8')
} catch (error) {
logger.error(error)
for (const key of keys) {
const existingKey = await appDataSource.getRepository(ApiKey).findOneBy({
apiKey: key.apiKey
})
// Only add if key doesn't already exist in DB
if (!existingKey) {
// Create a new API key for each workspace
if (workspaces.length > 0) {
for (const workspace of workspaces) {
const newKey = new ApiKey()
newKey.id = uuidv4()
newKey.apiKey = key.apiKey
newKey.apiSecret = key.apiSecret
newKey.keyName = key.keyName
newKey.workspaceId = workspace.id
const keyEntity = appDataSource.getRepository(ApiKey).create(newKey)
await appDataSource.getRepository(ApiKey).save(keyEntity)
const chatflows = await appDataSource.getRepository(ChatFlow).findBy({
apikeyid: key.id,
workspaceId: workspace.id
})
for (const chatflow of chatflows) {
chatflow.apikeyid = newKey.id
await appDataSource.getRepository(ChatFlow).save(chatflow)
}
await addChatflowsCount(chatflows)
}
} else {
// If no workspaces exist, create the key without a workspace ID and later will be updated by setNullWorkspaceId
const newKey = new ApiKey()
newKey.id = uuidv4()
newKey.apiKey = key.apiKey
newKey.apiSecret = key.apiSecret
newKey.keyName = key.keyName
const keyEntity = appDataSource.getRepository(ApiKey).create(newKey)
await appDataSource.getRepository(ApiKey).save(keyEntity)
const chatflows = await appDataSource.getRepository(ChatFlow).findBy({
apikeyid: key.id
})
for (const chatflow of chatflows) {
chatflow.apikeyid = newKey.id
await appDataSource.getRepository(ChatFlow).save(chatflow)
}
await addChatflowsCount(chatflows)
}
}
}
// Delete the JSON file
if (fs.existsSync(getAPIKeyPath())) {
fs.unlinkSync(getAPIKeyPath())
}
} catch (error) {
console.error('Error migrating API keys from JSON to DB', error)
}
}
}
}