import mysql, {type Connection, type ConnectionOptions} from 'mysql2'; import {Logger} from "tslog"; import type {IDbModel} from "@interfaces/database/IDbModel"; import type {IDbUser} from "@interfaces/database/IDbUser"; const access: ConnectionOptions = { host: `${process.env["MYSQL_HOST"]}`, port: Number.parseInt(`${process.env["MYSQL_PORT"]}`), user: `${process.env["MYSQL_USER"]}`, database: `${process.env["MYSQL_DATABASE"]}`, password: `${process.env["MYSQL_PASS"]}` }; class MysqlHandler { private readonly handlerName: string; private Logger: Logger private Connection: Connection; constructor(handlerName?: string) { this.handlerName = handlerName || 'Unknown'; this.Logger = new Logger({ name: `DB>> ${this.handlerName}` }); this.Connection = mysql.createConnection(access); this.Connection.connect((err) => { if (err) { this.Logger.error(`Error connecting to MySQL: ${err}`); throw new Error() } this.Logger.info(`Connected to MySQL database (${access.database})`); }); } closeConnection() { this.Connection.end(); }; query(queryString: string) { return new Promise((resolve, reject) => { this.Connection.query(queryString, (err, results) => { if (err) { this.Logger.error(`Error executing query: ${err}`); reject(err); } else { resolve(results); } }); }); } execute(queryString: string, values: Array): Promise { return new Promise((resolve, reject) => { this.Connection.execute(queryString, values, (err: mysql.QueryError | null, results: mysql.QueryResult) => { if (err) { this.Logger.error(`Error executing query: ${err}`); reject(err); } else { resolve(results); } }); }); } /** * Unprepare a previously prepared SQL query. * * @param {string} queryString * - The SQL query string to unprepare. * @return {Promise} * - A promise that resolves if the unprepare operation is successful, * or rejects with an error if there was an error unpreparing the query. */ unprepare(queryString: string): Promise { return new Promise((resolve, reject) => { try { resolve(this.Connection.unprepare(queryString)); } catch (err) { reject(err) } }); } } const MySqlService = { Handler : MysqlHandler, User: { /** * Insert a user into the database. * * @param {MysqlHandler} handler - The MySQL database handler. * @param {IDbUser} userData - The user data to insert. * @returns {Promise} A promise that resolves if the user was inserted successfully, or rejects with an error. * @throws {Error} If an error occurs while executing the query. */ insert(handler: MysqlHandler, userData: IDbUser) { return new Promise((resolve, reject) => { if (!userData.id) return reject('Id is undefined'); if (userData.id.length !== 36) return reject('Id invalid'); const _sql = "INSERT INTO `users`(`id`,`username`, `firstname`, `lastname`, `dob`, `email`, `is_mail_verified`, `is_admin`, `gdpr`, `hash`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const _values = [ userData.id, userData.username, userData.firstname, userData.lastname, userData.dob, userData.email, userData.is_mail_verified, userData.is_admin, userData.gdpr, userData.hash ] try { resolve(handler.execute(_sql, _values)) } catch (err: unknown) { reject(err as Error); } }) }, /** * Updates user data in the database. * @param {MysqlHandler} handler - The MySQL handler object. * @param {IDbUser} userData - The user data to be updated. * @returns {Promise} - A promise that resolves when the update is successful. * @throws {Error} - If an error occurs during the update process. */ update(handler: MysqlHandler, userData: IDbUser): Promise { return new Promise((resolve, reject) => { if (!userData.id) return reject('Id is undefined'); if (userData.id.length !== 36) return reject('Id invalid'); try { const _template = ` ${userData.username ? "`username` = ?," : null} ${userData.firstname ? "`firstname` = ?," : null} ${userData.lastname ? "`lastname` = ?," : null} ${userData.dob ? "`dob` = ?," : null} ${userData.email ? "`email` = ?," : null} ${userData.is_mail_verified ? "`is_mail_verified` = ?," : null} ${userData.is_admin ? "`is_admin` = ?," : null} ${userData.gdpr ? "`gdpr` = ?," : null} ${userData.hash ? "`hash` = ?" : null}` const _values = [ userData.username, userData.firstname, userData.lastname, userData.dob, userData.email, userData.is_mail_verified, userData.is_admin, userData.gdpr, userData.hash, userData.id ] const _sql = `UPDATE "users" SET ${_template} WHERE 'id' = ?`; return resolve(handler.execute(_sql, _values)); } catch (err: unknown) { reject(err as Error); } }); }, /** * Retrieves a user from the database based on their ID. * * @param {MysqlHandler} handler - The MySQL handler object used to execute the query. * @param {string} userId - The ID of the user to retrieve from the database. * * @return {Promise} - A promise that resolves with the retrieved user object (of type IDbUser). * - Rejects with an error if the ID is invalid or if an error occurs during the database operation. */ getById(handler: MysqlHandler, userId: string): Promise { return new Promise((resolve, reject) => { if (userId.length !== 36) return reject('Id invalid'); const _sql = "SELECT * FROM `users` WHERE `id` = ?"; const _values = [userId]; try { resolve(handler.execute(_sql, _values) as unknown as IDbUser); } catch (err: unknown) { reject(err as Error); } }); }, /** * Retrieves all users from the database. * * @param {MysqlHandler} handler - The MySQL handler object used to query the database. * @return {Promise>} - A promise that resolves with an array of user objects from the database. * @throws {Error} - If an error occurs while querying the database. */ getAll(handler: MysqlHandler): Promise> { return new Promise((resolve, reject) => { const _sql = "SELECT * FROM `users`"; try { resolve(handler.query(_sql) as unknown as Array); } catch (err: unknown) { reject(err as Error); } }); }, /** * Retrieves a user from the database by email. * * @param {MysqlHandler} handler - The MySQL database handler instance. * @param {string} email - The email of the user to retrieve. * @return {Promise} - A promise that resolves to the retrieved user object. * @throws {Error} - If an error occurs while retrieving the user. */ getByEmail(handler: MysqlHandler, email: string): Promise { return new Promise((resolve, reject) => { if (!email) return reject('email is undefined') const _sql = "SELECT * FROM `users` WHERE `email` = ?"; const _values = [email]; try { resolve(handler.execute(_sql, _values) as unknown as IDbUser); } catch (err: unknown) { reject(err as Error); } }); }, /** * Retrieves the admin state for a given user ID. * * @param {MysqlHandler} handler - The MySQL handler object used for executing the query. * @param {string} userId - The ID of the user to get the admin state for. * @return {Promise} A Promise that resolves to a boolean value indicating whether the user is an admin or not. * @throws {Error} if an error occurs during the execution of the query. */ getAdminStateForId(handler: MysqlHandler, userId: string) : Promise { return new Promise((resolve, reject) => { const _sql = "SELECT `is_admin` FROM `users` WHERE `id` = ?"; const _values = [userId]; try { const isAdmin = handler.execute(_sql, _values) isAdmin.then((result) => { if (result !== true) return resolve(false); return resolve(true) }); } catch (err: unknown) { reject(err as Error); } }); }, /** * Deletes a user from the database. * @param {MysqlHandler} handler - The MySQL handler object. * @param {string} userId - The ID of the user to delete. * @return {Promise} - A Promise that resolves when the deletion is successful, or rejects with an error. */ delete(handler: MysqlHandler, userId: string): Promise { return new Promise((resolve, reject) => { if (!userId) return reject('Id is undefined'); if (userId.length !== 36) return reject('Id invalid'); const _sql = "DELETE FROM `users` WHERE `id` = ?"; const _values = [userId]; try { resolve(handler.execute(_sql, _values)); } catch (err: unknown) { reject(err as Error); } }); } }, Model: { /** * Retrieves all records from the 'models' table. * * @param {MysqlHandler} handler - The MySQL handler object used to execute the query. * @return {Promise>} - A promise that resolves to an array of IDbModel objects representing the retrieved records from the 'models' table. * @throws {Error} - If there is an error executing the query. */ getAll(handler: MysqlHandler): Promise> { return new Promise((resolve, reject) => { const _sql = "SELECT * FROM `models`"; try { resolve(handler.query(_sql) as unknown as Array); } catch (err: unknown) { reject(err as Error); } }); }, /** * Retrieves a database model by slug from a given MySQL handler. * * @param {MysqlHandler} handler - The MySQL handler instance. * @param {string} slug - The slug of the model to retrieve. * @return {Promise} A promise that resolves with the retrieved model. * @throws {Error} If there was an error executing the query. */ getBySlug(handler: MysqlHandler, slug: string): Promise { return new Promise((resolve, reject) => { const _sql = "SELECT * FROM `models` WHERE `slug` = ?"; const _values = [slug]; try { resolve(handler.execute(_sql, _values) as unknown as IDbModel); } catch (err: unknown) { reject(err as Error); } }); }, getById(handler: MysqlHandler, modelId: string): Promise { return new Promise((resolve, reject) => { const _sql = "SELECT * FROM `models` WHERE `id` = ?"; const _values = [modelId]; try { resolve(handler.execute(_sql, _values) as unknown as IDbModel); } catch (err: unknown) { reject(err as Error); } }); }, insert(handler: MysqlHandler, data: IDbModel) { return new Promise((resolve, reject) => { if (!data.id) return reject('Id is undefined'); if (data.id.length !== 36) return reject('Id invalid'); const _sql = "INSERT INTO `users`(`id`,`username`, `firstname`, `lastname`, `dob`, `email`, `is_mail_verified`, `is_admin`, `gdpr`, `hash`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const _values = [ data.slug_name, data.display_name, data.brand_id, data.category_id, data.image_blob, data.is_trending, data.base_price ] try { resolve(handler.execute(_sql, _values)) } catch (err: unknown) { reject(err as Error); } }) } } } export default MySqlService