From eb3e930739428e73d7f584981b63da2f51bec818 Mon Sep 17 00:00:00 2001 From: Josh Moore Date: Sat, 15 Jul 2023 00:00:59 -0600 Subject: [PATCH] feat: added initial MySQL client (creates tables right now) --- backend/sql/mysql.ts | 86 ++++++++++++++++++++++++++++++++++++++++++++ common/types.d.ts | 10 ++++++ 2 files changed, 96 insertions(+) create mode 100644 backend/sql/mysql.ts diff --git a/backend/sql/mysql.ts b/backend/sql/mysql.ts new file mode 100644 index 0000000..6f07970 --- /dev/null +++ b/backend/sql/mysql.ts @@ -0,0 +1,86 @@ +import mysql, { Pool } from 'mysql2/promise'; +import { UserConfig } from '../UserConfig'; +import { log } from '../log'; + +export class MySql { + private static _pool: Pool; + + /** + * Quick function for creating a simple JSON table + */ + private static _tableManager(mode: 'create' | 'drop', name: string): Promise { + return new Promise((resolve, reject) => + MySql._pool.query( + mode === 'create' + ? `CREATE TABLE ${name} ( NanoID varchar(255), Data JSON );` + : `DROP TABLE ${name};`) + .then(() => resolve()) + .catch((err) => reject(err))); + } + + /** + * Build the MySQL client and create the tables + */ + public static configure(): Promise { + return new Promise(async (resolve, reject) => { + + // Config check + if (!UserConfig.ready) throw new Error('User configuration not ready'); + if (!UserConfig.config.sql?.mySql) throw new Error('MySQL configuration missing'); + + // Create the pool + MySql._pool = mysql.createPool(UserConfig.config.sql.mySql); + + try { + + // Check if the pool is usable + const [rowz, _fields] = await MySql._pool.query(`SHOW FULL TABLES WHERE Table_Type LIKE 'BASE TABLE';`); + const rows_tableData = rowz as unknown as { [key: string]: string }[]; + + // Create tables if needed + if (rows_tableData.length === 0) { + log.warn('MySQL', 'Tables do not exist, creating'); + await Promise.all([MySql._tableManager('create', 'assfiles'), MySql._tableManager('create', 'assusers')]); + log.success('MySQL', 'Tables created').callback(resolve); + } else { + + // There's at least one row, do further checks + const tablesExist = { files: false, users: false }; + + // Check which tables ACTUALLY do exist + for (let row of rows_tableData) { + const table = row[`Tables_in_${UserConfig.config.sql!.mySql!.database}`] as 'assfiles' | 'assusers'; + if (table === 'assfiles') tablesExist.files = true; + if (table === 'assusers') tablesExist.users = true; + // ! Don't use `= table === ''` because this is a loop + } + + // Mini-function for creating a one-off table + const createOneTable = async (name: string) => { + log.warn('MySQL', `Table '${name}' missing, creating`); + await MySql._tableManager('create', name); + log.success('MySQL', `Table '${name}' created`); + } + + // Check & create tables + if (!tablesExist.files) await createOneTable('assfiles'); + if (!tablesExist.users) await createOneTable('assusers'); + + // ! temp: drop tables for testing + /* await MySql._tableManager('drop', 'assfiles'); + await MySql._tableManager('drop', 'assusers'); + log.debug('Table dropped'); */ + + // Hopefully we are ready + if (tablesExist.files && tablesExist.users) + log.info('MySQL', 'Tables exist, ready').callback(resolve(void 0)); + else throw new Error('Table(s) missing!'); + } + } catch (err) { + log.error('MySQL', 'failed to initialize'); + console.error(err); + reject(err); + } + }); + } +} diff --git a/common/types.d.ts b/common/types.d.ts index 41af90d..88b939e 100644 --- a/common/types.d.ts +++ b/common/types.d.ts @@ -21,6 +21,7 @@ declare module 'ass' { maximumFileSize: number; s3?: S3Configuration; + sql?: SqlConfiguration; } interface S3Configuration { @@ -45,6 +46,15 @@ declare module 'ass' { } } + interface SqlConfiguration { + mySql?: { + host: string; + user: string; + password: string; + database: string; + } + } + interface UserConfigTypeChecker { uploadsDir: (val: any) => boolean; idType: (val: any) => boolean;