UNPKG

@tek-tech/deebee

Version:

talking with a mysql|postgres database made easier

567 lines (556 loc) 14.7 kB
let instances = [] const {Pool} = require('pg') let resetDbErrs = ['ECONNRESET','PROTOCOL_PACKETS_OUT_OF_ORDER'] const {Ear} = require('@tek-tech/ears') class PGDeeBee extends Ear{ static Builder = class { static _table(name,fields,keys){ return { name ,fields:fields.map( field=>{ return this._field(...field) } ) ,keys:this._keys(...keys) } } static _field(name,type,attrs){ return { name,type,attrs } } static _keys(primary,foreign){ return { primary, foreign:this._foreignKeys(foreign) } } static _foreignKeys(keys){ return keys.map( key=>{ return this._foreignKey(key) } ) } static _foreignKey([name,[field,update,remove]]){ return { name, references:{ field,update,remove } } } } builder(){ return DeeBee.Builder } _____registerAction(actionname,callback){ this[actionname] = callback } _tbs(cb){ this._db().query( `select * from pg_catalog.pg_tables where schemaname = '${this._db().database}'`,(e,r)=>{ e = e ? e.stack : e r = r ? r.rows : r cb(e,r) } // `select * from information_schema.tables`,cb ) } _tb_exists(name,cb){ let exists = null this._tbs( (e,tbs)=>{ if(e) cb(exists) else tbs.length ? tbs.forEach( (table,idx)=>{ if(table['tablename']==name) exists = table[`Tables_in_${this.dbname}`] if(idx+1==tbs.length)cb(exists) } ) : cb(exists) } ) } _setUsersTable(tbl = null){ this.usersTable = tbl } _setUsersPasswField(field='password'){ this.usersPasswField = field } _getUsersPasswField(){ return this.usersPasswField } _getUsersTable(tbl){ return this.usersTable } _setUsersLogField(field='name'){ this.usersLogField = field } _getUsersLogField(){ return this.usersLogField } _getAdminsLogField(){ return this.adminsLogField } _setAdminsTable(tbl = null){ this.adminsTable = tbl } _setAdminsPasswField(field='password'){ this.AdminsPasswField = field } _getAdminsPasswField(){ return this.AdminsPasswField } _getAdminsTable(tbl){ return this.adminsTable } _setAdminsLogField(field='name'){ this.adminsLogField = field } _getAdminsLogField(){ return this.adminsLogField } __newFieldStr({name,type,attrs}){ let fieldstr = `${name} ${type}` if(attrs&&attrs.length)attrs.forEach( attrname=>{ fieldstr = `${fieldstr} ${attrname}` } ) return fieldstr } __newFieldsStr(fields){ let fieldsStr = `` fields.forEach( field=>{ fieldsStr = `${fieldsStr == `` ? fieldsStr : `${fieldsStr},`}${this.__newFieldStr(field)}` } ) return fieldsStr } __newKeyStr({name,attrs,references}){ let keyStr = `${name}` if(attrs){ attrs.forEach( attrname=>{ keyStr = `${keyStr} ${attrs[attrname]}` } ) } if(references){ references = [references] references.forEach( ({field,update,remove})=>{ keyStr = `${ keyStr} REFERENCES ${field} ${(()=>{ return update ? `ON UPDATE ${update}` : "" })()} ${(()=>{ return remove ? `ON DELETE ${remove}` : "" })()} ` } ) } return keyStr } __newKeysStr(keys){ let keysStr = `` if(keys.hasOwnProperty('primary') && keys.primary){ keysStr = `${keysStr} ${this.__newKeyStr({name:`PRIMARY KEY (${keys.primary})`,attrs:[]})}` } if(keys.hasOwnProperty('foreign') && keys.foreign!=undefined){ if(keys.foreign.length){ keys.foreign.forEach( key=>{ key.name = `,FOREIGN KEY ${key.name}` keysStr = `${keysStr} ${this.__newKeyStr(key)}` } ) } } return keysStr } __newTableReq({name,fields,keys}){ return `CREATE TABLE IF NOT EXISTS ${name} (${this.__newFieldsStr(fields)} , ${this.__newKeysStr(keys)})` } __createTable(table,cb){ if(table){ let req = this.__newTableReq(table) this._db().query( req,cb ) }else{ cb(`provided argument ${table} is incorrect`,null) } } __createDataBase(name,tables,cb){ let req = `create DATABASE ${name}` this._db().query( req,(err,res)=>{ err = err ? err.stack : err res = res ? res.rows : res if(err){ cb(err,res) }else{ if(res){ let errs = [] let ress = [] if(tables.length){ tables.forEach( (table,idx)=>{ this.__createTable(table,(e,r)=>{ errs.push(e) ress.push(r) if(idx+1==tables.length){ cb(errs,ress,tables.length) } }) } ) return }else{ cb([err],[res]) } }else{ cb(['erreur non comprise||non understood error'],[res]) } } } ) } __dropTable(table,cb){ let req = `DROP TABLE ${table}` this._db().query( req,cb ) } _db(){ return this.db } __db(){ try{ var deebee = this.dbcreds.database this.db = new Pool(this.dbcreds) this.db.on( 'error',err=>{ this.handleConnectErr(err,deebee) } ) this.db.connect( err=>{ try{ if(err){ if(resetDbErrs.includes(err)){ console.log('error encounteered, made a fix\nretrying connection to database') }else{ this.handleConnectErr(err,deebee) } }else{ console.log('connected to pgsql database') this.dbcreds.database = deebee this._db().database=deebee this.setupTables() console.log('connected to pgsql server') } }catch(e){ if(resetDbErrs.includes(e.code)) this._db() this.handleConnectErr(e,deebee) } } ) return this.db; }catch(e){ if(resetDbErrs.includes(e.code)) this._db() this.handleConnectErr(e,deebee) } } handleConnectErr(err,deebee){ if(err.toString().match('too many clients already')) return if(err.toString().match(`database "${deebee}" does not exist`)){ this.dbcreds.database = deebee deebee = this.dbname this.dbcreds.database = '' this.db = new Pool(this.dbcreds) this.db.connect( err=>{ if(err)this.handleConnectErr(err) else{ this.dbcreds.database = deebee this.__createDataBase( this.dbcreds.database,[],((e,r,tablessize)=>{ console.log(e,r) let goterr=0 if(e.length){ e.forEach( err=>{ if(err){ console.log(err) goterr++ } } ) } if(goterr==0){ if(r && r.length){ if(tablessize && (tablessize == r.length)){ this.__db() }else{ this.__db() } }else{ this.__db() } } } ) ) } } ) console.log(`database ${deebee} not found creating it`) }else{ console.log(err.hasOwnProperty('sqlMessage')?err.sqlMessage:err) this.__db() } } __reqArr(fields_,vals_,statement){ for(let i = 0; i < (fields_.length) ; i++){ statement.bindParam(":"+fields_[i],vals_[i]); } return statement; } __valsStr(vals_,bef='',aft=''){ let vals=""; for(let i = 0 ; i < (vals_.length) ;i++ ){ vals+=(bef)+vals_[i]+(i+1 < (vals_.length) ? "," : "")+(aft); } return vals; } __fvalStr(fields_=[],vals_=[],sep=',',bef='',aft='',vbef=""){ let vals = ""; for(let i = 0 ; i < (vals_.length) ;i++ ){ vals+=(bef)+fields_[i]+"="+vbef+vals_[i]+(i+1 < (vals_.length) ? " "+sep+" " : "")+(aft); } return vals; } __condsStr(fields_=[],vals_=[],bef=""){ return (fields_.length) ? " WHERE "+this.__fvalStr(fields_,bef?fields_:vals_,"AND","","",bef) : ""; } __selectFrom(table_,fields_=[],conds=[[],[]]){ return "SELECT "+this.__valsStr(fields_)+" FROM "+ table_ +((conds.length) ? this.__condsStr(conds[0],conds[1]) : ""); } __delFrom(table_,conds=[[],[]]){ return "DELETE FROM "+ table_ + this.__condsStr(conds[0],conds[1],":"); } __updtWhere(table_,fields_,vals_,conds=[[],[]]){ return "UPDATE "+ table_ +" SET "+ this.__fvalStr(fields_,vals_,",") +this.__condsStr(conds[0],conds[1]); } __insertINTO(table,fields_=[],vals_=[]){ return "INSERT INTO "+table+" ("+this.__valsStr(fields_)+") VALUES ("+this.__valsStr(vals_)+")"; } _req(type_,table_,fields_=[],vals_=[],conds=[[],[]]){ let req = ""; switch (type_) { case 'select': req = this.__selectFrom(table_,fields_,conds); break; case 'insert': req = this.__insertINTO(table_,fields_,vals_); break; case 'delete': req = this.__delFrom(table_,conds); break; case 'update': req = this.__updtWhere(table_,fields_,vals_,conds); break; default: // code... break; } return req; } _insertReq(table_,fields_,vals_,c){ return this._req( 'insert' ,table_ ,fields_ ,vals_ ) } _updateReq(table_,fields_,vals_,conds_){ return this._req( 'update' ,table_ ,fields_ ,vals_ ,conds_ ) } _delReq(table_,conds_,cb){ return this._req( 'delete', table_, conds_ ) } ___updateMember(fields_,vals_,id=null){ let table=this._getUsersTable(); let conds=[ ['id'] ,[id] ]; return this._updateReq(table,fields_,vals_,conds); } ___loginreq(table,user,pass){ return this._req("select",table,["id",this._getUsersLogField()],[],[[this._getUsersLogField(),this._getUsersPasswField()],["'"+user+"'",`password('${pass}')`]]); } ___delMember(name,id=null){ let table=this._getUsersTable(); let conds=[ [''+(id?'id':this._getUsersLogField())] ,[''+(id?id:name)] ]; return this._delReq(table,conds); } ___newNotification({member_id,concerned_id,type_}){ let fields = ['member_id','concerned_id','type']; let vals = [`${member_id}`,`${concerned_id}`,`'${type_}'`]; let table = '_notifications'; return this._insertReq(table,fields,vals); } ___login(user,pass,cb){ this.db.query( this.___loginreq(this._getUsersTable(),user,pass) ,cb ) } ___all_members(cb){ let req = this._req('select',this._getUsersTable(),['*']); this.db.query(req,(err,res)=>{ err = err ? err.stack : err res = res ? res.rows : res if(res&&res.length){ let r = [] res.forEach( match=>{ match.passw = null r.push(match) } ) res = r } cb(err?err:res) } ) } ___search(name,cb){ let req = this._req('select',this._getUsersTable(),['id','name','email','gender','birthday','star_sign','zodiac','planet'],null,[['name'],[`'${name}' OR name LIKE '%${name}%' OR email LIKE '%${name}%'`]]) this.db.query(req,(err,res)=>{ err = err ? err.stack : err res = res ? res.rows : res if(res && res.length){ res = res.map(match=>{ if(match.name.match(name)) match.matchedBy = 'name' if(match.email.match(name)) match.matchedBy = 'email' return match }) } cb(err,res) } ) } ___member(id,cb){ let req = this._req('select',this._getUsersTable(),['id','name','email','gender','birthday','star_sign','zodiac','planet'],null,[['id'],[id]]); this.db.query(req,(err,res)=>{ err = err ? err.stack : err res = res ? res.rows : res if(err)cb(err,null) else{ cb(res) } } ) } ___update(type_,data,id,cb){ let fields = data[0] let vals = data[1] let flds = [] let vls = [] fields.forEach( (fld,i)=>{ if(fld!=this._getUsersPasswField()){ flds.push(fields[i]) vls.push(vals[i]) }else{ if(vals[i]){ flds.push(fields[i]) vls.push(`password(${vals[i]})`) } } } ) fields = flds vals = vls if(type_=='member'){ this.db.query( this.___updateMember(fields,vals,id),cb ) } } setupTables(){ if(this.configtables.length){ let errs = [] let res = [] const final = ()=>{ if(errs.length){ console.log('some errors occured when setting up the database') console.log(errs.join("\n")) }else{ console.log('DeeBee is Ready') this.ready = 1 } } let made = 0 this.configtables.forEach( (table,idx)=>{ this._tb_exists( table.name,tbl=>{ if(tbl){ made++ res.push(tbl) if(made==this.configtables.length)final() }else{ this.__createTable(table,(e,r)=>{ made++ if(e)errs.push(e) res.push(r) if(made==this.configtables.length)final() }) } } ) } ) }else{ console.log('DeeBee is Ready') this.ready = 1 } } constructor(creds,tables=[]){ super() this.configtables = tables this.dbname = creds.database this.db = null; this.dbcreds = creds this._setUsersTable('_members') this.__db() } } function cleanInstances(){ instances.map( instance=>{ instance.disconnect() } ) } module.exports = PGDeeBee process.on('exit',cleanInstances.bind(null, {exit:true}))