adejs/utils/model.js

263 lines
6.2 KiB
JavaScript

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