Comparing version 1.0.4 to 1.0.5
@@ -11,11 +11,13 @@ //Low level connection to PostgreSQL database | ||
//- | ||
const DefaultDB = 'template1' | ||
const Fs = require('fs') //Node filesystem module | ||
const { Client } = require('pg') //PostgreSQL | ||
const Format = require('pg-format') //String formatting/escaping | ||
var initialize = true //Next user should try to init DB if necessary | ||
const DefaultDB = 'template1' | ||
const Fs = require('fs') //Node filesystem module | ||
const { Client, types } = require('pg') //PostgreSQL | ||
const Format = require('pg-format') //String formatting/escaping | ||
var initialize = true //Next user should try to init DB if necessary | ||
types.setTypeParser(1082, d=>(d)) //Don't convert simple dates to JS date/time | ||
module.exports = class dbClient { | ||
constructor(conf, notifyCB, connectCB) { | ||
if (conf.logger) { | ||
if (conf && conf.logger) { | ||
this.log = conf.logger //Use a passed-in logger | ||
@@ -22,0 +24,0 @@ delete conf.logger |
@@ -6,2 +6,4 @@ //Manage the connection between a User Interface and the backend database | ||
//X- Works with dbclient | ||
//X- Allow separate db connection per ws connection | ||
//X- Allow separate instance of all variables per ws connection | ||
//- Should any of the %s's in pg-format really be %L's? | ||
@@ -15,3 +17,4 @@ //- Allow to specify 'returning' fields to insert, update overriding default * | ||
var dbClient = require('./dbclient.js') //PostgreSQL | ||
const DbClient = require('./dbclient.js') //PostgreSQL | ||
const Ws = require('ws') //Web sockets | ||
const Format = require('pg-format') //String formatting/escaping | ||
@@ -21,35 +24,29 @@ const Opers = ['=', '!=', '<', '<=', '>', '>=', '~', 'diff', 'in', 'null', 'true'] | ||
module.exports = class Wyseman { | ||
constructor(conf, clientPort, userControl) { | ||
this.ws = null //No web socket connection yet | ||
this.db = new dbClient(this.config = conf, (channel, message, mine) => { | ||
let data = JSON.parse(message) | ||
this.log.trace("Async notify from DB:", channel, data, mine) | ||
// if (this.ws && !mine) { //Ignore notices I caused | ||
this.ws.send(JSON.stringify({action: 'notify', channel, data}), err => { | ||
constructor(dbConf, wsConf) { | ||
let wss = new Ws.Server({ //Initiate a new websocket connection | ||
port: wsConf.port, clientTracking: true | ||
}) | ||
wss.on('connection', (ws) => { //When connection from view is open | ||
let ctx = {db:null, control:null, wsConf} | ||
ctx.db = new DbClient(dbConf, (channel, message, mine) => { | ||
let data = JSON.parse(message) | ||
this.log.trace("Async notify from DB:", channel, data, mine) | ||
ws.send(JSON.stringify({action: 'notify', channel, data}), err => { | ||
if (err) this.log.error(err) | ||
}) | ||
// } | ||
}) | ||
this.log = conf.logger || this.db.log | ||
this.log.trace("In Wyseman constructor conf:", JSON.stringify(conf), " Client port:", clientPort) | ||
this.userControl = userControl //Handler for custom actions | ||
var wss = new (require('ws')).Server({ //Initiate a new websocket connection | ||
port: clientPort, clientTracking: true | ||
}) | ||
}) | ||
this.log = dbConf.logger || ctx.db.log | ||
this.log.trace("In Wyseman constructor conf:", JSON.stringify(dbConf), " Client port:", wsConf.port) | ||
wss.on('connection', (ws) => { //When connection from view is open | ||
this.ws = ws //Note our connection (for asynch traffic) | ||
ws.on('close', (code, reason) => { | ||
this.log.debug("Wyseman socket connection closed:", code, reason) | ||
this.ws = null | ||
if (wss.clients.size <= 0 && this.db) | ||
this.db.disconnect() //Free up this DB connection | ||
ctx.db.disconnect() //Free up this DB connection | ||
}) | ||
ws.on('message', (imsg) => { //When message received from client | ||
// this.log.debug("Incoming Wyseman message:" + imsg + ";") | ||
this.log.trace("Incoming Wyseman message:" + imsg + ";") | ||
let packet = JSON.parse(imsg) | ||
this.handler(packet, (omsg) => { //Handle/control an incoming packet | ||
this.handler(packet, ctx, (omsg) => { //Handle/control an incoming packet | ||
let jmsg = JSON.stringify(omsg) | ||
@@ -62,3 +59,2 @@ //this.log.trace('Sending back:', JSON.stringify(omsg, null, 2)) | ||
}) | ||
this.log.debug("Connected clients: ", wss.clients.size) | ||
@@ -89,9 +85,56 @@ }) //wss.on connection | ||
// Attempt a DB query, processing any errors | ||
// Handle an incoming packet from the view client | ||
// ------------------------------------------------------------------- | ||
query(qstring, parms, tuples, msg, cb) { | ||
if (!qstring) return //Ignore null queries (result of an error in query builder) | ||
handler(msg, ctx, sender) { | ||
this.log.trace("Wyseman packet handler, msg:", JSON.stringify(msg)) | ||
let {id, view, action} = msg | ||
if (!view) return | ||
if (action == 'lang') { | ||
action = 'tuple'; | ||
Object.assign(msg, {fields: ['title','help','columns','messages'], table: 'wm.table_lang', where: {obj: view, language: msg.language || 'en'}}) | ||
} else if (msg.action == 'meta') { | ||
action = 'tuple' | ||
Object.assign(msg, {fields: ['obj','pkey','cols','columns','styles','fkeys'], table: 'wm.table_meta', where: {obj: view}}) | ||
} | ||
let {table, params, fields, where, order} = msg, argtypes | ||
this.log.debug(" From msg, table:", table, " view:", view, "order: ", order) | ||
let [sch, tab] = (table || view).split('.') //Split into schema and table | ||
if (!tab) {tab = sch; sch = 'public'} //Default to public if no schema specified | ||
;([tab, argtypes] = tab.split(/[\(\)]/)) //In case table is specified as a function | ||
this.log.trace(" tab:", tab, " argtypes:", argtypes) | ||
table = Format.ident(sch) + '.' + Format.ident(tab) | ||
if (argtypes) argtypes = argtypes.split(',') | ||
let tuples = 1, result = {query: null, parms: [], error: null} | ||
switch (action) { | ||
case 'tuple': | ||
this.buildSelect(result, {fields, table, argtypes, params, where}); break; | ||
case 'select': | ||
this.buildSelect(result, {fields, table, argtypes, params, where, order}) | ||
tuples = null; break; | ||
case 'update': | ||
this.buildUpdate(result, fields, table, where); break; | ||
case 'insert': | ||
this.buildInsert(result, fields, table); break; | ||
case 'delete': | ||
this.buildDelete(result, table, where) | ||
tuples = 0; break; | ||
default: | ||
if (!ctx.control && ctx.wsConf && ctx.wsConf.actionHandler) | ||
ctx.control = new ctx.wsConf.actionHandler(ctx.wsConf.expApp) //Start a controller just in time | ||
if (ctx.control && ctx.control.handle && ctx.control.handle(msg, sender)) return | ||
result.error = this.error('unknown action: ' + action, 'badAction') | ||
} | ||
if (result.error) { | ||
sender({error: result.error, id, view, action}) | ||
return | ||
} | ||
let { query, parms } = result | ||
if (!query) return //Ignore null queries (result of an error in query builder) | ||
this.db.query(qstring, parms, (err, res) => { //Run the user's query | ||
this.log.debug(" query:", qstring, "parms:", parms, "tuples:", tuples, "Err:", err) | ||
ctx.db.query(query, parms, (err, res) => { //Run the user's query | ||
this.log.debug(" query:", query, "parms:", parms, "tuples:", tuples, "Err:", err) | ||
if (err) { | ||
@@ -109,54 +152,6 @@ msg.error = this.error("from database", err) | ||
} | ||
if (cb) cb(msg) | ||
if (sender) sender(msg) | ||
}) | ||
} | ||
// Handle an incoming packet from the view client | ||
// ------------------------------------------------------------------- | ||
handler(msg, sender) { | ||
this.log.trace("Wyseman packet handler, msg:", JSON.stringify(msg)) | ||
let {id, view, action} = msg | ||
if (!view) return | ||
if (action == 'lang') { | ||
action = 'tuple'; | ||
Object.assign(msg, {fields: ['title','help','columns','messages'], table: 'wm.table_lang', where: {obj: view, language: msg.language || 'en'}}) | ||
} else if (msg.action == 'meta') { | ||
action = 'tuple' | ||
Object.assign(msg, {fields: ['obj','pkey','cols','columns','styles','fkeys'], table: 'wm.table_meta', where: {obj: view}}) | ||
} | ||
let {table, params, fields, where, order} = msg, argtypes | ||
this.log.debug(" From msg, table:", table, " view:", view, "order: ", order) | ||
let [sch, tab] = (table || view).split('.') //Split into schema and table | ||
if (!tab) {tab = sch; sch = 'public'} //Default to public if no schema specified | ||
;([tab, argtypes] = tab.split(/[\(\)]/)) //In case table is specified as a function | ||
this.log.trace(" tab:", tab, " argtypes:", argtypes) | ||
table = Format.ident(sch) + '.' + Format.ident(tab) | ||
if (argtypes) argtypes = argtypes.split(',') | ||
let tuples = 1, result = {query: null, parms: [], error: null} | ||
switch (action) { | ||
case 'tuple': | ||
this.buildSelect(result, {fields, table, argtypes, params, where}); break; | ||
case 'select': | ||
this.buildSelect(result, {fields, table, argtypes, params, where, order}) | ||
tuples = null; break; | ||
case 'update': | ||
this.buildUpdate(result, fields, table, where); break; | ||
case 'insert': | ||
this.buildInsert(result, fields, table); break; | ||
case 'delete': | ||
this.buildDelete(result, table, where) | ||
tuples = 0; break; | ||
default: | ||
if (this.userControl && this.userControl(msg, sender)) return | ||
result.error = this.error('unknown action: ' + action, 'badAction') | ||
} | ||
if (result.error) | ||
sender({error: result.error, id, view, action}) | ||
else | ||
this.query(result.query, result.parms, tuples, msg, sender) | ||
} | ||
// ----------------------------------------------------------------------------- | ||
@@ -163,0 +158,0 @@ buildSelect(res, spec) { |
{ | ||
"name": "wyseman", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"description": "PostgreSQL Schema Manager with Javascript, Ruby, TCL API", | ||
@@ -29,3 +29,4 @@ "main": "lib/index.js", | ||
"pg": "^7.5.0", | ||
"pg-format": "^1.0.4" | ||
"pg-format": "^1.0.4", | ||
"ws": "^6.1.2" | ||
}, | ||
@@ -32,0 +33,0 @@ "devDependencies": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
609679
3
523
+ Addedws@^6.1.2
+ Addedasync-limiter@1.0.1(transitive)
+ Addedws@6.2.3(transitive)