Initial commit

This commit is contained in:
2026-08-30 15:25:31 +02:00
commit 480e5e0386
18 changed files with 1969 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
/*
! /database/commands.js
? The command library for mariadb
*/
// % imports
import pools from './pools.js'; // functions "add(this)", "remove(this.name)", "status(name)"
// = command manager class
export default class Commands {
constructor(name, db, protocol = 'read', address = process.env.DB_CONN_IP) {
this.name = name; // persistent identifier
this.address = address; // database adress (host)
this.database = db; // database name
this.protocol = protocol; // user protocol
this.pool = null; // pool reference
this.connStat = false; // connection status
}
// $ add a connection to the pool manager
async connect() {
console.log(`[Database] Connecting command instance "${this.name}" to database "${this.database}"...`);
const result = await pools.add(this);
this.connStat = result.connStat;
this.pool = result.pool;
console.log(`[Database] Connection status for "${this.name}": ${this.connStat ? 'SUCCESS' : 'FAILED'}`);
}
// $ remove a connection from the pool manager
async disconnect() {
if (this.connStat) {
console.log(`[Database] Disconnecting instance "${this.name}"...`);
const result = await pools.remove(this.name);
this.connStat = result?.connStat ?? false;
this.pool = null;
console.log(`[Database] Instance "${this.name}" successfully disconnected.`);
}
}
// $ get the status of a connection from the pool manager
async status() {
if (this.connStat) {
console.log(`[Database] Checking status for instance "${this.name}"...`);
const result = await pools.status(this.name);
this.connStat = result?.connStat ?? false;
this.pool = result?.pool ?? null;
console.log(`[Database] Status for "${this.name}": Active = ${this.connStat}`);
return result;
} else {
console.log(`[Database] Status check skipped for "${this.name}" (currently not connected).`);
}
}
// $ execute an sql query
async query(query, params = []) {
// ~ check if connection is established
if (!this.connStat || !this.pool) {
console.error(`[Database Error] Tried to execute query on disconnected instance "${this.name}".`);
throw new Error(`Database connection '${this.name}' is not established.`);
}
// ~ connection reference
let conn;
// ~ try executing the query
try {
conn = await this.pool.getConnection();
const rows = await conn.query(query, params);
console.log(`[Database Query] [${this.name}] Executed successfully (${duration}ms):`, { query, params });
return rows;
// ~ throw error if failed
} catch(e) {
console.error(`[Database Error] Query failed in "${this.name}":`, e.message);
console.error(`[Failed Query]:`, query, params);
throw e;
// ~ release the connection back to the pool
} finally {
if (conn) conn.release();
}
}
// $ insert a record into a table
async insert(table, data) {
if (Object.keys(data).length === 0) return null;
const keys = Object.keys(data);
const values = Object.values(data);
const placeholders = keys.map(() => '?').join(', ');
const columns = keys.join(', ');
const query = `INSERT INTO ${table} (${columns}) VALUES (${placeholders})`;
return await this.query(query, values);
}
// $ update a record in a table
async update(table, data, where, whereParams = []) {
if (Object.keys(data).length === 0) return null;
const keys = Object.keys(data);
const values = Object.values(data);
const setClause = keys.map(key => `${key} = ?`).join(', ');
const query = `UPDATE ${table} SET ${setClause} WHERE ${where}`;
return await this.query(query, [...values, ...whereParams]);
}
// $ select records from a table
async select(table, columns = '*', where = '', params = []) {
let query = `SELECT ${Array.isArray(columns) ? columns.join(', ') : columns} FROM ${table}`;
if (where) {
query += ` WHERE ${where}`;
}
return await this.query(query, params);
}
// $ delete records from a table
async delete(table, where, params = []) {
const query = `DELETE FROM ${table} WHERE ${where}`;
return await this.query(query, params);
}
// $ create a new table
async createTable(table, columns = 'id INT AUTO_INCREMENT PRIMARY KEY') {
const query = `CREATE TABLE IF NOT EXISTS ${table} (${columns}) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci`;
return await this.query(query);
}
// $ alter an existing table
async alterTable(table, modificationClause) {
const query = `ALTER TABLE ${table} ${modificationClause}`;
return await this.query(query);
}
};
+143
View File
@@ -0,0 +1,143 @@
/*
! /database/index.js
? The central database worker factory for multi-project management.
*/
// % imports
/*
= The MariaDB-"Commands"-Library:
^ Create a new instance:
?? Create the command instance: "const dbName = new Commands(name, db, protocol, address);"
? Available protocols are "read", "write", "delete" and "backup".
? Address and protocol can be left empty for the default values. (selfdev-mariadb, read)
? Example: "const dbName = new Commands('reader', 'myDatabase');"
^ Connection Usage:
?? Add a new connection: "await dbName.connect();"
?? Remove a connection: "await dbName.disconnect();"
?? Check the status of a connection: "await dbName.status();"
^ Basic Query Usage:
?? Execute a basic sql query: "await dbName.query(query, params = []);"
? Example: "await dbName.query('SELECT * FROM langs');"
? Queries can be everything that is possible with mariadb.
? Returns an array of objects (for SELECT) or a result metadata object (for INSERT/UPDATE/DELETE).
^ Table Management Usage:
?? Execute a table creation: "await dbName.createTable(table, columns = 'id INT AUTO_INCREMENT PRIMARY KEY');"
? Example: "await dbName.createTable('projects', 'id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255)');"
? Automatically applies the ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci.
? Returns an object containing metadata like { affectedRows: 0, warningCount: 0 } or execution result.
?? Execute a table alteration: "await dbName.alterTable(table, modificationClause);"
? Example: "await dbName.alterTable('projects', 'ADD COLUMN created_at TIMESTAMP');"
? Returns an object containing metadata like { affectedRows: 0, warningCount: 0 } or execution result.
^ Helper Query Command Usage:
?? Execute an insert query: "await dbName.insert(table, data);"
? Example: "await dbName.insert('users', { name: 'Dummy', role: 'admin' });"
? Returns an object containing metadata like "{ affectedRows: 1, insertId: 12n }" or null if data is empty.
?? Execute an update query: "await dbName.update(table, data, where, whereParams = []);"
? Example: "await dbName.update('users', { role: 'editor' }, 'id = ?', [1]);"
? Returns an object containing metadata like { affectedRows: 1, warningCount: 0 } or null if data is empty.
?? Execute a selection query: "await dbName.select(table, columns = '*', where = '', params = []);"
? Example: "await dbName.select('users', ['id', 'name'], 'role = ?', ['admin']);"
? Returns an array of matching row objects, e.g. [{ id: 1, name: 'Dummy' }].
?? Execute a deletion query: "await dbName.delete(table, where, params = []);"
? Example: "await dbName.delete('users', 'id = ?', [1]);"
? Returns an object containing metadata like { affectedRows: 1 }.
*/
import Commands from "./commands.js";
// % internal registry for all dynamic project database instances
const registry = new Map();
/*
= The Database-Worker-Factory:
^ Get or create a project instance:
?? Usage: "const db = dbWorker.get(projectName, address);"
? Example: "const db = dbWorker.get('myProject', 'db_host_address');"
? Returns an object containing the command instances (e.g. { read, write, delete, backup }).
^ Connect all protocols for a certain project:
?? Usage: "await dbWorker.connectProject(projectName);"
? Example: "await dbWorker.connectProject('myProject');"
? Returns nothing.
^ Disconnect all protocols for a certain project:
?? Usage: "await dbWorker.disconnectProject(projectName);"
? Example: "await dbWorker.disconnectProject('myProject');"
? Returns nothing.
^ Query Usage Example:
?? const db = dbWorker.get('myProject');
?? await db.read.connect(); // optional, if "connectProject" was not called
?? const users = await db.read.select('users', '*', 'active = ?', [1]);
?? await db.write.insert('users', { name: 'Dummy' });
*/
export default {
// $ get or initialize database for a specific project
get(projectName, address) {
if (!projectName) {
throw new Error("A project name is required to get a database instance.");
}
// ~ return an existing project instance if already created
if (registry.has(projectName)) {
console.log(`[Database] Returning existing instance for project: "${projectName}"`);
return registry.get(projectName);
}
// $ debug-output
console.log(`[Database] Initializing new instances (read, write, delete, backup) for: "${projectName}"`);
// ~ create new instances (r,w,d,b) for the given project database
const instances = {
read: new Commands(`${projectName}_reader`, projectName, 'read', address || null),
write: new Commands(`${projectName}_writer`, projectName, 'write', address || null),
delete: new Commands(`${projectName}_deleter`, projectName, 'delete', address || null),
backup: new Commands(`${projectName}_backuper`, projectName, 'backup', address || null)
};
// ~ store the instance in the registry
registry.set(projectName, instances);
// ~ return result
return instances;
},
// $ helper to connect all instances of a specific project at once
async connectProject(projectName) {
console.log(`[Database] Connecting all instances for project: "${projectName}"...`);
const db = this.get(projectName);
await Promise.all([
db.read.connect(),
db.write.connect(),
db.delete.connect(),
db.backup.connect()
]);
},
// $ helper to disconnect all instances of a specific project
async disconnectProject(projectName) {
console.log(`[Database] Disconnecting all instances for project: "${projectName}"...`);
if (registry.has(projectName)) {
const db = registry.get(projectName);
await Promise.all([
db.read.disconnect(),
db.write.disconnect(),
db.delete.disconnect(),
db.backup.disconnect()
]);
registry.delete(projectName);
console.log(`[Database] Successfully disconnected and removed from registry: "${projectName}"`);
} else {
console.log(`[Database] Tried to disconnect project "${projectName}", but it was not found in registry.`);
}
}
};
+87
View File
@@ -0,0 +1,87 @@
/*
! /database/pools.js
? The main database pool (connection) manager
*/
// % imports
import mariadb from 'mariadb';
// % database credentials mapping
const CREDENTIALS_MAP = {
read: { user: process.env.DB_READ_USER, pass: process.env.DB_READ_PASS },
write: { user: process.env.DB_WRITE_USER, pass: process.env.DB_WRITE_PASS },
delete: { user: process.env.DB_DELETE_USER, pass: process.env.DB_DELETE_PASS },
backup: { user: process.env.DB_BACKUP_USER, pass: process.env.DB_BACKUP_PASS }
}
// $ get database credentials
function getCredentials(protocol = 'read') {
const key = String(protocol).toLowerCase();
return CREDENTIALS_MAP[key] ?? CREDENTIALS_MAP.read;
}
// = pool manager class
class PoolManager {
constructor() {
this.pools = new Map();
}
// $ function to add a connection
async add(config) {
// ~ check if this pool is already existing → then return
if (this.pools.has(config.name)) {
return { pool: this.pools.get(config.name), connStat: true };
}
// ~ get database credentials by protocol
const { user, pass } = getCredentials(config.protocol);
// ~ create the mariadb pool
const pool = mariadb.createPool({
host: config.address,
port: Number(process.env.DB_CONN_PORT),
user,
password: pass,
database: config.database,
waitForConnections: true,
connectionLimit: 5,
queueLimit: 0,
enableKeepAlive: true,
keepAliveInitialDelay: 10000
});
// ~ add the pool to the map
this.pools.set(config.name, pool);
// ~ return the pool
return { pool, connStat: true };
}
// $ function to remove a connection
async remove(name) {
if (this.pools.has(name)) {
const pool = this.pools.get(name);
await pool.end();
this.pools.delete(name);
}
return { connStat: false };
}
// $ function to get the status of a connection
async status(name) {
if (this.pools.has(name)) {
try {
const pool = this.pools.get(name);
await pool.query('SELECT 1');
return { pool, connStat: true };
} catch(err) {
return { connStat: false, msg: err }
}
} else {
return { connStat: false };
}
}
}
// $ export the pool manager
export default new PoolManager();