UNPKG

microkernel-mod-sequelize

Version:

Microkernel module for integrating Sequelize ORM

181 lines (171 loc) 7.79 kB
/* ** Microkernel -- Microkernel for Server Applications ** Copyright (c) 2016-2021 Dr. Ralf S. Engelschall <rse@engelschall.com> ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* external requirements */ const Sequelize = require("sequelize") /* the Microkernel module */ class Module { constructor (options = {}) { /* allow database to be configured initially */ this.options = Object.assign({}, options) } get module () { /* identify this module */ return { name: "microkernel-mod-sequelize", tag: "SEQUELIZE", group: "BASE", after: [ "CTX", "OPTIONS" ] } } latch (kernel) { kernel.latch("options:options", (options) => { options.push({ names: [ "db-dialect" ], type: "string", "default": "postgres", help: "Database Dialect", helpArg: "DIALECT" }) options.push({ names: [ "db-host" ], type: "string", "default": "localhost", help: "Database Host", helpArg: "HOST" }) options.push({ names: [ "db-port" ], type: "number", "default": 5432, help: "Database Port", helpArg: "PORT" }) options.push({ names: [ "db-database" ], type: "string", "default": "example", help: "Database Name", helpArg: "NAME" }) options.push({ names: [ "db-username" ], type: "string", "default": "example", help: "Database Username", helpArg: "USERNAME" }) options.push({ names: [ "db-password" ], type: "string", "default": "example", help: "Database Password", helpArg: "PASSWORD" }) options.push({ name: "db-schema-drop", type: "bool", "default": false, help: "Database Schema Dropping & Auto-Recreation" }) options.push({ name: "db-pool-min", type: "number", "default": 0, help: "Database Connection Pool: Minimum Size" }) options.push({ name: "db-pool-max", type: "number", "default": 5, help: "Database Connection Pool: Maximum Size" }) options.push({ name: "db-pool-idle", type: "number", "default": 10 * 1000, help: "Database Connection Pool: Maximum Connection Idle Time" }) options.push({ name: "db-pool-acquire", type: "number", "default": 10 * 1000, help: "Database Connection Pool: Maximum Connection Acquire Time" }) options.push({ name: "db-pool-evict", type: "number", "default": 10 * 1000, help: "Database Connection Pool: Maximum Connection Eviction Time" }) options.push({ name: "db-query-retry-match", type: "string", "default": "SQLITE_BUSY", help: "Database Query Retry: Matching Query" }) options.push({ name: "db-query-retry-max", type: "number", "default": 5, help: "Database Query Retry: Maximum Count" }) }) } async prepare (kernel) { /* we operate only in non-daemonized mode (microkernel-mod-daemon will remove option "--daemon" in child processes and then we want to operate only) */ const opts = kernel.rs("options:options") if (opts.daemon || opts.daemon_kill) return /* configure the database connection */ const options = { define: { freezeTableName: true, timestamps: false }, pool: { min: opts.db_pool_min, max: opts.db_pool_max, idle: opts.db_pool_idle, acquire: opts.db_pool_acquire, evict: opts.db_pool_evict }, retry: { max: opts.db_query_retry_max, match: opts.db_query_retry_match.split(/\s*,\s*/) }, logging: (msg) => { if (!msg.match(/FROM\s+pg_class/)) kernel.sv("log", "sequelize", "debug", `DB: ${msg}`) } } options.dialect = opts.db_dialect if (opts.db_dialect === "sqlite") { opts.db_username = "" opts.db_password = "" options.host = "" options.port = "" options.storage = opts.db_database } else { options.host = opts.db_host options.port = opts.db_port } const db = kernel.rs("db", new Sequelize(opts.db_database, opts.db_username, opts.db_password, options)) /* open connection to database system */ let url if (opts.db_dialect === "sqlite") url = `${opts.db_dialect}://${opts.db_database}` else url = `${opts.db_dialect}://${opts.db_username}@${opts.db_host}:${opts.db_port}/${opts.db_database}` await db.authenticate().then(() => { kernel.sv("log", "sequelize", "info", `opened database connection to ${url}`) }).catch((err) => { kernel.sv("fatal", `failed to open database connection to ${url}: ${err}`) throw err }) /* allow other modules to extend schema */ const dm = kernel.rs("dm", {}) kernel.hook("sequelize:ddl", "none", db, dm) /* synchronize the defined schema with the RDBMS */ if (kernel.rs("ctx:procmode") !== "worker") { await db.sync({ force: opts.db_schema_drop ? true : false }).then(() => { if (opts.db_schema_drop) kernel.sv("log", "sequelize", "info", "(re)created database schema from scratch") else kernel.sv("log", "sequelize", "info", "synchronized existing database schema") }, (err) => { kernel.sv("fatal", `failed to synchronize database schema: ${err}`) throw err }) } } async release (kernel) { /* we operate only in non-daemonized mode (microkernel-mod-daemon will remove option "--daemon" in child processes and then we want to operate only) */ const opts = kernel.rs("options:options") if (opts.daemon || opts.daemon_kill) return /* gracefully close connection on application shutdown */ kernel.sv("log", "sequelize", "info", "closing database connection") const db = kernel.rs("db") await db.close() } } /* export the Microkernel module */ module.exports = Module