Files
P42Modules/MySQLClient.js
T
STEINNI ab98457829 1st
2026-06-14 13:22:13 +00:00

116 lines
3.7 KiB
JavaScript

import mysql from 'mysql2/promise'
import { loadP42Secrets } from './secretsLoader.js'
export class MySQLClient {
constructor(db, kal = 0) {
this.db = db
this.lastInsertedId = -1
this.kalIntervalId = null
if(kal > 0) {
this.kalIntervalId = setInterval(() => {
this.db.query('SELECT 1')
}, 1000 * kal)
}
}
static #assertValidDbName(name) {
if(typeof(name) !== 'string' || !/^[A-Za-z0-9_]+$/.test(name)) {
throw(new Error(`Invalid MySQL database name: ${name}`))
}
}
static resolveDatabases(config = {}) {
loadP42Secrets()
const guiDatabase = process.env.mysql_guiDb || config.guiDatabase || config.database || 'p42GUI'
const simDatabase = process.env.mysql_simDb || config.simDatabase || 'p42SIM'
MySQLClient.#assertValidDbName(guiDatabase)
MySQLClient.#assertValidDbName(simDatabase)
return({ guiDatabase, simDatabase })
}
static resolveCredentials(config = {}) {
loadP42Secrets()
const user = process.env.mysql_user
const password = process.env.mysql_pass
if(!user || !password) {
throw(new Error('Missing MySQL credentials: set mysql_user and mysql_pass in environment'))
}
const { guiDatabase } = MySQLClient.resolveDatabases(config)
return({
socketPath: config.socketPath,
host: config.host,
port: config.port,
user,
password,
database: guiDatabase,
waitForConnections: true,
connectionLimit: config.connectionLimit ?? 5,
queueLimit: 0,
})
}
static async createPool(config) {
return(await mysql.createPool(MySQLClient.resolveCredentials(config)))
}
static async poolExecute(pool, query, values = []) {
const [rows] = await pool.execute(query, values)
return(rows)
}
async close() {
if(this.db) await this.db.end()
if(this.kalIntervalId) clearInterval(this.kalIntervalId)
this.db = null
}
escape(x) {
return(mysql.escape(x))
}
async execute(query, values = []) {
if(!this.db) return([])
if(query.search(RegExp('(^|;) *(INSERT|REPLACE) +INTO', 'i')) > -1) this.lastInsertedId = -1
const [result] = await this.db.execute(query, values)
if('insertId' in result) {
this.lastInsertedId = result.insertId
}
return(result)
}
async InsertObjectsStatement(table, rows) {
this.lastInsertedId = -1
if((!this.db) || (rows.length == 0)) return([])
const keys = Object.keys(rows[0]).map((key) => `\`${key}\``).join(', ')
const valuesArr = []
for(const row of rows) {
valuesArr.push(Object.values(row))
}
const query = `INSERT INTO ${table} (${keys}) VALUES ?`
const [result] = await this.db.query(query, [valuesArr])
if('insertId' in result) {
this.lastInsertedId = result.insertId
}
return(result)
}
async ReplaceObjectsStatement(table, rows) {
this.lastInsertedId = -1
if((!this.db) || (rows.length == 0)) return([])
const keys = Object.keys(rows[0]).map((key) => `\`${key}\``).join(', ')
const valuesArr = []
for(const row of rows) {
valuesArr.push(Object.values(row))
}
const query = `REPLACE INTO ${table} (${keys}) VALUES ?`
const [result] = await this.db.query(query, [valuesArr])
if('insertId' in result) {
this.lastInsertedId = result.insertId
}
return(result)
}
}