2020-10-19 11:57:02 +07:00
import config from './config' ;
2022-04-12 15:15:57 +09:00
import { createPool , Pool , PoolConnection } from 'mysql2/promise' ;
2020-10-13 15:27:52 +07:00
import logger from './logger' ;
2022-08-08 09:00:11 +02:00
import { FieldPacket , OkPacket , PoolOptions , ResultSetHeader , RowDataPacket } from 'mysql2/typings/mysql' ;
2019-07-21 17:59:47 +03:00
2022-04-13 17:38:42 +04:00
class DB {
constructor ( ) {
2022-04-12 15:15:57 +09:00
if ( config . DATABASE . SOCKET !== '' ) {
2022-04-13 17:38:42 +04:00
this . poolConfig . socketPath = config . DATABASE . SOCKET ;
2022-03-14 13:11:04 +00:00
} else {
2022-04-13 17:38:42 +04:00
this . poolConfig . host = config . DATABASE . HOST ;
2022-03-14 13:11:04 +00:00
}
2022-04-13 17:38:42 +04:00
}
private pool : Pool | null = null ;
private poolConfig : PoolOptions = {
port : config.DATABASE.PORT ,
database : config.DATABASE.DATABASE ,
user : config.DATABASE.USERNAME ,
password : config.DATABASE.PASSWORD ,
connectionLimit : 10 ,
supportBigNumbers : true ,
timezone : '+00:00' ,
} ;
2022-03-13 14:57:17 +01:00
2022-06-07 11:28:39 +02:00
private checkDBFlag() {
if ( config . DATABASE . ENABLED === false ) {
2023-02-19 19:17:51 +09:00
const stack = new Error ( ) . stack ;
logger . err ( ` Trying to use DB feature but config.DATABASE.ENABLED is set to false, please open an issue. \ nStack trace: ${ stack } } ` ) ;
2022-06-07 11:28:39 +02:00
}
}
2022-08-08 09:00:11 +02:00
public async query < T extends RowDataPacket [ ] [ ] | RowDataPacket [ ] | OkPacket |
2023-06-22 11:52:43 -04:00
OkPacket [ ] | ResultSetHeader > ( query , params ? , connection? : PoolConnection ) : Promise < [ T , FieldPacket [ ] ] >
2022-08-08 09:00:11 +02:00
{
2022-06-07 11:28:39 +02:00
this . checkDBFlag ( ) ;
2023-04-28 19:05:49 -06:00
let hardTimeout ;
if ( query ? . timeout != null ) {
hardTimeout = Math . floor ( query . timeout * 1.1 ) ;
} else {
hardTimeout = config . DATABASE . TIMEOUT ;
}
if ( hardTimeout > 0 ) {
return new Promise ( ( resolve , reject ) = > {
const timer = setTimeout ( ( ) = > {
reject ( new Error ( ` DB query failed to return, reject or time out within ${ hardTimeout / 1000 } s - ${ query ? . sql ? . slice ( 0 , 160 ) || ( typeof ( query ) === 'string' || query instanceof String ? query ? . slice ( 0 , 160 ) : 'unknown query' ) } ` ) ) ;
} , hardTimeout ) ;
2023-06-22 11:52:43 -04:00
// Use a specific connection if provided, otherwise delegate to the pool
const connectionPromise = connection ? Promise . resolve ( connection ) : this . getPool ( ) ;
connectionPromise . then ( ( pool : PoolConnection | Pool ) = > {
2023-04-28 19:05:49 -06:00
return pool . query ( query , params ) as Promise < [ T , FieldPacket [ ] ] > ;
} ) . then ( result = > {
resolve ( result ) ;
} ) . catch ( error = > {
reject ( error ) ;
} ) . finally ( ( ) = > {
clearTimeout ( timer ) ;
} ) ;
} ) ;
} else {
const pool = await this . getPool ( ) ;
return pool . query ( query , params ) ;
}
2022-03-13 14:57:17 +01:00
}
2022-03-12 14:47:33 +01:00
2023-06-22 11:52:43 -04:00
public async $atomicQuery < T extends RowDataPacket [ ] [ ] | RowDataPacket [ ] | OkPacket |
OkPacket [ ] | ResultSetHeader > ( queries : { query , params } [ ] ) : Promise < [ T , FieldPacket [ ] ] [ ] >
{
const pool = await this . getPool ( ) ;
const connection = await pool . getConnection ( ) ;
try {
await connection . beginTransaction ( ) ;
const results : [ T , FieldPacket [ ] ] [ ] = [ ] ;
for ( const query of queries ) {
const result = await this . query ( query . query , query . params , connection ) as [ T , FieldPacket [ ] ] ;
results . push ( result ) ;
}
await connection . commit ( ) ;
return results ;
} catch ( e ) {
logger . err ( 'Could not complete db transaction, rolling back: ' + ( e instanceof Error ? e.message : e ) ) ;
connection . rollback ( ) ;
connection . release ( ) ;
throw e ;
} finally {
connection . release ( ) ;
}
}
2022-04-13 17:38:42 +04:00
public async checkDbConnection() {
2022-06-07 11:28:39 +02:00
this . checkDBFlag ( ) ;
2022-04-13 17:38:42 +04:00
try {
await this . query ( 'SELECT ?' , [ 1 ] ) ;
logger . info ( 'Database connection established.' ) ;
} catch ( e ) {
logger . err ( 'Could not connect to database: ' + ( e instanceof Error ? e.message : e ) ) ;
process . exit ( 1 ) ;
2022-03-12 14:47:33 +01:00
}
2022-04-12 15:15:57 +09:00
}
2022-04-13 17:38:42 +04:00
private async getPool ( ) : Promise < Pool > {
if ( this . pool === null ) {
this . pool = createPool ( this . poolConfig ) ;
this . pool . on ( 'connection' , function ( newConnection : PoolConnection ) {
newConnection . query ( ` SET time_zone='+00:00' ` ) ;
} ) ;
}
return this . pool ;
2022-03-12 14:47:33 +01:00
}
2019-07-21 17:59:47 +03:00
}
2022-04-13 17:38:42 +04:00
export default new DB ( ) ;