refactor: move api key utilities to seperate file
This commit is contained in:
parent
0b93b337fd
commit
292b90ccb1
|
|
@ -31,18 +31,11 @@ import {
|
|||
constructGraphs,
|
||||
resolveVariables,
|
||||
isStartNodeDependOnInput,
|
||||
getAPIKeys,
|
||||
addAPIKey,
|
||||
updateAPIKey,
|
||||
deleteAPIKey,
|
||||
compareKeys,
|
||||
mapMimeTypeToInputField,
|
||||
findAvailableConfigs,
|
||||
isSameOverrideConfig,
|
||||
replaceAllAPIKeys,
|
||||
isFlowValidForStream,
|
||||
databaseEntities,
|
||||
getApiKey,
|
||||
transformToCredentialEntity,
|
||||
decryptCredentialData,
|
||||
clearAllSessionMemory,
|
||||
|
|
@ -64,6 +57,7 @@ import { ChatflowPool } from './ChatflowPool'
|
|||
import { CachePool } from './CachePool'
|
||||
import { ICommonObject, INodeOptionsValue } from 'flowise-components'
|
||||
import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
|
||||
import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, replaceAllAPIKeys, updateAPIKey } from './utils/apiKey'
|
||||
|
||||
export class App {
|
||||
app: express.Application
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
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'
|
||||
|
||||
/**
|
||||
* Returns the api key path
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getAPIKeyPath = (): string => {
|
||||
return process.env.APIKEY_PATH ? path.join(process.env.APIKEY_PATH, 'api.json') : path.join(__dirname, '..', '..', 'api.json')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the api key
|
||||
* @returns {string}
|
||||
*/
|
||||
export const generateAPIKey = (): string => {
|
||||
const buffer = randomBytes(32)
|
||||
return buffer.toString('base64')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the secret key
|
||||
* @param {string} apiKey
|
||||
* @returns {string}
|
||||
*/
|
||||
export const generateSecretHash = (apiKey: string): string => {
|
||||
const salt = randomBytes(8).toString('hex')
|
||||
const buffer = scryptSync(apiKey, salt, 64) as Buffer
|
||||
return `${buffer.toString('hex')}.${salt}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify valid keys
|
||||
* @param {string} storedKey
|
||||
* @param {string} suppliedKey
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const compareKeys = (storedKey: string, suppliedKey: string): boolean => {
|
||||
const [hashedPassword, salt] = storedKey.split('.')
|
||||
const buffer = scryptSync(suppliedKey, salt, 64) as Buffer
|
||||
return timingSafeEqual(Buffer.from(hashedPassword, 'hex'), buffer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API keys
|
||||
* @returns {Promise<ICommonObject[]>}
|
||||
*/
|
||||
export const getAPIKeys = async (): Promise<ICommonObject[]> => {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API Key details
|
||||
* @param {string} apiKey
|
||||
* @returns {Promise<ICommonObject[]>}
|
||||
*/
|
||||
export const getApiKey = async (apiKey: string) => {
|
||||
const existingAPIKeys = await getAPIKeys()
|
||||
const keyIndex = existingAPIKeys.findIndex((key) => key.apiKey === apiKey)
|
||||
if (keyIndex < 0) return undefined
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +1,32 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import moment from 'moment'
|
||||
import logger from './logger'
|
||||
import {
|
||||
IComponentCredentials,
|
||||
IComponentNodes,
|
||||
ICredentialDataDecrypted,
|
||||
ICredentialReqBody,
|
||||
IDepthQueue,
|
||||
IExploredNode,
|
||||
INodeData,
|
||||
INodeDependencies,
|
||||
INodeDirectedGraph,
|
||||
INodeQueue,
|
||||
IOverrideConfig,
|
||||
IReactFlowEdge,
|
||||
IReactFlowNode,
|
||||
IVariableDict,
|
||||
INodeData,
|
||||
IOverrideConfig,
|
||||
ICredentialDataDecrypted,
|
||||
IComponentCredentials,
|
||||
ICredentialReqBody
|
||||
IVariableDict
|
||||
} from '../Interface'
|
||||
import { cloneDeep, get, isEqual } from 'lodash'
|
||||
import {
|
||||
ICommonObject,
|
||||
convertChatHistoryToText,
|
||||
getInputVariables,
|
||||
IDatabaseEntity,
|
||||
handleEscapeCharacters,
|
||||
IMessage,
|
||||
convertChatHistoryToText
|
||||
ICommonObject,
|
||||
IDatabaseEntity,
|
||||
IMessage
|
||||
} from 'flowise-components'
|
||||
import { scryptSync, randomBytes, timingSafeEqual } from 'crypto'
|
||||
import { randomBytes } from 'crypto'
|
||||
import { AES, enc } from 'crypto-js'
|
||||
|
||||
import { ChatFlow } from '../database/entities/ChatFlow'
|
||||
|
|
@ -574,147 +573,6 @@ export const isSameOverrideConfig = (
|
|||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the api key path
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getAPIKeyPath = (): string => {
|
||||
return process.env.APIKEY_PATH ? path.join(process.env.APIKEY_PATH, 'api.json') : path.join(__dirname, '..', '..', 'api.json')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the api key
|
||||
* @returns {string}
|
||||
*/
|
||||
export const generateAPIKey = (): string => {
|
||||
const buffer = randomBytes(32)
|
||||
return buffer.toString('base64')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the secret key
|
||||
* @param {string} apiKey
|
||||
* @returns {string}
|
||||
*/
|
||||
export const generateSecretHash = (apiKey: string): string => {
|
||||
const salt = randomBytes(8).toString('hex')
|
||||
const buffer = scryptSync(apiKey, salt, 64) as Buffer
|
||||
return `${buffer.toString('hex')}.${salt}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify valid keys
|
||||
* @param {string} storedKey
|
||||
* @param {string} suppliedKey
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const compareKeys = (storedKey: string, suppliedKey: string): boolean => {
|
||||
const [hashedPassword, salt] = storedKey.split('.')
|
||||
const buffer = scryptSync(suppliedKey, salt, 64) as Buffer
|
||||
return timingSafeEqual(Buffer.from(hashedPassword, 'hex'), buffer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API keys
|
||||
* @returns {Promise<ICommonObject[]>}
|
||||
*/
|
||||
export const getAPIKeys = async (): Promise<ICommonObject[]> => {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API Key details
|
||||
* @param {string} apiKey
|
||||
* @returns {Promise<ICommonObject[]>}
|
||||
*/
|
||||
export const getApiKey = async (apiKey: string) => {
|
||||
const existingAPIKeys = await getAPIKeys()
|
||||
const keyIndex = existingAPIKeys.findIndex((key) => key.apiKey === apiKey)
|
||||
if (keyIndex < 0) return undefined
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map MimeType to InputField
|
||||
* @param {string} mimeType
|
||||
|
|
|
|||
Loading…
Reference in New Issue