/* ! /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.`); } } };