Models are getting better
This commit is contained in:
		
							parent
							
								
									3cfd483ae4
								
							
						
					
					
						commit
						40d5a0fa92
					
				
							
								
								
									
										10
									
								
								controllers/auth/models.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								controllers/auth/models.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | const model = require('model'); | ||||||
|  | 
 | ||||||
|  | var user = new model.BaseModel("auth_user"); | ||||||
|  | user.addField(new model.SerialField("id")); | ||||||
|  | user.addField(new model.TextField("email")); | ||||||
|  | user.addField(new model.PasswordField("password")); | ||||||
|  | user.addField(new model.SmallIntegerField("resources")); | ||||||
|  | user.addField(new model.SmallIntegerField("project_id")); | ||||||
|  | 
 | ||||||
|  | module.exports = model.createClass(user); | ||||||
| @ -1,3 +1,4 @@ | |||||||
|  | const User = require('./models.js'); | ||||||
| const getUrl = require('create-url').getUrl; | const getUrl = require('create-url').getUrl; | ||||||
| 
 | 
 | ||||||
| module.exports.login = function(req, res, render) { | module.exports.login = function(req, res, render) { | ||||||
|  | |||||||
							
								
								
									
										35
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								index.js
									
									
									
									
									
								
							| @ -3,13 +3,18 @@ require('app-module-path').addPath(config.BASE_DIR); | |||||||
| require('app-module-path').addPath(config.UTILS_DIR); | require('app-module-path').addPath(config.UTILS_DIR); | ||||||
| require('app-module-path').addPath(config.CONTROLLERS_DIR); | require('app-module-path').addPath(config.CONTROLLERS_DIR); | ||||||
| 
 | 
 | ||||||
|  | const repl = require('repl'); | ||||||
| const express = require('express'); | const express = require('express'); | ||||||
| const pug = require('pug'); | const pug = require('pug'); | ||||||
| const http = require('http'); | const http = require('http'); | ||||||
| const path = require('path'); | const path = require('path'); | ||||||
| const log = require('log'); | const log = require('log'); | ||||||
|  | const model = require('model'); | ||||||
| 
 | 
 | ||||||
| function main() { | // Load models
 | ||||||
|  | model.load(config.CONTROLLERS_DIR); | ||||||
|  | 
 | ||||||
|  | function startServer() { | ||||||
| 
 | 
 | ||||||
|     let app = express(); |     let app = express(); | ||||||
|     let http = require('http').Server(app); |     let http = require('http').Server(app); | ||||||
| @ -69,6 +74,30 @@ function main() { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| if (require.main === module) { | const commands = { | ||||||
|     main(); |     runserver: startServer, | ||||||
|  |     reinitdb: model.reinitialize, | ||||||
|  |     shell: repl.start, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function showHelp() { | ||||||
|  |     console.log("Commands available:"); | ||||||
|  |     for (let command in commands) { | ||||||
|  |         console.log('\t' + command); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (require.main === module) { | ||||||
|  |     if (process.argv.length != 3) { | ||||||
|  |         showHelp(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let command = process.argv[2]; | ||||||
|  |     if (commands[command] === undefined) { | ||||||
|  |         showHelp(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     commands[command](); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,10 @@ | |||||||
| module.exports = { | module.exports = { | ||||||
|     'SECRET_KEY': 'gi+u6x&1%*wa8e$)ngeg4v3_h044owr%i8-pao+z(_4-_if%7b' |     'SECRET_KEY': 'gi+u6x&1%*wa8e$)ngeg4v3_h044owr%i8-pao+z(_4-_if%7b', | ||||||
|  |     'DATABASE': { | ||||||
|  |         'HOST': 'localhost', | ||||||
|  |         'PORT': 5432, | ||||||
|  |         'USERNAME': 'adejs', | ||||||
|  |         'DBNAME': 'adejs', | ||||||
|  |         'PASSWORD':'97Cnqw023V1G95fQUJR8H7gwvqUke4', | ||||||
|  |     } | ||||||
| }; | }; | ||||||
|  | |||||||
							
								
								
									
										42
									
								
								test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								test.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | const config = require('./settings/config.js'); | ||||||
|  | require('app-module-path').addPath(config.BASE_DIR); | ||||||
|  | require('app-module-path').addPath(config.UTILS_DIR); | ||||||
|  | require('app-module-path').addPath(config.CONTROLLERS_DIR); | ||||||
|  | 
 | ||||||
|  | const model = require('./utils/model.js'); | ||||||
|  | const User = require('./controllers/auth/models.js'); | ||||||
|  | const log = require('./utils/log'); | ||||||
|  | 
 | ||||||
|  | model.reinitialize(() => { | ||||||
|  | 
 | ||||||
|  |     log.debug('Database reinitialized'); | ||||||
|  | 
 | ||||||
|  |     let user = new User(); | ||||||
|  |     user.email = "toto"; | ||||||
|  |     user.password = "tata"; | ||||||
|  |     user.resources = 23; | ||||||
|  |     user.project_id = 42; | ||||||
|  |     user.save((err, u) => { | ||||||
|  | 
 | ||||||
|  |         log.debug('New user created'); | ||||||
|  | 
 | ||||||
|  |         // Test password
 | ||||||
|  |         User.getById(1, (err, user) => { | ||||||
|  |             if (model.PasswordField.testSync("tata", user.password)) { | ||||||
|  |                 log.debug("Password authentication succeed"); | ||||||
|  |             } else { | ||||||
|  |                 log.error("Password should have succeeded but failed"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!model.PasswordField.testSync("toto", user.password)) { | ||||||
|  |                 log.debug("Password authentication failed as it was supposed to"); | ||||||
|  |             } else { | ||||||
|  |                 log.error("Password should have failed but succeded"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             process.exit(0); | ||||||
|  | 
 | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | }); | ||||||
							
								
								
									
										9
									
								
								utils/db.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								utils/db.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | const db = require('settings/config').DATABASE; | ||||||
|  | 
 | ||||||
|  | module.exports = new require('pg').Pool({ | ||||||
|  |     database: db.DBNAME, | ||||||
|  |     user: db.USERNAME, | ||||||
|  |     password: db.PASSWORD, | ||||||
|  |     host: db.HOST, | ||||||
|  |     port: db.PORT, | ||||||
|  | }); | ||||||
| @ -100,6 +100,10 @@ log.pugerror = function(error) { | |||||||
|     log.write('[PER] ' + new Date() + ' ' + error, log.Color.RED); |     log.write('[PER] ' + new Date() + ' ' + error, log.Color.RED); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | log.error = function(error) { | ||||||
|  |     log.write('[ERR] ' + new Date() + ' ' + error, log.Color.RED); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| log.warning = function(message) { | log.warning = function(message) { | ||||||
|     log.write('[WRN] ' + new Date() + ' ' + message, log.Color.ORANGE); |     log.write('[WRN] ' + new Date() + ' ' + message, log.Color.ORANGE); | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										262
									
								
								utils/model.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								utils/model.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,262 @@ | |||||||
|  | const fs = require('fs'); | ||||||
|  | const db = require('db'); | ||||||
|  | const log = require('log'); | ||||||
|  | const bc = require('bcryptjs'); | ||||||
|  | 
 | ||||||
|  | let models = []; | ||||||
|  | 
 | ||||||
|  | module.exports.BaseModel = class { | ||||||
|  |     constructor(name) { | ||||||
|  |         models.push(this); | ||||||
|  |         this.name = name; | ||||||
|  |         this.fields = []; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     addField(field) { | ||||||
|  |         this.fields.push(field); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     reinitialize(callback = () => {}) { | ||||||
|  |         log.debug('Reinitializing ' + this.name); | ||||||
|  |         db.query('DROP TABLE IF EXISTS ' + this.name + ' CASCADE') | ||||||
|  |           .then(res => this.createTable(callback)) | ||||||
|  |           .catch(err => log.dberror(err)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     createTable(callback) { | ||||||
|  |         let query = 'CREATE TABLE ' + this.name + '(\n'; | ||||||
|  |         query += this.fields | ||||||
|  |             .map(f => '\t' + f.getCreationString()) | ||||||
|  |             .join(',\n'); | ||||||
|  |         query += ');'; | ||||||
|  | 
 | ||||||
|  |         log.debug(query); | ||||||
|  | 
 | ||||||
|  |         db.query(query) | ||||||
|  |           .then(res => { | ||||||
|  |               log.debug("Table " + this.name + " created") | ||||||
|  |               callback(); | ||||||
|  |           }) | ||||||
|  |           .catch(err => log.dberror(err)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.BaseField = class { | ||||||
|  |     constructor(name) { | ||||||
|  |         this.name = name; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     createMutators() { | ||||||
|  |         return { | ||||||
|  |             get: this.createGetter(), | ||||||
|  |             set: this.createSetter(), | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     createGetter() { | ||||||
|  |         var self = this; | ||||||
|  |         return function() { | ||||||
|  |             return this['_' + self.name].value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     createSetter() { | ||||||
|  |         var self = this; | ||||||
|  |         return function(e) { | ||||||
|  |             this['_' + self.name] = { | ||||||
|  |                 value: e, | ||||||
|  |                 changed: true, | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.SmallIntegerField = class extends module.exports.BaseField { | ||||||
|  |     constructor(name) { | ||||||
|  |         super(name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getCreationString() { | ||||||
|  |         return this.name + " SMALLINT"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.SerialField = class extends module.exports.BaseField { | ||||||
|  |     constructor(name) { | ||||||
|  |         super(name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getCreationString() { | ||||||
|  |         return this.name + " SERIAL"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.TextField = class extends module.exports.BaseField { | ||||||
|  |     constructor(name) { | ||||||
|  |         super(name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getCreationString() { | ||||||
|  |         return this.name + " TEXT"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.PasswordField = class extends module.exports.BaseField { | ||||||
|  |     constructor(name) { | ||||||
|  |         super(name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getCreationString() { | ||||||
|  |         return this.name + ' TEXT'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     createSetter() { | ||||||
|  |         let self = this; | ||||||
|  |         return function(e) { | ||||||
|  |             this['_' + self.name] = { | ||||||
|  |                 value: bc.hashSync(e, 8), | ||||||
|  |                 changed: true, | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.PasswordField.testSync = function(password, hash) { | ||||||
|  |     return bc.compareSync(password, hash); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | module.exports.createClass = function(model) { | ||||||
|  | 
 | ||||||
|  |     let ret = function(param) { | ||||||
|  |         this._persisted = false; | ||||||
|  |         for (let field of model.fields) { | ||||||
|  |             this['_' + field.name] = { | ||||||
|  |                 value: undefined, | ||||||
|  |                 changed: false, | ||||||
|  |             } | ||||||
|  |             Object.defineProperty(this, field.name, field.createMutators()); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     ret.getById = function(id, callback) { | ||||||
|  |         db | ||||||
|  |             .query('SELECT * FROM ' + model.name + ' WHERE id=$1;', [id]) | ||||||
|  |             .then(res => { | ||||||
|  |                 // Create the instance
 | ||||||
|  |                 let instance = new ret(); | ||||||
|  |                 instance._persisted = true; | ||||||
|  |                 instance._setFromDbResult(res); | ||||||
|  |                 callback(undefined, instance); | ||||||
|  |             }) | ||||||
|  |             .catch(err => callback(err)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ret.prototype._setFromDbResult = function(res) { | ||||||
|  |         for (let field of model.fields) { | ||||||
|  |             this['_' + field.name] = { | ||||||
|  |                 value: res.rows[0][field.name], | ||||||
|  |                 changed: false, | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ret.prototype.save = function(callback = () => {}) { | ||||||
|  |         let fieldsToSave = []; | ||||||
|  |         for (let field of model.fields) { | ||||||
|  |             if (this['_' + field.name].changed) { | ||||||
|  |                 fieldsToSave.push({ | ||||||
|  |                     field: field, | ||||||
|  |                     value: this[field.name], | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fieldsToSave = fieldsToSave.filter((field) => { | ||||||
|  |             return !(field.field instanceof module.exports.SerialField); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if (!this._persisted) { | ||||||
|  |             // INSERT INTO
 | ||||||
|  |             let queryBegin = 'INSERT INTO ' + model.name + '(' | ||||||
|  |             let queryEnd = ' VALUES ('; | ||||||
|  |             queryBegin += fieldsToSave | ||||||
|  |                 .map(f => f.field.name) | ||||||
|  |                 .join(','); | ||||||
|  | 
 | ||||||
|  |             queryEnd += fieldsToSave | ||||||
|  |                 .map((f, id) => '$' + (id + 1)) | ||||||
|  |                 .join(','); | ||||||
|  | 
 | ||||||
|  |             queryBegin += ')' | ||||||
|  |             queryEnd += ') RETURNING *;' | ||||||
|  | 
 | ||||||
|  |             let query = queryBegin + queryEnd; | ||||||
|  | 
 | ||||||
|  |             db | ||||||
|  |                 .query(query, fieldsToSave.map(f => f.value)) | ||||||
|  |                 .then(res => { | ||||||
|  |                     this._setFromDbResult(res); | ||||||
|  |                     this._persisted = true; | ||||||
|  |                     callback(undefined, this); | ||||||
|  |                 }) | ||||||
|  |                 .catch(err => callback(err)); | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             // UPDATE FROM
 | ||||||
|  |             let query = 'UPDATE FROM ' + model.name + ' SET '; | ||||||
|  | 
 | ||||||
|  |             query += fieldsToSave.map(f => { | ||||||
|  |                 return f.field.name + '=' + f.value; | ||||||
|  |             }).join(','); | ||||||
|  | 
 | ||||||
|  |             query += ') RETURNING *;' | ||||||
|  | 
 | ||||||
|  |             db | ||||||
|  |                 .query(query, fieldsToSave.map(f => f.value)) | ||||||
|  |                 .then(res => { | ||||||
|  |                     this._setFromDbResult(res); | ||||||
|  |                     this._persisted = true; | ||||||
|  |                     callback(undefined, this); | ||||||
|  |                 }) | ||||||
|  |                 .catch(err => callback(err)); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.reinitialize = function(callback = () => {}) { | ||||||
|  |     let finished = models.map(m => false); | ||||||
|  |     for (let i = 0; i < models.length; i++) { | ||||||
|  |         let model = models[i]; | ||||||
|  |         model.reinitialize(() => { | ||||||
|  |             finished[i] = true; | ||||||
|  |             if (finished.reduce((a, b) => a && b, true)) { | ||||||
|  |                 callback(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.load = function(controllersDir) { | ||||||
|  | 
 | ||||||
|  |     fs.readdirSync(controllersDir).forEach(function(name) { | ||||||
|  | 
 | ||||||
|  |         let path = controllersDir + '/' + name + '/models.js'; | ||||||
|  |         try { | ||||||
|  |             fs.accessSync(path); | ||||||
|  |         } catch (e) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         require(path); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user