diff options
Diffstat (limited to 'util/src/util/Database.ts')
-rw-r--r-- | util/src/util/Database.ts | 106 |
1 files changed, 67 insertions, 39 deletions
diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts index 31a30561..c4693108 100644 --- a/util/src/util/Database.ts +++ b/util/src/util/Database.ts @@ -1,72 +1,100 @@ import path from "path"; import "reflect-metadata"; -import { DataSource, createConnection } from "typeorm"; +import { DataSource, createConnection, DataSourceOptions, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm"; import * as Models from "../entities"; import { Migration } from "../entities/Migration"; import { yellow, green, red } from "picocolors"; +import fs from "fs"; +import { exit } from "process"; +import { BaseClass, BaseClassWithoutId } from "../entities"; // UUID extension option is only supported with postgres // We want to generate all id's with Snowflakes that's why we have our own BaseEntity class let promise: Promise<any>; -let dataSource: DataSource | undefined; -let dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db"); -let verbose_db = false; +let dataSource: DataSource; + + export async function initDatabase(): Promise<DataSource> { if (dataSource) return dataSource; // prevent initalizing multiple times + let dso = getDataSourceOptions(); + console.log(`[Database] ${yellow(`Connecting to ${dso.type} database...`)}`); + + //promise = dataSource.initialize(); + //await promise; + + console.log(`[Database] ${green("Connected!")}`); + + return promise; +} + + +export function closeDatabase() { + dataSource?.destroy(); +} + +function getDataSourceOptions(): DataSourceOptions { + //get connection string and check for migrations + const dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db"); const type = dbConnectionString.includes("://") ? dbConnectionString.split(":")[0]?.replace("+srv", "") : "sqlite" as any; const isSqlite = type.includes("sqlite"); - if(process.env.DB_VERBOSE) verbose_db = true; - - console.log(`[Database] ${yellow(`connecting to ${type} db`)}`); + const migrationsExist = fs.existsSync(path.join(__dirname, "..", "migrations", type)); + //read env vars + const synchronizeInsteadOfMigrations = "DB_UNSAFE" in process.env; + const verboseDb = "DB_VERBOSE" in process.env; + if(isSqlite) console.log(`[Database] ${red(`You are running sqlite! Please keep in mind that we recommend setting up a dedicated database!`)}`); - if(verbose_db) - console.log(`[Database] ${red(`Verbose database logging is enabled, this might impact performance! Unset VERBOSE_DB to disable.`)}`); - // @ts-ignore - dataSource = new DataSource({ + if(verboseDb) + console.log(`[Database] ${red(`Verbose database logging is enabled, this might impact performance! Unset DB_VERBOSE to disable.`)}`); + + if(synchronizeInsteadOfMigrations){ + console.log(`[Database] ${red(`Unsafe database upgrades are enabled! We are not responsible for broken databases! Unset DB_UNSAFE to disable.`)}`); + } + else if(!migrationsExist) { + console.log(`[Database] ${red(`Database engine not supported! Set UNSAFE_DB to bypass.`)}`); + console.log(`[Database] ${red(`Please mention this to Fosscord developers, and provide this info:`)}`); + console.log(`[Database]\n${red(JSON.stringify({ + db_type: type, + migrations_exist: migrationsExist + }, null, 4))}`); + + //exit(1); + } + + return { type, charset: 'utf8mb4', url: isSqlite ? undefined : dbConnectionString, database: isSqlite ? dbConnectionString : undefined, // @ts-ignore - entities: Object.values(Models).filter((x) => x.constructor.name !== "Object" && x.name), - synchronize: type !== "mongodb", - logging: verbose_db, + //entities: Object.values(Models).filter((x) => x.constructor.name !== "Object" && x.constructor.name !== "Array" && x.constructor.name !== "BigInt" && x).map(x=>x.name), + entities: Object.values(Models).filter((x) => x.constructor.name == "Function" && shouldIncludeEntity(x.name)), + synchronize: synchronizeInsteadOfMigrations, + logging: verboseDb, cache: { duration: 1000 * 3, // cache all find queries for 3 seconds }, bigNumberStrings: false, supportBigNumbers: true, name: "default", - migrations: [path.join(__dirname, "..", "migrations", "*.js")], - }); - promise = dataSource.initialize(); - await promise; - // run migrations, and if it is a new fresh database, set it to the last migration - if (dataSource.migrations.length) { - if (!(await Migration.findOne({}))) { - let i = 0; - - await Migration.insert( - dataSource.migrations.map((x) => ({ - id: i++, - name: x.name, - timestamp: Date.now(), - })) - ); + migrations: synchronizeInsteadOfMigrations ? [] : [path.join(__dirname, "..", "migrations", type, "*.js")], + migrationsRun: !synchronizeInsteadOfMigrations, + cli: { + migrationsDir: `src/migrations/${type}` } - } - await dataSource.runMigrations(); - console.log(`[Database] ${green("connected")}`); - - return promise; + } as DataSourceOptions; } -export { dataSource }; - -export function closeDatabase() { - dataSource?.destroy(); +function shouldIncludeEntity(name: string): boolean { + return ![ + BaseClassWithoutId, + PrimaryColumn, + BaseClass, + PrimaryGeneratedColumn + ].map(x=>x.name).includes(name); } + +export default dataSource = new DataSource(getDataSourceOptions()); \ No newline at end of file |