Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Option to add apikeys to the DB instead of api.json. #2783

Merged
merged 6 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Flowise has 3 different modules in a single mono repository.
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)

| Variable | Description | Type | Default |
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- |
| ---------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------ |-------------------------------------|
| PORT | The HTTP port Flowise runs on | Number | 3000 |
| CORS_ORIGINS | The allowed origins for all cross-origin HTTP calls | String | |
| IFRAME_ORIGINS | The allowed origins for iframe src embedding | String | |
Expand All @@ -133,7 +133,8 @@ Flowise support different environment variables to configure your instance. You
| LOG_PATH | Location where log files are stored | String | `your-path/Flowise/logs` |
| LOG_LEVEL | Different levels of logs | Enum String: `error`, `info`, `verbose`, `debug` | `info` |
| LOG_JSON_SPACES | Spaces to beautify JSON logs | | 2 |
| APIKEY_PATH | Location where api keys are saved | String | `your-path/Flowise/packages/server` |
| APIKEY_STORAGE_TYPE | To store api keys on a JSON file or database. Default is `json` | Enum String: `json`, `db` | `json` |
| APIKEY_PATH | Location where api keys are saved when `APIKEY_STORAGE_TYPE` is `json` | String | `your-path/Flowise/packages/server` |
| TOOL_FUNCTION_BUILTIN_DEP | NodeJS built-in modules to be used for Tool Function | String | |
| TOOL_FUNCTION_EXTERNAL_DEP | External modules to be used for Tool Function | String | |
| DATABASE_TYPE | Type of database to store the flowise data | Enum String: `sqlite`, `mysql`, `postgres` | `sqlite` |
Expand All @@ -146,8 +147,8 @@ Flowise support different environment variables to configure your instance. You
| DATABASE_SSL_KEY_BASE64 | Database SSL client cert in base64 (takes priority over DATABASE_SSL) | Boolean | false |
| DATABASE_SSL | Database connection overssl (When DATABASE_TYPE is postgre) | Boolean | false |
| SECRETKEY_PATH | Location where encryption key (used to encrypt/decrypt credentials) is saved | String | `your-path/Flowise/packages/server` |
| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String |
| DISABLE_FLOWISE_TELEMETRY | Turn off telemetry | Boolean |
| FLOWISE_SECRETKEY_OVERWRITE | Encryption key to be used instead of the key stored in SECRETKEY_PATH | String | |
| DISABLE_FLOWISE_TELEMETRY | Turn off telemetry | Boolean | |
| MODEL_LIST_CONFIG_JSON | File path to load list of models from your local config file | String | `/your_model_list_config_file_path` |
| STORAGE_TYPE | Type of storage for uploaded files. default is `local` | Enum String: `s3`, `local` | `local` |
| BLOB_STORAGE_PATH | Local folder path where uploaded files are stored when `STORAGE_TYPE` is `local` | String | `your-home-dir/.flowise/storage` |
Expand Down
4 changes: 3 additions & 1 deletion docker/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ BLOB_STORAGE_PATH=/root/.flowise/storage
# S3_STORAGE_ACCESS_KEY_ID=<your-access-key>
# S3_STORAGE_SECRET_ACCESS_KEY=<your-secret-key>
# S3_STORAGE_REGION=us-west-2
# S3_ENDPOINT_URL=<custom-s3-endpoint-url>
# S3_ENDPOINT_URL=<custom-s3-endpoint-url>

# APIKEY_STORAGE_TYPE=json (json | db)
3 changes: 2 additions & 1 deletion i18n/CONTRIBUTING-ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ Flowise 支持不同的环境变量来配置您的实例。您可以在 `package
| DEBUG | 打印组件的日志 | 布尔值 | |
| LOG_PATH | 存储日志文件的位置 | 字符串 | `your-path/Flowise/logs` |
| LOG_LEVEL | 日志的不同级别 | 枚举字符串: `error`, `info`, `verbose`, `debug` | `info` |
| APIKEY_PATH | 存储 API 密钥的位置 | 字符串 | `your-path/Flowise/packages/server` |
| APIKEY_STORAGE_TYPE | 存储 API 密钥的存储类型 | 枚举字符串: `json`, `db` | `json` |
| APIKEY_PATH | 存储 API 密钥的位置, 当`APIKEY_STORAGE_TYPE`是`json` | 字符串 | `your-path/Flowise/packages/server` |
| TOOL_FUNCTION_BUILTIN_DEP | 用于工具函数的 NodeJS 内置模块 | 字符串 | |
| TOOL_FUNCTION_EXTERNAL_DEP | 用于工具函数的外部模块 | 字符串 | |
| DATABASE_TYPE | 存储 flowise 数据的数据库类型 | 枚举字符串: `sqlite`, `mysql`, `postgres` | `sqlite` |
Expand Down
4 changes: 3 additions & 1 deletion packages/server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ PORT=3000
# S3_STORAGE_ACCESS_KEY_ID=<your-access-key>
# S3_STORAGE_SECRET_ACCESS_KEY=<your-secret-key>
# S3_STORAGE_REGION=us-west-2
# S3_ENDPOINT_URL=<custom-s3-endpoint-url>
# S3_ENDPOINT_URL=<custom-s3-endpoint-url>

# APIKEY_STORAGE_TYPE=json (json | db)
6 changes: 6 additions & 0 deletions packages/server/src/AppConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const appConfig = {
apiKeys: {
storageType: process.env.APIKEY_STORAGE_TYPE ? process.env.APIKEY_STORAGE_TYPE.toLowerCase() : 'json'
}
// todo: add more config options here like database, log, storage, credential and allow modification from UI
}
8 changes: 8 additions & 0 deletions packages/server/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,5 +263,13 @@ export interface IUploadFileSizeAndTypes {
maxUploadSize: number
}

export interface IApiKey {
id: string
keyName: string
apiKey: string
apiSecret: string
updatedDate: Date
}

// DocumentStore related
export * from './Interface.DocumentStore'
2 changes: 2 additions & 0 deletions packages/server/src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default class Start extends Command {
IFRAME_ORIGINS: Flags.string(),
DEBUG: Flags.string(),
BLOB_STORAGE_PATH: Flags.string(),
APIKEY_STORAGE_TYPE: Flags.string(),
APIKEY_PATH: Flags.string(),
SECRETKEY_PATH: Flags.string(),
FLOWISE_SECRETKEY_OVERWRITE: Flags.string(),
Expand Down Expand Up @@ -100,6 +101,7 @@ export default class Start extends Command {
// Authorization
if (flags.FLOWISE_USERNAME) process.env.FLOWISE_USERNAME = flags.FLOWISE_USERNAME
if (flags.FLOWISE_PASSWORD) process.env.FLOWISE_PASSWORD = flags.FLOWISE_PASSWORD
if (flags.APIKEY_STORAGE_TYPE) process.env.APIKEY_STORAGE_TYPE = flags.APIKEY_STORAGE_TYPE
if (flags.APIKEY_PATH) process.env.APIKEY_PATH = flags.APIKEY_PATH

// API Configuration
Expand Down
16 changes: 15 additions & 1 deletion packages/server/src/controllers/apikey/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ const updateApiKey = async (req: Request, res: Response, next: NextFunction) =>
}
}

// Import Keys from JSON file
const importKeys = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || !req.body.jsonFile) {
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.importKeys - body not provided!`)
}
const apiResponse = await apikeyService.importKeys(req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}

// Delete api key
const deleteApiKey = async (req: Request, res: Response, next: NextFunction) => {
try {
Expand Down Expand Up @@ -72,5 +85,6 @@ export default {
deleteApiKey,
getAllApiKeys,
updateApiKey,
verifyApiKey
verifyApiKey,
importKeys
}
6 changes: 3 additions & 3 deletions packages/server/src/controllers/chatflows/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { NextFunction, Request, Response } from 'express'
import { StatusCodes } from 'http-status-codes'
import apiKeyService from '../../services/apikey'
import { ChatFlow } from '../../database/entities/ChatFlow'
import { createRateLimiter } from '../../utils/rateLimit'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { ChatflowType } from '../../Interface'
import chatflowsService from '../../services/chatflows'
import { getApiKey } from '../../utils/apiKey'
import { createRateLimiter } from '../../utils/rateLimit'

const checkIfChatflowIsValidForStreaming = async (req: Request, res: Response, next: NextFunction) => {
try {
Expand Down Expand Up @@ -67,7 +67,7 @@ const getChatflowByApiKey = async (req: Request, res: Response, next: NextFuncti
`Error: chatflowsRouter.getChatflowByApiKey - apikey not provided!`
)
}
const apikey = await getApiKey(req.params.apikey)
const apikey = await apiKeyService.getApiKey(req.params.apikey)
if (!apikey) {
return res.status(401).send('Unauthorized')
}
Expand Down
21 changes: 21 additions & 0 deletions packages/server/src/database/entities/ApiKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Column, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'
import { IApiKey } from '../../Interface'

@Entity('apikey')
export class ApiKey implements IApiKey {
@PrimaryColumn({ type: 'varchar', length: 20 })
id: string

@Column({ type: 'text' })
apiKey: string

@Column({ type: 'text' })
apiSecret: string

@Column({ type: 'text' })
keyName: string

@Column({ type: 'timestamp' })
@UpdateDateColumn()
updatedDate: Date
}
4 changes: 3 additions & 1 deletion packages/server/src/database/entities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DocumentStore } from './DocumentStore'
import { DocumentStoreFileChunk } from './DocumentStoreFileChunk'
import { Lead } from './Lead'
import { UpsertHistory } from './UpsertHistory'
import { ApiKey } from './ApiKey'

export const entities = {
ChatFlow,
Expand All @@ -21,5 +22,6 @@ export const entities = {
DocumentStore,
DocumentStoreFileChunk,
Lead,
UpsertHistory
UpsertHistory,
ApiKey
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddApiKey1720230151480 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE IF NOT EXISTS \`apikey\` (
\`id\` varchar(36) NOT NULL,
\`apiKey\` varchar(255) NOT NULL,
\`apiSecret\` varchar(255) NOT NULL,
\`keyName\` varchar(255) NOT NULL,
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;`
)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE apikey`)
}
}
2 changes: 2 additions & 0 deletions packages/server/src/database/migrations/mariadb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { AddLead1710832127079 } from './1710832127079-AddLead'
import { AddLeadToChatMessage1711538023578 } from './1711538023578-AddLeadToChatMessage'
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'

export const mariadbMigrations = [
Expand All @@ -45,5 +46,6 @@ export const mariadbMigrations = [
AddLeadToChatMessage1711538023578,
AddAgentReasoningToChatMessage1714679514451,
AddTypeToChatFlow1766759476232,
AddApiKey1720230151480,
AddActionToChatMessage1721078251523
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddApiKey1720230151480 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE IF NOT EXISTS \`apikey\` (
\`id\` varchar(36) NOT NULL,
\`apiKey\` varchar(255) NOT NULL,
\`apiSecret\` varchar(255) NOT NULL,
\`keyName\` varchar(255) NOT NULL,
\`updatedDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;`
)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE apikey`)
}
}
2 changes: 2 additions & 0 deletions packages/server/src/database/migrations/mysql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { AddLead1710832127079 } from './1710832127079-AddLead'
import { AddLeadToChatMessage1711538023578 } from './1711538023578-AddLeadToChatMessage'
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'

export const mysqlMigrations = [
Expand All @@ -45,5 +46,6 @@ export const mysqlMigrations = [
AddLeadToChatMessage1711538023578,
AddAgentReasoningToChatMessage1714679514451,
AddTypeToChatFlow1766759476232,
AddApiKey1720230151480,
AddActionToChatMessage1721078251523
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddApiKey1720230151480 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE IF NOT EXISTS apikey (
id uuid NOT NULL DEFAULT uuid_generate_v4(),
"apiKey" varchar NOT NULL,
"apiSecret" varchar NOT NULL,
"keyName" varchar NOT NULL,
"updatedDate" timestamp NOT NULL DEFAULT now(),
CONSTRAINT "PK_96109043dd704f53-9830ab78f0" PRIMARY KEY (id)
);`
)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE apikey`)
}
}
2 changes: 2 additions & 0 deletions packages/server/src/database/migrations/postgres/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { AddLead1710832137905 } from './1710832137905-AddLead'
import { AddLeadToChatMessage1711538016098 } from './1711538016098-AddLeadToChatMessage'
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'

export const postgresMigrations = [
Expand All @@ -47,5 +48,6 @@ export const postgresMigrations = [
AddLeadToChatMessage1711538016098,
AddAgentReasoningToChatMessage1714679514451,
AddTypeToChatFlow1766759476232,
AddApiKey1720230151480,
AddActionToChatMessage1721078251523
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddApiKey1720230151480 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE IF NOT EXISTS "apikey" ("id" varchar PRIMARY KEY NOT NULL,
"apiKey" varchar NOT NULL,
"apiSecret" varchar NOT NULL,
"keyName" varchar NOT NULL,
"updatedDate" datetime NOT NULL DEFAULT (datetime('now')));`
)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE IF EXISTS "apikey";`)
}
}
2 changes: 2 additions & 0 deletions packages/server/src/database/migrations/sqlite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { AddLeadToChatMessage1711537986113 } from './1711537986113-AddLeadToChat
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
import { AddTypeToChatFlow1766759476232 } from './1766759476232-AddTypeToChatFlow'
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'

export const sqliteMigrations = [
Init1693835579790,
Expand All @@ -45,5 +46,6 @@ export const sqliteMigrations = [
AddLeadToChatMessage1711537986113,
AddAgentReasoningToChatMessage1714679514451,
AddTypeToChatFlow1766759476232,
AddApiKey1720230151480,
AddActionToChatMessage1721078251523
]
1 change: 1 addition & 0 deletions packages/server/src/routes/apikey/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const router = express.Router()

// CREATE
router.post('/', apikeyController.createApiKey)
router.post('/import', apikeyController.importKeys)

// READ
router.get('/', apikeyController.getAllApiKeys)
Expand Down
Loading
Loading