Skip to content

Commit 2dd1791

Browse files
Feature: Option to add apikeys to the DB instead of api.json. (#2783)
* Feature: Option to add apikeys to the DB instead of api.json. * add api storage type env variable * code cleanup and simplification. * md table fixes --------- Co-authored-by: Henry <[email protected]>
1 parent e5018d2 commit 2dd1791

File tree

26 files changed

+647
-38
lines changed

26 files changed

+647
-38
lines changed

CONTRIBUTING.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ Flowise has 3 different modules in a single mono repository.
121121
Flowise support different environment variables to configure your instance. You can specify the following variables in the `.env` file inside `packages/server` folder. Read [more](https://docs.flowiseai.com/environment-variables)
122122

123123
| Variable | Description | Type | Default |
124-
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- |
124+
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ |-------------------------------------|
125125
| PORT | The HTTP port Flowise runs on | Number | 3000 |
126126
| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | |
127127
| IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | |
@@ -133,7 +133,8 @@ Flowise support different environment variables to configure your instance. You
133133
| LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` |
134134
| LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` |
135135
| LOG_JSON_SPACES | Spaces to beautify JSON logs | | 2 |
136-
| APIKEY_PATH | Location where api keys are saved | String | `your-path/Flowise/packages/server` |
136+
| APIKEY_STORAGE_TYPE | To store api keys on a JSON file or database. Default is `json` | Enum String: `json`, `db` | `json` |
137+
| APIKEY_PATH | Location where api keys are saved when `APIKEY_STORAGE_TYPE` is `json` | String | `your-path/Flowise/packages/server` |
137138
| TOOL_FUNCTION_BUILTIN_DEP | NodeJS built-in modules to be used for Tool Function | String | |
138139
| TOOL_FUNCTION_EXTERNAL_DEP | External modules to be used for Tool Function | String | |
139140
| DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` |
@@ -146,8 +147,8 @@ Flowise support different environment variables to configure your instance. You
146147
| DATABASE_SSL_KEY_BASE64 | Database SSL client cert in base64 (takes priority over DATABASE_SSL) | Boolean | false |
147148
| DATABASE_SSL | Database connection overssl (When DATABASE_TYPE is postgre) | Boolean | false |
148149
| SECRETKEY_PATH | Location where encryption key (used to encrypt/decrypt credentials) is saved | String | `your-path/Flowise/packages/server` |
149-
| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String |
150-
| DISABLE_FLOWISE_TELEMETRY | Turn off telemetry | Boolean |
150+
| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String | |
151+
| DISABLE_FLOWISE_TELEMETRY | Turn off telemetry | Boolean | |
151152
| MODEL_LIST_CONFIG_JSON | File path to load list of models from your local config file | String | `/your_model_list_config_file_path` |
152153
| STORAGE_TYPE | Type of storage for uploaded files. default is `local` | Enum String: `s3`, `local` | `local` |
153154
| BLOB_STORAGE_PATH | Local folder path where uploaded files are stored when `STORAGE_TYPE` is `local` | String | `your-home-dir/.flowise/storage` |

docker/.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,6 @@ BLOB_STORAGE_PATH=/root/.flowise/storage
4747
# S3_STORAGE_ACCESS_KEY_ID=<your-access-key>
4848
# S3_STORAGE_SECRET_ACCESS_KEY=<your-secret-key>
4949
# S3_STORAGE_REGION=us-west-2
50-
# S3_ENDPOINT_URL=<custom-s3-endpoint-url>
50+
# S3_ENDPOINT_URL=<custom-s3-endpoint-url>
51+
52+
# APIKEY_STORAGE_TYPE=json (json | db)

i18n/CONTRIBUTING-ZH.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ Flowise 支持不同的环境变量来配置您的实例。您可以在 `package
128128
| DEBUG | 打印组件的日志 | 布尔值 | |
129129
| LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` |
130130
| LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` |
131-
| APIKEY_PATH | 存储 API 密钥的位置 | 字符串 | `your-path/Flowise/packages/server` |
131+
| APIKEY_STORAGE_TYPE | 存储 API 密钥的存储类型 | 枚举字符串: `json`, `db` | `json` |
132+
| APIKEY_PATH | 存储 API 密钥的位置, 当`APIKEY_STORAGE_TYPE``json` | 字符串 | `your-path/Flowise/packages/server` |
132133
| TOOL_FUNCTION_BUILTIN_DEP | 用于工具函数的 NodeJS 内置模块 | 字符串 | |
133134
| TOOL_FUNCTION_EXTERNAL_DEP | 用于工具函数的外部模块 | 字符串 | |
134135
| DATABASE_TYPE | 存储 flowise 数据的数据库类型 | 枚举字符串: `sqlite`, `mysql`, `postgres` | `sqlite` |

packages/server/.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,6 @@ PORT=3000
4747
# S3_STORAGE_ACCESS_KEY_ID=<your-access-key>
4848
# S3_STORAGE_SECRET_ACCESS_KEY=<your-secret-key>
4949
# S3_STORAGE_REGION=us-west-2
50-
# S3_ENDPOINT_URL=<custom-s3-endpoint-url>
50+
# S3_ENDPOINT_URL=<custom-s3-endpoint-url>
51+
52+
# APIKEY_STORAGE_TYPE=json (json | db)

packages/server/src/AppConfig.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const appConfig = {
2+
apiKeys: {
3+
storageType: process.env.APIKEY_STORAGE_TYPE ? process.env.APIKEY_STORAGE_TYPE.toLowerCase() : 'json'
4+
}
5+
// todo: add more config options here like database, log, storage, credential and allow modification from UI
6+
}

packages/server/src/Interface.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,5 +263,13 @@ export interface IUploadFileSizeAndTypes {
263263
maxUploadSize: number
264264
}
265265

266+
export interface IApiKey {
267+
id: string
268+
keyName: string
269+
apiKey: string
270+
apiSecret: string
271+
updatedDate: Date
272+
}
273+
266274
// DocumentStore related
267275
export * from './Interface.DocumentStore'

packages/server/src/commands/start.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default class Start extends Command {
2424
IFRAME_ORIGINS: Flags.string(),
2525
DEBUG: Flags.string(),
2626
BLOB_STORAGE_PATH: Flags.string(),
27+
APIKEY_STORAGE_TYPE: Flags.string(),
2728
APIKEY_PATH: Flags.string(),
2829
SECRETKEY_PATH: Flags.string(),
2930
FLOWISE_SECRETKEY_OVERWRITE: Flags.string(),
@@ -100,6 +101,7 @@ export default class Start extends Command {
100101
// Authorization
101102
if (flags.FLOWISE_USERNAME) process.env.FLOWISE_USERNAME = flags.FLOWISE_USERNAME
102103
if (flags.FLOWISE_PASSWORD) process.env.FLOWISE_PASSWORD = flags.FLOWISE_PASSWORD
104+
if (flags.APIKEY_STORAGE_TYPE) process.env.APIKEY_STORAGE_TYPE = flags.APIKEY_STORAGE_TYPE
103105
if (flags.APIKEY_PATH) process.env.APIKEY_PATH = flags.APIKEY_PATH
104106

105107
// API Configuration

packages/server/src/controllers/apikey/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ const updateApiKey = async (req: Request, res: Response, next: NextFunction) =>
4141
}
4242
}
4343

44+
// Import Keys from JSON file
45+
const importKeys = async (req: Request, res: Response, next: NextFunction) => {
46+
try {
47+
if (typeof req.body === 'undefined' || !req.body.jsonFile) {
48+
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.importKeys - body not provided!`)
49+
}
50+
const apiResponse = await apikeyService.importKeys(req.body)
51+
return res.json(apiResponse)
52+
} catch (error) {
53+
next(error)
54+
}
55+
}
56+
4457
// Delete api key
4558
const deleteApiKey = async (req: Request, res: Response, next: NextFunction) => {
4659
try {
@@ -72,5 +85,6 @@ export default {
7285
deleteApiKey,
7386
getAllApiKeys,
7487
updateApiKey,
75-
verifyApiKey
88+
verifyApiKey,
89+
importKeys
7690
}

packages/server/src/controllers/chatflows/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { NextFunction, Request, Response } from 'express'
22
import { StatusCodes } from 'http-status-codes'
3+
import apiKeyService from '../../services/apikey'
34
import { ChatFlow } from '../../database/entities/ChatFlow'
5+
import { createRateLimiter } from '../../utils/rateLimit'
46
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
57
import { ChatflowType } from '../../Interface'
68
import chatflowsService from '../../services/chatflows'
7-
import { getApiKey } from '../../utils/apiKey'
8-
import { createRateLimiter } from '../../utils/rateLimit'
99

1010
const checkIfChatflowIsValidForStreaming = async (req: Request, res: Response, next: NextFunction) => {
1111
try {
@@ -67,7 +67,7 @@ const getChatflowByApiKey = async (req: Request, res: Response, next: NextFuncti
6767
`Error: chatflowsRouter.getChatflowByApiKey - apikey not provided!`
6868
)
6969
}
70-
const apikey = await getApiKey(req.params.apikey)
70+
const apikey = await apiKeyService.getApiKey(req.params.apikey)
7171
if (!apikey) {
7272
return res.status(401).send('Unauthorized')
7373
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Column, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'
2+
import { IApiKey } from '../../Interface'
3+
4+
@Entity('apikey')
5+
export class ApiKey implements IApiKey {
6+
@PrimaryColumn({ type: 'varchar', length: 20 })
7+
id: string
8+
9+
@Column({ type: 'text' })
10+
apiKey: string
11+
12+
@Column({ type: 'text' })
13+
apiSecret: string
14+
15+
@Column({ type: 'text' })
16+
keyName: string
17+
18+
@Column({ type: 'timestamp' })
19+
@UpdateDateColumn()
20+
updatedDate: Date
21+
}

packages/server/src/database/entities/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { DocumentStore } from './DocumentStore'
99
import { DocumentStoreFileChunk } from './DocumentStoreFileChunk'
1010
import { Lead } from './Lead'
1111
import { UpsertHistory } from './UpsertHistory'
12+
import { ApiKey } from './ApiKey'
1213

1314
export const entities = {
1415
ChatFlow,
@@ -21,5 +22,6 @@ export const entities = {
2122
DocumentStore,
2223
DocumentStoreFileChunk,
2324
Lead,
24-
UpsertHistory
25+
UpsertHistory,
26+
ApiKey
2527
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
3+
export class AddApiKey1720230151480 implements MigrationInterface {
4+
public async up(queryRunner: QueryRunner): Promise<void> {
5+
await queryRunner.query(
6+
`CREATE TABLE IF NOT EXISTS \`apikey\` (
7+
\`id\` varchar(36) NOT NULL,
8+
\`apiKey\` varchar(255) NOT NULL,
9+
\`apiSecret\` varchar(255) NOT NULL,
10+
\`keyName\` varchar(255) NOT NULL,
11+
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
12+
PRIMARY KEY (\`id\`)
13+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
14+
)
15+
}
16+
17+
public async down(queryRunner: QueryRunner): Promise<void> {
18+
await queryRunner.query(`DROP TABLE apikey`)
19+
}
20+
}

packages/server/src/database/migrations/mariadb/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { AddLead1710832127079 } from './1710832127079-AddLead'
2020
import { AddLeadToChatMessage1711538023578 } from './1711538023578-AddLeadToChatMessage'
2121
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
2222
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
23+
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
2324
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
2425

2526
export const mariadbMigrations = [
@@ -45,5 +46,6 @@ export const mariadbMigrations = [
4546
AddLeadToChatMessage1711538023578,
4647
AddAgentReasoningToChatMessage1714679514451,
4748
AddTypeToChatFlow1766759476232,
49+
AddApiKey1720230151480,
4850
AddActionToChatMessage1721078251523
4951
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
3+
export class AddApiKey1720230151480 implements MigrationInterface {
4+
public async up(queryRunner: QueryRunner): Promise<void> {
5+
await queryRunner.query(
6+
`CREATE TABLE IF NOT EXISTS \`apikey\` (
7+
\`id\` varchar(36) NOT NULL,
8+
\`apiKey\` varchar(255) NOT NULL,
9+
\`apiSecret\` varchar(255) NOT NULL,
10+
\`keyName\` varchar(255) NOT NULL,
11+
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
12+
PRIMARY KEY (\`id\`)
13+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;`
14+
)
15+
}
16+
17+
public async down(queryRunner: QueryRunner): Promise<void> {
18+
await queryRunner.query(`DROP TABLE apikey`)
19+
}
20+
}

packages/server/src/database/migrations/mysql/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { AddLead1710832127079 } from './1710832127079-AddLead'
2020
import { AddLeadToChatMessage1711538023578 } from './1711538023578-AddLeadToChatMessage'
2121
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
2222
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
23+
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
2324
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
2425

2526
export const mysqlMigrations = [
@@ -45,5 +46,6 @@ export const mysqlMigrations = [
4546
AddLeadToChatMessage1711538023578,
4647
AddAgentReasoningToChatMessage1714679514451,
4748
AddTypeToChatFlow1766759476232,
49+
AddApiKey1720230151480,
4850
AddActionToChatMessage1721078251523
4951
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
3+
export class AddApiKey1720230151480 implements MigrationInterface {
4+
public async up(queryRunner: QueryRunner): Promise<void> {
5+
await queryRunner.query(
6+
`CREATE TABLE IF NOT EXISTS apikey (
7+
id uuid NOT NULL DEFAULT uuid_generate_v4(),
8+
"apiKey" varchar NOT NULL,
9+
"apiSecret" varchar NOT NULL,
10+
"keyName" varchar NOT NULL,
11+
"updatedDate" timestamp NOT NULL DEFAULT now(),
12+
CONSTRAINT "PK_96109043dd704f53-9830ab78f0" PRIMARY KEY (id)
13+
);`
14+
)
15+
}
16+
17+
public async down(queryRunner: QueryRunner): Promise<void> {
18+
await queryRunner.query(`DROP TABLE apikey`)
19+
}
20+
}

packages/server/src/database/migrations/postgres/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { AddLead1710832137905 } from './1710832137905-AddLead'
2121
import { AddLeadToChatMessage1711538016098 } from './1711538016098-AddLeadToChatMessage'
2222
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
2323
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
24+
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
2425
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
2526

2627
export const postgresMigrations = [
@@ -47,5 +48,6 @@ export const postgresMigrations = [
4748
AddLeadToChatMessage1711538016098,
4849
AddAgentReasoningToChatMessage1714679514451,
4950
AddTypeToChatFlow1766759476232,
51+
AddApiKey1720230151480,
5052
AddActionToChatMessage1721078251523
5153
]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
3+
export class AddApiKey1720230151480 implements MigrationInterface {
4+
public async up(queryRunner: QueryRunner): Promise<void> {
5+
await queryRunner.query(
6+
`CREATE TABLE IF NOT EXISTS "apikey" ("id" varchar PRIMARY KEY NOT NULL,
7+
"apiKey" varchar NOT NULL,
8+
"apiSecret" varchar NOT NULL,
9+
"keyName" varchar NOT NULL,
10+
"updatedDate" datetime NOT NULL DEFAULT (datetime('now')));`
11+
)
12+
}
13+
14+
public async down(queryRunner: QueryRunner): Promise<void> {
15+
await queryRunner.query(`DROP TABLE IF EXISTS "apikey";`)
16+
}
17+
}

packages/server/src/database/migrations/sqlite/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { AddLeadToChatMessage1711537986113 } from './1711537986113-AddLeadToChat
2121
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
2222
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
2323
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
24+
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
2425

2526
export const sqliteMigrations = [
2627
Init1693835579790,
@@ -45,5 +46,6 @@ export const sqliteMigrations = [
4546
AddLeadToChatMessage1711537986113,
4647
AddAgentReasoningToChatMessage1714679514451,
4748
AddTypeToChatFlow1766759476232,
49+
AddApiKey1720230151480,
4850
AddActionToChatMessage1721078251523
4951
]

packages/server/src/routes/apikey/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const router = express.Router()
44

55
// CREATE
66
router.post('/', apikeyController.createApiKey)
7+
router.post('/import', apikeyController.importKeys)
78

89
// READ
910
router.get('/', apikeyController.getAllApiKeys)

0 commit comments

Comments
 (0)