Models are getting better
This commit is contained in:
parent
3cfd483ae4
commit
40d5a0fa92
|
@ -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',
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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…
Reference in New Issue