mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 19:00:59 +03:00
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:
@@ -1,19 +1,10 @@
|
||||
PORT=3000
|
||||
|
||||
# APIKEY_STORAGE_TYPE=json (json | db)
|
||||
# APIKEY_PATH=/your_api_key_path/.flowise
|
||||
# APIKEY_PATH=/your_apikey_path/.flowise # (will be deprecated by end of 2025)
|
||||
|
||||
# SECRETKEY_STORAGE_TYPE=local #(local | aws)
|
||||
# SECRETKEY_PATH=/your_api_key_path/.flowise
|
||||
# FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey
|
||||
# SECRETKEY_AWS_ACCESS_KEY=<your-access-key>
|
||||
# SECRETKEY_AWS_SECRET_KEY=<your-secret-key>
|
||||
# SECRETKEY_AWS_REGION=us-west-2
|
||||
# SECRETKEY_AWS_NAME=FlowiseEncryptionKey
|
||||
|
||||
# NUMBER_OF_PROXIES= 1
|
||||
# CORS_ORIGINS=*
|
||||
# IFRAME_ORIGINS=*
|
||||
############################################################################################################
|
||||
############################################## DATABASE ####################################################
|
||||
############################################################################################################
|
||||
|
||||
# DATABASE_PATH=/your_database_path/.flowise
|
||||
# DATABASE_TYPE=postgres
|
||||
@@ -25,26 +16,36 @@ PORT=3000
|
||||
# DATABASE_SSL=true
|
||||
# DATABASE_SSL_KEY_BASE64=<Self signed certificate in BASE64>
|
||||
|
||||
# FLOWISE_USERNAME=user
|
||||
# FLOWISE_PASSWORD=1234
|
||||
# FLOWISE_FILE_SIZE_LIMIT=50mb
|
||||
|
||||
############################################################################################################
|
||||
############################################## SECRET KEYS #################################################
|
||||
############################################################################################################
|
||||
|
||||
# SECRETKEY_STORAGE_TYPE=local #(local | aws)
|
||||
# SECRETKEY_PATH=/your_secret_path/.flowise
|
||||
# FLOWISE_SECRETKEY_OVERWRITE=myencryptionkey # (if you want to overwrite the secret key)
|
||||
# SECRETKEY_AWS_ACCESS_KEY=<your-access-key>
|
||||
# SECRETKEY_AWS_SECRET_KEY=<your-secret-key>
|
||||
# SECRETKEY_AWS_REGION=us-west-2
|
||||
# SECRETKEY_AWS_NAME=FlowiseEncryptionKey
|
||||
|
||||
|
||||
############################################################################################################
|
||||
############################################## LOGGING #####################################################
|
||||
############################################################################################################
|
||||
|
||||
# DEBUG=true
|
||||
# LOG_PATH=/your_log_path/.flowise/logs
|
||||
# LOG_LEVEL=info (error | warn | info | verbose | debug)
|
||||
# LOG_LEVEL=info #(error | warn | info | verbose | debug)
|
||||
# TOOL_FUNCTION_BUILTIN_DEP=crypto,fs
|
||||
# TOOL_FUNCTION_EXTERNAL_DEP=moment,lodash
|
||||
|
||||
# LANGCHAIN_TRACING_V2=true
|
||||
# LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
|
||||
# LANGCHAIN_API_KEY=your_api_key
|
||||
# LANGCHAIN_PROJECT=your_project
|
||||
|
||||
# Uncomment the following line to enable model list config, load the list of models from your local config file
|
||||
# see https://raw.githubusercontent.com/FlowiseAI/Flowise/main/packages/components/models.json for the format
|
||||
# MODEL_LIST_CONFIG_JSON=/your_model_list_config_file_path
|
||||
############################################################################################################
|
||||
############################################## STORAGE #####################################################
|
||||
############################################################################################################
|
||||
|
||||
# STORAGE_TYPE=local (local | s3)
|
||||
# STORAGE_TYPE=local (local | s3 | gcs)
|
||||
# BLOB_STORAGE_PATH=/your_storage_path/.flowise/storage
|
||||
# S3_STORAGE_BUCKET_NAME=flowise
|
||||
# S3_STORAGE_ACCESS_KEY_ID=<your-access-key>
|
||||
@@ -57,12 +58,69 @@ PORT=3000
|
||||
# GOOGLE_CLOUD_STORAGE_BUCKET_NAME=<the-bucket-name>
|
||||
# GOOGLE_CLOUD_UNIFORM_BUCKET_ACCESS=true
|
||||
|
||||
# SHOW_COMMUNITY_NODES=true
|
||||
# DISABLED_NODES=bufferMemory,chatOpenAI (comma separated list of node names to disable)
|
||||
|
||||
######################
|
||||
# METRICS COLLECTION
|
||||
#######################
|
||||
############################################################################################################
|
||||
############################################## SETTINGS ####################################################
|
||||
############################################################################################################
|
||||
|
||||
# NUMBER_OF_PROXIES= 1
|
||||
# CORS_ORIGINS=*
|
||||
# IFRAME_ORIGINS=*
|
||||
# FLOWISE_FILE_SIZE_LIMIT=50mb
|
||||
# SHOW_COMMUNITY_NODES=true
|
||||
# DISABLE_FLOWISE_TELEMETRY=true
|
||||
# DISABLED_NODES=bufferMemory,chatOpenAI (comma separated list of node names to disable)
|
||||
# Uncomment the following line to enable model list config, load the list of models from your local config file
|
||||
# see https://raw.githubusercontent.com/FlowiseAI/Flowise/main/packages/components/models.json for the format
|
||||
# MODEL_LIST_CONFIG_JSON=/your_model_list_config_file_path
|
||||
|
||||
|
||||
############################################################################################################
|
||||
############################################ AUTH PARAMETERS ###############################################
|
||||
############################################################################################################
|
||||
|
||||
# APP_URL=http://localhost:3000
|
||||
|
||||
# SMTP_HOST=smtp.host.com
|
||||
# SMTP_PORT=465
|
||||
# SMTP_USER=smtp_user
|
||||
# SMTP_PASSWORD=smtp_password
|
||||
# SMTP_SECURE=true
|
||||
# ALLOW_UNAUTHORIZED_CERTS=false
|
||||
# SENDER_EMAIL=team@example.com
|
||||
|
||||
# JWT_AUTH_TOKEN_SECRET='AABBCCDDAABBCCDDAABBCCDDAABBCCDDAABBCCDD'
|
||||
# JWT_REFRESH_TOKEN_SECRET='AABBCCDDAABBCCDDAABBCCDDAABBCCDDAABBCCDD'
|
||||
# JWT_ISSUER='ISSUER'
|
||||
# JWT_AUDIENCE='AUDIENCE'
|
||||
# JWT_TOKEN_EXPIRY_IN_MINUTES=360
|
||||
# JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200
|
||||
# EXPIRE_AUTH_TOKENS_ON_RESTART=true # (if you need to expire all tokens on app restart)
|
||||
# EXPRESS_SESSION_SECRET=flowise
|
||||
|
||||
# INVITE_TOKEN_EXPIRY_IN_HOURS=24
|
||||
# PASSWORD_RESET_TOKEN_EXPIRY_IN_MINS=15
|
||||
# PASSWORD_SALT_HASH_ROUNDS=10
|
||||
# TOKEN_HASH_SECRET='popcorn'
|
||||
|
||||
# WORKSPACE_INVITE_TEMPLATE_PATH=/path/to/custom/workspace_invite.hbs
|
||||
|
||||
|
||||
############################################################################################################
|
||||
############################################# ENTERPRISE ###################################################
|
||||
############################################################################################################
|
||||
|
||||
# LICENSE_URL=
|
||||
# FLOWISE_EE_LICENSE_KEY=
|
||||
# OFFLINE=
|
||||
|
||||
|
||||
############################################################################################################
|
||||
########################################### METRICS COLLECTION #############################################
|
||||
############################################################################################################
|
||||
|
||||
# POSTHOG_PUBLIC_API_KEY=your_posthog_public_api_key
|
||||
|
||||
# ENABLE_METRICS=false
|
||||
# METRICS_PROVIDER=prometheus # prometheus | open_telemetry
|
||||
# METRICS_INCLUDE_NODE_METRICS=true # default is true
|
||||
@@ -73,15 +131,21 @@ PORT=3000
|
||||
# METRICS_OPEN_TELEMETRY_PROTOCOL=http # http | grpc | proto (default is http)
|
||||
# METRICS_OPEN_TELEMETRY_DEBUG=true # default is false
|
||||
|
||||
# Uncomment the following lines to enable global agent proxy
|
||||
# see https://www.npmjs.com/package/global-agent for more details
|
||||
|
||||
############################################################################################################
|
||||
############################################### PROXY ######################################################
|
||||
############################################################################################################
|
||||
|
||||
# Uncomment the following lines to enable global agent proxy, see https://www.npmjs.com/package/global-agent for more details
|
||||
# GLOBAL_AGENT_HTTP_PROXY=CorporateHttpProxyUrl
|
||||
# GLOBAL_AGENT_HTTPS_PROXY=CorporateHttpsProxyUrl
|
||||
# GLOBAL_AGENT_NO_PROXY=ExceptionHostsToBypassProxyIfNeeded
|
||||
|
||||
######################
|
||||
# QUEUE CONFIGURATION
|
||||
#######################
|
||||
|
||||
############################################################################################################
|
||||
########################################### QUEUE CONFIGURATION ############################################
|
||||
############################################################################################################
|
||||
|
||||
# MODE=queue #(queue | main)
|
||||
# QUEUE_NAME=flowise-queue
|
||||
# QUEUE_REDIS_EVENT_STREAM_MAX_LEN=100000
|
||||
|
||||
@@ -22,15 +22,6 @@
|
||||
|
||||
3. 打开[http://localhost:3000](http://localhost:3000)
|
||||
|
||||
## 🔒 身份验证
|
||||
|
||||
要启用应用级身份验证,请将`FLOWISE_USERNAME`和`FLOWISE_PASSWORD`添加到`.env`文件中:
|
||||
|
||||
```
|
||||
FLOWISE_USERNAME=user
|
||||
FLOWISE_PASSWORD=1234
|
||||
```
|
||||
|
||||
## 🌱 环境变量
|
||||
|
||||
Flowise 支持不同的环境变量来配置您的实例。您可以在`packages/server`文件夹中的`.env`文件中指定以下变量。阅读[更多](https://docs.flowiseai.com/environment-variables)
|
||||
|
||||
@@ -22,15 +22,6 @@ English | [中文](./README-ZH.md)
|
||||
|
||||
3. Open [http://localhost:3000](http://localhost:3000)
|
||||
|
||||
## 🔒 Authentication
|
||||
|
||||
To enable app level authentication, add `FLOWISE_USERNAME` and `FLOWISE_PASSWORD` to the `.env` file:
|
||||
|
||||
```
|
||||
FLOWISE_USERNAME=user
|
||||
FLOWISE_PASSWORD=1234
|
||||
```
|
||||
|
||||
## 🌱 Env Variables
|
||||
|
||||
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://github.com/FlowiseAI/Flowise/blob/main/CONTRIBUTING.md#-env-variables)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/*
|
||||
* TODO: Disabling for now as we need to enable login first
|
||||
*
|
||||
describe('E2E suite for api/v1/apikey API endpoint', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('http://localhost:3000/apikey')
|
||||
@@ -43,3 +46,4 @@ describe('E2E suite for api/v1/apikey API endpoint', () => {
|
||||
cy.get('table.MuiTable-root tbody tr').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/*
|
||||
* TODO: Disabling for now as we need to enable login first
|
||||
*
|
||||
describe('E2E suite for api/v1/variables API endpoint', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('http://localhost:3000/variables')
|
||||
@@ -9,7 +12,7 @@ describe('E2E suite for api/v1/variables API endpoint', () => {
|
||||
})
|
||||
|
||||
// CREATE
|
||||
it('can add new variable', () => {
|
||||
it.skip('can add new variable', () => {
|
||||
const newVariableName = 'MafiVariable'
|
||||
const newVariableValue = 'shh!!! secret value'
|
||||
cy.get('#btn_createVariable').click()
|
||||
@@ -21,14 +24,14 @@ describe('E2E suite for api/v1/variables API endpoint', () => {
|
||||
})
|
||||
|
||||
// READ
|
||||
it('can retrieve all api keys', () => {
|
||||
it.skip('can retrieve all api keys', () => {
|
||||
const newVariableName = 'MafiVariable'
|
||||
cy.get('.MuiTable-root tbody tr').should('have.length', 1)
|
||||
cy.get('.MuiTable-root tbody tr').last().find('th').first().find('div').first().should('have.text', newVariableName)
|
||||
})
|
||||
|
||||
// UPDATE
|
||||
it('can update new api key', () => {
|
||||
it.skip('can update new api key', () => {
|
||||
const updatedVariableName = 'PichiVariable'
|
||||
const updatedVariableValue = 'silence shh! value'
|
||||
cy.get('.MuiTable-root tbody tr').last().find('td').eq(4).find('button').click()
|
||||
@@ -40,10 +43,11 @@ describe('E2E suite for api/v1/variables API endpoint', () => {
|
||||
})
|
||||
|
||||
// DELETE
|
||||
it('can delete new api key', () => {
|
||||
it.skip('can delete new api key', () => {
|
||||
cy.get('.MuiTable-root tbody tr').last().find('td').eq(5).find('button').click()
|
||||
cy.get('.MuiDialog-scrollPaper .MuiDialogActions-spacing button').last().click()
|
||||
cy.get('.MuiTable-root tbody tr').should('have.length', 0)
|
||||
cy.get('.MuiCardContent-root .MuiStack-root').last().find('div').last().should('have.text', 'No Variables Yet')
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { dest, src } from 'gulp'
|
||||
|
||||
function copyEmailTemplates() {
|
||||
return src(['src/enterprise/emails/*.hbs']).pipe(dest('dist/enterprise/emails'))
|
||||
}
|
||||
|
||||
exports.default = copyEmailTemplates
|
||||
@@ -0,0 +1,23 @@
|
||||
module.exports = {
|
||||
// Use ts-jest preset for testing TypeScript files with Jest
|
||||
preset: 'ts-jest',
|
||||
// Set the test environment to Node.js
|
||||
testEnvironment: 'node',
|
||||
|
||||
// Define the root directory for tests and modules
|
||||
roots: ['<rootDir>/test'],
|
||||
|
||||
// Use ts-jest to transform TypeScript files
|
||||
transform: {
|
||||
'^.+\\.tsx?$': 'ts-jest'
|
||||
},
|
||||
|
||||
// Regular expression to find test files
|
||||
testRegex: '((\\.|/)index\\.test)\\.tsx?$',
|
||||
|
||||
// File extensions to recognize in module resolution
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
||||
|
||||
// Display individual test results with the test suite hierarchy.
|
||||
verbose: true
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
"commands": "./dist/commands"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build": "tsc && rimraf dist/enterprise/emails && gulp",
|
||||
"start": "run-script-os",
|
||||
"clean": "rimraf dist",
|
||||
"nuke": "rimraf dist node_modules .turbo",
|
||||
@@ -37,13 +37,14 @@
|
||||
"typeorm": "typeorm-ts-node-commonjs",
|
||||
"typeorm:migration-generate": "pnpm typeorm migration:generate -d ./src/utils/typeormDataSource.ts",
|
||||
"typeorm:migration-run": "pnpm typeorm migration:run -d ./src/utils/typeormDataSource.ts",
|
||||
"typeorm:migration-revert": "pnpm typeorm migration:revert -d ./src/utils/typeormDataSource.ts",
|
||||
"watch": "tsc --watch",
|
||||
"version": "oclif readme && git add README.md",
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:run": "cypress run",
|
||||
"e2e": "start-server-and-test dev http://localhost:3000 cypress:run",
|
||||
"cypress:ci": "START_SERVER_AND_TEST_INSECURE=1 start-server-and-test start https-get://localhost:3000 cypress:run",
|
||||
"test": "jest"
|
||||
"test": "jest --runInBand --detectOpenHandles --forceExit"
|
||||
},
|
||||
"keywords": [],
|
||||
"homepage": "https://flowiseai.com",
|
||||
@@ -58,6 +59,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.699.0",
|
||||
"@google-cloud/logging-winston": "^6.0.0",
|
||||
"@keyv/redis": "^4.2.0",
|
||||
"@oclif/core": "4.0.7",
|
||||
"@opentelemetry/api": "^1.3.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.52.0",
|
||||
@@ -73,24 +75,42 @@
|
||||
"@opentelemetry/sdk-node": "^0.54.0",
|
||||
"@opentelemetry/sdk-trace-base": "1.27.0",
|
||||
"@opentelemetry/semantic-conventions": "1.27.0",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/passport": "^1.0.16",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/passport-local": "^1.0.38",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"async-mutex": "^0.4.0",
|
||||
"axios": "1.7.9",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bull-board": "^2.1.3",
|
||||
"bullmq": "^5.42.0",
|
||||
"bullmq": "5.45.2",
|
||||
"cache-manager": "^6.3.2",
|
||||
"connect-pg-simple": "^10.0.0",
|
||||
"connect-redis": "^8.0.1",
|
||||
"connect-sqlite3": "^0.9.15",
|
||||
"content-disposition": "0.5.4",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"crypto-js": "^4.1.1",
|
||||
"csv-parser": "^3.0.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"express": "^4.17.3",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
"express-mysql-session": "^3.0.3",
|
||||
"express-rate-limit": "^6.9.0",
|
||||
"express-session": "^1.18.1",
|
||||
"flowise-components": "workspace:^",
|
||||
"flowise-nim-container-manager": "^1.0.11",
|
||||
"flowise-ui": "workspace:^",
|
||||
"global-agent": "^3.0.0",
|
||||
"gulp": "^4.0.2",
|
||||
"handlebars": "^4.7.8",
|
||||
"http-errors": "^2.0.0",
|
||||
"http-status-codes": "^2.3.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"langchainhub": "^0.0.11",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.3",
|
||||
@@ -99,8 +119,17 @@
|
||||
"multer-cloud-storage": "^4.0.0",
|
||||
"multer-s3": "^3.0.1",
|
||||
"mysql2": "^3.11.3",
|
||||
"flowise-nim-container-manager": "^1.0.11",
|
||||
"nanoid": "3",
|
||||
"nodemailer": "^6.9.14",
|
||||
"openai": "^4.96.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-auth0": "^1.4.4",
|
||||
"passport-cookie": "^1.0.9",
|
||||
"passport-github": "^1.1.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-openidconnect": "^0.1.2",
|
||||
"pg": "^8.11.1",
|
||||
"posthog-node": "^3.5.0",
|
||||
"prom-client": "^15.1.3",
|
||||
@@ -109,27 +138,41 @@
|
||||
"s3-streamlogger": "^1.11.0",
|
||||
"sanitize-html": "^2.11.0",
|
||||
"sqlite3": "^5.1.6",
|
||||
"stripe": "^15.6.0",
|
||||
"turndown": "^7.2.0",
|
||||
"typeorm": "^0.3.6",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.9.0"
|
||||
"winston": "^3.9.0",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/content-disposition": "0.5.8",
|
||||
"@types/cookie-parser": "^1.4.7",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/express-session": "^1.18.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/multer-s3": "^3.0.3",
|
||||
"@types/nodemailer": "^6.4.15",
|
||||
"@types/passport-auth0": "^1.0.9",
|
||||
"@types/passport-github": "^1.1.12",
|
||||
"@types/passport-openidconnect": "^0.1.3",
|
||||
"@types/sanitize-html": "^2.9.5",
|
||||
"@types/supertest": "^6.0.3",
|
||||
"@types/turndown": "^5.0.5",
|
||||
"concurrently": "^7.1.0",
|
||||
"cypress": "^13.13.0",
|
||||
"jest": "^29.7.0",
|
||||
"nodemon": "^2.0.22",
|
||||
"oclif": "^3",
|
||||
"rimraf": "^5.0.5",
|
||||
"run-script-os": "^1.1.6",
|
||||
"shx": "^0.3.3",
|
||||
"start-server-and-test": "^2.0.3",
|
||||
"supertest": "^7.1.0",
|
||||
"ts-jest": "^29.3.2",
|
||||
"ts-node": "^10.7.0",
|
||||
"tsc-watch": "^6.0.4",
|
||||
"typescript": "^5.4.5"
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
export const appConfig = {
|
||||
apiKeys: {
|
||||
storageType: process.env.APIKEY_STORAGE_TYPE ? process.env.APIKEY_STORAGE_TYPE.toLowerCase() : 'json'
|
||||
},
|
||||
showCommunityNodes: process.env.SHOW_COMMUNITY_NODES ? process.env.SHOW_COMMUNITY_NODES.toLowerCase() === 'true' : false
|
||||
// todo: add more config options here like database, log, storage, credential and allow modification from UI
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { sqliteMigrations } from './database/migrations/sqlite'
|
||||
import { mysqlMigrations } from './database/migrations/mysql'
|
||||
import { mariadbMigrations } from './database/migrations/mariadb'
|
||||
import { postgresMigrations } from './database/migrations/postgres'
|
||||
import logger from './utils/logger'
|
||||
|
||||
let appDataSource: DataSource
|
||||
|
||||
@@ -73,7 +74,17 @@ export const init = async (): Promise<void> => {
|
||||
synchronize: false,
|
||||
migrationsRun: false,
|
||||
entities: Object.values(entities),
|
||||
migrations: postgresMigrations
|
||||
migrations: postgresMigrations,
|
||||
extra: {
|
||||
idleTimeoutMillis: 120000
|
||||
},
|
||||
logging: ['error', 'warn', 'info', 'log'],
|
||||
logger: 'advanced-console',
|
||||
logNotifications: true,
|
||||
poolErrorHandler: (err) => {
|
||||
logger.error(`Database pool error: ${JSON.stringify(err)}`)
|
||||
},
|
||||
applicationName: 'Flowise'
|
||||
})
|
||||
break
|
||||
default:
|
||||
@@ -97,7 +108,7 @@ export function getDataSource(): DataSource {
|
||||
return appDataSource
|
||||
}
|
||||
|
||||
const getDatabaseSSLFromEnv = () => {
|
||||
export const getDatabaseSSLFromEnv = () => {
|
||||
if (process.env.DATABASE_SSL_KEY_BASE64) {
|
||||
return {
|
||||
rejectUnauthorized: false,
|
||||
|
||||
@@ -0,0 +1,524 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present FlowiseAI, Inc.
|
||||
*
|
||||
* The Enterprise and Cloud versions of Flowise are licensed under the [Commercial License](https://github.com/FlowiseAI/Flowise/tree/main/packages/server/src/enterprise/LICENSE.md).
|
||||
* Unauthorized copying, modification, distribution, or use of the Enterprise and Cloud versions is strictly prohibited without a valid license agreement from FlowiseAI, Inc.
|
||||
*
|
||||
* The Open Source version is licensed under the Apache License, Version 2.0 (the "License")
|
||||
*
|
||||
* For information about licensing of the Enterprise and Cloud versions, please contact:
|
||||
* security@flowiseai.com
|
||||
*/
|
||||
|
||||
import axios from 'axios'
|
||||
import express, { Application, NextFunction, Request, Response } from 'express'
|
||||
import * as fs from 'fs'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import path from 'path'
|
||||
import { LoginMethodStatus } from './enterprise/database/entities/login-method.entity'
|
||||
import { ErrorMessage, LoggedInUser } from './enterprise/Interface.Enterprise'
|
||||
import { Permissions } from './enterprise/rbac/Permissions'
|
||||
import { LoginMethodService } from './enterprise/services/login-method.service'
|
||||
import { OrganizationService } from './enterprise/services/organization.service'
|
||||
import Auth0SSO from './enterprise/sso/Auth0SSO'
|
||||
import AzureSSO from './enterprise/sso/AzureSSO'
|
||||
import GithubSSO from './enterprise/sso/GithubSSO'
|
||||
import GoogleSSO from './enterprise/sso/GoogleSSO'
|
||||
import SSOBase from './enterprise/sso/SSOBase'
|
||||
import { InternalFlowiseError } from './errors/internalFlowiseError'
|
||||
import { Platform, UserPlan } from './Interface'
|
||||
import { StripeManager } from './StripeManager'
|
||||
import { UsageCacheManager } from './UsageCacheManager'
|
||||
import { GeneralErrorMessage, LICENSE_QUOTAS } from './utils/constants'
|
||||
import { getRunningExpressApp } from './utils/getRunningExpressApp'
|
||||
import { ENTERPRISE_FEATURE_FLAGS } from './utils/quotaUsage'
|
||||
import Stripe from 'stripe'
|
||||
|
||||
const allSSOProviders = ['azure', 'google', 'auth0', 'github']
|
||||
export class IdentityManager {
|
||||
private static instance: IdentityManager
|
||||
private stripeManager?: StripeManager
|
||||
licenseValid: boolean = false
|
||||
permissions: Permissions
|
||||
ssoProviderName: string = ''
|
||||
currentInstancePlatform: Platform = Platform.OPEN_SOURCE
|
||||
// create a map to store the sso provider name and the sso provider instance
|
||||
ssoProviders: Map<string, SSOBase> = new Map()
|
||||
|
||||
public static async getInstance(): Promise<IdentityManager> {
|
||||
if (!IdentityManager.instance) {
|
||||
IdentityManager.instance = new IdentityManager()
|
||||
await IdentityManager.instance.initialize()
|
||||
}
|
||||
return IdentityManager.instance
|
||||
}
|
||||
|
||||
public async initialize() {
|
||||
await this._validateLicenseKey()
|
||||
this.permissions = new Permissions()
|
||||
if (process.env.STRIPE_SECRET_KEY) {
|
||||
this.stripeManager = await StripeManager.getInstance()
|
||||
}
|
||||
}
|
||||
|
||||
public getPlatformType = () => {
|
||||
return this.currentInstancePlatform
|
||||
}
|
||||
|
||||
public getPermissions = () => {
|
||||
return this.permissions
|
||||
}
|
||||
|
||||
public isEnterprise = () => {
|
||||
return this.currentInstancePlatform === Platform.ENTERPRISE
|
||||
}
|
||||
|
||||
public isCloud = () => {
|
||||
return this.currentInstancePlatform === Platform.CLOUD
|
||||
}
|
||||
|
||||
public isOpenSource = () => {
|
||||
return this.currentInstancePlatform === Platform.OPEN_SOURCE
|
||||
}
|
||||
|
||||
public isLicenseValid = () => {
|
||||
return this.licenseValid
|
||||
}
|
||||
|
||||
private _offlineVerifyLicense(licenseKey: string): any {
|
||||
try {
|
||||
const publicKey = fs.readFileSync(path.join(__dirname, '../', 'src/enterprise/license/public.pem'), 'utf8')
|
||||
const decoded = jwt.verify(licenseKey, publicKey, {
|
||||
algorithms: ['RS256']
|
||||
})
|
||||
return decoded
|
||||
} catch (error) {
|
||||
console.error('Error verifying license key:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private _validateLicenseKey = async () => {
|
||||
const LICENSE_URL = process.env.LICENSE_URL
|
||||
const FLOWISE_EE_LICENSE_KEY = process.env.FLOWISE_EE_LICENSE_KEY
|
||||
|
||||
// First check if license key is missing
|
||||
if (!FLOWISE_EE_LICENSE_KEY) {
|
||||
this.licenseValid = false
|
||||
this.currentInstancePlatform = Platform.OPEN_SOURCE
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (process.env.OFFLINE === 'true') {
|
||||
const decodedLicense = this._offlineVerifyLicense(FLOWISE_EE_LICENSE_KEY)
|
||||
|
||||
if (!decodedLicense) {
|
||||
this.licenseValid = false
|
||||
} else {
|
||||
const issuedAtSeconds = decodedLicense.iat
|
||||
if (!issuedAtSeconds) {
|
||||
this.licenseValid = false
|
||||
} else {
|
||||
const issuedAt = new Date(issuedAtSeconds * 1000)
|
||||
const expiryDurationInMonths = decodedLicense.expiryDurationInMonths || 0
|
||||
|
||||
const expiryDate = new Date(issuedAt)
|
||||
expiryDate.setMonth(expiryDate.getMonth() + expiryDurationInMonths)
|
||||
|
||||
if (new Date() > expiryDate) {
|
||||
this.licenseValid = false
|
||||
} else {
|
||||
this.licenseValid = true
|
||||
}
|
||||
}
|
||||
}
|
||||
this.currentInstancePlatform = Platform.ENTERPRISE
|
||||
} else if (LICENSE_URL) {
|
||||
try {
|
||||
const response = await axios.post(`${LICENSE_URL}/enterprise/verify`, { license: FLOWISE_EE_LICENSE_KEY })
|
||||
this.licenseValid = response.data?.valid
|
||||
|
||||
if (!LICENSE_URL.includes('api')) this.currentInstancePlatform = Platform.ENTERPRISE
|
||||
else if (LICENSE_URL.includes('v1')) this.currentInstancePlatform = Platform.ENTERPRISE
|
||||
else if (LICENSE_URL.includes('v2')) this.currentInstancePlatform = response.data?.platform
|
||||
else throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
} catch (error) {
|
||||
console.error('Error verifying license key:', error)
|
||||
this.licenseValid = false
|
||||
this.currentInstancePlatform = Platform.ENTERPRISE
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.licenseValid = false
|
||||
}
|
||||
}
|
||||
|
||||
public initializeSSO = async (app: express.Application) => {
|
||||
if (this.getPlatformType() === Platform.CLOUD || this.getPlatformType() === Platform.ENTERPRISE) {
|
||||
const loginMethodService = new LoginMethodService()
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
let organizationId = undefined
|
||||
if (this.getPlatformType() === Platform.ENTERPRISE) {
|
||||
const organizationService = new OrganizationService()
|
||||
const organizations = await organizationService.readOrganization(queryRunner)
|
||||
if (organizations.length > 0) {
|
||||
organizationId = organizations[0].id
|
||||
} else {
|
||||
this.initializeEmptySSO(app)
|
||||
return
|
||||
}
|
||||
}
|
||||
const loginMethods = await loginMethodService.readLoginMethodByOrganizationId(organizationId, queryRunner)
|
||||
if (loginMethods && loginMethods.length > 0) {
|
||||
for (let method of loginMethods) {
|
||||
if (method.status === LoginMethodStatus.ENABLE) {
|
||||
method.config = JSON.parse(await loginMethodService.decryptLoginMethodConfig(method.config))
|
||||
this.initializeSsoProvider(app, method.name, method.config)
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
// iterate through the remaining providers and initialize them with configEnabled as false
|
||||
this.initializeEmptySSO(app)
|
||||
}
|
||||
|
||||
initializeEmptySSO(app: Application) {
|
||||
allSSOProviders.map((providerName) => {
|
||||
if (!this.ssoProviders.has(providerName)) {
|
||||
this.initializeSsoProvider(app, providerName, undefined)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
initializeSsoProvider(app: Application, providerName: string, providerConfig: any) {
|
||||
if (this.ssoProviders.has(providerName)) {
|
||||
const provider = this.ssoProviders.get(providerName)
|
||||
if (provider) {
|
||||
if (providerConfig && providerConfig.configEnabled === true) {
|
||||
provider.setSSOConfig(providerConfig)
|
||||
provider.initialize()
|
||||
} else {
|
||||
// if false, disable the provider
|
||||
provider.setSSOConfig(undefined)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (providerName) {
|
||||
case 'azure': {
|
||||
const azureSSO = new AzureSSO(app, providerConfig)
|
||||
azureSSO.initialize()
|
||||
this.ssoProviders.set(providerName, azureSSO)
|
||||
break
|
||||
}
|
||||
case 'google': {
|
||||
const googleSSO = new GoogleSSO(app, providerConfig)
|
||||
googleSSO.initialize()
|
||||
this.ssoProviders.set(providerName, googleSSO)
|
||||
break
|
||||
}
|
||||
case 'auth0': {
|
||||
const auth0SSO = new Auth0SSO(app, providerConfig)
|
||||
auth0SSO.initialize()
|
||||
this.ssoProviders.set(providerName, auth0SSO)
|
||||
break
|
||||
}
|
||||
case 'github': {
|
||||
const githubSSO = new GithubSSO(app, providerConfig)
|
||||
githubSSO.initialize()
|
||||
this.ssoProviders.set(providerName, githubSSO)
|
||||
break
|
||||
}
|
||||
default:
|
||||
throw new Error(`SSO Provider ${providerName} not found`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getRefreshToken(providerName: any, ssoRefreshToken: string) {
|
||||
if (!this.ssoProviders.has(providerName)) {
|
||||
throw new Error(`SSO Provider ${providerName} not found`)
|
||||
}
|
||||
return await (this.ssoProviders.get(providerName) as SSOBase).refreshToken(ssoRefreshToken)
|
||||
}
|
||||
|
||||
public async getProductIdFromSubscription(subscriptionId: string) {
|
||||
if (!subscriptionId) return ''
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
return await this.stripeManager.getProductIdFromSubscription(subscriptionId)
|
||||
}
|
||||
|
||||
public async getFeaturesByPlan(subscriptionId: string, withoutCache: boolean = false) {
|
||||
if (this.isEnterprise()) {
|
||||
const features: Record<string, string> = {}
|
||||
for (const feature of ENTERPRISE_FEATURE_FLAGS) {
|
||||
features[feature] = 'true'
|
||||
}
|
||||
return features
|
||||
} else if (this.isCloud()) {
|
||||
if (!this.stripeManager || !subscriptionId) {
|
||||
return {}
|
||||
}
|
||||
return await this.stripeManager.getFeaturesByPlan(subscriptionId, withoutCache)
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
public static checkFeatureByPlan(feature: string) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
const user = req.user
|
||||
if (user) {
|
||||
if (!user.features || Object.keys(user.features).length === 0) {
|
||||
return res.status(403).json({ message: ErrorMessage.FORBIDDEN })
|
||||
}
|
||||
if (Object.keys(user.features).includes(feature) && user.features[feature] === 'true') {
|
||||
return next()
|
||||
}
|
||||
}
|
||||
return res.status(403).json({ message: ErrorMessage.FORBIDDEN })
|
||||
}
|
||||
}
|
||||
|
||||
public async createStripeCustomerPortalSession(req: Request) {
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
return await this.stripeManager.createStripeCustomerPortalSession(req)
|
||||
}
|
||||
|
||||
public async getAdditionalSeatsQuantity(subscriptionId: string) {
|
||||
if (!subscriptionId) return {}
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
return await this.stripeManager.getAdditionalSeatsQuantity(subscriptionId)
|
||||
}
|
||||
|
||||
public async getCustomerWithDefaultSource(customerId: string) {
|
||||
if (!customerId) return
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
return await this.stripeManager.getCustomerWithDefaultSource(customerId)
|
||||
}
|
||||
|
||||
public async getAdditionalSeatsProration(subscriptionId: string, newQuantity: number) {
|
||||
if (!subscriptionId) return {}
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
return await this.stripeManager.getAdditionalSeatsProration(subscriptionId, newQuantity)
|
||||
}
|
||||
|
||||
public async updateAdditionalSeats(subscriptionId: string, quantity: number, prorationDate: number) {
|
||||
if (!subscriptionId) return {}
|
||||
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
const { success, subscription, invoice } = await this.stripeManager.updateAdditionalSeats(subscriptionId, quantity, prorationDate)
|
||||
|
||||
// Fetch product details to get quotas
|
||||
const items = subscription.items.data
|
||||
if (items.length === 0) {
|
||||
throw new Error('No subscription items found')
|
||||
}
|
||||
|
||||
const productId = items[0].price.product as string
|
||||
const product = await this.stripeManager.getStripe().products.retrieve(productId)
|
||||
const productMetadata = product.metadata
|
||||
|
||||
// Extract quotas from metadata
|
||||
const quotas: Record<string, number> = {}
|
||||
for (const key in productMetadata) {
|
||||
if (key.startsWith('quota:')) {
|
||||
quotas[key] = parseInt(productMetadata[key])
|
||||
}
|
||||
}
|
||||
quotas[LICENSE_QUOTAS.ADDITIONAL_SEATS_LIMIT] = quantity
|
||||
|
||||
// Get features from Stripe
|
||||
const features = await this.getFeaturesByPlan(subscription.id, true)
|
||||
|
||||
// Update the cache with new subscription data including quotas
|
||||
const cacheManager = await UsageCacheManager.getInstance()
|
||||
await cacheManager.updateSubscriptionDataToCache(subscriptionId, {
|
||||
features,
|
||||
quotas,
|
||||
subsriptionDetails: this.stripeManager.getSubscriptionObject(subscription)
|
||||
})
|
||||
|
||||
return { success, subscription, invoice }
|
||||
}
|
||||
|
||||
public async getPlanProration(subscriptionId: string, newPlanId: string) {
|
||||
if (!subscriptionId || !newPlanId) return {}
|
||||
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
return await this.stripeManager.getPlanProration(subscriptionId, newPlanId)
|
||||
}
|
||||
|
||||
public async updateSubscriptionPlan(req: Request, subscriptionId: string, newPlanId: string, prorationDate: number) {
|
||||
if (!subscriptionId || !newPlanId) return {}
|
||||
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
if (!req.user) {
|
||||
throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, GeneralErrorMessage.UNAUTHORIZED)
|
||||
}
|
||||
const { success, subscription } = await this.stripeManager.updateSubscriptionPlan(subscriptionId, newPlanId, prorationDate)
|
||||
if (success) {
|
||||
// Fetch product details to get quotas
|
||||
const product = await this.stripeManager.getStripe().products.retrieve(newPlanId)
|
||||
const productMetadata = product.metadata
|
||||
|
||||
// Extract quotas from metadata
|
||||
const quotas: Record<string, number> = {}
|
||||
for (const key in productMetadata) {
|
||||
if (key.startsWith('quota:')) {
|
||||
quotas[key] = parseInt(productMetadata[key])
|
||||
}
|
||||
}
|
||||
|
||||
const additionalSeatsItem = subscription.items.data.find(
|
||||
(item) => (item.price.product as string) === process.env.ADDITIONAL_SEAT_ID
|
||||
)
|
||||
quotas[LICENSE_QUOTAS.ADDITIONAL_SEATS_LIMIT] = additionalSeatsItem?.quantity || 0
|
||||
|
||||
// Get features from Stripe
|
||||
const features = await this.getFeaturesByPlan(subscription.id, true)
|
||||
|
||||
// Update the cache with new subscription data including quotas
|
||||
const cacheManager = await UsageCacheManager.getInstance()
|
||||
|
||||
const updateCacheData: Record<string, any> = {
|
||||
features,
|
||||
quotas,
|
||||
subsriptionDetails: this.stripeManager.getSubscriptionObject(subscription)
|
||||
}
|
||||
|
||||
if (
|
||||
newPlanId === process.env.CLOUD_FREE_ID ||
|
||||
newPlanId === process.env.CLOUD_STARTER_ID ||
|
||||
newPlanId === process.env.CLOUD_PRO_ID
|
||||
) {
|
||||
updateCacheData.productId = newPlanId
|
||||
}
|
||||
|
||||
await cacheManager.updateSubscriptionDataToCache(subscriptionId, updateCacheData)
|
||||
|
||||
const loggedInUser: LoggedInUser = {
|
||||
...req.user,
|
||||
activeOrganizationSubscriptionId: subscription.id,
|
||||
features
|
||||
}
|
||||
|
||||
if (
|
||||
newPlanId === process.env.CLOUD_FREE_ID ||
|
||||
newPlanId === process.env.CLOUD_STARTER_ID ||
|
||||
newPlanId === process.env.CLOUD_PRO_ID
|
||||
) {
|
||||
loggedInUser.activeOrganizationProductId = newPlanId
|
||||
}
|
||||
|
||||
req.user = {
|
||||
...req.user,
|
||||
...loggedInUser
|
||||
}
|
||||
|
||||
// Update passport session
|
||||
// @ts-ignore
|
||||
req.session.passport.user = {
|
||||
...req.user,
|
||||
...loggedInUser
|
||||
}
|
||||
|
||||
req.session.save((err) => {
|
||||
if (err) throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
})
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
user: loggedInUser
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: 'error',
|
||||
message: 'Payment or subscription update not completed'
|
||||
}
|
||||
}
|
||||
|
||||
public async createStripeUserAndSubscribe({ email, userPlan, referral }: { email: string; userPlan: UserPlan; referral?: string }) {
|
||||
if (!this.stripeManager) {
|
||||
throw new Error('Stripe manager is not initialized')
|
||||
}
|
||||
|
||||
try {
|
||||
// Create a customer in Stripe
|
||||
let customer: Stripe.Response<Stripe.Customer>
|
||||
if (referral) {
|
||||
customer = await this.stripeManager.getStripe().customers.create({
|
||||
email: email,
|
||||
metadata: {
|
||||
referral
|
||||
}
|
||||
})
|
||||
} else {
|
||||
customer = await this.stripeManager.getStripe().customers.create({
|
||||
email: email
|
||||
})
|
||||
}
|
||||
|
||||
let productId = ''
|
||||
switch (userPlan) {
|
||||
case UserPlan.STARTER:
|
||||
productId = process.env.CLOUD_STARTER_ID as string
|
||||
break
|
||||
case UserPlan.PRO:
|
||||
productId = process.env.CLOUD_PRO_ID as string
|
||||
break
|
||||
case UserPlan.FREE:
|
||||
productId = process.env.CLOUD_FREE_ID as string
|
||||
break
|
||||
}
|
||||
|
||||
// Get the default price ID for the product
|
||||
const prices = await this.stripeManager.getStripe().prices.list({
|
||||
product: productId,
|
||||
active: true,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (!prices.data.length) {
|
||||
throw new Error('No active price found for the product')
|
||||
}
|
||||
|
||||
// Create the subscription
|
||||
const subscription = await this.stripeManager.getStripe().subscriptions.create({
|
||||
customer: customer.id,
|
||||
items: [{ price: prices.data[0].id }]
|
||||
})
|
||||
|
||||
return {
|
||||
customerId: customer.id,
|
||||
subscriptionId: subscription.id
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating Stripe user and subscription:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { DataSource } from 'typeorm'
|
||||
import { IComponentNodes } from './Interface'
|
||||
import { Telemetry } from './utils/telemetry'
|
||||
import { CachePool } from './CachePool'
|
||||
import { UsageCacheManager } from './UsageCacheManager'
|
||||
|
||||
export enum DocumentStoreStatus {
|
||||
EMPTY_SYNC = 'EMPTY',
|
||||
@@ -27,6 +28,7 @@ export interface IDocumentStore {
|
||||
vectorStoreConfig: string | null // JSON string
|
||||
embeddingConfig: string | null // JSON string
|
||||
recordManagerConfig: string | null // JSON string
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface IDocumentStoreFileChunk {
|
||||
@@ -47,6 +49,7 @@ export interface IDocumentStoreFileChunkPagedResponse {
|
||||
storeName: string
|
||||
description: string
|
||||
docId: string
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface IDocumentStoreLoader {
|
||||
@@ -119,9 +122,13 @@ export interface IDocumentStoreWhereUsed {
|
||||
}
|
||||
|
||||
export interface IUpsertQueueAppServer {
|
||||
orgId: string
|
||||
workspaceId: string
|
||||
subscriptionId: string
|
||||
appDataSource: DataSource
|
||||
componentNodes: IComponentNodes
|
||||
telemetry: Telemetry
|
||||
usageCacheManager: UsageCacheManager
|
||||
cachePool?: CachePool
|
||||
}
|
||||
|
||||
@@ -231,6 +238,7 @@ export class DocumentStoreDTO {
|
||||
totalChunks: number
|
||||
totalChars: number
|
||||
chunkSize: number
|
||||
workspaceId?: string
|
||||
loaders: IDocumentStoreLoader[]
|
||||
vectorStoreConfig: any
|
||||
embeddingConfig: any
|
||||
@@ -246,6 +254,7 @@ export class DocumentStoreDTO {
|
||||
documentStoreDTO.name = entity.name
|
||||
documentStoreDTO.description = entity.description
|
||||
documentStoreDTO.status = entity.status
|
||||
documentStoreDTO.workspaceId = entity.workspaceId
|
||||
documentStoreDTO.totalChars = 0
|
||||
documentStoreDTO.totalChunks = 0
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// Evaluation Related Interfaces
|
||||
import { Evaluator } from './database/entities/Evaluator'
|
||||
|
||||
export interface IDataset {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
createdDate: Date
|
||||
updatedDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
export interface IDatasetRow {
|
||||
id: string
|
||||
datasetId: string
|
||||
input: string
|
||||
output: string
|
||||
updatedDate: Date
|
||||
sequenceNo: number
|
||||
}
|
||||
|
||||
export enum EvaluationStatus {
|
||||
PENDING = 'pending',
|
||||
COMPLETED = 'completed',
|
||||
ERROR = 'error'
|
||||
}
|
||||
|
||||
export interface IEvaluation {
|
||||
id: string
|
||||
name: string
|
||||
chatflowId: string
|
||||
chatflowName: string
|
||||
datasetId: string
|
||||
datasetName: string
|
||||
evaluationType: string
|
||||
additionalConfig: string //json
|
||||
average_metrics: string //json
|
||||
status: string
|
||||
runDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface IEvaluationResult extends IEvaluation {
|
||||
latestEval: boolean
|
||||
version: number
|
||||
}
|
||||
|
||||
export interface IEvaluationRun {
|
||||
id: string
|
||||
evaluationId: string
|
||||
input: string
|
||||
expectedOutput: string
|
||||
actualOutput: string // JSON
|
||||
metrics: string // JSON
|
||||
runDate: Date
|
||||
llmEvaluators?: string // JSON
|
||||
evaluators?: string // JSON
|
||||
errors?: string // JSON
|
||||
}
|
||||
|
||||
export interface IEvaluator {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
config: string // JSON
|
||||
updatedDate: Date
|
||||
createdDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export class EvaluatorDTO {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
measure?: string
|
||||
operator?: string
|
||||
value?: string
|
||||
prompt?: string
|
||||
evaluatorType?: string
|
||||
outputSchema?: []
|
||||
updatedDate: Date
|
||||
createdDate: Date
|
||||
|
||||
static toEntity(body: any): Evaluator {
|
||||
const newDs = new Evaluator()
|
||||
Object.assign(newDs, body)
|
||||
let config: any = {}
|
||||
if (body.type === 'llm') {
|
||||
config = {
|
||||
prompt: body.prompt,
|
||||
outputSchema: body.outputSchema
|
||||
}
|
||||
} else if (body.type === 'text') {
|
||||
config = {
|
||||
operator: body.operator,
|
||||
value: body.value
|
||||
}
|
||||
} else if (body.type === 'json') {
|
||||
config = {
|
||||
operator: body.operator
|
||||
}
|
||||
} else if (body.type === 'numeric') {
|
||||
config = {
|
||||
operator: body.operator,
|
||||
value: body.value,
|
||||
measure: body.measure
|
||||
}
|
||||
} else {
|
||||
throw new Error('Invalid evaluator type')
|
||||
}
|
||||
newDs.config = JSON.stringify(config)
|
||||
return newDs
|
||||
}
|
||||
|
||||
static fromEntity(entity: Evaluator): EvaluatorDTO {
|
||||
const newDs = new EvaluatorDTO()
|
||||
Object.assign(newDs, entity)
|
||||
const config = JSON.parse(entity.config)
|
||||
if (entity.type === 'llm') {
|
||||
newDs.prompt = config.prompt
|
||||
newDs.outputSchema = config.outputSchema
|
||||
} else if (entity.type === 'text') {
|
||||
newDs.operator = config.operator
|
||||
newDs.value = config.value
|
||||
} else if (entity.type === 'json') {
|
||||
newDs.operator = config.operator
|
||||
newDs.value = config.value
|
||||
} else if (entity.type === 'numeric') {
|
||||
newDs.operator = config.operator
|
||||
newDs.value = config.value
|
||||
newDs.measure = config.measure
|
||||
}
|
||||
delete (newDs as any).config
|
||||
return newDs
|
||||
}
|
||||
|
||||
static fromEntities(entities: Evaluator[]): EvaluatorDTO[] {
|
||||
return entities.map((entity) => this.fromEntity(entity))
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { DataSource } from 'typeorm'
|
||||
import { CachePool } from './CachePool'
|
||||
import { Telemetry } from './utils/telemetry'
|
||||
import { UsageCacheManager } from './UsageCacheManager'
|
||||
|
||||
export type MessageType = 'apiMessage' | 'userMessage'
|
||||
|
||||
@@ -28,13 +29,27 @@ export enum MODE {
|
||||
|
||||
export enum ChatType {
|
||||
INTERNAL = 'INTERNAL',
|
||||
EXTERNAL = 'EXTERNAL'
|
||||
EXTERNAL = 'EXTERNAL',
|
||||
EVALUATION = 'EVALUATION'
|
||||
}
|
||||
|
||||
export enum ChatMessageRatingType {
|
||||
THUMBS_UP = 'THUMBS_UP',
|
||||
THUMBS_DOWN = 'THUMBS_DOWN'
|
||||
}
|
||||
|
||||
export enum Platform {
|
||||
OPEN_SOURCE = 'open source',
|
||||
CLOUD = 'cloud',
|
||||
ENTERPRISE = 'enterprise'
|
||||
}
|
||||
|
||||
export enum UserPlan {
|
||||
STARTER = 'STARTER',
|
||||
PRO = 'PRO',
|
||||
FREE = 'FREE'
|
||||
}
|
||||
|
||||
/**
|
||||
* Databases
|
||||
*/
|
||||
@@ -54,6 +69,7 @@ export interface IChatFlow {
|
||||
apiConfig?: string
|
||||
category?: string
|
||||
type?: ChatflowType
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface IChatMessage {
|
||||
@@ -98,6 +114,7 @@ export interface ITool {
|
||||
func?: string
|
||||
updatedDate: Date
|
||||
createdDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface IAssistant {
|
||||
@@ -107,6 +124,7 @@ export interface IAssistant {
|
||||
iconSrc?: string
|
||||
updatedDate: Date
|
||||
createdDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface ICredential {
|
||||
@@ -116,6 +134,7 @@ export interface ICredential {
|
||||
encryptedData: string
|
||||
updatedDate: Date
|
||||
createdDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface IVariable {
|
||||
@@ -125,6 +144,7 @@ export interface IVariable {
|
||||
type: string
|
||||
updatedDate: Date
|
||||
createdDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface ILead {
|
||||
@@ -156,6 +176,7 @@ export interface IExecution {
|
||||
createdDate: Date
|
||||
updatedDate: Date
|
||||
stoppedDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface IComponentNodes {
|
||||
@@ -311,6 +332,7 @@ export interface ICredentialReqBody {
|
||||
name: string
|
||||
credentialName: string
|
||||
plainDataObj: ICredentialDataDecrypted
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
// Decrypted credential object sent back to client
|
||||
@@ -329,6 +351,7 @@ export interface IApiKey {
|
||||
apiKey: string
|
||||
apiSecret: string
|
||||
updatedDate: Date
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface ICustomTemplate {
|
||||
@@ -342,6 +365,7 @@ export interface ICustomTemplate {
|
||||
badge?: string
|
||||
framework?: string
|
||||
usecases?: string
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
export interface IFlowConfig {
|
||||
@@ -361,14 +385,20 @@ export interface IPredictionQueueAppServer {
|
||||
sseStreamer: IServerSideEventStreamer
|
||||
telemetry: Telemetry
|
||||
cachePool: CachePool
|
||||
usageCacheManager: UsageCacheManager
|
||||
}
|
||||
|
||||
export interface IExecuteFlowParams extends IPredictionQueueAppServer {
|
||||
incomingInput: IncomingInput
|
||||
chatflow: IChatFlow
|
||||
chatId: string
|
||||
orgId: string
|
||||
workspaceId: string
|
||||
subscriptionId: string
|
||||
baseURL: string
|
||||
isInternal: boolean
|
||||
isEvaluation?: boolean
|
||||
evaluationRunId?: string
|
||||
signal?: AbortController
|
||||
files?: Express.Multer.File[]
|
||||
fileUploads?: IFileUpload[]
|
||||
@@ -398,3 +428,6 @@ export interface IVariableOverride {
|
||||
|
||||
// DocumentStore related
|
||||
export * from './Interface.DocumentStore'
|
||||
|
||||
// Evaluations related
|
||||
export * from './Interface.Evaluation'
|
||||
|
||||
@@ -0,0 +1,606 @@
|
||||
import Stripe from 'stripe'
|
||||
import { Request } from 'express'
|
||||
import { UsageCacheManager } from './UsageCacheManager'
|
||||
import { UserPlan } from './Interface'
|
||||
import { LICENSE_QUOTAS } from './utils/constants'
|
||||
|
||||
export class StripeManager {
|
||||
private static instance: StripeManager
|
||||
private stripe?: Stripe
|
||||
private cacheManager: UsageCacheManager
|
||||
|
||||
public static async getInstance(): Promise<StripeManager> {
|
||||
if (!StripeManager.instance) {
|
||||
StripeManager.instance = new StripeManager()
|
||||
await StripeManager.instance.initialize()
|
||||
}
|
||||
return StripeManager.instance
|
||||
}
|
||||
|
||||
private async initialize() {
|
||||
if (!this.stripe && process.env.STRIPE_SECRET_KEY) {
|
||||
this.stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
|
||||
}
|
||||
this.cacheManager = await UsageCacheManager.getInstance()
|
||||
}
|
||||
|
||||
public getStripe() {
|
||||
if (!this.stripe) throw new Error('Stripe is not initialized')
|
||||
return this.stripe
|
||||
}
|
||||
|
||||
public getSubscriptionObject(subscription: Stripe.Response<Stripe.Subscription>) {
|
||||
return {
|
||||
customer: subscription.customer,
|
||||
status: subscription.status,
|
||||
created: subscription.created
|
||||
}
|
||||
}
|
||||
|
||||
public async getProductIdFromSubscription(subscriptionId: string) {
|
||||
if (!this.stripe) {
|
||||
throw new Error('Stripe is not initialized')
|
||||
}
|
||||
|
||||
const subscriptionData = await this.cacheManager.getSubscriptionDataFromCache(subscriptionId)
|
||||
if (subscriptionData?.productId) {
|
||||
return subscriptionData.productId
|
||||
}
|
||||
|
||||
try {
|
||||
const subscription = await this.stripe.subscriptions.retrieve(subscriptionId)
|
||||
const items = subscription.items.data
|
||||
if (items.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const productId = items[0].price.product as string
|
||||
await this.cacheManager.updateSubscriptionDataToCache(subscriptionId, {
|
||||
productId,
|
||||
subsriptionDetails: this.getSubscriptionObject(subscription)
|
||||
})
|
||||
|
||||
return productId
|
||||
} catch (error) {
|
||||
console.error('Error getting product ID from subscription:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async getFeaturesByPlan(subscriptionId: string, withoutCache: boolean = false) {
|
||||
if (!this.stripe || !subscriptionId) {
|
||||
return {}
|
||||
}
|
||||
|
||||
if (!withoutCache) {
|
||||
const subscriptionData = await this.cacheManager.getSubscriptionDataFromCache(subscriptionId)
|
||||
if (subscriptionData?.features) {
|
||||
return subscriptionData.features
|
||||
}
|
||||
}
|
||||
|
||||
const subscription = await this.stripe.subscriptions.retrieve(subscriptionId, {
|
||||
timeout: 5000
|
||||
})
|
||||
const items = subscription.items.data
|
||||
if (items.length === 0) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const productId = items[0].price.product as string
|
||||
const product = await this.stripe.products.retrieve(productId, {
|
||||
timeout: 5000
|
||||
})
|
||||
const productMetadata = product.metadata
|
||||
|
||||
if (!productMetadata || Object.keys(productMetadata).length === 0) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const features: Record<string, string> = {}
|
||||
for (const key in productMetadata) {
|
||||
if (key.startsWith('feat:')) {
|
||||
features[key] = productMetadata[key]
|
||||
}
|
||||
}
|
||||
|
||||
await this.cacheManager.updateSubscriptionDataToCache(subscriptionId, {
|
||||
features,
|
||||
subsriptionDetails: this.getSubscriptionObject(subscription)
|
||||
})
|
||||
|
||||
return features
|
||||
}
|
||||
|
||||
public async createStripeCustomerPortalSession(req: Request) {
|
||||
if (!this.stripe) {
|
||||
throw new Error('Stripe is not initialized')
|
||||
}
|
||||
|
||||
const customerId = req.user?.activeOrganizationCustomerId
|
||||
if (!customerId) {
|
||||
throw new Error('Customer ID is required')
|
||||
}
|
||||
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId
|
||||
if (!subscriptionId) {
|
||||
throw new Error('Subscription ID is required')
|
||||
}
|
||||
|
||||
try {
|
||||
const prodPriceIds = await this.getPriceIds()
|
||||
const configuration = await this.createPortalConfiguration(prodPriceIds)
|
||||
|
||||
const portalSession = await this.stripe.billingPortal.sessions.create({
|
||||
customer: customerId,
|
||||
configuration: configuration.id,
|
||||
return_url: `${process.env.APP_URL}/account`
|
||||
/* We can't have flow_data because it does not support multiple subscription items
|
||||
flow_data: {
|
||||
type: 'subscription_update',
|
||||
subscription_update: {
|
||||
subscription: subscriptionId
|
||||
},
|
||||
after_completion: {
|
||||
type: 'redirect',
|
||||
redirect: {
|
||||
return_url: `${process.env.APP_URL}/account/subscription?subscriptionId=${subscriptionId}`
|
||||
}
|
||||
}
|
||||
}*/
|
||||
})
|
||||
|
||||
return { url: portalSession.url }
|
||||
} catch (error) {
|
||||
console.error('Error creating customer portal session:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
private async getPriceIds() {
|
||||
const prodPriceIds: Record<string, { product: string; price: string }> = {
|
||||
[UserPlan.STARTER]: {
|
||||
product: process.env.CLOUD_STARTER_ID as string,
|
||||
price: ''
|
||||
},
|
||||
[UserPlan.PRO]: {
|
||||
product: process.env.CLOUD_PRO_ID as string,
|
||||
price: ''
|
||||
},
|
||||
[UserPlan.FREE]: {
|
||||
product: process.env.CLOUD_FREE_ID as string,
|
||||
price: ''
|
||||
},
|
||||
SEAT: {
|
||||
product: process.env.ADDITIONAL_SEAT_ID as string,
|
||||
price: ''
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in prodPriceIds) {
|
||||
const prices = await this.stripe!.prices.list({
|
||||
product: prodPriceIds[key].product,
|
||||
active: true,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (prices.data.length) {
|
||||
prodPriceIds[key].price = prices.data[0].id
|
||||
}
|
||||
}
|
||||
|
||||
return prodPriceIds
|
||||
}
|
||||
|
||||
private async createPortalConfiguration(_: Record<string, { product: string; price: string }>) {
|
||||
return await this.stripe!.billingPortal.configurations.create({
|
||||
business_profile: {
|
||||
privacy_policy_url: `${process.env.APP_URL}/privacy-policy`,
|
||||
terms_of_service_url: `${process.env.APP_URL}/terms-of-service`
|
||||
},
|
||||
features: {
|
||||
invoice_history: {
|
||||
enabled: true
|
||||
},
|
||||
payment_method_update: {
|
||||
enabled: true
|
||||
},
|
||||
subscription_cancel: {
|
||||
enabled: false
|
||||
}
|
||||
/*subscription_update: {
|
||||
enabled: false,
|
||||
default_allowed_updates: ['price'],
|
||||
products: [
|
||||
{
|
||||
product: prodPriceIds[UserPlan.FREE].product,
|
||||
prices: [prodPriceIds[UserPlan.FREE].price]
|
||||
},
|
||||
{
|
||||
product: prodPriceIds[UserPlan.STARTER].product,
|
||||
prices: [prodPriceIds[UserPlan.STARTER].price]
|
||||
},
|
||||
{
|
||||
product: prodPriceIds[UserPlan.PRO].product,
|
||||
prices: [prodPriceIds[UserPlan.PRO].price]
|
||||
}
|
||||
],
|
||||
proration_behavior: 'always_invoice'
|
||||
}*/
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public async getAdditionalSeatsQuantity(subscriptionId: string): Promise<{ quantity: number; includedSeats: number }> {
|
||||
if (!this.stripe) {
|
||||
throw new Error('Stripe is not initialized')
|
||||
}
|
||||
|
||||
try {
|
||||
const subscription = await this.stripe.subscriptions.retrieve(subscriptionId)
|
||||
const additionalSeatsItem = subscription.items.data.find(
|
||||
(item) => (item.price.product as string) === process.env.ADDITIONAL_SEAT_ID
|
||||
)
|
||||
const quotas = await this.cacheManager.getQuotas(subscriptionId)
|
||||
|
||||
return { quantity: additionalSeatsItem?.quantity || 0, includedSeats: quotas[LICENSE_QUOTAS.USERS_LIMIT] }
|
||||
} catch (error) {
|
||||
console.error('Error getting additional seats quantity:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async getCustomerWithDefaultSource(customerId: string) {
|
||||
if (!this.stripe) {
|
||||
throw new Error('Stripe is not initialized')
|
||||
}
|
||||
|
||||
try {
|
||||
const customer = (await this.stripe.customers.retrieve(customerId, {
|
||||
expand: ['default_source', 'invoice_settings.default_payment_method']
|
||||
})) as Stripe.Customer
|
||||
|
||||
return customer
|
||||
} catch (error) {
|
||||
console.error('Error retrieving customer with default source:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async getAdditionalSeatsProration(subscriptionId: string, quantity: number) {
|
||||
if (!this.stripe) {
|
||||
throw new Error('Stripe is not initialized')
|
||||
}
|
||||
|
||||
try {
|
||||
const subscription = await this.stripe.subscriptions.retrieve(subscriptionId)
|
||||
|
||||
// Get customer's credit balance
|
||||
const customer = await this.stripe.customers.retrieve(subscription.customer as string)
|
||||
const creditBalance = (customer as Stripe.Customer).balance // Balance is in cents, negative for credit, positive for amount owed
|
||||
|
||||
// Get the current subscription's base price (without seats)
|
||||
const basePlanItem = subscription.items.data.find((item) => (item.price.product as string) !== process.env.ADDITIONAL_SEAT_ID)
|
||||
const basePlanAmount = basePlanItem ? basePlanItem.price.unit_amount! * 1 : 0
|
||||
|
||||
const existingInvoice = await this.stripe.invoices.retrieveUpcoming({
|
||||
customer: subscription.customer as string,
|
||||
subscription: subscriptionId
|
||||
})
|
||||
|
||||
const existingInvoiceTotal = existingInvoice.total
|
||||
|
||||
// Get the price ID for additional seats
|
||||
const prices = await this.stripe.prices.list({
|
||||
product: process.env.ADDITIONAL_SEAT_ID,
|
||||
active: true,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (prices.data.length === 0) {
|
||||
throw new Error('No active price found for additional seats')
|
||||
}
|
||||
|
||||
const seatPrice = prices.data[0]
|
||||
const pricePerSeat = seatPrice.unit_amount || 0
|
||||
|
||||
// Use current timestamp for proration calculation
|
||||
const prorationDate = Math.floor(Date.now() / 1000)
|
||||
|
||||
const additionalSeatsItem = subscription.items.data.find(
|
||||
(item) => (item.price.product as string) === process.env.ADDITIONAL_SEAT_ID
|
||||
)
|
||||
|
||||
const upcomingInvoice = await this.stripe.invoices.retrieveUpcoming({
|
||||
customer: subscription.customer as string,
|
||||
subscription: subscriptionId,
|
||||
subscription_details: {
|
||||
proration_behavior: 'always_invoice',
|
||||
proration_date: prorationDate,
|
||||
items: [
|
||||
additionalSeatsItem
|
||||
? {
|
||||
id: additionalSeatsItem.id,
|
||||
quantity: quantity
|
||||
}
|
||||
: {
|
||||
// If the item doesn't exist yet, create a new one
|
||||
// This will be used to calculate the proration amount
|
||||
price: prices.data[0].id,
|
||||
quantity: quantity
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// Calculate proration amount from the relevant line items
|
||||
// Only consider prorations that match our proration date
|
||||
const prorationLineItems = upcomingInvoice.lines.data.filter(
|
||||
(line) => line.type === 'invoiceitem' && line.period.start === prorationDate
|
||||
)
|
||||
|
||||
const prorationAmount = prorationLineItems.reduce((total, item) => total + item.amount, 0)
|
||||
|
||||
return {
|
||||
basePlanAmount: basePlanAmount / 100,
|
||||
additionalSeatsProratedAmount: (existingInvoiceTotal + prorationAmount - basePlanAmount) / 100,
|
||||
seatPerUnitPrice: pricePerSeat / 100,
|
||||
prorationAmount: prorationAmount / 100,
|
||||
creditBalance: creditBalance / 100,
|
||||
nextInvoiceTotal: (existingInvoiceTotal + prorationAmount) / 100,
|
||||
currency: upcomingInvoice.currency.toUpperCase(),
|
||||
prorationDate,
|
||||
currentPeriodStart: subscription.current_period_start,
|
||||
currentPeriodEnd: subscription.current_period_end
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error calculating additional seats proration:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async updateAdditionalSeats(subscriptionId: string, quantity: number, prorationDate: number) {
|
||||
if (!this.stripe) {
|
||||
throw new Error('Stripe is not initialized')
|
||||
}
|
||||
|
||||
try {
|
||||
const subscription = await this.stripe.subscriptions.retrieve(subscriptionId)
|
||||
const additionalSeatsItem = subscription.items.data.find(
|
||||
(item) => (item.price.product as string) === process.env.ADDITIONAL_SEAT_ID
|
||||
)
|
||||
|
||||
// Get the price ID for additional seats if needed
|
||||
const prices = await this.stripe.prices.list({
|
||||
product: process.env.ADDITIONAL_SEAT_ID,
|
||||
active: true,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (prices.data.length === 0) {
|
||||
throw new Error('No active price found for additional seats')
|
||||
}
|
||||
|
||||
// Create an invoice immediately for the proration
|
||||
const updatedSubscription = await this.stripe.subscriptions.update(subscriptionId, {
|
||||
items: [
|
||||
additionalSeatsItem
|
||||
? {
|
||||
id: additionalSeatsItem.id,
|
||||
quantity: quantity
|
||||
}
|
||||
: {
|
||||
price: prices.data[0].id,
|
||||
quantity: quantity
|
||||
}
|
||||
],
|
||||
proration_behavior: 'always_invoice',
|
||||
proration_date: prorationDate
|
||||
})
|
||||
|
||||
// Get the latest invoice for this subscription
|
||||
const invoice = await this.stripe.invoices.list({
|
||||
subscription: subscriptionId,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (invoice.data.length > 0) {
|
||||
const latestInvoice = invoice.data[0]
|
||||
// Only try to pay if the invoice is not already paid
|
||||
if (latestInvoice.status !== 'paid') {
|
||||
await this.stripe.invoices.pay(latestInvoice.id)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
subscription: updatedSubscription,
|
||||
invoice: invoice.data[0]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating additional seats:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async getPlanProration(subscriptionId: string, newPlanId: string) {
|
||||
if (!this.stripe) {
|
||||
throw new Error('Stripe is not initialized')
|
||||
}
|
||||
|
||||
try {
|
||||
const subscription = await this.stripe.subscriptions.retrieve(subscriptionId)
|
||||
const customerId = subscription.customer as string
|
||||
|
||||
// Get customer's credit balance and metadata
|
||||
const customer = await this.stripe.customers.retrieve(customerId)
|
||||
const creditBalance = (customer as Stripe.Customer).balance
|
||||
const customerMetadata = (customer as Stripe.Customer).metadata || {}
|
||||
|
||||
// Get the price ID for the new plan
|
||||
const prices = await this.stripe.prices.list({
|
||||
product: newPlanId,
|
||||
active: true,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (prices.data.length === 0) {
|
||||
throw new Error('No active price found for the selected plan')
|
||||
}
|
||||
|
||||
const newPlan = prices.data[0]
|
||||
const newPlanPrice = newPlan.unit_amount || 0
|
||||
|
||||
// Check if this is the STARTER plan and eligible for first month free
|
||||
const isStarterPlan = newPlanId === process.env.CLOUD_STARTER_ID
|
||||
const hasUsedFirstMonthFreeCoupon = customerMetadata.has_used_first_month_free === 'true'
|
||||
const eligibleForFirstMonthFree = isStarterPlan && !hasUsedFirstMonthFreeCoupon
|
||||
|
||||
// Use current timestamp for proration calculation
|
||||
const prorationDate = Math.floor(Date.now() / 1000)
|
||||
|
||||
const upcomingInvoice = await this.stripe.invoices.retrieveUpcoming({
|
||||
customer: customerId,
|
||||
subscription: subscriptionId,
|
||||
subscription_details: {
|
||||
proration_behavior: 'always_invoice',
|
||||
proration_date: prorationDate,
|
||||
items: [
|
||||
{
|
||||
id: subscription.items.data[0].id,
|
||||
price: newPlan.id
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
let prorationAmount = upcomingInvoice.lines.data.reduce((total, item) => total + item.amount, 0)
|
||||
if (eligibleForFirstMonthFree) {
|
||||
prorationAmount = 0
|
||||
}
|
||||
|
||||
return {
|
||||
newPlanAmount: newPlanPrice / 100,
|
||||
prorationAmount: prorationAmount / 100,
|
||||
creditBalance: creditBalance / 100,
|
||||
currency: upcomingInvoice.currency.toUpperCase(),
|
||||
prorationDate,
|
||||
currentPeriodStart: subscription.current_period_start,
|
||||
currentPeriodEnd: subscription.current_period_end,
|
||||
eligibleForFirstMonthFree
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error calculating plan proration:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async updateSubscriptionPlan(subscriptionId: string, newPlanId: string, prorationDate: number) {
|
||||
if (!this.stripe) {
|
||||
throw new Error('Stripe is not initialized')
|
||||
}
|
||||
|
||||
try {
|
||||
const subscription = await this.stripe.subscriptions.retrieve(subscriptionId)
|
||||
const customerId = subscription.customer as string
|
||||
|
||||
// Get customer details and metadata
|
||||
const customer = await this.stripe.customers.retrieve(customerId)
|
||||
const customerMetadata = (customer as Stripe.Customer).metadata || {}
|
||||
|
||||
// Get the price ID for the new plan
|
||||
const prices = await this.stripe.prices.list({
|
||||
product: newPlanId,
|
||||
active: true,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (prices.data.length === 0) {
|
||||
throw new Error('No active price found for the selected plan')
|
||||
}
|
||||
|
||||
const newPlan = prices.data[0]
|
||||
let updatedSubscription: Stripe.Response<Stripe.Subscription>
|
||||
|
||||
// Check if this is an upgrade to CLOUD_STARTER_ID and eligible for first month free
|
||||
const isStarterPlan = newPlanId === process.env.CLOUD_STARTER_ID
|
||||
const hasUsedFirstMonthFreeCoupon = customerMetadata.has_used_first_month_free === 'true'
|
||||
|
||||
if (isStarterPlan && !hasUsedFirstMonthFreeCoupon) {
|
||||
// Create the one-time 100% off coupon
|
||||
const coupon = await this.stripe.coupons.create({
|
||||
duration: 'once',
|
||||
percent_off: 100,
|
||||
max_redemptions: 1,
|
||||
metadata: {
|
||||
type: 'first_month_free',
|
||||
customer_id: customerId,
|
||||
plan_id: process.env.CLOUD_STARTER_ID || ''
|
||||
}
|
||||
})
|
||||
|
||||
// Create a promotion code linked to the coupon
|
||||
const promotionCode = await this.stripe.promotionCodes.create({
|
||||
coupon: coupon.id,
|
||||
max_redemptions: 1
|
||||
})
|
||||
|
||||
// Update the subscription with the new plan and apply the promotion code
|
||||
updatedSubscription = await this.stripe.subscriptions.update(subscriptionId, {
|
||||
items: [
|
||||
{
|
||||
id: subscription.items.data[0].id,
|
||||
price: newPlan.id
|
||||
}
|
||||
],
|
||||
proration_behavior: 'always_invoice',
|
||||
proration_date: prorationDate,
|
||||
promotion_code: promotionCode.id
|
||||
})
|
||||
|
||||
// Update customer metadata to mark the coupon as used
|
||||
await this.stripe.customers.update(customerId, {
|
||||
metadata: {
|
||||
...customerMetadata,
|
||||
has_used_first_month_free: 'true',
|
||||
first_month_free_date: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Regular plan update without coupon
|
||||
updatedSubscription = await this.stripe.subscriptions.update(subscriptionId, {
|
||||
items: [
|
||||
{
|
||||
id: subscription.items.data[0].id,
|
||||
price: newPlan.id
|
||||
}
|
||||
],
|
||||
proration_behavior: 'always_invoice',
|
||||
proration_date: prorationDate
|
||||
})
|
||||
}
|
||||
|
||||
// Get and pay the latest invoice
|
||||
const invoice = await this.stripe.invoices.list({
|
||||
subscription: subscriptionId,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (invoice.data.length > 0) {
|
||||
const latestInvoice = invoice.data[0]
|
||||
if (latestInvoice.status !== 'paid') {
|
||||
await this.stripe.invoices.pay(latestInvoice.id)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
subscription: updatedSubscription,
|
||||
invoice: invoice.data[0]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating subscription plan:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
import { Keyv } from 'keyv'
|
||||
import KeyvRedis from '@keyv/redis'
|
||||
import { Cache, createCache } from 'cache-manager'
|
||||
import { MODE } from './Interface'
|
||||
import { LICENSE_QUOTAS } from './utils/constants'
|
||||
import { StripeManager } from './StripeManager'
|
||||
|
||||
const DISABLED_QUOTAS = {
|
||||
[LICENSE_QUOTAS.PREDICTIONS_LIMIT]: 0,
|
||||
[LICENSE_QUOTAS.STORAGE_LIMIT]: 0, // in MB
|
||||
[LICENSE_QUOTAS.FLOWS_LIMIT]: 0,
|
||||
[LICENSE_QUOTAS.USERS_LIMIT]: 0,
|
||||
[LICENSE_QUOTAS.ADDITIONAL_SEATS_LIMIT]: 0
|
||||
}
|
||||
|
||||
const UNLIMITED_QUOTAS = {
|
||||
[LICENSE_QUOTAS.PREDICTIONS_LIMIT]: -1,
|
||||
[LICENSE_QUOTAS.STORAGE_LIMIT]: -1,
|
||||
[LICENSE_QUOTAS.FLOWS_LIMIT]: -1,
|
||||
[LICENSE_QUOTAS.USERS_LIMIT]: -1,
|
||||
[LICENSE_QUOTAS.ADDITIONAL_SEATS_LIMIT]: -1
|
||||
}
|
||||
|
||||
export class UsageCacheManager {
|
||||
private cache: Cache
|
||||
private static instance: UsageCacheManager
|
||||
|
||||
public static async getInstance(): Promise<UsageCacheManager> {
|
||||
if (!UsageCacheManager.instance) {
|
||||
UsageCacheManager.instance = new UsageCacheManager()
|
||||
await UsageCacheManager.instance.initialize()
|
||||
}
|
||||
return UsageCacheManager.instance
|
||||
}
|
||||
|
||||
private async initialize(): Promise<void> {
|
||||
if (process.env.MODE === MODE.QUEUE) {
|
||||
let redisConfig: string | Record<string, any>
|
||||
if (process.env.REDIS_URL) {
|
||||
redisConfig = process.env.REDIS_URL
|
||||
} else {
|
||||
redisConfig = {
|
||||
username: process.env.REDIS_USERNAME || undefined,
|
||||
password: process.env.REDIS_PASSWORD || undefined,
|
||||
socket: {
|
||||
host: process.env.REDIS_HOST || 'localhost',
|
||||
port: parseInt(process.env.REDIS_PORT || '6379'),
|
||||
tls: process.env.REDIS_TLS === 'true',
|
||||
cert: process.env.REDIS_CERT ? Buffer.from(process.env.REDIS_CERT, 'base64') : undefined,
|
||||
key: process.env.REDIS_KEY ? Buffer.from(process.env.REDIS_KEY, 'base64') : undefined,
|
||||
ca: process.env.REDIS_CA ? Buffer.from(process.env.REDIS_CA, 'base64') : undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cache = createCache({
|
||||
stores: [
|
||||
new Keyv({
|
||||
store: new KeyvRedis(redisConfig)
|
||||
})
|
||||
]
|
||||
})
|
||||
} else {
|
||||
this.cache = createCache()
|
||||
}
|
||||
}
|
||||
|
||||
public async getSubscriptionDetails(subscriptionId: string, withoutCache: boolean = false): Promise<Record<string, any>> {
|
||||
const stripeManager = await StripeManager.getInstance()
|
||||
if (!stripeManager || !subscriptionId) {
|
||||
return UNLIMITED_QUOTAS
|
||||
}
|
||||
|
||||
// Skip cache if withoutCache is true
|
||||
if (!withoutCache) {
|
||||
const subscriptionData = await this.getSubscriptionDataFromCache(subscriptionId)
|
||||
if (subscriptionData?.subsriptionDetails) {
|
||||
return subscriptionData.subsriptionDetails
|
||||
}
|
||||
}
|
||||
|
||||
// If not in cache, retrieve from Stripe
|
||||
const subscription = await stripeManager.getStripe().subscriptions.retrieve(subscriptionId)
|
||||
|
||||
// Update subscription data cache
|
||||
await this.updateSubscriptionDataToCache(subscriptionId, { subsriptionDetails: stripeManager.getSubscriptionObject(subscription) })
|
||||
|
||||
return stripeManager.getSubscriptionObject(subscription)
|
||||
}
|
||||
|
||||
public async getQuotas(subscriptionId: string, withoutCache: boolean = false): Promise<Record<string, number>> {
|
||||
const stripeManager = await StripeManager.getInstance()
|
||||
if (!stripeManager || !subscriptionId) {
|
||||
return UNLIMITED_QUOTAS
|
||||
}
|
||||
|
||||
// Skip cache if withoutCache is true
|
||||
if (!withoutCache) {
|
||||
const subscriptionData = await this.getSubscriptionDataFromCache(subscriptionId)
|
||||
if (subscriptionData?.quotas) {
|
||||
return subscriptionData.quotas
|
||||
}
|
||||
}
|
||||
|
||||
// If not in cache, retrieve from Stripe
|
||||
const subscription = await stripeManager.getStripe().subscriptions.retrieve(subscriptionId)
|
||||
const items = subscription.items.data
|
||||
if (items.length === 0) {
|
||||
return DISABLED_QUOTAS
|
||||
}
|
||||
|
||||
const productId = items[0].price.product as string
|
||||
const product = await stripeManager.getStripe().products.retrieve(productId)
|
||||
const productMetadata = product.metadata
|
||||
|
||||
if (!productMetadata || Object.keys(productMetadata).length === 0) {
|
||||
return DISABLED_QUOTAS
|
||||
}
|
||||
|
||||
const quotas: Record<string, number> = {}
|
||||
for (const key in productMetadata) {
|
||||
if (key.startsWith('quota:')) {
|
||||
quotas[key] = parseInt(productMetadata[key])
|
||||
}
|
||||
}
|
||||
|
||||
const additionalSeatsItem = subscription.items.data.find(
|
||||
(item) => (item.price.product as string) === process.env.ADDITIONAL_SEAT_ID
|
||||
)
|
||||
quotas[LICENSE_QUOTAS.ADDITIONAL_SEATS_LIMIT] = additionalSeatsItem?.quantity || 0
|
||||
|
||||
// Update subscription data cache with quotas
|
||||
await this.updateSubscriptionDataToCache(subscriptionId, {
|
||||
quotas,
|
||||
subsriptionDetails: stripeManager.getSubscriptionObject(subscription)
|
||||
})
|
||||
|
||||
return quotas
|
||||
}
|
||||
|
||||
public async getSubscriptionDataFromCache(subscriptionId: string) {
|
||||
const cacheKey = `subscription:${subscriptionId}`
|
||||
return await this.get<{
|
||||
quotas?: Record<string, number>
|
||||
productId?: string
|
||||
features?: Record<string, string>
|
||||
subsriptionDetails?: Record<string, any>
|
||||
}>(cacheKey)
|
||||
}
|
||||
|
||||
public async updateSubscriptionDataToCache(
|
||||
subscriptionId: string,
|
||||
data: Partial<{
|
||||
quotas: Record<string, number>
|
||||
productId: string
|
||||
features: Record<string, string>
|
||||
subsriptionDetails: Record<string, any>
|
||||
}>
|
||||
) {
|
||||
const cacheKey = `subscription:${subscriptionId}`
|
||||
const existingData = (await this.getSubscriptionDataFromCache(subscriptionId)) || {}
|
||||
const updatedData = { ...existingData, ...data }
|
||||
this.set(cacheKey, updatedData, 3600000) // Cache for 1 hour
|
||||
}
|
||||
|
||||
public async get<T>(key: string): Promise<T | null> {
|
||||
if (!this.cache) await this.initialize()
|
||||
const value = await this.cache.get<T>(key)
|
||||
return value
|
||||
}
|
||||
|
||||
public async getTTL(key: string): Promise<number | null> {
|
||||
if (!this.cache) await this.initialize()
|
||||
const value = await this.cache.ttl(key)
|
||||
return value
|
||||
}
|
||||
|
||||
public async mget<T>(keys: string[]): Promise<(T | null)[]> {
|
||||
if (this.cache) {
|
||||
const values = await this.cache.mget<T>(keys)
|
||||
return values
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public set<T>(key: string, value: T, ttl?: number) {
|
||||
if (this.cache) {
|
||||
this.cache.set(key, value, ttl)
|
||||
}
|
||||
}
|
||||
|
||||
public mset<T>(keys: [{ key: string; value: T; ttl: number }]) {
|
||||
if (this.cache) {
|
||||
this.cache.mset(keys)
|
||||
}
|
||||
}
|
||||
|
||||
public async del(key: string): Promise<void> {
|
||||
await this.cache.del(key)
|
||||
}
|
||||
|
||||
public async mdel(keys: string[]): Promise<void> {
|
||||
await this.cache.mdel(keys)
|
||||
}
|
||||
|
||||
public async clear(): Promise<void> {
|
||||
await this.cache.clear()
|
||||
}
|
||||
|
||||
public async wrap<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T> {
|
||||
return this.cache.wrap(key, fn, ttl)
|
||||
}
|
||||
}
|
||||
@@ -12,16 +12,12 @@ enum EXIT_CODE {
|
||||
|
||||
export abstract class BaseCommand extends Command {
|
||||
static flags = {
|
||||
FLOWISE_USERNAME: Flags.string(),
|
||||
FLOWISE_PASSWORD: Flags.string(),
|
||||
FLOWISE_FILE_SIZE_LIMIT: Flags.string(),
|
||||
PORT: Flags.string(),
|
||||
CORS_ORIGINS: Flags.string(),
|
||||
IFRAME_ORIGINS: Flags.string(),
|
||||
DEBUG: Flags.string(),
|
||||
BLOB_STORAGE_PATH: Flags.string(),
|
||||
APIKEY_STORAGE_TYPE: Flags.string(),
|
||||
APIKEY_PATH: Flags.string(),
|
||||
LOG_PATH: Flags.string(),
|
||||
LOG_LEVEL: Flags.string(),
|
||||
TOOL_FUNCTION_BUILTIN_DEP: Flags.string(),
|
||||
@@ -59,6 +55,7 @@ export abstract class BaseCommand extends Command {
|
||||
SECRETKEY_AWS_ACCESS_KEY: Flags.string(),
|
||||
SECRETKEY_AWS_SECRET_KEY: Flags.string(),
|
||||
SECRETKEY_AWS_REGION: Flags.string(),
|
||||
SECRETKEY_AWS_NAME: Flags.string(),
|
||||
DISABLED_NODES: Flags.string(),
|
||||
MODE: Flags.string(),
|
||||
WORKER_CONCURRENCY: Flags.string(),
|
||||
@@ -131,14 +128,6 @@ export abstract class BaseCommand extends Command {
|
||||
if (flags.NUMBER_OF_PROXIES) process.env.NUMBER_OF_PROXIES = flags.NUMBER_OF_PROXIES
|
||||
if (flags.SHOW_COMMUNITY_NODES) process.env.SHOW_COMMUNITY_NODES = flags.SHOW_COMMUNITY_NODES
|
||||
if (flags.DISABLED_NODES) process.env.DISABLED_NODES = flags.DISABLED_NODES
|
||||
|
||||
// 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
|
||||
if (flags.FLOWISE_FILE_SIZE_LIMIT) process.env.FLOWISE_FILE_SIZE_LIMIT = flags.FLOWISE_FILE_SIZE_LIMIT
|
||||
|
||||
// Credentials
|
||||
@@ -148,6 +137,7 @@ export abstract class BaseCommand extends Command {
|
||||
if (flags.SECRETKEY_AWS_ACCESS_KEY) process.env.SECRETKEY_AWS_ACCESS_KEY = flags.SECRETKEY_AWS_ACCESS_KEY
|
||||
if (flags.SECRETKEY_AWS_SECRET_KEY) process.env.SECRETKEY_AWS_SECRET_KEY = flags.SECRETKEY_AWS_SECRET_KEY
|
||||
if (flags.SECRETKEY_AWS_REGION) process.env.SECRETKEY_AWS_REGION = flags.SECRETKEY_AWS_REGION
|
||||
if (flags.SECRETKEY_AWS_NAME) process.env.SECRETKEY_AWS_NAME = flags.SECRETKEY_AWS_NAME
|
||||
|
||||
// Logs
|
||||
if (flags.LOG_PATH) process.env.LOG_PATH = flags.LOG_PATH
|
||||
|
||||
@@ -7,6 +7,7 @@ import { NodesPool } from '../NodesPool'
|
||||
import { CachePool } from '../CachePool'
|
||||
import { QueueEvents, QueueEventsListener } from 'bullmq'
|
||||
import { AbortControllerPool } from '../AbortControllerPool'
|
||||
import { UsageCacheManager } from '../UsageCacheManager'
|
||||
|
||||
interface CustomListener extends QueueEventsListener {
|
||||
abort: (args: { id: string }, id: string) => void
|
||||
@@ -19,7 +20,7 @@ export default class Worker extends BaseCommand {
|
||||
async run(): Promise<void> {
|
||||
logger.info('Starting Flowise Worker...')
|
||||
|
||||
const { appDataSource, telemetry, componentNodes, cachePool, abortControllerPool } = await this.prepareData()
|
||||
const { appDataSource, telemetry, componentNodes, cachePool, abortControllerPool, usageCacheManager } = await this.prepareData()
|
||||
|
||||
const queueManager = QueueManager.getInstance()
|
||||
queueManager.setupAllQueues({
|
||||
@@ -27,7 +28,8 @@ export default class Worker extends BaseCommand {
|
||||
telemetry,
|
||||
cachePool,
|
||||
appDataSource,
|
||||
abortControllerPool
|
||||
abortControllerPool,
|
||||
usageCacheManager
|
||||
})
|
||||
|
||||
/** Prediction */
|
||||
@@ -72,7 +74,10 @@ export default class Worker extends BaseCommand {
|
||||
// Initialize cache pool
|
||||
const cachePool = new CachePool()
|
||||
|
||||
return { appDataSource, telemetry, componentNodes: nodesPool.componentNodes, cachePool, abortControllerPool }
|
||||
// Initialize usage cache manager
|
||||
const usageCacheManager = await UsageCacheManager.getInstance()
|
||||
|
||||
return { appDataSource, telemetry, componentNodes: nodesPool.componentNodes, cachePool, abortControllerPool, usageCacheManager }
|
||||
}
|
||||
|
||||
async catch(error: Error) {
|
||||
|
||||
@@ -6,7 +6,8 @@ import apikeyService from '../../services/apikey'
|
||||
// Get api keys
|
||||
const getAllApiKeys = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await apikeyService.getAllApiKeys()
|
||||
const autoCreateNewKey = true
|
||||
const apiResponse = await apikeyService.getAllApiKeys(req.user?.activeWorkspaceId, autoCreateNewKey)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -18,7 +19,7 @@ const createApiKey = async (req: Request, res: Response, next: NextFunction) =>
|
||||
if (typeof req.body === 'undefined' || !req.body.keyName) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.createApiKey - keyName not provided!`)
|
||||
}
|
||||
const apiResponse = await apikeyService.createApiKey(req.body.keyName)
|
||||
const apiResponse = await apikeyService.createApiKey(req.body.keyName, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -34,7 +35,7 @@ const updateApiKey = async (req: Request, res: Response, next: NextFunction) =>
|
||||
if (typeof req.body === 'undefined' || !req.body.keyName) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.updateApiKey - keyName not provided!`)
|
||||
}
|
||||
const apiResponse = await apikeyService.updateApiKey(req.params.id, req.body.keyName)
|
||||
const apiResponse = await apikeyService.updateApiKey(req.params.id, req.body.keyName, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -47,6 +48,7 @@ const importKeys = async (req: Request, res: Response, next: NextFunction) => {
|
||||
if (typeof req.body === 'undefined' || !req.body.jsonFile) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.importKeys - body not provided!`)
|
||||
}
|
||||
req.body.workspaceId = req.user?.activeWorkspaceId
|
||||
const apiResponse = await apikeyService.importKeys(req.body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
@@ -60,7 +62,7 @@ const deleteApiKey = async (req: Request, res: Response, next: NextFunction) =>
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.deleteApiKey - id not provided!`)
|
||||
}
|
||||
const apiResponse = await apikeyService.deleteApiKey(req.params.id)
|
||||
const apiResponse = await apikeyService.deleteApiKey(req.params.id, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import assistantsService from '../../services/assistants'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { AssistantType } from '../../Interface'
|
||||
import assistantsService from '../../services/assistants'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { checkUsageLimit } from '../../utils/quotaUsage'
|
||||
|
||||
const createAssistant = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
@@ -12,7 +14,30 @@ const createAssistant = async (req: Request, res: Response, next: NextFunction)
|
||||
`Error: assistantsController.createAssistant - body not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await assistantsService.createAssistant(req.body)
|
||||
const body = req.body
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: assistantsController.createAssistant - organization ${orgId} not found!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: assistantsController.createAssistant - workspace ${workspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
|
||||
const existingAssistantCount = await assistantsService.getAssistantsCountByOrganization(body.type, orgId)
|
||||
const newAssistantCount = 1
|
||||
await checkUsageLimit('flows', subscriptionId, getRunningExpressApp().usageCacheManager, existingAssistantCount + newAssistantCount)
|
||||
|
||||
body.workspaceId = workspaceId
|
||||
const apiResponse = await assistantsService.createAssistant(body, orgId)
|
||||
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -37,7 +62,7 @@ const deleteAssistant = async (req: Request, res: Response, next: NextFunction)
|
||||
const getAllAssistants = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const type = req.query.type as AssistantType
|
||||
const apiResponse = await assistantsService.getAllAssistants(type)
|
||||
const apiResponse = await assistantsService.getAllAssistants(type, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -91,7 +116,7 @@ const getChatModels = async (req: Request, res: Response, next: NextFunction) =>
|
||||
|
||||
const getDocumentStores = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await assistantsService.getDocumentStores()
|
||||
const apiResponse = await assistantsService.getDocumentStores(req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -62,6 +62,7 @@ const getAllChatMessages = async (req: Request, res: Response, next: NextFunctio
|
||||
chatTypes = [_chatTypes as ChatType]
|
||||
}
|
||||
}
|
||||
const activeWorkspaceId = req.user?.activeWorkspaceId
|
||||
const sortOrder = req.query?.order as string | undefined
|
||||
const chatId = req.query?.chatId as string | undefined
|
||||
const memoryType = req.query?.memoryType as string | undefined
|
||||
@@ -91,9 +92,9 @@ const getAllChatMessages = async (req: Request, res: Response, next: NextFunctio
|
||||
endDate,
|
||||
messageId,
|
||||
feedback,
|
||||
feedbackTypeFilters
|
||||
feedbackTypeFilters,
|
||||
activeWorkspaceId
|
||||
)
|
||||
|
||||
return res.json(parseAPIResponse(apiResponse))
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -102,6 +103,7 @@ const getAllChatMessages = async (req: Request, res: Response, next: NextFunctio
|
||||
|
||||
const getAllInternalChatMessages = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const activeWorkspaceId = req.user?.activeWorkspaceId
|
||||
const sortOrder = req.query?.order as string | undefined
|
||||
const chatId = req.query?.chatId as string | undefined
|
||||
const memoryType = req.query?.memoryType as string | undefined
|
||||
@@ -125,7 +127,8 @@ const getAllInternalChatMessages = async (req: Request, res: Response, next: Nex
|
||||
endDate,
|
||||
messageId,
|
||||
feedback,
|
||||
feedbackTypeFilters
|
||||
feedbackTypeFilters,
|
||||
activeWorkspaceId
|
||||
)
|
||||
return res.json(parseAPIResponse(apiResponse))
|
||||
} catch (error) {
|
||||
@@ -142,6 +145,20 @@ const removeAllChatMessages = async (req: Request, res: Response, next: NextFunc
|
||||
'Error: chatMessagesController.removeAllChatMessages - id not provided!'
|
||||
)
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatMessagesController.removeAllChatMessages - organization ${orgId} not found!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatMessagesController.removeAllChatMessages - workspace ${workspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const chatflowid = req.params.id
|
||||
const chatflow = await chatflowsService.getChatflowById(req.params.id)
|
||||
if (!chatflow) {
|
||||
@@ -177,6 +194,7 @@ const removeAllChatMessages = async (req: Request, res: Response, next: NextFunc
|
||||
if (!chatId) {
|
||||
const isFeedback = feedbackTypeFilters?.length ? true : false
|
||||
const hardDelete = req.query?.hardDelete as boolean | undefined
|
||||
|
||||
const messages = await utilGetChatMessage({
|
||||
chatflowid,
|
||||
chatTypes,
|
||||
@@ -216,6 +234,7 @@ const removeAllChatMessages = async (req: Request, res: Response, next: NextFunc
|
||||
appServer.nodesPool.componentNodes,
|
||||
chatId,
|
||||
appServer.AppDataSource,
|
||||
orgId,
|
||||
sessionId,
|
||||
memoryType,
|
||||
isClearFromViewMessageDialog
|
||||
@@ -226,7 +245,14 @@ const removeAllChatMessages = async (req: Request, res: Response, next: NextFunc
|
||||
}
|
||||
}
|
||||
|
||||
const apiResponse = await chatMessagesService.removeChatMessagesByMessageIds(chatflowid, chatIdMap, messageIds)
|
||||
const apiResponse = await chatMessagesService.removeChatMessagesByMessageIds(
|
||||
chatflowid,
|
||||
chatIdMap,
|
||||
messageIds,
|
||||
orgId,
|
||||
workspaceId,
|
||||
appServer.usageCacheManager
|
||||
)
|
||||
return res.json(apiResponse)
|
||||
} else {
|
||||
try {
|
||||
@@ -235,6 +261,7 @@ const removeAllChatMessages = async (req: Request, res: Response, next: NextFunc
|
||||
appServer.nodesPool.componentNodes,
|
||||
chatId,
|
||||
appServer.AppDataSource,
|
||||
orgId,
|
||||
sessionId,
|
||||
memoryType,
|
||||
isClearFromViewMessageDialog
|
||||
@@ -255,7 +282,14 @@ const removeAllChatMessages = async (req: Request, res: Response, next: NextFunc
|
||||
const toDate = new Date(endDate)
|
||||
deleteOptions.createdDate = Between(fromDate ?? aMonthAgo(), toDate ?? new Date())
|
||||
}
|
||||
const apiResponse = await chatMessagesService.removeAllChatMessages(chatId, chatflowid, deleteOptions)
|
||||
const apiResponse = await chatMessagesService.removeAllChatMessages(
|
||||
chatId,
|
||||
chatflowid,
|
||||
deleteOptions,
|
||||
orgId,
|
||||
workspaceId,
|
||||
appServer.usageCacheManager
|
||||
)
|
||||
return res.json(apiResponse)
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -282,26 +316,30 @@ const parseAPIResponse = (apiResponse: ChatMessage | ChatMessage[]): ChatMessage
|
||||
const parseResponse = (response: ChatMessage): ChatMessage => {
|
||||
const parsedResponse = { ...response }
|
||||
|
||||
if (parsedResponse.sourceDocuments) {
|
||||
parsedResponse.sourceDocuments = JSON.parse(parsedResponse.sourceDocuments)
|
||||
}
|
||||
if (parsedResponse.usedTools) {
|
||||
parsedResponse.usedTools = JSON.parse(parsedResponse.usedTools)
|
||||
}
|
||||
if (parsedResponse.fileAnnotations) {
|
||||
parsedResponse.fileAnnotations = JSON.parse(parsedResponse.fileAnnotations)
|
||||
}
|
||||
if (parsedResponse.agentReasoning) {
|
||||
parsedResponse.agentReasoning = JSON.parse(parsedResponse.agentReasoning)
|
||||
}
|
||||
if (parsedResponse.fileUploads) {
|
||||
parsedResponse.fileUploads = JSON.parse(parsedResponse.fileUploads)
|
||||
}
|
||||
if (parsedResponse.action) {
|
||||
parsedResponse.action = JSON.parse(parsedResponse.action)
|
||||
}
|
||||
if (parsedResponse.artifacts) {
|
||||
parsedResponse.artifacts = JSON.parse(parsedResponse.artifacts)
|
||||
try {
|
||||
if (parsedResponse.sourceDocuments) {
|
||||
parsedResponse.sourceDocuments = JSON.parse(parsedResponse.sourceDocuments)
|
||||
}
|
||||
if (parsedResponse.usedTools) {
|
||||
parsedResponse.usedTools = JSON.parse(parsedResponse.usedTools)
|
||||
}
|
||||
if (parsedResponse.fileAnnotations) {
|
||||
parsedResponse.fileAnnotations = JSON.parse(parsedResponse.fileAnnotations)
|
||||
}
|
||||
if (parsedResponse.agentReasoning) {
|
||||
parsedResponse.agentReasoning = JSON.parse(parsedResponse.agentReasoning)
|
||||
}
|
||||
if (parsedResponse.fileUploads) {
|
||||
parsedResponse.fileUploads = JSON.parse(parsedResponse.fileUploads)
|
||||
}
|
||||
if (parsedResponse.action) {
|
||||
parsedResponse.action = JSON.parse(parsedResponse.action)
|
||||
}
|
||||
if (parsedResponse.artifacts) {
|
||||
parsedResponse.artifacts = JSON.parse(parsedResponse.artifacts)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing chat message response', e)
|
||||
}
|
||||
|
||||
return parsedResponse
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import apiKeyService from '../../services/apikey'
|
||||
import { ChatFlow } from '../../database/entities/ChatFlow'
|
||||
import { RateLimiterManager } from '../../utils/rateLimit'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { ChatflowType } from '../../Interface'
|
||||
import apiKeyService from '../../services/apikey'
|
||||
import chatflowsService from '../../services/chatflows'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { checkUsageLimit } from '../../utils/quotaUsage'
|
||||
import { RateLimiterManager } from '../../utils/rateLimit'
|
||||
|
||||
const checkIfChatflowIsValidForStreaming = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: chatflowsRouter.checkIfChatflowIsValidForStreaming - id not provided!`
|
||||
`Error: chatflowsController.checkIfChatflowIsValidForStreaming - id not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await chatflowsService.checkIfChatflowIsValidForStreaming(req.params.id)
|
||||
@@ -27,7 +29,7 @@ const checkIfChatflowIsValidForUploads = async (req: Request, res: Response, nex
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: chatflowsRouter.checkIfChatflowIsValidForUploads - id not provided!`
|
||||
`Error: chatflowsController.checkIfChatflowIsValidForUploads - id not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await chatflowsService.checkIfChatflowIsValidForUploads(req.params.id)
|
||||
@@ -40,9 +42,23 @@ const checkIfChatflowIsValidForUploads = async (req: Request, res: Response, nex
|
||||
const deleteChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: chatflowsRouter.deleteChatflow - id not provided!`)
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: chatflowsController.deleteChatflow - id not provided!`)
|
||||
}
|
||||
const apiResponse = await chatflowsService.deleteChatflow(req.params.id)
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatflowsController.deleteChatflow - organization ${orgId} not found!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatflowsController.deleteChatflow - workspace ${workspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await chatflowsService.deleteChatflow(req.params.id, orgId, workspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -51,7 +67,7 @@ const deleteChatflow = async (req: Request, res: Response, next: NextFunction) =
|
||||
|
||||
const getAllChatflows = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await chatflowsService.getAllChatflows(req.query?.type as ChatflowType)
|
||||
const apiResponse = await chatflowsService.getAllChatflows(req.query?.type as ChatflowType, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -64,7 +80,7 @@ const getChatflowByApiKey = async (req: Request, res: Response, next: NextFuncti
|
||||
if (typeof req.params === 'undefined' || !req.params.apikey) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: chatflowsRouter.getChatflowByApiKey - apikey not provided!`
|
||||
`Error: chatflowsController.getChatflowByApiKey - apikey not provided!`
|
||||
)
|
||||
}
|
||||
const apikey = await apiKeyService.getApiKey(req.params.apikey)
|
||||
@@ -81,7 +97,7 @@ const getChatflowByApiKey = async (req: Request, res: Response, next: NextFuncti
|
||||
const getChatflowById = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: chatflowsRouter.getChatflowById - id not provided!`)
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: chatflowsController.getChatflowById - id not provided!`)
|
||||
}
|
||||
const apiResponse = await chatflowsService.getChatflowById(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
@@ -93,12 +109,40 @@ const getChatflowById = async (req: Request, res: Response, next: NextFunction)
|
||||
const saveChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: chatflowsRouter.saveChatflow - body not provided!`)
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: chatflowsController.saveChatflow - body not provided!`)
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatflowsController.saveChatflow - organization ${orgId} not found!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatflowsController.saveChatflow - workspace ${workspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
const body = req.body
|
||||
|
||||
const existingChatflowCount = await chatflowsService.getAllChatflowsCountByOrganization(body.type, orgId)
|
||||
const newChatflowCount = 1
|
||||
await checkUsageLimit('flows', subscriptionId, getRunningExpressApp().usageCacheManager, existingChatflowCount + newChatflowCount)
|
||||
|
||||
const newChatFlow = new ChatFlow()
|
||||
Object.assign(newChatFlow, body)
|
||||
const apiResponse = await chatflowsService.saveChatflow(newChatFlow)
|
||||
newChatFlow.workspaceId = workspaceId
|
||||
const apiResponse = await chatflowsService.saveChatflow(
|
||||
newChatFlow,
|
||||
orgId,
|
||||
workspaceId,
|
||||
subscriptionId,
|
||||
getRunningExpressApp().usageCacheManager
|
||||
)
|
||||
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -108,7 +152,23 @@ const saveChatflow = async (req: Request, res: Response, next: NextFunction) =>
|
||||
const importChatflows = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const chatflows: Partial<ChatFlow>[] = req.body.Chatflows
|
||||
const apiResponse = await chatflowsService.importChatflows(chatflows)
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatflowsController.saveChatflow - organization ${orgId} not found!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatflowsController.saveChatflow - workspace ${workspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
req.body.workspaceId = req.user?.activeWorkspaceId
|
||||
const apiResponse = await chatflowsService.importChatflows(chatflows, orgId, workspaceId, subscriptionId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -118,13 +178,27 @@ const importChatflows = async (req: Request, res: Response, next: NextFunction)
|
||||
const updateChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: chatflowsRouter.updateChatflow - id not provided!`)
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: chatflowsController.updateChatflow - id not provided!`)
|
||||
}
|
||||
const chatflow = await chatflowsService.getChatflowById(req.params.id)
|
||||
if (!chatflow) {
|
||||
return res.status(404).send(`Chatflow ${req.params.id} not found`)
|
||||
}
|
||||
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatflowsController.saveChatflow - organization ${orgId} not found!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: chatflowsController.saveChatflow - workspace ${workspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
const body = req.body
|
||||
const updateChatFlow = new ChatFlow()
|
||||
Object.assign(updateChatFlow, body)
|
||||
@@ -133,7 +207,7 @@ const updateChatflow = async (req: Request, res: Response, next: NextFunction) =
|
||||
const rateLimiterManager = RateLimiterManager.getInstance()
|
||||
await rateLimiterManager.updateRateLimiter(updateChatFlow)
|
||||
|
||||
const apiResponse = await chatflowsService.updateChatflow(chatflow, updateChatFlow)
|
||||
const apiResponse = await chatflowsService.updateChatflow(chatflow, updateChatFlow, orgId, workspaceId, subscriptionId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -145,7 +219,7 @@ const getSinglePublicChatflow = async (req: Request, res: Response, next: NextFu
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: chatflowsRouter.getSinglePublicChatflow - id not provided!`
|
||||
`Error: chatflowsController.getSinglePublicChatflow - id not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await chatflowsService.getSinglePublicChatflow(req.params.id)
|
||||
@@ -160,7 +234,7 @@ const getSinglePublicChatbotConfig = async (req: Request, res: Response, next: N
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: chatflowsRouter.getSinglePublicChatbotConfig - id not provided!`
|
||||
`Error: chatflowsController.getSinglePublicChatbotConfig - id not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await chatflowsService.getSinglePublicChatbotConfig(req.params.id)
|
||||
@@ -170,6 +244,27 @@ const getSinglePublicChatbotConfig = async (req: Request, res: Response, next: N
|
||||
}
|
||||
}
|
||||
|
||||
const checkIfChatflowHasChanged = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: chatflowsController.checkIfChatflowHasChanged - id not provided!`
|
||||
)
|
||||
}
|
||||
if (!req.params.lastUpdatedDateTime) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: chatflowsController.checkIfChatflowHasChanged - lastUpdatedDateTime not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await chatflowsService.checkIfChatflowHasChanged(req.params.id, req.params.lastUpdatedDateTime)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
checkIfChatflowIsValidForStreaming,
|
||||
checkIfChatflowIsValidForUploads,
|
||||
@@ -181,5 +276,6 @@ export default {
|
||||
importChatflows,
|
||||
updateChatflow,
|
||||
getSinglePublicChatflow,
|
||||
getSinglePublicChatbotConfig
|
||||
getSinglePublicChatbotConfig,
|
||||
checkIfChatflowHasChanged
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ const createCredential = async (req: Request, res: Response, next: NextFunction)
|
||||
`Error: credentialsController.createCredential - body not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await credentialsService.createCredential(req.body)
|
||||
const body = req.body
|
||||
body.workspaceId = req.user?.activeWorkspaceId
|
||||
const apiResponse = await credentialsService.createCredential(body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -35,7 +37,7 @@ const deleteCredentials = async (req: Request, res: Response, next: NextFunction
|
||||
|
||||
const getAllCredentials = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await credentialsService.getAllCredentials(req.query.credentialName)
|
||||
const apiResponse = await credentialsService.getAllCredentials(req.query.credentialName, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -50,7 +52,7 @@ const getCredentialById = async (req: Request, res: Response, next: NextFunction
|
||||
`Error: credentialsController.getCredentialById - id not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await credentialsService.getCredentialById(req.params.id)
|
||||
const apiResponse = await credentialsService.getCredentialById(req.params.id, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import datasetService from '../../services/dataset'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
|
||||
const getAllDatasets = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await datasetService.getAllDatasets(req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getDataset = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.getDataset - id not provided!`)
|
||||
}
|
||||
const apiResponse = await datasetService.getDataset(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const createDataset = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.createDataset - body not provided!`)
|
||||
}
|
||||
const body = req.body
|
||||
body.workspaceId = req.user?.activeWorkspaceId
|
||||
const apiResponse = await datasetService.createDataset(body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const updateDataset = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.updateDataset - body not provided!`)
|
||||
}
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.updateDataset - id not provided!`)
|
||||
}
|
||||
const apiResponse = await datasetService.updateDataset(req.params.id, req.body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteDataset = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.deleteDataset - id not provided!`)
|
||||
}
|
||||
const apiResponse = await datasetService.deleteDataset(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const addDatasetRow = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.addDatasetRow - body not provided!`)
|
||||
}
|
||||
if (!req.body.datasetId) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.addDatasetRow - datasetId not provided!`)
|
||||
}
|
||||
const apiResponse = await datasetService.addDatasetRow(req.body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const updateDatasetRow = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.updateDatasetRow - body not provided!`)
|
||||
}
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.updateDatasetRow - id not provided!`)
|
||||
}
|
||||
const apiResponse = await datasetService.updateDatasetRow(req.params.id, req.body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteDatasetRow = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.deleteDatasetRow - id not provided!`)
|
||||
}
|
||||
const apiResponse = await datasetService.deleteDatasetRow(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const patchDeleteRows = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const ids = req.body.ids ?? []
|
||||
const apiResponse = await datasetService.patchDeleteRows(ids)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const reorderDatasetRow = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: datasetService.reorderDatasetRow - body not provided!`)
|
||||
}
|
||||
|
||||
const apiResponse = await datasetService.reorderDatasetRow(req.body.datasetId, req.body.rows)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
export default {
|
||||
getAllDatasets,
|
||||
getDataset,
|
||||
createDataset,
|
||||
updateDataset,
|
||||
deleteDataset,
|
||||
addDatasetRow,
|
||||
updateDatasetRow,
|
||||
deleteDatasetRow,
|
||||
patchDeleteRows,
|
||||
reorderDatasetRow
|
||||
}
|
||||
@@ -15,9 +15,20 @@ const createDocumentStore = async (req: Request, res: Response, next: NextFuncti
|
||||
`Error: documentStoreController.createDocumentStore - body not provided!`
|
||||
)
|
||||
}
|
||||
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - organizationId not provided!`
|
||||
)
|
||||
}
|
||||
|
||||
const body = req.body
|
||||
body.workspaceId = req.user?.activeWorkspaceId
|
||||
|
||||
const docStore = DocumentStoreDTO.toEntity(body)
|
||||
const apiResponse = await documentStoreService.createDocumentStore(docStore)
|
||||
const apiResponse = await documentStoreService.createDocumentStore(docStore, orgId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -26,7 +37,7 @@ const createDocumentStore = async (req: Request, res: Response, next: NextFuncti
|
||||
|
||||
const getAllDocumentStores = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await documentStoreService.getAllDocumentStores()
|
||||
const apiResponse = await documentStoreService.getAllDocumentStores(req.user?.activeWorkspaceId)
|
||||
return res.json(DocumentStoreDTO.fromEntities(apiResponse))
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -44,7 +55,29 @@ const deleteLoaderFromDocumentStore = async (req: Request, res: Response, next:
|
||||
`Error: documentStoreController.deleteLoaderFromDocumentStore - missing storeId or loaderId.`
|
||||
)
|
||||
}
|
||||
const apiResponse = await documentStoreService.deleteLoaderFromDocumentStore(storeId, loaderId)
|
||||
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - organizationId not provided!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - workspaceId not provided!`
|
||||
)
|
||||
}
|
||||
|
||||
const apiResponse = await documentStoreService.deleteLoaderFromDocumentStore(
|
||||
storeId,
|
||||
loaderId,
|
||||
orgId,
|
||||
workspaceId,
|
||||
getRunningExpressApp().usageCacheManager
|
||||
)
|
||||
return res.json(DocumentStoreDTO.fromEntity(apiResponse))
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -199,10 +232,33 @@ const processLoader = async (req: Request, res: Response, next: NextFunction) =>
|
||||
`Error: documentStoreController.processLoader - body not provided!`
|
||||
)
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - organizationId not provided!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - workspaceId not provided!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
const docLoaderId = req.params.loaderId
|
||||
const body = req.body
|
||||
const isInternalRequest = req.headers['x-request-from'] === 'internal'
|
||||
const apiResponse = await documentStoreService.processLoaderMiddleware(body, docLoaderId, isInternalRequest)
|
||||
const apiResponse = await documentStoreService.processLoaderMiddleware(
|
||||
body,
|
||||
docLoaderId,
|
||||
orgId,
|
||||
workspaceId,
|
||||
subscriptionId,
|
||||
getRunningExpressApp().usageCacheManager,
|
||||
isInternalRequest
|
||||
)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -248,7 +304,26 @@ const deleteDocumentStore = async (req: Request, res: Response, next: NextFuncti
|
||||
`Error: documentStoreController.deleteDocumentStore - storeId not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await documentStoreService.deleteDocumentStore(req.params.id)
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - organizationId not provided!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - workspaceId not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await documentStoreService.deleteDocumentStore(
|
||||
req.params.id,
|
||||
orgId,
|
||||
workspaceId,
|
||||
getRunningExpressApp().usageCacheManager
|
||||
)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -263,9 +338,30 @@ const previewFileChunks = async (req: Request, res: Response, next: NextFunction
|
||||
`Error: documentStoreController.previewFileChunks - body not provided!`
|
||||
)
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - organizationId not provided!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - workspaceId not provided!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
const body = req.body
|
||||
body.preview = true
|
||||
const apiResponse = await documentStoreService.previewChunksMiddleware(body)
|
||||
const apiResponse = await documentStoreService.previewChunksMiddleware(
|
||||
body,
|
||||
orgId,
|
||||
workspaceId,
|
||||
subscriptionId,
|
||||
getRunningExpressApp().usageCacheManager
|
||||
)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -286,8 +382,30 @@ const insertIntoVectorStore = async (req: Request, res: Response, next: NextFunc
|
||||
if (typeof req.body === 'undefined') {
|
||||
throw new Error('Error: documentStoreController.insertIntoVectorStore - body not provided!')
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - organizationId not provided!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - workspaceId not provided!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
const body = req.body
|
||||
const apiResponse = await documentStoreService.insertIntoVectorStoreMiddleware(body)
|
||||
const apiResponse = await documentStoreService.insertIntoVectorStoreMiddleware(
|
||||
body,
|
||||
false,
|
||||
orgId,
|
||||
workspaceId,
|
||||
subscriptionId,
|
||||
getRunningExpressApp().usageCacheManager
|
||||
)
|
||||
getRunningExpressApp().metricsProvider?.incrementCounter(FLOWISE_METRIC_COUNTERS.VECTORSTORE_UPSERT, {
|
||||
status: FLOWISE_COUNTER_STATUS.SUCCESS
|
||||
})
|
||||
@@ -393,9 +511,32 @@ const upsertDocStoreMiddleware = async (req: Request, res: Response, next: NextF
|
||||
if (typeof req.body === 'undefined') {
|
||||
throw new Error('Error: documentStoreController.upsertDocStoreMiddleware - body not provided!')
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - organizationId not provided!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - workspaceId not provided!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
const body = req.body
|
||||
const files = (req.files as Express.Multer.File[]) || []
|
||||
const apiResponse = await documentStoreService.upsertDocStoreMiddleware(req.params.id, body, files)
|
||||
const apiResponse = await documentStoreService.upsertDocStoreMiddleware(
|
||||
req.params.id,
|
||||
body,
|
||||
files,
|
||||
orgId,
|
||||
workspaceId,
|
||||
subscriptionId,
|
||||
getRunningExpressApp().usageCacheManager
|
||||
)
|
||||
getRunningExpressApp().metricsProvider?.incrementCounter(FLOWISE_METRIC_COUNTERS.VECTORSTORE_UPSERT, {
|
||||
status: FLOWISE_COUNTER_STATUS.SUCCESS
|
||||
})
|
||||
@@ -416,8 +557,30 @@ const refreshDocStoreMiddleware = async (req: Request, res: Response, next: Next
|
||||
`Error: documentStoreController.refreshDocStoreMiddleware - storeId not provided!`
|
||||
)
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - organizationId not provided!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: documentStoreController.createDocumentStore - workspaceId not provided!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
const body = req.body
|
||||
const apiResponse = await documentStoreService.refreshDocStoreMiddleware(req.params.id, body)
|
||||
const apiResponse = await documentStoreService.refreshDocStoreMiddleware(
|
||||
req.params.id,
|
||||
body,
|
||||
orgId,
|
||||
workspaceId,
|
||||
subscriptionId,
|
||||
getRunningExpressApp().usageCacheManager
|
||||
)
|
||||
getRunningExpressApp().metricsProvider?.incrementCounter(FLOWISE_METRIC_COUNTERS.VECTORSTORE_UPSERT, {
|
||||
status: FLOWISE_COUNTER_STATUS.SUCCESS
|
||||
})
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import evaluationsService from '../../services/evaluations'
|
||||
|
||||
const createEvaluation = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: evaluationsService.createEvaluation - body not provided!`
|
||||
)
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: evaluationsService.createEvaluation - organization ${orgId} not found!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: evaluationsService.createEvaluation - workspace ${workspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const body = req.body
|
||||
body.workspaceId = workspaceId
|
||||
|
||||
const httpProtocol = req.get('x-forwarded-proto') || req.get('X-Forwarded-Proto') || req.protocol
|
||||
const baseURL = `${httpProtocol}://${req.get('host')}`
|
||||
const apiResponse = await evaluationsService.createEvaluation(body, baseURL, orgId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const runAgain = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluationsService.runAgain - id not provided!`)
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Error: evaluationsService.runAgain - organization ${orgId} not found!`)
|
||||
}
|
||||
const httpProtocol = req.get('x-forwarded-proto') || req.get('X-Forwarded-Proto') || req.protocol
|
||||
const baseURL = `${httpProtocol}://${req.get('host')}`
|
||||
const apiResponse = await evaluationsService.runAgain(req.params.id, baseURL, orgId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getEvaluation = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluationsService.getEvaluation - id not provided!`)
|
||||
}
|
||||
const apiResponse = await evaluationsService.getEvaluation(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteEvaluation = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluationsService.deleteEvaluation - id not provided!`)
|
||||
}
|
||||
const apiResponse = await evaluationsService.deleteEvaluation(req.params.id, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getAllEvaluations = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await evaluationsService.getAllEvaluations(req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const isOutdated = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluationsService.isOutdated - id not provided!`)
|
||||
}
|
||||
const apiResponse = await evaluationsService.isOutdated(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getVersions = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluationsService.getVersions - id not provided!`)
|
||||
}
|
||||
const apiResponse = await evaluationsService.getVersions(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const patchDeleteEvaluations = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const ids = req.body.ids ?? []
|
||||
const isDeleteAllVersion = req.body.isDeleteAllVersion ?? false
|
||||
const apiResponse = await evaluationsService.patchDeleteEvaluations(ids, isDeleteAllVersion, req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
createEvaluation,
|
||||
getEvaluation,
|
||||
deleteEvaluation,
|
||||
getAllEvaluations,
|
||||
isOutdated,
|
||||
runAgain,
|
||||
getVersions,
|
||||
patchDeleteEvaluations
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import evaluatorService from '../../services/evaluator'
|
||||
|
||||
const getAllEvaluators = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await evaluatorService.getAllEvaluators(req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getEvaluator = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluatorService.getEvaluator - id not provided!`)
|
||||
}
|
||||
const apiResponse = await evaluatorService.getEvaluator(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const createEvaluator = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluatorService.createEvaluator - body not provided!`)
|
||||
}
|
||||
const body = req.body
|
||||
body.workspaceId = req.user?.activeWorkspaceId
|
||||
const apiResponse = await evaluatorService.createEvaluator(body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const updateEvaluator = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluatorService.updateEvaluator - body not provided!`)
|
||||
}
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluatorService.updateEvaluator - id not provided!`)
|
||||
}
|
||||
const apiResponse = await evaluatorService.updateEvaluator(req.params.id, req.body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteEvaluator = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: evaluatorService.deleteEvaluator - id not provided!`)
|
||||
}
|
||||
const apiResponse = await evaluatorService.deleteEvaluator(req.params.id)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getAllEvaluators,
|
||||
getEvaluator,
|
||||
createEvaluator,
|
||||
updateEvaluator,
|
||||
deleteEvaluator
|
||||
}
|
||||
@@ -5,7 +5,8 @@ import { ExecutionState } from '../../Interface'
|
||||
const getExecutionById = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const executionId = req.params.id
|
||||
const execution = await executionsService.getExecutionById(executionId)
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
const execution = await executionsService.getExecutionById(executionId, workspaceId)
|
||||
return res.json(execution)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -25,7 +26,8 @@ const getPublicExecutionById = async (req: Request, res: Response, next: NextFun
|
||||
const updateExecution = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const executionId = req.params.id
|
||||
const execution = await executionsService.updateExecution(executionId, req.body)
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
const execution = await executionsService.updateExecution(executionId, req.body, workspaceId)
|
||||
return res.json(execution)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -37,6 +39,9 @@ const getAllExecutions = async (req: Request, res: Response, next: NextFunction)
|
||||
// Extract all possible filters from query params
|
||||
const filters: any = {}
|
||||
|
||||
// Add workspace ID filter
|
||||
filters.workspaceId = req.user?.activeWorkspaceId
|
||||
|
||||
// ID filter
|
||||
if (req.query.id) filters.id = req.query.id as string
|
||||
|
||||
@@ -86,6 +91,7 @@ const getAllExecutions = async (req: Request, res: Response, next: NextFunction)
|
||||
const deleteExecutions = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
let executionIds: string[] = []
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
|
||||
// Check if we're deleting a single execution from URL param
|
||||
if (req.params.id) {
|
||||
@@ -98,7 +104,7 @@ const deleteExecutions = async (req: Request, res: Response, next: NextFunction)
|
||||
return res.status(400).json({ success: false, message: 'No execution IDs provided' })
|
||||
}
|
||||
|
||||
const result = await executionsService.deleteExecutions(executionIds)
|
||||
const result = await executionsService.deleteExecutions(executionIds, workspaceId)
|
||||
return res.json(result)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
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))
|
||||
const apiResponse = await exportImportService.exportData(
|
||||
exportImportService.convertExportInput(req.body),
|
||||
req.user?.activeWorkspaceId
|
||||
)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -12,8 +17,28 @@ const exportData = async (req: Request, res: Response, next: NextFunction) => {
|
||||
|
||||
const importData = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: exportImportController.importData - organization ${orgId} not found!`
|
||||
)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: exportImportController.importData - workspace ${workspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
|
||||
const importData = req.body
|
||||
await exportImportService.importData(importData)
|
||||
if (!importData) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'Error: exportImportController.importData - importData is required!')
|
||||
}
|
||||
|
||||
await exportImportService.importData(importData, orgId, workspaceId, subscriptionId)
|
||||
return res.json({ message: 'success' })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import path from 'path'
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { getFilesListFromStorage, getStoragePath, removeSpecificFileFromStorage } from 'flowise-components'
|
||||
import { updateStorageUsage } from '../../utils/quotaUsage'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
|
||||
const getAllFiles = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const activeOrganizationId = req.user?.activeOrganizationId
|
||||
if (!activeOrganizationId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: filesController.getAllFiles - organization ${activeOrganizationId} not found!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await getFilesListFromStorage(activeOrganizationId)
|
||||
const filesList = apiResponse.map((file: any) => ({
|
||||
...file,
|
||||
// replace org id because we don't want to expose it
|
||||
path: file.path.replace(getStoragePath(), '').replace(`${path.sep}${activeOrganizationId}${path.sep}`, '')
|
||||
}))
|
||||
return res.json(filesList)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteFile = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const activeOrganizationId = req.user?.activeOrganizationId
|
||||
if (!activeOrganizationId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: filesController.deleteFile - organization ${activeOrganizationId} not found!`
|
||||
)
|
||||
}
|
||||
const activeWorkspaceId = req.user?.activeWorkspaceId
|
||||
if (!activeWorkspaceId) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.NOT_FOUND,
|
||||
`Error: filesController.deleteFile - workspace ${activeWorkspaceId} not found!`
|
||||
)
|
||||
}
|
||||
const filePath = req.query.path as string
|
||||
const paths = filePath.split(path.sep).filter((path) => path !== '')
|
||||
const { totalSize } = await removeSpecificFileFromStorage(activeOrganizationId, ...paths)
|
||||
await updateStorageUsage(activeOrganizationId, activeWorkspaceId, totalSize, getRunningExpressApp().usageCacheManager)
|
||||
return res.json({ message: 'file_deleted' })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getAllFiles,
|
||||
deleteFile
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import contentDisposition from 'content-disposition'
|
||||
import { streamStorageFile } from 'flowise-components'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { ChatFlow } from '../../database/entities/ChatFlow'
|
||||
import { Workspace } from '../../enterprise/database/entities/workspace.entity'
|
||||
|
||||
const streamUploadedFile = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
@@ -13,8 +16,27 @@ const streamUploadedFile = async (req: Request, res: Response, next: NextFunctio
|
||||
const chatflowId = req.query.chatflowId as string
|
||||
const chatId = req.query.chatId as string
|
||||
const fileName = req.query.fileName as string
|
||||
|
||||
const appServer = getRunningExpressApp()
|
||||
|
||||
// This can be public API, so we can only get orgId from the chatflow
|
||||
const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||
id: chatflowId
|
||||
})
|
||||
if (!chatflow) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowId} not found`)
|
||||
}
|
||||
const chatflowWorkspaceId = chatflow.workspaceId
|
||||
const workspace = await appServer.AppDataSource.getRepository(Workspace).findOneBy({
|
||||
id: chatflowWorkspaceId
|
||||
})
|
||||
if (!workspace) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Workspace ${chatflowWorkspaceId} not found`)
|
||||
}
|
||||
const orgId = workspace.organizationId as string
|
||||
|
||||
res.setHeader('Content-Disposition', contentDisposition(fileName))
|
||||
const fileStream = await streamStorageFile(chatflowId, chatId, fileName)
|
||||
const fileStream = await streamStorageFile(chatflowId, chatId, fileName, orgId)
|
||||
|
||||
if (!fileStream) throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: streamStorageFile`)
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import logService from '../../services/log'
|
||||
|
||||
// Get logs
|
||||
const getLogs = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await logService.getLogs(req.query?.startDate as string, req.query?.endDate as string)
|
||||
res.send(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getLogs
|
||||
}
|
||||
@@ -30,7 +30,7 @@ const deleteCustomTemplate = async (req: Request, res: Response, next: NextFunct
|
||||
|
||||
const getAllCustomTemplates = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await marketplacesService.getAllCustomTemplates()
|
||||
const apiResponse = await marketplacesService.getAllCustomTemplates(req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -45,7 +45,9 @@ const saveCustomTemplate = async (req: Request, res: Response, next: NextFunctio
|
||||
`Error: marketplacesService.saveCustomTemplate - body not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await marketplacesService.saveCustomTemplate(req.body)
|
||||
const body = req.body
|
||||
body.workspaceId = req.user?.activeWorkspaceId
|
||||
const apiResponse = await marketplacesService.saveCustomTemplate(body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -3,6 +3,7 @@ import _ from 'lodash'
|
||||
import nodesService from '../../services/nodes'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { getWorkspaceSearchOptionsFromReq } from '../../enterprise/utils/ControllerServiceUtils'
|
||||
|
||||
const getAllNodes = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
@@ -67,7 +68,9 @@ const getSingleNodeAsyncOptions = async (req: Request, res: Response, next: Next
|
||||
`Error: nodesController.getSingleNodeAsyncOptions - name not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await nodesService.getSingleNodeAsyncOptions(req.params.name, req.body)
|
||||
const body = req.body
|
||||
body.searchOptions = getWorkspaceSearchOptionsFromReq(req)
|
||||
const apiResponse = await nodesService.getSingleNodeAsyncOptions(req.params.name, body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -82,7 +85,8 @@ const executeCustomFunction = async (req: Request, res: Response, next: NextFunc
|
||||
`Error: nodesController.executeCustomFunction - body not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await nodesService.executeCustomFunction(req.body)
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
const apiResponse = await nodesService.executeCustomFunction(req.body, orgId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -5,6 +5,9 @@ import contentDisposition from 'content-disposition'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { streamStorageFile } from 'flowise-components'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { ChatFlow } from '../../database/entities/ChatFlow'
|
||||
import { Workspace } from '../../enterprise/database/entities/workspace.entity'
|
||||
|
||||
// List available assistants
|
||||
const getAllOpenaiAssistants = async (req: Request, res: Response, next: NextFunction) => {
|
||||
@@ -50,11 +53,29 @@ const getFileFromAssistant = async (req: Request, res: Response, next: NextFunct
|
||||
if (!req.body.chatflowId || !req.body.chatId || !req.body.fileName) {
|
||||
return res.status(500).send(`Invalid file path`)
|
||||
}
|
||||
const appServer = getRunningExpressApp()
|
||||
const chatflowId = req.body.chatflowId as string
|
||||
const chatId = req.body.chatId as string
|
||||
const fileName = req.body.fileName as string
|
||||
|
||||
// This can be public API, so we can only get orgId from the chatflow
|
||||
const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||
id: chatflowId
|
||||
})
|
||||
if (!chatflow) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowId} not found`)
|
||||
}
|
||||
const chatflowWorkspaceId = chatflow.workspaceId
|
||||
const workspace = await appServer.AppDataSource.getRepository(Workspace).findOneBy({
|
||||
id: chatflowWorkspaceId
|
||||
})
|
||||
if (!workspace) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Workspace ${chatflowWorkspaceId} not found`)
|
||||
}
|
||||
const orgId = workspace.organizationId as string
|
||||
|
||||
res.setHeader('Content-Disposition', contentDisposition(fileName))
|
||||
const fileStream = await streamStorageFile(chatflowId, chatId, fileName)
|
||||
const fileStream = await streamStorageFile(chatflowId, chatId, fileName, orgId)
|
||||
|
||||
if (!fileStream) throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: getFileFromAssistant`)
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
|
||||
const getPricing = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const PRODUCT_IDS = {
|
||||
FREE: process.env.CLOUD_FREE_ID,
|
||||
STARTER: process.env.CLOUD_STARTER_ID,
|
||||
PRO: process.env.CLOUD_PRO_ID
|
||||
}
|
||||
const pricingPlans = [
|
||||
{
|
||||
prodId: PRODUCT_IDS.FREE,
|
||||
title: 'Free',
|
||||
subtitle: 'For trying out the platform',
|
||||
price: '$0',
|
||||
period: '/month',
|
||||
features: [
|
||||
{ text: '2 Flows & Assistants' },
|
||||
{ text: '100 Predictions / month' },
|
||||
{ text: '5MB Storage' },
|
||||
{ text: 'Evaluations & Metrics' },
|
||||
{ text: 'Custom Embedded Chatbot Branding' },
|
||||
{ text: 'Community Support' }
|
||||
]
|
||||
},
|
||||
{
|
||||
prodId: PRODUCT_IDS.STARTER,
|
||||
title: 'Starter',
|
||||
subtitle: 'For individuals & small teams',
|
||||
mostPopular: true,
|
||||
price: '$35',
|
||||
period: '/month',
|
||||
features: [
|
||||
{ text: 'Everything in Free plan, plus' },
|
||||
{ text: 'Unlimited Flows & Assistants' },
|
||||
{ text: '10,000 Predictions / month' },
|
||||
{ text: '1GB Storage' },
|
||||
{ text: 'Email Support' }
|
||||
]
|
||||
},
|
||||
{
|
||||
prodId: PRODUCT_IDS.PRO,
|
||||
title: 'Pro',
|
||||
subtitle: 'For medium-sized businesses',
|
||||
price: '$65',
|
||||
period: '/month',
|
||||
features: [
|
||||
{ text: 'Everything in Starter plan, plus' },
|
||||
{ text: '50,000 Predictions / month' },
|
||||
{ text: '10GB Storage' },
|
||||
{ text: 'Unlimited Workspaces' },
|
||||
{ text: '5 users', subtext: '+ $15/user/month' },
|
||||
{ text: 'Admin Roles & Permissions' },
|
||||
{ text: 'Priority Support' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Enterprise',
|
||||
subtitle: 'For large organizations',
|
||||
price: 'Contact Us',
|
||||
features: [
|
||||
{ text: 'On-Premise Deployment' },
|
||||
{ text: 'Air-gapped Environments' },
|
||||
{ text: 'SSO & SAML' },
|
||||
{ text: 'LDAP & RBAC' },
|
||||
{ text: 'Versioning' },
|
||||
{ text: 'Audit Logs' },
|
||||
{ text: '99.99% Uptime SLA' },
|
||||
{ text: 'Personalized Support' }
|
||||
]
|
||||
}
|
||||
]
|
||||
return res.status(200).json(pricingPlans)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getPricing
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import settingsService from '../../services/settings'
|
||||
|
||||
const getSettingsList = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await settingsService.getSettings()
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getSettingsList
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import toolsService from '../../services/tools'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
@@ -8,7 +8,18 @@ const createTool = async (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: toolsController.createTool - body not provided!`)
|
||||
}
|
||||
const apiResponse = await toolsService.createTool(req.body)
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Error: toolsController.createTool - organization ${orgId} not found!`)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Error: toolsController.createTool - workspace ${workspaceId} not found!`)
|
||||
}
|
||||
const body = req.body
|
||||
body.workspaceId = workspaceId
|
||||
|
||||
const apiResponse = await toolsService.createTool(body, orgId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -29,7 +40,7 @@ const deleteTool = async (req: Request, res: Response, next: NextFunction) => {
|
||||
|
||||
const getAllTools = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await toolsService.getAllTools()
|
||||
const apiResponse = await toolsService.getAllTools(req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -12,7 +12,8 @@ const checkFlowValidation = async (req: Request, res: Response, next: NextFuncti
|
||||
`Error: validationController.checkFlowValidation - id not provided!`
|
||||
)
|
||||
}
|
||||
const apiResponse = await validationService.checkFlowValidation(flowId)
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
const apiResponse = await validationService.checkFlowValidation(flowId, workspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -12,10 +12,19 @@ const createVariable = async (req: Request, res: Response, next: NextFunction) =
|
||||
`Error: variablesController.createVariable - body not provided!`
|
||||
)
|
||||
}
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
if (!orgId) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Error: toolsController.createTool - organization ${orgId} not found!`)
|
||||
}
|
||||
const workspaceId = req.user?.activeWorkspaceId
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Error: toolsController.createTool - workspace ${workspaceId} not found!`)
|
||||
}
|
||||
const body = req.body
|
||||
body.workspaceId = workspaceId
|
||||
const newVariable = new Variable()
|
||||
Object.assign(newVariable, body)
|
||||
const apiResponse = await variablesService.createVariable(newVariable)
|
||||
const apiResponse = await variablesService.createVariable(newVariable, orgId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
@@ -36,7 +45,7 @@ const deleteVariable = async (req: Request, res: Response, next: NextFunction) =
|
||||
|
||||
const getAllVariables = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const apiResponse = await variablesService.getAllVariables()
|
||||
const apiResponse = await variablesService.getAllVariables(req.user?.activeWorkspaceId)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -18,4 +18,7 @@ export class ApiKey implements IApiKey {
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
@@ -26,4 +26,7 @@ export class Assistant implements IAssistant {
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
@@ -50,4 +50,7 @@ export class ChatFlow implements IChatFlow {
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
@@ -23,4 +23,7 @@ export class Credential implements ICredential {
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ export class CustomTemplate implements ICustomTemplate {
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
type?: string
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId: string
|
||||
|
||||
@Column({ type: 'timestamp' })
|
||||
@CreateDateColumn()
|
||||
createdDate: Date
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable */
|
||||
import { Entity, Column, CreateDateColumn, UpdateDateColumn, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { IAssistant, IDataset } from '../../Interface'
|
||||
|
||||
@Entity()
|
||||
export class Dataset implements IDataset {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
name: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
description: string
|
||||
|
||||
@CreateDateColumn()
|
||||
createdDate: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* eslint-disable */
|
||||
import { Entity, Column, CreateDateColumn, UpdateDateColumn, PrimaryGeneratedColumn, Index } from 'typeorm'
|
||||
import { IAssistant, IDataset, IDatasetRow } from '../../Interface'
|
||||
|
||||
@Entity()
|
||||
export class DatasetRow implements IDatasetRow {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
@Index()
|
||||
datasetId: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
input: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
output: string
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ name: 'sequence_no' })
|
||||
sequenceNo: number
|
||||
}
|
||||
@@ -37,4 +37,7 @@ export class DocumentStore implements IDocumentStore {
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
recordManagerConfig: string | null
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
|
||||
import { IEvaluation } from '../../Interface'
|
||||
|
||||
@Entity()
|
||||
export class Evaluation implements IEvaluation {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
average_metrics: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
additionalConfig: string
|
||||
|
||||
@Column()
|
||||
name: string
|
||||
|
||||
@Column()
|
||||
evaluationType: string
|
||||
|
||||
@Column()
|
||||
chatflowId: string
|
||||
|
||||
@Column()
|
||||
chatflowName: string
|
||||
|
||||
@Column()
|
||||
datasetId: string
|
||||
|
||||
@Column()
|
||||
datasetName: string
|
||||
|
||||
@Column()
|
||||
status: string
|
||||
|
||||
@UpdateDateColumn()
|
||||
runDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
|
||||
import { IEvaluationRun } from '../../Interface'
|
||||
|
||||
@Entity()
|
||||
export class EvaluationRun implements IEvaluationRun {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column()
|
||||
evaluationId: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
input: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
expectedOutput: string
|
||||
|
||||
@UpdateDateColumn()
|
||||
runDate: Date
|
||||
|
||||
@Column({ type: 'text' })
|
||||
actualOutput: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
metrics: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
llmEvaluators: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
evaluators: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
errors: string
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
|
||||
import { IEvaluator } from '../../Interface'
|
||||
|
||||
//1714808591644
|
||||
|
||||
@Entity()
|
||||
export class Evaluator implements IEvaluator {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column()
|
||||
name: string
|
||||
|
||||
@Column()
|
||||
type: string
|
||||
|
||||
@Column()
|
||||
config: string
|
||||
|
||||
@CreateDateColumn()
|
||||
createdDate: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
@@ -41,4 +41,7 @@ export class Execution implements IExecution {
|
||||
@ManyToOne(() => ChatFlow)
|
||||
@JoinColumn({ name: 'agentflowId' })
|
||||
agentflow: ChatFlow
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
@@ -32,4 +32,7 @@ export class Tool implements ITool {
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
@@ -23,4 +23,7 @@ export class Variable implements IVariable {
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
workspaceId?: string
|
||||
}
|
||||
|
||||
@@ -9,9 +9,22 @@ import { DocumentStore } from './DocumentStore'
|
||||
import { DocumentStoreFileChunk } from './DocumentStoreFileChunk'
|
||||
import { Lead } from './Lead'
|
||||
import { UpsertHistory } from './UpsertHistory'
|
||||
import { Dataset } from './Dataset'
|
||||
import { DatasetRow } from './DatasetRow'
|
||||
import { EvaluationRun } from './EvaluationRun'
|
||||
import { Evaluation } from './Evaluation'
|
||||
import { Evaluator } from './Evaluator'
|
||||
import { ApiKey } from './ApiKey'
|
||||
import { CustomTemplate } from './CustomTemplate'
|
||||
import { Execution } from './Execution'
|
||||
import { LoginActivity, WorkspaceShared, WorkspaceUsers } from '../../enterprise/database/entities/EnterpriseEntities'
|
||||
import { User } from '../../enterprise/database/entities/user.entity'
|
||||
import { Organization } from '../../enterprise/database/entities/organization.entity'
|
||||
import { Role } from '../../enterprise/database/entities/role.entity'
|
||||
import { OrganizationUser } from '../../enterprise/database/entities/organization-user.entity'
|
||||
import { Workspace } from '../../enterprise/database/entities/workspace.entity'
|
||||
import { WorkspaceUser } from '../../enterprise/database/entities/workspace-user.entity'
|
||||
import { LoginMethod } from '../../enterprise/database/entities/login-method.entity'
|
||||
|
||||
export const entities = {
|
||||
ChatFlow,
|
||||
@@ -21,11 +34,26 @@ export const entities = {
|
||||
Tool,
|
||||
Assistant,
|
||||
Variable,
|
||||
UpsertHistory,
|
||||
DocumentStore,
|
||||
DocumentStoreFileChunk,
|
||||
Lead,
|
||||
UpsertHistory,
|
||||
Dataset,
|
||||
DatasetRow,
|
||||
Evaluation,
|
||||
EvaluationRun,
|
||||
Evaluator,
|
||||
ApiKey,
|
||||
User,
|
||||
WorkspaceUsers,
|
||||
LoginActivity,
|
||||
WorkspaceShared,
|
||||
CustomTemplate,
|
||||
Execution
|
||||
Execution,
|
||||
Organization,
|
||||
Role,
|
||||
OrganizationUser,
|
||||
Workspace,
|
||||
WorkspaceUser,
|
||||
LoginMethod
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddEvaluation1714548873039 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`evaluation\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`chatflowId\` LONGTEXT NOT NULL,
|
||||
\`datasetId\` LONGTEXT NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`chatflowName\` varchar(255) NOT NULL,
|
||||
\`datasetName\` varchar(255) NOT NULL,
|
||||
\`additionalConfig\` LONGTEXT,
|
||||
\`average_metrics\` LONGTEXT NOT NULL,
|
||||
\`status\` varchar(10) NOT NULL,
|
||||
\`evaluationType\` varchar(20) NOT NULL,
|
||||
\`runDate\` 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;`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`evaluation_run\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`evaluationId\` varchar(36) NOT NULL,
|
||||
\`expectedOutput\` LONGTEXT NOT NULL,
|
||||
\`actualOutput\` LONGTEXT NOT NULL,
|
||||
\`evaluators\` LONGTEXT,
|
||||
\`input\` LONGTEXT DEFAULT NULL,
|
||||
\`metrics\` TEXT DEFAULT NULL,
|
||||
\`llmEvaluators\` TEXT DEFAULT NULL,
|
||||
\`runDate\` 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 evaluation`)
|
||||
await queryRunner.query(`DROP TABLE evaluation_run`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddDatasets1714548903384 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`dataset\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`description\` varchar(255) DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`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;`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`dataset_row\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`datasetId\` varchar(36) NOT NULL,
|
||||
\`input\` LONGTEXT NOT NULL,
|
||||
\`output\` LONGTEXT DEFAULT 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 dataset`)
|
||||
await queryRunner.query(`DROP TABLE dataset_row`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddEvaluator1714808591644 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`evaluator\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`type\` varchar(25) DEFAULT NULL,
|
||||
\`config\` LONGTEXT DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`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 evaluator`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSeqNoToDatasetRow1733752119696 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('dataset_row', 'sequence_no')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`dataset_row\` ADD COLUMN \`sequence_no\` INT DEFAULT -1;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "dataset_row" DROP COLUMN "sequence_no";`)
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
import { Assistant } from '../../entities/Assistant'
|
||||
|
||||
export class FixOpenSourceAssistantTable1743758056188 implements MigrationInterface {
|
||||
name = 'FixOpenSourceAssistantTable1743758056188'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('assistant', 'type')
|
||||
if (!columnExists) {
|
||||
await queryRunner.query(`ALTER TABLE \`assistant\` ADD COLUMN \`type\` TEXT;`)
|
||||
await queryRunner.query(`UPDATE \`assistant\` SET \`type\` = 'OPENAI';`)
|
||||
|
||||
const assistants: Assistant[] = await queryRunner.query(`SELECT * FROM \`assistant\`;`)
|
||||
for (let assistant of assistants) {
|
||||
const details = JSON.parse(assistant.details)
|
||||
if (!details?.id) await queryRunner.query(`UPDATE \`assistant\` SET \`type\` = 'CUSTOM' WHERE id = '${assistant.id}';`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`assistant\` DROP COLUMN \`type\`;`)
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddErrorToEvaluationRun1744964560174 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('evaluation_run', 'errors')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`evaluation_run\` ADD COLUMN \`errors\` LONGTEXT NULL DEFAULT '[]';`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "evaluation_run" DROP COLUMN "errors";`)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class ModifyExecutionDataColumnType1747902489801 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.query(`ALTER TABLE \`execution\` MODIFY COLUMN \`executionData\` LONGTEXT NOT NULL;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.query(`ALTER TABLE \`execution\` MODIFY COLUMN \`executionData\` TEXT NOT NULL;`)
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,12 @@ import { AddFeedback1707213626553 } from './1707213626553-AddFeedback'
|
||||
import { AddUpsertHistoryEntity1709814301358 } from './1709814301358-AddUpsertHistoryEntity'
|
||||
import { AddLead1710832127079 } from './1710832127079-AddLead'
|
||||
import { AddLeadToChatMessage1711538023578 } from './1711538023578-AddLeadToChatMessage'
|
||||
import { AddVectorStoreConfigToDocStore1715861032479 } from './1715861032479-AddVectorStoreConfigToDocStore'
|
||||
import { AddDocumentStore1711637331047 } from './1711637331047-AddDocumentStore'
|
||||
import { AddEvaluation1714548873039 } from './1714548873039-AddEvaluation'
|
||||
import { AddDatasets1714548903384 } from './1714548903384-AddDataset'
|
||||
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
|
||||
import { AddEvaluator1714808591644 } from './1714808591644-AddEvaluator'
|
||||
import { AddVectorStoreConfigToDocStore1715861032479 } from './1715861032479-AddVectorStoreConfigToDocStore'
|
||||
import { AddTypeToChatFlow1716300000000 } from './1716300000000-AddTypeToChatFlow'
|
||||
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
|
||||
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
|
||||
@@ -28,7 +31,23 @@ import { AddCustomTemplate1725629836652 } from './1725629836652-AddCustomTemplat
|
||||
import { AddArtifactsToChatMessage1726156258465 } from './1726156258465-AddArtifactsToChatMessage'
|
||||
import { AddFollowUpPrompts1726666318346 } from './1726666318346-AddFollowUpPrompts'
|
||||
import { AddTypeToAssistant1733011290987 } from './1733011290987-AddTypeToAssistant'
|
||||
import { AddSeqNoToDatasetRow1733752119696 } from './1733752119696-AddSeqNoToDatasetRow'
|
||||
import { AddExecutionEntity1738090872625 } from './1738090872625-AddExecutionEntity'
|
||||
import { FixOpenSourceAssistantTable1743758056188 } from './1743758056188-FixOpenSourceAssistantTable'
|
||||
import { AddErrorToEvaluationRun1744964560174 } from './1744964560174-AddErrorToEvaluationRun'
|
||||
import { ModifyExecutionDataColumnType1747902489801 } from './1747902489801-ModifyExecutionDataColumnType'
|
||||
|
||||
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/mariadb/1720230151482-AddAuthTables'
|
||||
import { AddWorkspace1725437498242 } from '../../../enterprise/database/migrations/mariadb/1725437498242-AddWorkspace'
|
||||
import { AddWorkspaceShared1726654922034 } from '../../../enterprise/database/migrations/mariadb/1726654922034-AddWorkspaceShared'
|
||||
import { AddWorkspaceIdToCustomTemplate1726655750383 } from '../../../enterprise/database/migrations/mariadb/1726655750383-AddWorkspaceIdToCustomTemplate'
|
||||
import { AddOrganization1727798417345 } from '../../../enterprise/database/migrations/mariadb/1727798417345-AddOrganization'
|
||||
import { LinkWorkspaceId1729130948686 } from '../../../enterprise/database/migrations/mariadb/1729130948686-LinkWorkspaceId'
|
||||
import { LinkOrganizationId1729133111652 } from '../../../enterprise/database/migrations/mariadb/1729133111652-LinkOrganizationId'
|
||||
import { AddSSOColumns1730519457880 } from '../../../enterprise/database/migrations/mariadb/1730519457880-AddSSOColumns'
|
||||
import { AddPersonalWorkspace1734074497540 } from '../../../enterprise/database/migrations/mariadb/1734074497540-AddPersonalWorkspace'
|
||||
import { RefactorEnterpriseDatabase1737076223692 } from '../../../enterprise/database/migrations/mariadb/1737076223692-RefactorEnterpriseDatabase'
|
||||
import { ExecutionLinkWorkspaceId1746862866554 } from '../../../enterprise/database/migrations/mariadb/1746862866554-ExecutionLinkWorkspaceId'
|
||||
|
||||
export const mariadbMigrations = [
|
||||
Init1693840429259,
|
||||
@@ -51,15 +70,33 @@ export const mariadbMigrations = [
|
||||
AddDocumentStore1711637331047,
|
||||
AddLead1710832127079,
|
||||
AddLeadToChatMessage1711538023578,
|
||||
AddEvaluation1714548873039,
|
||||
AddDatasets1714548903384,
|
||||
AddAgentReasoningToChatMessage1714679514451,
|
||||
AddTypeToChatFlow1716300000000,
|
||||
AddEvaluator1714808591644,
|
||||
AddVectorStoreConfigToDocStore1715861032479,
|
||||
AddTypeToChatFlow1716300000000,
|
||||
AddApiKey1720230151480,
|
||||
AddActionToChatMessage1721078251523,
|
||||
LongTextColumn1722301395521,
|
||||
AddCustomTemplate1725629836652,
|
||||
AddArtifactsToChatMessage1726156258465,
|
||||
AddFollowUpPrompts1726666318346,
|
||||
AddTypeToAssistant1733011290987,
|
||||
AddExecutionEntity1738090872625
|
||||
AddArtifactsToChatMessage1726156258465,
|
||||
AddAuthTables1720230151482,
|
||||
AddWorkspace1725437498242,
|
||||
AddWorkspaceShared1726654922034,
|
||||
AddWorkspaceIdToCustomTemplate1726655750383,
|
||||
AddOrganization1727798417345,
|
||||
LinkWorkspaceId1729130948686,
|
||||
LinkOrganizationId1729133111652,
|
||||
AddSSOColumns1730519457880,
|
||||
AddSeqNoToDatasetRow1733752119696,
|
||||
AddPersonalWorkspace1734074497540,
|
||||
RefactorEnterpriseDatabase1737076223692,
|
||||
AddExecutionEntity1738090872625,
|
||||
FixOpenSourceAssistantTable1743758056188,
|
||||
AddErrorToEvaluationRun1744964560174,
|
||||
ExecutionLinkWorkspaceId1746862866554,
|
||||
ModifyExecutionDataColumnType1747902489801
|
||||
]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddEvaluation1714548873039 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`evaluation\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`chatflowId\` LONGTEXT NOT NULL,
|
||||
\`datasetId\` LONGTEXT NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`chatflowName\` varchar(255) NOT NULL,
|
||||
\`datasetName\` varchar(255) NOT NULL,
|
||||
\`additionalConfig\` LONGTEXT,
|
||||
\`average_metrics\` LONGTEXT NOT NULL,
|
||||
\`status\` varchar(10) NOT NULL,
|
||||
\`evaluationType\` varchar(20) NOT NULL,
|
||||
\`runDate\` 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;`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`evaluation_run\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`evaluationId\` varchar(36) NOT NULL,
|
||||
\`expectedOutput\` LONGTEXT NOT NULL,
|
||||
\`actualOutput\` LONGTEXT NOT NULL,
|
||||
\`evaluators\` LONGTEXT,
|
||||
\`input\` LONGTEXT DEFAULT NULL,
|
||||
\`metrics\` TEXT DEFAULT NULL,
|
||||
\`llmEvaluators\` TEXT DEFAULT NULL,
|
||||
\`runDate\` 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 evaluation`)
|
||||
await queryRunner.query(`DROP TABLE evaluation_run`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddDatasets1714548903384 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`dataset\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`description\` varchar(255) DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`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;`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`dataset_row\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`datasetId\` varchar(36) NOT NULL,
|
||||
\`input\` LONGTEXT NOT NULL,
|
||||
\`output\` LONGTEXT DEFAULT 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 dataset`)
|
||||
await queryRunner.query(`DROP TABLE dataset_row`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddEvaluator1714808591644 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS \`evaluator\` (
|
||||
\`id\` varchar(36) NOT NULL,
|
||||
\`name\` varchar(255) NOT NULL,
|
||||
\`type\` varchar(25) DEFAULT NULL,
|
||||
\`config\` LONGTEXT DEFAULT NULL,
|
||||
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
\`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 evaluator`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSeqNoToDatasetRow1733752119696 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('dataset_row', 'sequence_no')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`dataset_row\` ADD COLUMN \`sequence_no\` INT DEFAULT -1;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "dataset_row" DROP COLUMN "sequence_no";`)
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
import { Assistant } from '../../entities/Assistant'
|
||||
|
||||
export class FixOpenSourceAssistantTable1743758056188 implements MigrationInterface {
|
||||
name = 'FixOpenSourceAssistantTable1743758056188'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('assistant', 'type')
|
||||
if (!columnExists) {
|
||||
await queryRunner.query(`ALTER TABLE \`assistant\` ADD COLUMN \`type\` TEXT;`)
|
||||
await queryRunner.query(`UPDATE \`assistant\` SET \`type\` = 'OPENAI';`)
|
||||
|
||||
const assistants: Assistant[] = await queryRunner.query(`SELECT * FROM \`assistant\`;`)
|
||||
for (let assistant of assistants) {
|
||||
const details = JSON.parse(assistant.details)
|
||||
if (!details?.id) await queryRunner.query(`UPDATE \`assistant\` SET \`type\` = 'CUSTOM' WHERE id = '${assistant.id}';`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`assistant\` DROP COLUMN \`type\`;`)
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddErrorToEvaluationRun1744964560174 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('evaluation_run', 'errors')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`evaluation_run\` ADD COLUMN \`errors\` LONGTEXT NULL DEFAULT ('[]');`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "evaluation_run" DROP COLUMN "errors";`)
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class FixErrorsColumnInEvaluationRun1746437114935 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('evaluation_run', 'errors')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`evaluation_run\` ADD COLUMN \`errors\` LONGTEXT NULL DEFAULT ('[]');`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "evaluation_run" DROP COLUMN "errors";`)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class ModifyExecutionDataColumnType1747902489801 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.query(`ALTER TABLE \`execution\` MODIFY COLUMN \`executionData\` LONGTEXT NOT NULL;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.query(`ALTER TABLE \`execution\` MODIFY COLUMN \`executionData\` TEXT NOT NULL;`)
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,12 @@ import { AddFeedback1707213626553 } from './1707213626553-AddFeedback'
|
||||
import { AddUpsertHistoryEntity1709814301358 } from './1709814301358-AddUpsertHistoryEntity'
|
||||
import { AddLead1710832127079 } from './1710832127079-AddLead'
|
||||
import { AddLeadToChatMessage1711538023578 } from './1711538023578-AddLeadToChatMessage'
|
||||
import { AddVectorStoreConfigToDocStore1715861032479 } from './1715861032479-AddVectorStoreConfigToDocStore'
|
||||
import { AddDocumentStore1711637331047 } from './1711637331047-AddDocumentStore'
|
||||
import { AddEvaluation1714548873039 } from './1714548873039-AddEvaluation'
|
||||
import { AddDatasets1714548903384 } from './1714548903384-AddDataset'
|
||||
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
|
||||
import { AddEvaluator1714808591644 } from './1714808591644-AddEvaluator'
|
||||
import { AddVectorStoreConfigToDocStore1715861032479 } from './1715861032479-AddVectorStoreConfigToDocStore'
|
||||
import { AddTypeToChatFlow1716300000000 } from './1716300000000-AddTypeToChatFlow'
|
||||
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
|
||||
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
|
||||
@@ -28,7 +31,24 @@ import { AddCustomTemplate1725629836652 } from './1725629836652-AddCustomTemplat
|
||||
import { AddArtifactsToChatMessage1726156258465 } from './1726156258465-AddArtifactsToChatMessage'
|
||||
import { AddFollowUpPrompts1726666302024 } from './1726666302024-AddFollowUpPrompts'
|
||||
import { AddTypeToAssistant1733011290987 } from './1733011290987-AddTypeToAssistant'
|
||||
import { AddSeqNoToDatasetRow1733752119696 } from './1733752119696-AddSeqNoToDatasetRow'
|
||||
import { AddExecutionEntity1738090872625 } from './1738090872625-AddExecutionEntity'
|
||||
import { FixOpenSourceAssistantTable1743758056188 } from './1743758056188-FixOpenSourceAssistantTable'
|
||||
import { AddErrorToEvaluationRun1744964560174 } from './1744964560174-AddErrorToEvaluationRun'
|
||||
import { FixErrorsColumnInEvaluationRun1746437114935 } from './1746437114935-FixErrorsColumnInEvaluationRun'
|
||||
import { ModifyExecutionDataColumnType1747902489801 } from './1747902489801-ModifyExecutionDataColumnType'
|
||||
|
||||
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/mysql/1720230151482-AddAuthTables'
|
||||
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/mysql/1720230151484-AddWorkspace'
|
||||
import { AddWorkspaceShared1726654922034 } from '../../../enterprise/database/migrations/mysql/1726654922034-AddWorkspaceShared'
|
||||
import { AddWorkspaceIdToCustomTemplate1726655750383 } from '../../../enterprise/database/migrations/mysql/1726655750383-AddWorkspaceIdToCustomTemplate'
|
||||
import { AddOrganization1727798417345 } from '../../../enterprise/database/migrations/mysql/1727798417345-AddOrganization'
|
||||
import { LinkWorkspaceId1729130948686 } from '../../../enterprise/database/migrations/mysql/1729130948686-LinkWorkspaceId'
|
||||
import { LinkOrganizationId1729133111652 } from '../../../enterprise/database/migrations/mysql/1729133111652-LinkOrganizationId'
|
||||
import { AddSSOColumns1730519457880 } from '../../../enterprise/database/migrations/mysql/1730519457880-AddSSOColumns'
|
||||
import { AddPersonalWorkspace1734074497540 } from '../../../enterprise/database/migrations/mysql/1734074497540-AddPersonalWorkspace'
|
||||
import { RefactorEnterpriseDatabase1737076223692 } from '../../../enterprise/database/migrations/mysql/1737076223692-RefactorEnterpriseDatabase'
|
||||
import { ExecutionLinkWorkspaceId1746862866554 } from '../../../enterprise/database/migrations/mysql/1746862866554-ExecutionLinkWorkspaceId'
|
||||
|
||||
export const mysqlMigrations = [
|
||||
Init1693840429259,
|
||||
@@ -48,12 +68,15 @@ export const mysqlMigrations = [
|
||||
AddSpeechToText1706364937060,
|
||||
AddUpsertHistoryEntity1709814301358,
|
||||
AddFeedback1707213626553,
|
||||
AddEvaluation1714548873039,
|
||||
AddDatasets1714548903384,
|
||||
AddEvaluator1714808591644,
|
||||
AddDocumentStore1711637331047,
|
||||
AddLead1710832127079,
|
||||
AddLeadToChatMessage1711538023578,
|
||||
AddAgentReasoningToChatMessage1714679514451,
|
||||
AddTypeToChatFlow1716300000000,
|
||||
AddVectorStoreConfigToDocStore1715861032479,
|
||||
AddTypeToChatFlow1716300000000,
|
||||
AddApiKey1720230151480,
|
||||
AddActionToChatMessage1721078251523,
|
||||
LongTextColumn1722301395521,
|
||||
@@ -61,5 +84,21 @@ export const mysqlMigrations = [
|
||||
AddArtifactsToChatMessage1726156258465,
|
||||
AddFollowUpPrompts1726666302024,
|
||||
AddTypeToAssistant1733011290987,
|
||||
AddExecutionEntity1738090872625
|
||||
AddAuthTables1720230151482,
|
||||
AddWorkspace1720230151484,
|
||||
AddWorkspaceShared1726654922034,
|
||||
AddWorkspaceIdToCustomTemplate1726655750383,
|
||||
AddOrganization1727798417345,
|
||||
LinkWorkspaceId1729130948686,
|
||||
LinkOrganizationId1729133111652,
|
||||
AddSSOColumns1730519457880,
|
||||
AddSeqNoToDatasetRow1733752119696,
|
||||
AddPersonalWorkspace1734074497540,
|
||||
RefactorEnterpriseDatabase1737076223692,
|
||||
FixOpenSourceAssistantTable1743758056188,
|
||||
AddExecutionEntity1738090872625,
|
||||
AddErrorToEvaluationRun1744964560174,
|
||||
FixErrorsColumnInEvaluationRun1746437114935,
|
||||
ExecutionLinkWorkspaceId1746862866554,
|
||||
ModifyExecutionDataColumnType1747902489801
|
||||
]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddEvaluation1714548873039 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS evaluation (
|
||||
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"name" varchar NOT NULL,
|
||||
"chatflowId" text NOT NULL,
|
||||
"chatflowName" text NOT NULL,
|
||||
"datasetId" varchar NOT NULL,
|
||||
"datasetName" varchar NOT NULL,
|
||||
"additionalConfig" text NULL,
|
||||
"evaluationType" varchar NOT NULL,
|
||||
"status" varchar NOT NULL,
|
||||
"average_metrics" text NULL,
|
||||
"runDate" timestamp NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_98989043dd804f54-9830ab99f8" PRIMARY KEY (id)
|
||||
);`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS evaluation_run (
|
||||
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"evaluationId" varchar NOT NULL,
|
||||
"input" text NOT NULL,
|
||||
"expectedOutput" text NULL,
|
||||
"actualOutput" text NULL,
|
||||
"evaluators" text NULL,
|
||||
"llmEvaluators" text DEFAULT NULL,
|
||||
"metrics" text NULL,
|
||||
"runDate" timestamp NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_98989927dd804f54-9840ab23f8" PRIMARY KEY (id)
|
||||
);`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE evaluation`)
|
||||
await queryRunner.query(`DROP TABLE evaluation_run`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddDatasets1714548903384 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS dataset (
|
||||
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"name" varchar NOT NULL,
|
||||
"description" varchar NULL,
|
||||
"createdDate" timestamp NOT NULL DEFAULT now(),
|
||||
"updatedDate" timestamp NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_98419043dd804f54-9830ab99f8" PRIMARY KEY (id)
|
||||
);`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS dataset_row (
|
||||
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"datasetId" varchar NOT NULL,
|
||||
"input" text NOT NULL,
|
||||
"output" text NULL,
|
||||
"updatedDate" timestamp NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_98909027dd804f54-9840ab99f8" PRIMARY KEY (id)
|
||||
);`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE dataset`)
|
||||
await queryRunner.query(`DROP TABLE dataset_row`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddEvaluator1714808591644 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS evaluator (
|
||||
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"name" varchar NOT NULL,
|
||||
"type" text NULL,
|
||||
"config" text NULL,
|
||||
"createdDate" timestamp NOT NULL DEFAULT now(),
|
||||
"updatedDate" timestamp NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_90019043dd804f54-9830ab11f8" PRIMARY KEY (id)
|
||||
);`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE evaluator`)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSeqNoToDatasetRow1733752119696 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "dataset_row" ADD COLUMN IF NOT EXISTS "sequence_no" integer DEFAULT -1;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "dataset_row" DROP COLUMN "sequence_no";`)
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
import { Assistant } from '../../entities/Assistant'
|
||||
|
||||
export class FixOpenSourceAssistantTable1743758056188 implements MigrationInterface {
|
||||
name = 'FixOpenSourceAssistantTable1743758056188'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('assistant', 'type')
|
||||
if (!columnExists) {
|
||||
await queryRunner.query(`ALTER TABLE "assistant" ADD COLUMN "type" TEXT;`)
|
||||
await queryRunner.query(`UPDATE "assistant" SET "type" = 'OPENAI';`)
|
||||
|
||||
const assistants: Assistant[] = await queryRunner.query(`SELECT * FROM "assistant";`)
|
||||
for (let assistant of assistants) {
|
||||
const details = JSON.parse(assistant.details)
|
||||
if (!details?.id) await queryRunner.query(`UPDATE "assistant" SET "type" = 'CUSTOM' WHERE id = '${assistant.id}';`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "assistant" DROP COLUMN "type";`)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddErrorToEvaluationRun1744964560174 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "evaluation_run" ADD COLUMN IF NOT EXISTS "errors" TEXT NULL DEFAULT '[]';`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "evaluation_run" DROP COLUMN "errors";`)
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,12 @@ import { AddUpsertHistoryEntity1709814301358 } from './1709814301358-AddUpsertHi
|
||||
import { FieldTypes1710497452584 } from './1710497452584-FieldTypes'
|
||||
import { AddLead1710832137905 } from './1710832137905-AddLead'
|
||||
import { AddLeadToChatMessage1711538016098 } from './1711538016098-AddLeadToChatMessage'
|
||||
import { AddVectorStoreConfigToDocStore1715861032479 } from './1715861032479-AddVectorStoreConfigToDocStore'
|
||||
import { AddDocumentStore1711637331047 } from './1711637331047-AddDocumentStore'
|
||||
import { AddEvaluation1714548873039 } from './1714548873039-AddEvaluation'
|
||||
import { AddDatasets1714548903384 } from './1714548903384-AddDataset'
|
||||
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
|
||||
import { AddEvaluator1714808591644 } from './1714808591644-AddEvaluator'
|
||||
import { AddVectorStoreConfigToDocStore1715861032479 } from './1715861032479-AddVectorStoreConfigToDocStore'
|
||||
import { AddTypeToChatFlow1716300000000 } from './1716300000000-AddTypeToChatFlow'
|
||||
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
|
||||
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
|
||||
@@ -28,7 +31,22 @@ import { AddCustomTemplate1725629836652 } from './1725629836652-AddCustomTemplat
|
||||
import { AddArtifactsToChatMessage1726156258465 } from './1726156258465-AddArtifactsToChatMessage'
|
||||
import { AddFollowUpPrompts1726666309552 } from './1726666309552-AddFollowUpPrompts'
|
||||
import { AddTypeToAssistant1733011290987 } from './1733011290987-AddTypeToAssistant'
|
||||
import { AddSeqNoToDatasetRow1733752119696 } from './1733752119696-AddSeqNoToDatasetRow'
|
||||
import { AddExecutionEntity1738090872625 } from './1738090872625-AddExecutionEntity'
|
||||
import { FixOpenSourceAssistantTable1743758056188 } from './1743758056188-FixOpenSourceAssistantTable'
|
||||
import { AddErrorToEvaluationRun1744964560174 } from './1744964560174-AddErrorToEvaluationRun'
|
||||
|
||||
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/postgres/1720230151482-AddAuthTables'
|
||||
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/postgres/1720230151484-AddWorkspace'
|
||||
import { AddWorkspaceShared1726654922034 } from '../../../enterprise/database/migrations/postgres/1726654922034-AddWorkspaceShared'
|
||||
import { AddWorkspaceIdToCustomTemplate1726655750383 } from '../../../enterprise/database/migrations/postgres/1726655750383-AddWorkspaceIdToCustomTemplate'
|
||||
import { AddOrganization1727798417345 } from '../../../enterprise/database/migrations/postgres/1727798417345-AddOrganization'
|
||||
import { LinkWorkspaceId1729130948686 } from '../../../enterprise/database/migrations/postgres/1729130948686-LinkWorkspaceId'
|
||||
import { LinkOrganizationId1729133111652 } from '../../../enterprise/database/migrations/postgres/1729133111652-LinkOrganizationId'
|
||||
import { AddSSOColumns1730519457880 } from '../../../enterprise/database/migrations/postgres/1730519457880-AddSSOColumns'
|
||||
import { AddPersonalWorkspace1734074497540 } from '../../../enterprise/database/migrations/postgres/1734074497540-AddPersonalWorkspace'
|
||||
import { RefactorEnterpriseDatabase1737076223692 } from '../../../enterprise/database/migrations/postgres/1737076223692-RefactorEnterpriseDatabase'
|
||||
import { ExecutionLinkWorkspaceId1746862866554 } from '../../../enterprise/database/migrations/postgres/1746862866554-ExecutionLinkWorkspaceId'
|
||||
|
||||
export const postgresMigrations = [
|
||||
Init1693891895163,
|
||||
@@ -49,17 +67,34 @@ export const postgresMigrations = [
|
||||
AddUpsertHistoryEntity1709814301358,
|
||||
AddFeedback1707213601923,
|
||||
FieldTypes1710497452584,
|
||||
AddEvaluation1714548873039,
|
||||
AddDatasets1714548903384,
|
||||
AddEvaluator1714808591644,
|
||||
AddDocumentStore1711637331047,
|
||||
AddLead1710832137905,
|
||||
AddLeadToChatMessage1711538016098,
|
||||
AddAgentReasoningToChatMessage1714679514451,
|
||||
AddTypeToChatFlow1716300000000,
|
||||
AddVectorStoreConfigToDocStore1715861032479,
|
||||
AddTypeToChatFlow1716300000000,
|
||||
AddApiKey1720230151480,
|
||||
AddActionToChatMessage1721078251523,
|
||||
AddCustomTemplate1725629836652,
|
||||
AddArtifactsToChatMessage1726156258465,
|
||||
AddFollowUpPrompts1726666309552,
|
||||
AddTypeToAssistant1733011290987,
|
||||
AddExecutionEntity1738090872625
|
||||
AddAuthTables1720230151482,
|
||||
AddWorkspace1720230151484,
|
||||
AddWorkspaceShared1726654922034,
|
||||
AddWorkspaceIdToCustomTemplate1726655750383,
|
||||
AddOrganization1727798417345,
|
||||
LinkWorkspaceId1729130948686,
|
||||
LinkOrganizationId1729133111652,
|
||||
AddSSOColumns1730519457880,
|
||||
AddSeqNoToDatasetRow1733752119696,
|
||||
AddPersonalWorkspace1734074497540,
|
||||
RefactorEnterpriseDatabase1737076223692,
|
||||
AddExecutionEntity1738090872625,
|
||||
FixOpenSourceAssistantTable1743758056188,
|
||||
AddErrorToEvaluationRun1744964560174,
|
||||
ExecutionLinkWorkspaceId1746862866554
|
||||
]
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddEvaluation1714548873039 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS "evaluation" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"name" varchar NOT NULL,
|
||||
"chatflowId" text NOT NULL,
|
||||
"chatflowName" text NOT NULL,
|
||||
"datasetId" varchar NOT NULL,
|
||||
"datasetName" varchar NOT NULL,
|
||||
"additionalConfig" text,
|
||||
"status" varchar NOT NULL,
|
||||
"evaluationType" varchar,
|
||||
"average_metrics" text,
|
||||
"runDate" datetime NOT NULL DEFAULT (datetime('now')));`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS "evaluation_run" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"evaluationId" text NOT NULL,
|
||||
"input" text NOT NULL,
|
||||
"expectedOutput" text NOT NULL,
|
||||
"actualOutput" text NOT NULL,
|
||||
"evaluators" text,
|
||||
"llmEvaluators" TEXT DEFAULT NULL,
|
||||
"metrics" text NULL,
|
||||
"runDate" datetime NOT NULL DEFAULT (datetime('now')));`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE evaluation`)
|
||||
await queryRunner.query(`DROP TABLE evaluation_run`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddDatasets1714548903384 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS "dataset" ("id" varchar PRIMARY KEY NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"description" varchar,
|
||||
"createdDate" datetime NOT NULL DEFAULT (datetime('now')),
|
||||
"updatedDate" datetime NOT NULL DEFAULT (datetime('now')));`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS "dataset_row" ("id" varchar PRIMARY KEY NOT NULL,
|
||||
"datasetId" text NOT NULL,
|
||||
"input" text NOT NULL,
|
||||
"output" text NOT NULL,
|
||||
"updatedDate" datetime NOT NULL DEFAULT (datetime('now')));`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE dataset`)
|
||||
await queryRunner.query(`DROP TABLE dataset_row`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddEvaluator1714808591644 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS "evaluator" ("id" varchar PRIMARY KEY NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"type" varchar,
|
||||
"config" text,
|
||||
"createdDate" datetime NOT NULL DEFAULT (datetime('now')),
|
||||
"updatedDate" datetime NOT NULL DEFAULT (datetime('now')));`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE evaluator`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSeqNoToDatasetRow1733752119696 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "dataset_row" ADD COLUMN "sequence_no" integer DEFAULT -1;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "dataset_row" DROP COLUMN "sequence_no";`)
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
import { Assistant } from '../../entities/Assistant'
|
||||
|
||||
export async function fixOpenSourceAssistantTable(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('assistant', 'type')
|
||||
if (!columnExists) {
|
||||
await queryRunner.query(`ALTER TABLE "assistant" ADD COLUMN "type" TEXT;`)
|
||||
await queryRunner.query(`UPDATE "assistant" SET "type" = 'OPENAI';`)
|
||||
|
||||
const assistants: Assistant[] = await queryRunner.query(`SELECT * FROM "assistant";`)
|
||||
for (let assistant of assistants) {
|
||||
const details = JSON.parse(assistant.details)
|
||||
if (!details?.id) await queryRunner.query(`UPDATE "assistant" SET "type" = 'CUSTOM' WHERE id = '${assistant.id}';`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FixOpenSourceAssistantTable1743758056188 implements MigrationInterface {
|
||||
name = 'FixOpenSourceAssistantTable1743758056188'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await fixOpenSourceAssistantTable(queryRunner)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "assistant" DROP COLUMN "type";`)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddErrorToEvaluationRun1744964560174 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "evaluation_run" ADD COLUMN "errors" TEXT NULL DEFAULT '[]';`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "evaluation_run" DROP COLUMN "errors";`)
|
||||
}
|
||||
}
|
||||
@@ -17,17 +17,35 @@ import { AddFeedback1707213619308 } from './1707213619308-AddFeedback'
|
||||
import { AddUpsertHistoryEntity1709814301358 } from './1709814301358-AddUpsertHistoryEntity'
|
||||
import { AddLead1710832117612 } from './1710832117612-AddLead'
|
||||
import { AddLeadToChatMessage1711537986113 } from './1711537986113-AddLeadToChatMessage'
|
||||
import { AddVectorStoreConfigToDocStore1715861032479 } from './1715861032479-AddVectorStoreConfigToDocStore'
|
||||
import { AddDocumentStore1711637331047 } from './1711637331047-AddDocumentStore'
|
||||
import { AddEvaluation1714548873039 } from './1714548873039-AddEvaluation'
|
||||
import { AddDatasets1714548903384 } from './1714548903384-AddDataset'
|
||||
import { AddAgentReasoningToChatMessage1714679514451 } from './1714679514451-AddAgentReasoningToChatMessage'
|
||||
import { AddEvaluator1714808591644 } from './1714808591644-AddEvaluator'
|
||||
import { AddVectorStoreConfigToDocStore1715861032479 } from './1715861032479-AddVectorStoreConfigToDocStore'
|
||||
import { AddTypeToChatFlow1716300000000 } from './1716300000000-AddTypeToChatFlow'
|
||||
import { AddApiKey1720230151480 } from './1720230151480-AddApiKey'
|
||||
import { AddActionToChatMessage1721078251523 } from './1721078251523-AddActionToChatMessage'
|
||||
import { AddArtifactsToChatMessage1726156258465 } from './1726156258465-AddArtifactsToChatMessage'
|
||||
import { AddCustomTemplate1725629836652 } from './1725629836652-AddCustomTemplate'
|
||||
import { AddArtifactsToChatMessage1726156258465 } from './1726156258465-AddArtifactsToChatMessage'
|
||||
import { AddFollowUpPrompts1726666294213 } from './1726666294213-AddFollowUpPrompts'
|
||||
import { AddTypeToAssistant1733011290987 } from './1733011290987-AddTypeToAssistant'
|
||||
import { AddSeqNoToDatasetRow1733752119696 } from './1733752119696-AddSeqNoToDatasetRow'
|
||||
import { AddExecutionEntity1738090872625 } from './1738090872625-AddExecutionEntity'
|
||||
import { FixOpenSourceAssistantTable1743758056188 } from './1743758056188-FixOpenSourceAssistantTable'
|
||||
import { AddErrorToEvaluationRun1744964560174 } from './1744964560174-AddErrorToEvaluationRun'
|
||||
|
||||
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/sqlite/1720230151482-AddAuthTables'
|
||||
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/sqlite/1720230151484-AddWorkspace'
|
||||
import { AddWorkspaceShared1726654922034 } from '../../../enterprise/database/migrations/sqlite/1726654922034-AddWorkspaceShared'
|
||||
import { AddWorkspaceIdToCustomTemplate1726655750383 } from '../../../enterprise/database/migrations/sqlite/1726655750383-AddWorkspaceIdToCustomTemplate'
|
||||
import { AddOrganization1727798417345 } from '../../../enterprise/database/migrations/sqlite/1727798417345-AddOrganization'
|
||||
import { LinkWorkspaceId1729130948686 } from '../../../enterprise/database/migrations/sqlite/1729130948686-LinkWorkspaceId'
|
||||
import { LinkOrganizationId1729133111652 } from '../../../enterprise/database/migrations/sqlite/1729133111652-LinkOrganizationId'
|
||||
import { AddSSOColumns1730519457880 } from '../../../enterprise/database/migrations/sqlite/1730519457880-AddSSOColumns'
|
||||
import { AddPersonalWorkspace1734074497540 } from '../../../enterprise/database/migrations/sqlite/1734074497540-AddPersonalWorkspace'
|
||||
import { RefactorEnterpriseDatabase1737076223692 } from '../../../enterprise/database/migrations/sqlite/1737076223692-RefactorEnterpriseDatabase'
|
||||
import { ExecutionLinkWorkspaceId1746862866554 } from '../../../enterprise/database/migrations/sqlite/1746862866554-ExecutionLinkWorkspaceId'
|
||||
|
||||
export const sqliteMigrations = [
|
||||
Init1693835579790,
|
||||
@@ -46,18 +64,35 @@ export const sqliteMigrations = [
|
||||
AddFileUploadsToChatMessage1701788586491,
|
||||
AddSpeechToText1706364937060,
|
||||
AddUpsertHistoryEntity1709814301358,
|
||||
AddEvaluation1714548873039,
|
||||
AddDatasets1714548903384,
|
||||
AddEvaluator1714808591644,
|
||||
AddFeedback1707213619308,
|
||||
AddDocumentStore1711637331047,
|
||||
AddLead1710832117612,
|
||||
AddLeadToChatMessage1711537986113,
|
||||
AddAgentReasoningToChatMessage1714679514451,
|
||||
AddTypeToChatFlow1716300000000,
|
||||
AddVectorStoreConfigToDocStore1715861032479,
|
||||
AddTypeToChatFlow1716300000000,
|
||||
AddApiKey1720230151480,
|
||||
AddActionToChatMessage1721078251523,
|
||||
AddArtifactsToChatMessage1726156258465,
|
||||
AddCustomTemplate1725629836652,
|
||||
AddFollowUpPrompts1726666294213,
|
||||
AddTypeToAssistant1733011290987,
|
||||
AddExecutionEntity1738090872625
|
||||
AddCustomTemplate1725629836652,
|
||||
AddAuthTables1720230151482,
|
||||
AddWorkspace1720230151484,
|
||||
AddWorkspaceShared1726654922034,
|
||||
AddWorkspaceIdToCustomTemplate1726655750383,
|
||||
AddOrganization1727798417345,
|
||||
LinkWorkspaceId1729130948686,
|
||||
LinkOrganizationId1729133111652,
|
||||
AddSSOColumns1730519457880,
|
||||
AddSeqNoToDatasetRow1733752119696,
|
||||
AddPersonalWorkspace1734074497540,
|
||||
RefactorEnterpriseDatabase1737076223692,
|
||||
AddExecutionEntity1738090872625,
|
||||
FixOpenSourceAssistantTable1743758056188,
|
||||
AddErrorToEvaluationRun1744964560174,
|
||||
ExecutionLinkWorkspaceId1746862866554
|
||||
]
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export enum UserStatus {
|
||||
INVITED = 'invited',
|
||||
DISABLED = 'disabled',
|
||||
ACTIVE = 'active'
|
||||
}
|
||||
|
||||
export class IUser {
|
||||
id: string
|
||||
email: string
|
||||
name: string
|
||||
credential: string
|
||||
status: UserStatus
|
||||
tempToken: string
|
||||
tokenExpiry?: Date
|
||||
role: string
|
||||
lastLogin: Date
|
||||
activeWorkspaceId: string
|
||||
isApiKeyValidated?: boolean
|
||||
loginMode?: string
|
||||
activeOrganizationId?: string
|
||||
}
|
||||
|
||||
export interface IWorkspaceUser {
|
||||
id: string
|
||||
workspaceId: string
|
||||
userId: string
|
||||
role: string
|
||||
}
|
||||
|
||||
export interface IWorkspaceShared {
|
||||
id: string
|
||||
workspaceId: string
|
||||
sharedItemId: string
|
||||
itemType: string
|
||||
createdDate: Date
|
||||
updatedDate: Date
|
||||
}
|
||||
|
||||
export interface ILoginActivity {
|
||||
id: string
|
||||
username: string
|
||||
activityCode: number
|
||||
message: string
|
||||
loginMode: string
|
||||
attemptedDateTime: Date
|
||||
}
|
||||
|
||||
export enum LoginActivityCode {
|
||||
LOGIN_SUCCESS = 0,
|
||||
LOGOUT_SUCCESS = 1,
|
||||
UNKNOWN_USER = -1,
|
||||
INCORRECT_CREDENTIAL = -2,
|
||||
USER_DISABLED = -3,
|
||||
NO_ASSIGNED_WORKSPACE = -4,
|
||||
INVALID_LOGIN_MODE = -5,
|
||||
REGISTRATION_PENDING = -6,
|
||||
UNKNOWN_ERROR = -99
|
||||
}
|
||||
|
||||
export type IAssignedWorkspace = { id: string; name: string; role: string; organizationId: string }
|
||||
export type LoggedInUser = {
|
||||
id: string
|
||||
email: string
|
||||
name: string
|
||||
roleId: string
|
||||
activeOrganizationId: string
|
||||
activeOrganizationSubscriptionId: string
|
||||
activeOrganizationCustomerId: string
|
||||
activeOrganizationProductId: string
|
||||
isOrganizationAdmin: boolean
|
||||
activeWorkspaceId: string
|
||||
activeWorkspace: string
|
||||
assignedWorkspaces: IAssignedWorkspace[]
|
||||
isApiKeyValidated: boolean
|
||||
permissions?: string[]
|
||||
features?: Record<string, string>
|
||||
ssoRefreshToken?: string
|
||||
ssoToken?: string
|
||||
ssoProvider?: string
|
||||
}
|
||||
|
||||
export enum ErrorMessage {
|
||||
INVALID_MISSING_TOKEN = 'Invalid or Missing token',
|
||||
TOKEN_EXPIRED = 'Token Expired',
|
||||
REFRESH_TOKEN_EXPIRED = 'Refresh Token Expired',
|
||||
FORBIDDEN = 'Forbidden',
|
||||
UNKNOWN_USER = 'Unknown Username or Password',
|
||||
INCORRECT_PASSWORD = 'Incorrect Password',
|
||||
INACTIVE_USER = 'Inactive User',
|
||||
INVITED_USER = 'User Invited, but has not registered',
|
||||
INVALID_WORKSPACE = 'No Workspace Assigned',
|
||||
UNKNOWN_ERROR = 'Unknown Error'
|
||||
}
|
||||
|
||||
// IMPORTANT: update the schema on the client side as well
|
||||
// packages/ui/src/views/organization/index.jsx
|
||||
export const OrgSetupSchema = z
|
||||
.object({
|
||||
orgName: z.string().min(1, 'Organization name is required'),
|
||||
username: z.string().min(1, 'Name is required'),
|
||||
email: z.string().min(1, 'Email is required').email('Invalid email address'),
|
||||
password: z
|
||||
.string()
|
||||
.min(8, 'Password must be at least 8 characters')
|
||||
.regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
|
||||
.regex(/[!@#$%^&*]/, 'Password must contain at least one special character'),
|
||||
confirmPassword: z.string().min(1, 'Confirm Password is required')
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
message: "Passwords don't match",
|
||||
path: ['confirmPassword']
|
||||
})
|
||||
|
||||
// IMPORTANT: when updating this schema, update the schema on the server as well
|
||||
// packages/ui/src/views/auth/register.jsx
|
||||
export const RegisterUserSchema = z
|
||||
.object({
|
||||
username: z.string().min(1, 'Name is required'),
|
||||
email: z.string().min(1, 'Email is required').email('Invalid email address'),
|
||||
password: z
|
||||
.string()
|
||||
.min(8, 'Password must be at least 8 characters')
|
||||
.regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
|
||||
.regex(/[!@#$%^&*]/, 'Password must contain at least one special character'),
|
||||
confirmPassword: z.string().min(1, 'Confirm Password is required'),
|
||||
token: z.string().min(1, 'Invite Code is required')
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
message: "Passwords don't match",
|
||||
path: ['confirmPassword']
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
The FlowiseAI Inc Commercial License (the "Commercial License")
|
||||
Copyright (c) 2023-present FlowiseAI, Inc
|
||||
|
||||
With regard to the FlowiseAI Inc Software:
|
||||
|
||||
This software and associated documentation files (the "Software") may only be
|
||||
used in production, if you (and any entity that you represent) have agreed to,
|
||||
and are in compliance with, the FlowiseAI Inc Subscription Terms available
|
||||
at https://flowiseai.com/terms, or other agreements governing
|
||||
the use of the Software, as mutually agreed by you and FlowiseAI Inc, Inc ("FlowiseAI"),
|
||||
and otherwise have a valid FlowiseAI Inc Enterprise Edition subscription ("Commercial Subscription")
|
||||
for the correct number of hosts as defined in the "Commercial Terms ("Hosts"). Subject to the foregoing sentence,
|
||||
you are free to modify this Software and publish patches to the Software. You agree
|
||||
that FlowiseAI Inc and/or its licensors (as applicable) retain all right, title and interest in
|
||||
and to all such modifications and/or patches, and all such modifications and/or
|
||||
patches may only be used, copied, modified, displayed, distributed, or otherwise
|
||||
exploited with a valid Commercial Subscription for the correct number of hosts.
|
||||
Notwithstanding the foregoing, you may copy and modify the Software for development
|
||||
and testing purposes, without requiring a subscription. You agree that FlowiseAI Inc and/or
|
||||
its licensors (as applicable) retain all right, title and interest in and to all such
|
||||
modifications. You are not granted any other rights beyond what is expressly stated herein.
|
||||
Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
|
||||
and/or sell the Software.
|
||||
|
||||
This Commercial License applies only to the part of this Software that is not distributed under
|
||||
the Apache 2.0 license. The Open Source version of Flowise is licensed under the Apache License, Version 2.0.
|
||||
Unauthorized copying, modification, distribution, or use of the Enterprise and Cloud versions
|
||||
is strictly prohibited without a valid license agreement from FlowiseAI, Inc.
|
||||
|
||||
For information about licensing of the Enterprise and Cloud versions, please contact:
|
||||
security@flowiseai.com
|
||||
|
||||
The full text of this Commercial License shall
|
||||
be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
For all third party components incorporated into the FlowiseAI Inc Software, those
|
||||
components are licensed under the original license provided by the owner of the
|
||||
applicable component.
|
||||
@@ -0,0 +1,161 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { AccountService } from '../services/account.service'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import axios from 'axios'
|
||||
|
||||
export class AccountController {
|
||||
public async register(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const accountService = new AccountService()
|
||||
const data = await accountService.register(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(data)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async invite(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const accountService = new AccountService()
|
||||
const data = await accountService.invite(req.body, req.user)
|
||||
return res.status(StatusCodes.CREATED).json(data)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async login(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const accountService = new AccountService()
|
||||
const data = await accountService.login(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(data)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async verify(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const accountService = new AccountService()
|
||||
const data = await accountService.verify(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(data)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async resendVerificationEmail(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const accountService = new AccountService()
|
||||
const data = await accountService.resendVerificationEmail(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(data)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async forgotPassword(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const accountService = new AccountService()
|
||||
const data = await accountService.forgotPassword(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(data)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async resetPassword(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const accountService = new AccountService()
|
||||
const data = await accountService.resetPassword(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(data)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async createStripeCustomerPortalSession(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { url: portalSessionUrl } = await getRunningExpressApp().identityManager.createStripeCustomerPortalSession(req)
|
||||
return res.status(StatusCodes.OK).json({ url: portalSessionUrl })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async cancelPreviousCloudSubscrption(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { email } = req.body
|
||||
if (!email) {
|
||||
return res.status(StatusCodes.BAD_REQUEST).json({ message: 'Email is required' })
|
||||
}
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json'
|
||||
}
|
||||
|
||||
const response = await axios.post(`${process.env.ENGINE_URL}/cancel-subscription`, { email }, { headers })
|
||||
|
||||
if (response.status === 200) {
|
||||
return res.status(StatusCodes.OK).json(response.data)
|
||||
} else {
|
||||
return res.status(response.status).json(response.data)
|
||||
}
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async logout(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (req.user) {
|
||||
const accountService = new AccountService()
|
||||
await accountService.logout(req.user)
|
||||
if (req.isAuthenticated()) {
|
||||
req.logout((err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ message: 'Logout failed' })
|
||||
}
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ message: 'Failed to destroy session' })
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// For JWT-based users (owner, org_admin)
|
||||
res.clearCookie('connect.sid') // Clear the session cookie
|
||||
res.clearCookie('token') // Clear the JWT cookie
|
||||
res.clearCookie('refreshToken') // Clear the JWT cookie
|
||||
return res.redirect('/login') // Redirect to the login page
|
||||
}
|
||||
}
|
||||
return res.status(200).json({ message: 'logged_out', redirectTo: `/login` })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async getBasicAuth(req: Request, res: Response) {
|
||||
if (process.env.FLOWISE_USERNAME && process.env.FLOWISE_PASSWORD) {
|
||||
return res.status(StatusCodes.OK).json({
|
||||
isUsernamePasswordSet: true
|
||||
})
|
||||
} else {
|
||||
return res.status(StatusCodes.OK).json({
|
||||
isUsernamePasswordSet: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public async checkBasicAuth(req: Request, res: Response) {
|
||||
const { username, password } = req.body
|
||||
if (username === process.env.FLOWISE_USERNAME && password === process.env.FLOWISE_PASSWORD) {
|
||||
return res.json({ message: 'Authentication successful' })
|
||||
} else {
|
||||
return res.json({ message: 'Authentication failed' })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import auditService from '../../services/audit'
|
||||
import { InternalFlowiseError } from '../../../errors/internalFlowiseError'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
|
||||
const fetchLoginActivity = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.body === 'undefined') {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: auditService.fetchLoginHistory - body not provided!`)
|
||||
}
|
||||
const apiResponse = await auditService.fetchLoginActivity(req.body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteLoginActivity = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
if (typeof req.body === 'undefined') {
|
||||
throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: auditService.deleteLoginHistory - body not provided!`)
|
||||
}
|
||||
const apiResponse = await auditService.deleteLoginActivity(req.body)
|
||||
return res.json(apiResponse)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchLoginActivity,
|
||||
deleteLoginActivity
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { getRunningExpressApp } from '../../../utils/getRunningExpressApp'
|
||||
|
||||
const getAllPermissions = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const appServer = getRunningExpressApp()
|
||||
return res.json(appServer.identityManager.getPermissions())
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getAllPermissions
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { LoginMethodErrorMessage, LoginMethodService } from '../services/login-method.service'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { LoginMethod, LoginMethodStatus } from '../database/entities/login-method.entity'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { decrypt } from '../utils/encryption.util'
|
||||
import AzureSSO from '../sso/AzureSSO'
|
||||
import GoogleSSO from '../sso/GoogleSSO'
|
||||
import Auth0SSO from '../sso/Auth0SSO'
|
||||
import { OrganizationService } from '../services/organization.service'
|
||||
import { Platform } from '../../Interface'
|
||||
import GithubSSO from '../sso/GithubSSO'
|
||||
|
||||
export class LoginMethodController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const loginMethodService = new LoginMethodService()
|
||||
const loginMethod = await loginMethodService.createLoginMethod(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(loginMethod)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async defaultMethods(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
let organizationId
|
||||
if (getRunningExpressApp().identityManager.getPlatformType() === Platform.CLOUD) {
|
||||
organizationId = undefined
|
||||
} else if (getRunningExpressApp().identityManager.getPlatformType() === Platform.ENTERPRISE) {
|
||||
const organizationService = new OrganizationService()
|
||||
const organizations = await organizationService.readOrganization(queryRunner)
|
||||
if (organizations.length > 0) {
|
||||
organizationId = organizations[0].id
|
||||
} else {
|
||||
return res.status(StatusCodes.OK).json({})
|
||||
}
|
||||
} else {
|
||||
return res.status(StatusCodes.OK).json({})
|
||||
}
|
||||
const loginMethodService = new LoginMethodService()
|
||||
|
||||
const providers: string[] = []
|
||||
|
||||
let loginMethod = await loginMethodService.readLoginMethodByOrganizationId(organizationId, queryRunner)
|
||||
if (loginMethod) {
|
||||
for (let method of loginMethod) {
|
||||
if (method.status === LoginMethodStatus.ENABLE) providers.push(method.name)
|
||||
}
|
||||
}
|
||||
return res.status(StatusCodes.OK).json({ providers: providers })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async read(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as Partial<LoginMethod>
|
||||
const loginMethodService = new LoginMethodService()
|
||||
|
||||
const loginMethodConfig = {
|
||||
providers: [],
|
||||
callbacks: [
|
||||
{ providerName: 'azure', callbackURL: AzureSSO.getCallbackURL() },
|
||||
{ providerName: 'google', callbackURL: GoogleSSO.getCallbackURL() },
|
||||
{ providerName: 'auth0', callbackURL: Auth0SSO.getCallbackURL() },
|
||||
{ providerName: 'github', callbackURL: GithubSSO.getCallbackURL() }
|
||||
]
|
||||
}
|
||||
let loginMethod: any
|
||||
if (query.id) {
|
||||
loginMethod = await loginMethodService.readLoginMethodById(query.id, queryRunner)
|
||||
if (!loginMethod) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, LoginMethodErrorMessage.LOGIN_METHOD_NOT_FOUND)
|
||||
loginMethod.config = JSON.parse(await decrypt(loginMethod.config))
|
||||
} else {
|
||||
loginMethod = await loginMethodService.readLoginMethodByOrganizationId(query.organizationId, queryRunner)
|
||||
|
||||
for (let method of loginMethod) {
|
||||
method.config = JSON.parse(await decrypt(method.config))
|
||||
}
|
||||
loginMethodConfig.providers = loginMethod
|
||||
}
|
||||
return res.status(StatusCodes.OK).json(loginMethodConfig)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const loginMethodService = new LoginMethodService()
|
||||
const loginMethod = await loginMethodService.createOrUpdateConfig(req.body)
|
||||
if (loginMethod?.status === 'OK' && loginMethod?.organizationId) {
|
||||
const appServer = getRunningExpressApp()
|
||||
let providers: any[] = req.body.providers
|
||||
providers.map((provider: any) => {
|
||||
const identityManager = appServer.identityManager
|
||||
if (provider.config.clientID) {
|
||||
provider.config.configEnabled = provider.status === LoginMethodStatus.ENABLE
|
||||
identityManager.initializeSsoProvider(appServer.app, provider.providerName, provider.config)
|
||||
}
|
||||
})
|
||||
}
|
||||
return res.status(StatusCodes.OK).json(loginMethod)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
public async testConfig(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const providers = req.body.providers
|
||||
if (req.body.providerName === 'azure') {
|
||||
const response = await AzureSSO.testSetup(providers[0].config)
|
||||
return res.json(response)
|
||||
} else if (req.body.providerName === 'google') {
|
||||
const response = await GoogleSSO.testSetup(providers[0].config)
|
||||
return res.json(response)
|
||||
} else if (req.body.providerName === 'auth0') {
|
||||
const response = await Auth0SSO.testSetup(providers[0].config)
|
||||
return res.json(response)
|
||||
} else if (req.body.providerName === 'github') {
|
||||
const response = await GithubSSO.testSetup(providers[0].config)
|
||||
return res.json(response)
|
||||
} else {
|
||||
return res.json({ error: 'Provider not supported' })
|
||||
}
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { GeneralErrorMessage } from '../../utils/constants'
|
||||
import { checkUsageLimit } from '../../utils/quotaUsage'
|
||||
import { OrganizationUser } from '../database/entities/organization-user.entity'
|
||||
import { Organization } from '../database/entities/organization.entity'
|
||||
|
||||
type OrganizationUserQuery = Partial<Pick<OrganizationUser, 'organizationId' | 'userId' | 'roleId'>>
|
||||
|
||||
import { QueryRunner } from 'typeorm'
|
||||
import { Platform } from '../../Interface'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { GeneralRole } from '../database/entities/role.entity'
|
||||
import { User, UserStatus } from '../database/entities/user.entity'
|
||||
import { WorkspaceUser } from '../database/entities/workspace-user.entity'
|
||||
import { OrganizationUserService } from '../services/organization-user.service'
|
||||
import { RoleService } from '../services/role.service'
|
||||
import { WorkspaceService } from '../services/workspace.service'
|
||||
|
||||
export class OrganizationUserController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const organizationUserservice = new OrganizationUserService()
|
||||
const totalOrgUsers = await organizationUserservice.readOrgUsersCountByOrgId(req.body.organizationId)
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId || ''
|
||||
await checkUsageLimit('users', subscriptionId, getRunningExpressApp().usageCacheManager, totalOrgUsers + 1)
|
||||
const newOrganizationUser = await organizationUserservice.createOrganizationUser(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(newOrganizationUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async read(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as OrganizationUserQuery
|
||||
const organizationUserservice = new OrganizationUserService()
|
||||
|
||||
let organizationUser:
|
||||
| {
|
||||
organization: Organization
|
||||
organizationUser: OrganizationUser | null
|
||||
}
|
||||
| OrganizationUser
|
||||
| null
|
||||
| OrganizationUser[]
|
||||
| (OrganizationUser & {
|
||||
roleCount: number
|
||||
})[]
|
||||
if (query.organizationId && query.userId) {
|
||||
organizationUser = await organizationUserservice.readOrganizationUserByOrganizationIdUserId(
|
||||
query.organizationId,
|
||||
query.userId,
|
||||
queryRunner
|
||||
)
|
||||
} else if (query.organizationId && query.roleId) {
|
||||
organizationUser = await organizationUserservice.readOrganizationUserByOrganizationIdRoleId(
|
||||
query.organizationId,
|
||||
query.roleId,
|
||||
queryRunner
|
||||
)
|
||||
} else if (query.organizationId) {
|
||||
organizationUser = await organizationUserservice.readOrganizationUserByOrganizationId(query.organizationId, queryRunner)
|
||||
} else if (query.userId) {
|
||||
organizationUser = await organizationUserservice.readOrganizationUserByUserId(query.userId, queryRunner)
|
||||
} else {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
}
|
||||
|
||||
return res.status(StatusCodes.OK).json(organizationUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const organizationUserService = new OrganizationUserService()
|
||||
const organizationUser = await organizationUserService.updateOrganizationUser(req.body)
|
||||
return res.status(StatusCodes.OK).json(organizationUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner: QueryRunner | undefined
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
const currentPlatform = getRunningExpressApp().identityManager.getPlatformType()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as Partial<OrganizationUser>
|
||||
if (!query.organizationId) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'Organization ID is required')
|
||||
}
|
||||
if (!query.userId) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'User ID is required')
|
||||
}
|
||||
|
||||
const organizationUserService = new OrganizationUserService()
|
||||
const workspaceService = new WorkspaceService()
|
||||
const roleService = new RoleService()
|
||||
|
||||
let organizationUser: OrganizationUser
|
||||
await queryRunner.startTransaction()
|
||||
if (currentPlatform === Platform.ENTERPRISE) {
|
||||
const personalRole = await roleService.readGeneralRoleByName(GeneralRole.PERSONAL_WORKSPACE, queryRunner)
|
||||
const personalWorkspaces = await queryRunner.manager.findBy(WorkspaceUser, {
|
||||
userId: query.userId,
|
||||
roleId: personalRole.id
|
||||
})
|
||||
if (personalWorkspaces.length === 1)
|
||||
// delete personal workspace
|
||||
await workspaceService.deleteWorkspaceById(queryRunner, personalWorkspaces[0].workspaceId)
|
||||
// remove user from other workspces
|
||||
organizationUser = await organizationUserService.deleteOrganizationUser(queryRunner, query.organizationId, query.userId)
|
||||
// soft delete user because they might workspace might created by them
|
||||
const deleteUser = await queryRunner.manager.findOneBy(User, { id: query.userId })
|
||||
if (!deleteUser) throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
deleteUser.name = UserStatus.DELETED
|
||||
deleteUser.email = `deleted_${deleteUser.id}_${Date.now()}@deleted.flowise`
|
||||
deleteUser.status = UserStatus.DELETED
|
||||
deleteUser.credential = null
|
||||
deleteUser.tokenExpiry = null
|
||||
deleteUser.tempToken = null
|
||||
await queryRunner.manager.save(User, deleteUser)
|
||||
} else {
|
||||
organizationUser = await organizationUserService.deleteOrganizationUser(queryRunner, query.organizationId, query.userId)
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
return res.status(StatusCodes.OK).json(organizationUser)
|
||||
} catch (error) {
|
||||
if (queryRunner && queryRunner.isTransactionActive) await queryRunner.rollbackTransaction()
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner && !queryRunner.isReleased) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { OrganizationErrorMessage, OrganizationService } from '../services/organization.service'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { Organization } from '../database/entities/organization.entity'
|
||||
import { GeneralErrorMessage } from '../../utils/constants'
|
||||
import { OrganizationUserService } from '../services/organization-user.service'
|
||||
import { getCurrentUsage } from '../../utils/quotaUsage'
|
||||
|
||||
export class OrganizationController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const organizationUserService = new OrganizationUserService()
|
||||
const newOrganization = await organizationUserService.createOrganization(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(newOrganization)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async read(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as Partial<Organization>
|
||||
const organizationService = new OrganizationService()
|
||||
|
||||
let organization: Organization | null
|
||||
if (query.id) {
|
||||
organization = await organizationService.readOrganizationById(query.id, queryRunner)
|
||||
if (!organization) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, OrganizationErrorMessage.ORGANIZATION_NOT_FOUND)
|
||||
} else if (query.name) {
|
||||
organization = await organizationService.readOrganizationByName(query.name, queryRunner)
|
||||
if (!organization) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, OrganizationErrorMessage.ORGANIZATION_NOT_FOUND)
|
||||
} else {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
}
|
||||
|
||||
return res.status(StatusCodes.OK).json(organization)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const organizationService = new OrganizationService()
|
||||
const organization = await organizationService.updateOrganization(req.body)
|
||||
return res.status(StatusCodes.OK).json(organization)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async getAdditionalSeatsQuantity(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { subscriptionId } = req.query
|
||||
if (!subscriptionId) {
|
||||
return res.status(400).json({ error: 'Subscription ID is required' })
|
||||
}
|
||||
const organizationUserservice = new OrganizationUserService()
|
||||
const totalOrgUsers = await organizationUserservice.readOrgUsersCountByOrgId(req.user?.activeOrganizationId as string)
|
||||
|
||||
const identityManager = getRunningExpressApp().identityManager
|
||||
const result = await identityManager.getAdditionalSeatsQuantity(subscriptionId as string)
|
||||
|
||||
return res.status(StatusCodes.OK).json({ ...result, totalOrgUsers })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async getCustomerWithDefaultSource(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { customerId } = req.query
|
||||
if (!customerId) {
|
||||
return res.status(400).json({ error: 'Customer ID is required' })
|
||||
}
|
||||
const identityManager = getRunningExpressApp().identityManager
|
||||
const result = await identityManager.getCustomerWithDefaultSource(customerId as string)
|
||||
|
||||
return res.status(StatusCodes.OK).json(result)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async getAdditionalSeatsProration(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { subscriptionId, quantity } = req.query
|
||||
if (!subscriptionId) {
|
||||
return res.status(400).json({ error: 'Customer ID is required' })
|
||||
}
|
||||
if (quantity === undefined) {
|
||||
return res.status(400).json({ error: 'Quantity is required' })
|
||||
}
|
||||
const identityManager = getRunningExpressApp().identityManager
|
||||
const result = await identityManager.getAdditionalSeatsProration(subscriptionId as string, parseInt(quantity as string))
|
||||
|
||||
return res.status(StatusCodes.OK).json(result)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async getPlanProration(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { subscriptionId, newPlanId } = req.query
|
||||
if (!subscriptionId) {
|
||||
return res.status(400).json({ error: 'Subscription ID is required' })
|
||||
}
|
||||
if (!newPlanId) {
|
||||
return res.status(400).json({ error: 'New plan ID is required' })
|
||||
}
|
||||
const identityManager = getRunningExpressApp().identityManager
|
||||
const result = await identityManager.getPlanProration(subscriptionId as string, newPlanId as string)
|
||||
|
||||
return res.status(StatusCodes.OK).json(result)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async updateAdditionalSeats(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { subscriptionId, quantity, prorationDate } = req.body
|
||||
if (!subscriptionId) {
|
||||
return res.status(400).json({ error: 'Subscription ID is required' })
|
||||
}
|
||||
if (quantity === undefined) {
|
||||
return res.status(400).json({ error: 'Quantity is required' })
|
||||
}
|
||||
if (!prorationDate) {
|
||||
return res.status(400).json({ error: 'Proration date is required' })
|
||||
}
|
||||
const identityManager = getRunningExpressApp().identityManager
|
||||
const result = await identityManager.updateAdditionalSeats(subscriptionId, quantity, prorationDate)
|
||||
|
||||
return res.status(StatusCodes.OK).json(result)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async updateSubscriptionPlan(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { subscriptionId, newPlanId, prorationDate } = req.body
|
||||
if (!subscriptionId) {
|
||||
return res.status(400).json({ error: 'Subscription ID is required' })
|
||||
}
|
||||
if (!newPlanId) {
|
||||
return res.status(400).json({ error: 'New plan ID is required' })
|
||||
}
|
||||
if (!prorationDate) {
|
||||
return res.status(400).json({ error: 'Proration date is required' })
|
||||
}
|
||||
const identityManager = getRunningExpressApp().identityManager
|
||||
const result = await identityManager.updateSubscriptionPlan(req, subscriptionId, newPlanId, prorationDate)
|
||||
|
||||
return res.status(StatusCodes.OK).json(result)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async getCurrentUsage(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const orgId = req.user?.activeOrganizationId
|
||||
const subscriptionId = req.user?.activeOrganizationSubscriptionId
|
||||
if (!orgId) {
|
||||
return res.status(400).json({ error: 'Organization ID is required' })
|
||||
}
|
||||
if (!subscriptionId) {
|
||||
return res.status(400).json({ error: 'Subscription ID is required' })
|
||||
}
|
||||
const usageCacheManager = getRunningExpressApp().usageCacheManager
|
||||
const result = await getCurrentUsage(orgId, subscriptionId, usageCacheManager)
|
||||
return res.status(StatusCodes.OK).json(result)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { Role } from '../database/entities/role.entity'
|
||||
import { RoleService } from '../services/role.service'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
|
||||
export class RoleController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const roleService = new RoleService()
|
||||
const newRole = await roleService.createRole(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(newRole)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async read(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as Partial<Role>
|
||||
const roleService = new RoleService()
|
||||
|
||||
let role: Role | Role[] | null | (Role & { userCount: number })[]
|
||||
if (query.id) {
|
||||
role = await roleService.readRoleById(query.id, queryRunner)
|
||||
} else if (query.organizationId) {
|
||||
role = await roleService.readRoleByOrganizationId(query.organizationId, queryRunner)
|
||||
} else {
|
||||
role = await roleService.readRoleByGeneral(queryRunner)
|
||||
}
|
||||
|
||||
return res.status(StatusCodes.OK).json(role)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const roleService = new RoleService()
|
||||
const role = await roleService.updateRole(req.body)
|
||||
return res.status(StatusCodes.OK).json(role)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const query = req.query as Partial<Role>
|
||||
if (!query.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'Role ID is required')
|
||||
}
|
||||
if (!query.organizationId) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'Organization ID is required')
|
||||
}
|
||||
const roleService = new RoleService()
|
||||
const role = await roleService.deleteRole(query.organizationId, query.id)
|
||||
return res.status(StatusCodes.OK).json(role)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { GeneralErrorMessage } from '../../utils/constants'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { User } from '../database/entities/user.entity'
|
||||
import { UserErrorMessage, UserService } from '../services/user.service'
|
||||
|
||||
export class UserController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const userService = new UserService()
|
||||
const user = await userService.createUser(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(user)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async read(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as Partial<User>
|
||||
const userService = new UserService()
|
||||
|
||||
let user: User | null
|
||||
if (query.id) {
|
||||
user = await userService.readUserById(query.id, queryRunner)
|
||||
if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
|
||||
} else if (query.email) {
|
||||
user = await userService.readUserByEmail(query.email, queryRunner)
|
||||
if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
|
||||
} else {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
}
|
||||
|
||||
if (user) {
|
||||
delete user.credential
|
||||
delete user.tempToken
|
||||
delete user.tokenExpiry
|
||||
}
|
||||
return res.status(StatusCodes.OK).json(user)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const userService = new UserService()
|
||||
const currentUser = req.user
|
||||
if (!currentUser) {
|
||||
throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, UserErrorMessage.USER_NOT_FOUND)
|
||||
}
|
||||
const { id } = req.body
|
||||
if (currentUser.id !== id) {
|
||||
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, UserErrorMessage.USER_NOT_FOUND)
|
||||
}
|
||||
const user = await userService.updateUser(req.body)
|
||||
return res.status(StatusCodes.OK).json(user)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async test(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
return res.status(StatusCodes.OK).json({ message: 'Hello World' })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { WorkspaceUserService } from '../services/workspace-user.service'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { WorkspaceUser } from '../database/entities/workspace-user.entity'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { GeneralErrorMessage } from '../../utils/constants'
|
||||
|
||||
export class WorkspaceUserController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const newWorkspaceUser = await workspaceUserService.createWorkspaceUser(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(newWorkspaceUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async read(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as Partial<WorkspaceUser & { organizationId: string | undefined }>
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
|
||||
let workspaceUser: any
|
||||
if (query.workspaceId && query.userId) {
|
||||
workspaceUser = await workspaceUserService.readWorkspaceUserByWorkspaceIdUserId(
|
||||
query.workspaceId,
|
||||
query.userId,
|
||||
queryRunner
|
||||
)
|
||||
} else if (query.workspaceId) {
|
||||
workspaceUser = await workspaceUserService.readWorkspaceUserByWorkspaceId(query.workspaceId, queryRunner)
|
||||
} else if (query.organizationId && query.userId) {
|
||||
workspaceUser = await workspaceUserService.readWorkspaceUserByOrganizationIdUserId(
|
||||
query.organizationId,
|
||||
query.userId,
|
||||
queryRunner
|
||||
)
|
||||
} else if (query.userId) {
|
||||
workspaceUser = await workspaceUserService.readWorkspaceUserByUserId(query.userId, queryRunner)
|
||||
} else if (query.roleId) {
|
||||
workspaceUser = await workspaceUserService.readWorkspaceUserByRoleId(query.roleId, queryRunner)
|
||||
} else {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
}
|
||||
|
||||
return res.status(StatusCodes.OK).json(workspaceUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const workspaceUser = await workspaceUserService.updateWorkspaceUser(req.body)
|
||||
return res.status(StatusCodes.OK).json(workspaceUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const query = req.query as Partial<WorkspaceUser>
|
||||
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const workspaceUser = await workspaceUserService.deleteWorkspaceUser(query.workspaceId, query.userId)
|
||||
return res.status(StatusCodes.OK).json(workspaceUser)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { QueryRunner } from 'typeorm'
|
||||
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
|
||||
import { GeneralErrorMessage } from '../../utils/constants'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import { OrganizationUserStatus } from '../database/entities/organization-user.entity'
|
||||
import { GeneralRole } from '../database/entities/role.entity'
|
||||
import { WorkspaceUserStatus } from '../database/entities/workspace-user.entity'
|
||||
import { Workspace } from '../database/entities/workspace.entity'
|
||||
import { IAssignedWorkspace, LoggedInUser } from '../Interface.Enterprise'
|
||||
import { OrganizationUserErrorMessage, OrganizationUserService } from '../services/organization-user.service'
|
||||
import { OrganizationErrorMessage, OrganizationService } from '../services/organization.service'
|
||||
import { RoleErrorMessage, RoleService } from '../services/role.service'
|
||||
import { UserErrorMessage, UserService } from '../services/user.service'
|
||||
import { WorkspaceUserErrorMessage, WorkspaceUserService } from '../services/workspace-user.service'
|
||||
import { WorkspaceErrorMessage, WorkspaceService } from '../services/workspace.service'
|
||||
|
||||
export class WorkspaceController {
|
||||
public async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const newWorkspace = await workspaceUserService.createWorkspace(req.body)
|
||||
return res.status(StatusCodes.CREATED).json(newWorkspace)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async read(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as Partial<Workspace>
|
||||
const workspaceService = new WorkspaceService()
|
||||
|
||||
let workspace:
|
||||
| Workspace
|
||||
| null
|
||||
| (Workspace & {
|
||||
userCount: number
|
||||
})[]
|
||||
if (query.id) {
|
||||
workspace = await workspaceService.readWorkspaceById(query.id, queryRunner)
|
||||
} else if (query.organizationId) {
|
||||
workspace = await workspaceService.readWorkspaceByOrganizationId(query.organizationId, queryRunner)
|
||||
} else {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
}
|
||||
|
||||
return res.status(StatusCodes.OK).json(workspace)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async switchWorkspace(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.user) {
|
||||
return next(new InternalFlowiseError(StatusCodes.UNAUTHORIZED, `Unauthorized: User not found`))
|
||||
}
|
||||
let queryRunner
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const query = req.query as Partial<Workspace>
|
||||
await queryRunner.startTransaction()
|
||||
|
||||
const workspaceService = new WorkspaceService()
|
||||
const workspace = await workspaceService.readWorkspaceById(query.id, queryRunner)
|
||||
if (!workspace) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceErrorMessage.WORKSPACE_NOT_FOUND)
|
||||
|
||||
const userService = new UserService()
|
||||
const user = await userService.readUserById(req.user.id, queryRunner)
|
||||
if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
|
||||
|
||||
const workspaceUserService = new WorkspaceUserService()
|
||||
const { workspaceUser } = await workspaceUserService.readWorkspaceUserByWorkspaceIdUserId(query.id, req.user.id, queryRunner)
|
||||
if (!workspaceUser) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, WorkspaceUserErrorMessage.WORKSPACE_USER_NOT_FOUND)
|
||||
workspaceUser.lastLogin = new Date().toISOString()
|
||||
workspaceUser.status = WorkspaceUserStatus.ACTIVE
|
||||
workspaceUser.updatedBy = user.id
|
||||
await workspaceUserService.saveWorkspaceUser(workspaceUser, queryRunner)
|
||||
|
||||
const organizationUserService = new OrganizationUserService()
|
||||
const { organizationUser } = await organizationUserService.readOrganizationUserByWorkspaceIdUserId(
|
||||
workspaceUser.workspaceId,
|
||||
workspaceUser.userId,
|
||||
queryRunner
|
||||
)
|
||||
if (!organizationUser)
|
||||
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, OrganizationUserErrorMessage.ORGANIZATION_USER_NOT_FOUND)
|
||||
organizationUser.status = OrganizationUserStatus.ACTIVE
|
||||
organizationUser.updatedBy = user.id
|
||||
await organizationUserService.saveOrganizationUser(organizationUser, queryRunner)
|
||||
|
||||
const roleService = new RoleService()
|
||||
const ownerRole = await roleService.readGeneralRoleByName(GeneralRole.OWNER, queryRunner)
|
||||
const role = await roleService.readRoleById(workspaceUser.roleId, queryRunner)
|
||||
if (!role) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, RoleErrorMessage.ROLE_NOT_FOUND)
|
||||
|
||||
const orgService = new OrganizationService()
|
||||
const org = await orgService.readOrganizationById(organizationUser.organizationId, queryRunner)
|
||||
if (!org) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, OrganizationErrorMessage.ORGANIZATION_NOT_FOUND)
|
||||
const subscriptionId = org.subscriptionId as string
|
||||
const customerId = org.customerId as string
|
||||
const features = await getRunningExpressApp().identityManager.getFeaturesByPlan(subscriptionId)
|
||||
const productId = await getRunningExpressApp().identityManager.getProductIdFromSubscription(subscriptionId)
|
||||
|
||||
const workspaceUsers = await workspaceUserService.readWorkspaceUserByUserId(req.user.id, queryRunner)
|
||||
const assignedWorkspaces: IAssignedWorkspace[] = workspaceUsers.map((workspaceUser) => {
|
||||
return {
|
||||
id: workspaceUser.workspace.id,
|
||||
name: workspaceUser.workspace.name,
|
||||
role: workspaceUser.role?.name,
|
||||
organizationId: workspaceUser.workspace.organizationId
|
||||
} as IAssignedWorkspace
|
||||
})
|
||||
|
||||
const loggedInUser: LoggedInUser & { role: string; isSSO: boolean } = {
|
||||
...req.user,
|
||||
activeOrganizationId: org.id,
|
||||
activeOrganizationSubscriptionId: subscriptionId,
|
||||
activeOrganizationCustomerId: customerId,
|
||||
activeOrganizationProductId: productId,
|
||||
isOrganizationAdmin: workspaceUser.roleId === ownerRole.id,
|
||||
activeWorkspaceId: workspace.id,
|
||||
activeWorkspace: workspace.name,
|
||||
assignedWorkspaces,
|
||||
isApiKeyValidated: true,
|
||||
isSSO: req.user.ssoProvider ? true : false,
|
||||
permissions: [...JSON.parse(role.permissions)],
|
||||
features,
|
||||
role: role.name,
|
||||
roleId: role.id
|
||||
}
|
||||
|
||||
// update the passport session
|
||||
req.user = {
|
||||
...req.user,
|
||||
...loggedInUser
|
||||
}
|
||||
|
||||
// Update passport session
|
||||
// @ts-ignore
|
||||
req.session.passport.user = {
|
||||
...req.user,
|
||||
...loggedInUser
|
||||
}
|
||||
|
||||
req.session.save((err) => {
|
||||
if (err) throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, GeneralErrorMessage.UNHANDLED_EDGE_CASE)
|
||||
})
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
return res.status(StatusCodes.OK).json(loggedInUser)
|
||||
} catch (error) {
|
||||
if (queryRunner && !queryRunner.isTransactionActive) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
}
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner && !queryRunner.isReleased) {
|
||||
await queryRunner.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const workspaceService = new WorkspaceService()
|
||||
const workspace = await workspaceService.updateWorkspace(req.body)
|
||||
return res.status(StatusCodes.OK).json(workspace)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(req: Request, res: Response, next: NextFunction) {
|
||||
let queryRunner: QueryRunner | undefined
|
||||
try {
|
||||
queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
const workspaceId = req.params.id
|
||||
if (!workspaceId) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, WorkspaceErrorMessage.INVALID_WORKSPACE_ID)
|
||||
}
|
||||
const workspaceService = new WorkspaceService()
|
||||
await queryRunner.startTransaction()
|
||||
|
||||
const workspace = await workspaceService.deleteWorkspaceById(queryRunner, workspaceId)
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
return res.status(StatusCodes.OK).json(workspace)
|
||||
} catch (error) {
|
||||
if (queryRunner && queryRunner.isTransactionActive) await queryRunner.rollbackTransaction()
|
||||
next(error)
|
||||
} finally {
|
||||
if (queryRunner && !queryRunner.isReleased) await queryRunner.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async getSharedWorkspacesForItem(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, WorkspaceErrorMessage.INVALID_WORKSPACE_ID)
|
||||
}
|
||||
const workspaceService = new WorkspaceService()
|
||||
return res.json(await workspaceService.getSharedWorkspacesForItem(req.params.id))
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async setSharedWorkspacesForItem(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
if (!req.user) {
|
||||
throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, `Unauthorized: User not found`)
|
||||
}
|
||||
if (typeof req.params === 'undefined' || !req.params.id) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.UNAUTHORIZED,
|
||||
`Error: workspaceController.setSharedWorkspacesForItem - id not provided!`
|
||||
)
|
||||
}
|
||||
if (!req.body) {
|
||||
throw new InternalFlowiseError(
|
||||
StatusCodes.PRECONDITION_FAILED,
|
||||
`Error: workspaceController.setSharedWorkspacesForItem - body not provided!`
|
||||
)
|
||||
}
|
||||
const workspaceService = new WorkspaceService()
|
||||
return res.json(await workspaceService.setSharedWorkspacesForItem(req.params.id, req.body))
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
|
||||
import { ILoginActivity, IWorkspaceShared, IWorkspaceUser } from '../../Interface.Enterprise'
|
||||
|
||||
@Entity('workspace_users')
|
||||
export class WorkspaceUsers implements IWorkspaceUser {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
workspaceId: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
userId: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
role: string
|
||||
}
|
||||
|
||||
@Entity('workspace_shared')
|
||||
export class WorkspaceShared implements IWorkspaceShared {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
workspaceId: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
sharedItemId: string
|
||||
|
||||
@Column({ type: 'text', name: 'itemType' })
|
||||
itemType: string
|
||||
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
createdDate: Date
|
||||
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
updatedDate: Date
|
||||
}
|
||||
|
||||
@Entity('login_activity')
|
||||
export class LoginActivity implements ILoginActivity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
username: string
|
||||
|
||||
@Column({ name: 'activity_code' })
|
||||
activityCode: number
|
||||
|
||||
@Column({ name: 'login_mode' })
|
||||
loginMode: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
message: string
|
||||
|
||||
@Column({ type: 'timestamp' })
|
||||
@UpdateDateColumn()
|
||||
attemptedDateTime: Date
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
|
||||
import { User } from './user.entity'
|
||||
import { Organization } from './organization.entity'
|
||||
|
||||
export enum LoginMethodStatus {
|
||||
ENABLE = 'enable',
|
||||
DISABLE = 'disable'
|
||||
}
|
||||
|
||||
@Entity({ name: 'login_method' })
|
||||
export class LoginMethod {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column({ nullable: true })
|
||||
organizationId?: string
|
||||
@ManyToOne(() => Organization, (organization) => organization.id)
|
||||
@JoinColumn({ name: 'organizationId' })
|
||||
organization?: Organization
|
||||
|
||||
@Column({ type: 'varchar', length: 100 })
|
||||
name: string
|
||||
|
||||
@Column({ type: 'text' })
|
||||
config: string
|
||||
|
||||
@Column({ type: 'varchar', length: 20, default: LoginMethodStatus.ENABLE })
|
||||
status?: string
|
||||
|
||||
@CreateDateColumn()
|
||||
createdDate?: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedDate?: Date
|
||||
|
||||
@Column({ nullable: true })
|
||||
createdBy?: string
|
||||
@ManyToOne(() => User, (user) => user.createdByLoginMethod)
|
||||
@JoinColumn({ name: 'createdBy' })
|
||||
createdByUser?: User
|
||||
|
||||
@Column({ nullable: true })
|
||||
updatedBy?: string
|
||||
@ManyToOne(() => User, (user) => user.updatedByLoginMethod)
|
||||
@JoinColumn({ name: 'updatedBy' })
|
||||
updatedByUser?: User
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user