Models are getting better

This commit is contained in:
Thomas Forgione 2017-09-23 12:53:56 +00:00
parent 3cfd483ae4
commit 40d5a0fa92
No known key found for this signature in database
GPG Key ID: 95D964F74A96119E
8 changed files with 368 additions and 4 deletions

View 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);

View File

@ -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) {

View File

@ -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]();
} }

View File

@ -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
View 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
View 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,
});

View File

@ -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
View 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);
});
}