We have email verification
This commit is contained in:
parent
fa105f855a
commit
dae9643cd0
|
@ -1,10 +1,14 @@
|
||||||
const model = require('model');
|
const model = require('model');
|
||||||
|
|
||||||
var user = new model.BaseModel("auth_user");
|
var user = new model.BaseModel("authUser");
|
||||||
user.addField(new model.SerialField("id"));
|
user.addField(new model.SerialField("id"));
|
||||||
user.addField(new model.TextField("email"));
|
user.addField(new model.TextField("username", {unique: true, notNull: true}));
|
||||||
user.addField(new model.PasswordField("password"));
|
user.addField(new model.TextField("email", {unique: true, notNull: true}));
|
||||||
|
user.addField(new model.BoolField("active", {notNull: true, default: false}));
|
||||||
|
user.addField(new model.TextField("activationKey"));
|
||||||
|
user.addField(new model.PasswordField("password", {notNull: true}));
|
||||||
user.addField(new model.SmallIntegerField("resources"));
|
user.addField(new model.SmallIntegerField("resources"));
|
||||||
user.addField(new model.SmallIntegerField("project_id"));
|
user.addField(new model.SmallIntegerField("projectId"));
|
||||||
|
|
||||||
module.exports = model.createClass(user);
|
module.exports = model.createClass(user);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends "pyade/base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
{% if login_failed %}
|
||||||
|
<div class="alert">Username or password incorrect. Please try again.</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col">
|
||||||
|
<form method="POST" action="{% url "login_target" %}">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="username" placeholder="Username" autofocus>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" class="form-control" name="password" placeholder="Password">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input class="btn btn-primary form-control" type="submit" value="Log in">
|
||||||
|
</div>
|
||||||
|
{% csrf_token %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col"></div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -1,4 +1,17 @@
|
||||||
extends ../../../templates/base.pug
|
extends ../../../templates/base.pug
|
||||||
|
|
||||||
block content
|
block content
|
||||||
p Login
|
if loginFailed
|
||||||
|
.alert
|
||||||
|
| Username or password incorrect. Please try again.
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
.col
|
||||||
|
form(method="POST", action=getUrl("loginTarget"))
|
||||||
|
.form-group
|
||||||
|
input.form-control(type="text", name="username", placeholder="Username", autofocus)
|
||||||
|
.form-group
|
||||||
|
input.form-control(type="password", name="password", placeholder="Password")
|
||||||
|
.form-group
|
||||||
|
input.btn.btn-primary.form-control(type="submit", value="Log in")
|
||||||
|
.col
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
{% extends "pyade/base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>
|
||||||
|
A mail was sent to you.
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
In this mail, you'll find a link to activate your account.
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
||||||
|
extends ../../../templates/base.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
h1 A mail was sent to you
|
||||||
|
p.
|
||||||
|
In this mail, you'll find a link to activate your account.
|
||||||
|
p.
|
||||||
|
Please, don't forget that my server is cheap, so depending on your mail provider, my email might get thrown in the spam :'(
|
|
@ -0,0 +1,38 @@
|
||||||
|
extends ../../../templates/base.pug
|
||||||
|
block content
|
||||||
|
if registeringFailed
|
||||||
|
.alert There was an error.
|
||||||
|
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
.col
|
||||||
|
form(method="POST", action=getUrl("signupTarget"))
|
||||||
|
.form-group
|
||||||
|
input.form-control(type='text', name='username', placeholder='Username', autofocus='')
|
||||||
|
.form-group
|
||||||
|
input#email.form-control(type='email', name='email', placeholder='E-mail address')
|
||||||
|
.form-group
|
||||||
|
input#pass1.form-control(type='password', name='password', placeholder='Password')
|
||||||
|
.form-group
|
||||||
|
input#pass2.form-control(type='password', placeholder='Retype your password')
|
||||||
|
.form-group
|
||||||
|
input.btn.btn-primary.form-control(type='submit', value='Log in')
|
||||||
|
.col
|
||||||
|
|
||||||
|
block extrajs
|
||||||
|
script.
|
||||||
|
var mail = document.getElementById('email'),
|
||||||
|
pass = document.getElementById('pass1'),
|
||||||
|
pass2 = document.getElementById('pass2');
|
||||||
|
|
||||||
|
function validatePasswords() {
|
||||||
|
if (pass1.value === pass2.value) {
|
||||||
|
pass2.setCustomValidity('');
|
||||||
|
} else {
|
||||||
|
pass2.setCustomValidity("Passwords don't match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pass1.addEventListener('change', validatePasswords);
|
||||||
|
pass2.addEventListener('keyup', validatePasswords);
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
style.
|
||||||
|
.body {
|
||||||
|
width: 570px;
|
||||||
|
padding: 0px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
border: solid;
|
||||||
|
border-width: thin;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: solid;
|
||||||
|
border-bottom-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
font-size: 75%;
|
||||||
|
padding: 10px;
|
||||||
|
border-top: solid;
|
||||||
|
border-top-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body
|
||||||
|
.header
|
||||||
|
h1 Welcome on ADEjs!
|
||||||
|
p.
|
||||||
|
Hello #{user.username}!
|
||||||
|
p.
|
||||||
|
You recently subscribed on ADEjs with the email <a href="mailto:#{user.email}">#{user.email}</a>.
|
||||||
|
p.
|
||||||
|
To finalize your subscription, please <a href="#{activationUrl}">click here to activate your account</a>.
|
||||||
|
|
||||||
|
.footer.
|
||||||
|
If you did not subscribe on PyADE with this email address, please ignore this e-mail.
|
|
@ -0,0 +1,10 @@
|
||||||
|
Hello #{user.username}, and welcome on ADEjs!
|
||||||
|
|
||||||
|
You recently subscribed on ADEjs with the email <a href="mailto:#{user.email}">#{user.email}</a>.
|
||||||
|
|
||||||
|
To finalize your subscription, please <a href="#{activationUrl}">click here to activate your account</a>.
|
||||||
|
|
||||||
|
See you later on ADEjs!
|
||||||
|
|
||||||
|
---
|
||||||
|
If you did not subscribe on ADEjs with this email address, please ignore this e-mail.
|
|
@ -2,4 +2,10 @@ const url = require('create-url').url;
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
url('/login', 'login', 'login'),
|
url('/login', 'login', 'login'),
|
||||||
|
url('/logout', 'logout', 'logout'),
|
||||||
|
url('/login-target', 'loginTarget', 'loginTarget', 'POST'),
|
||||||
|
url('/signup', 'signup', 'signup'),
|
||||||
|
url('/signup-target', 'signupTarget', 'signupTarget', 'POST'),
|
||||||
|
url('/mail-was-sent', 'mailWasSent', 'mailWasSent'),
|
||||||
|
url('/activate/:activationKey', 'activate', 'activate'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,10 +1,95 @@
|
||||||
|
const testPassword = require('model').PasswordField.testSync;
|
||||||
const User = require('./models.js');
|
const User = require('./models.js');
|
||||||
const getUrl = require('create-url').getUrl;
|
const getUrl = require('create-url').getUrl;
|
||||||
|
const mail = require('mail');
|
||||||
|
const pug = require('pug');
|
||||||
|
const config = require('settings/config');
|
||||||
|
|
||||||
module.exports.login = function(req, res, render) {
|
module.exports.login = function(req, res, render) {
|
||||||
render('login.pug');
|
render('login.pug');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.signup = function(req, res, render) {
|
||||||
|
render('signup.pug');
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.logout = function(req, res, render) {
|
module.exports.logout = function(req, res, render) {
|
||||||
|
req.session.user = undefined;
|
||||||
|
req.session.save();
|
||||||
res.redirect(getUrl("index"));
|
res.redirect(getUrl("index"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.loginTarget = function(req, res, render) {
|
||||||
|
User.getByUsername(req.body.username, (err, user) => {
|
||||||
|
if (user === undefined || !user.active) {
|
||||||
|
res.redirect(getUrl('login'));
|
||||||
|
} else {
|
||||||
|
if (testPassword(req.body.password, user.password)) {
|
||||||
|
req.session.user = user;
|
||||||
|
req.session.save();
|
||||||
|
res.redirect(getUrl('index'));
|
||||||
|
} else {
|
||||||
|
res.redirect(getUrl('login'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.signupTarget = function(req, res, render) {
|
||||||
|
let user = new User();
|
||||||
|
user.username = req.body.username;
|
||||||
|
user.email = req.body.email;
|
||||||
|
user.password = req.body.password;
|
||||||
|
|
||||||
|
require('crypto').randomBytes(48, function(err, buffer) {
|
||||||
|
user.activationKey = buffer.toString('hex');
|
||||||
|
user.save((err) => {
|
||||||
|
res.locals.user = user;
|
||||||
|
|
||||||
|
let baseUrl =
|
||||||
|
req.headers.referer.split('/').slice(0, 3).join('/') + '/'
|
||||||
|
|
||||||
|
res.locals.activationUrl =
|
||||||
|
baseUrl + 'activate/' + user.activationKey;
|
||||||
|
|
||||||
|
let html = pug.renderFile(
|
||||||
|
__dirname + '/templates/validation-mail.pug',
|
||||||
|
res.locals,
|
||||||
|
);
|
||||||
|
|
||||||
|
mail({
|
||||||
|
from: config.MAIL.FROM,
|
||||||
|
to: user.username + ' <' + user.email + '>',
|
||||||
|
subject: 'Welcome on ADEjs!',
|
||||||
|
text:'',
|
||||||
|
attachment: [
|
||||||
|
{data: html, alternative: true}
|
||||||
|
]
|
||||||
|
}, (err, result) => {
|
||||||
|
res.redirect(getUrl('mailWasSent'));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.mailWasSent = function(req, res, render) {
|
||||||
|
render('mailWasSent.pug');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.activate = function(req, res, render, next) {
|
||||||
|
User.getByActivationKey(req.params.activationKey, (err, user) => {
|
||||||
|
if (user === undefined) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
user.active = true;
|
||||||
|
user.activationKey = undefined;
|
||||||
|
user.save((err, user) => {
|
||||||
|
req.session.user = user;
|
||||||
|
req.session.save();
|
||||||
|
res.redirect(getUrl('index'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
9
index.js
9
index.js
|
@ -3,7 +3,6 @@ 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');
|
||||||
|
@ -19,7 +18,10 @@ function startServer() {
|
||||||
let app = express();
|
let app = express();
|
||||||
let http = require('http').Server(app);
|
let http = require('http').Server(app);
|
||||||
|
|
||||||
let bodyParser = require('body-parser');
|
var bodyParser = require('body-parser');
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
|
||||||
let session = require('cookie-session');
|
let session = require('cookie-session');
|
||||||
let cookieParser = require('cookie-parser');
|
let cookieParser = require('cookie-parser');
|
||||||
|
|
||||||
|
@ -52,7 +54,7 @@ function startServer() {
|
||||||
app.use(function(req, res) {
|
app.use(function(req, res) {
|
||||||
res.setHeader('Content-Type', 'text/html');
|
res.setHeader('Content-Type', 'text/html');
|
||||||
|
|
||||||
res.render('404.pug', res.locals, function(err, result) {
|
res.render(config.BASE_DIR + '/templates/404.pug', res.locals, function(err, result) {
|
||||||
if (err)
|
if (err)
|
||||||
console.log(err);
|
console.log(err);
|
||||||
res.send(result);
|
res.send(result);
|
||||||
|
@ -77,7 +79,6 @@ function startServer() {
|
||||||
const commands = {
|
const commands = {
|
||||||
runserver: startServer,
|
runserver: startServer,
|
||||||
reinitdb: model.reinitialize,
|
reinitdb: model.reinitialize,
|
||||||
shell: repl.start,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showHelp() {
|
function showHelp() {
|
||||||
|
|
|
@ -14,10 +14,12 @@
|
||||||
"author": "Thomas Forgione",
|
"author": "Thomas Forgione",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"app-module-path": "^2.2.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"cookie-parser": "^1.4.3",
|
"cookie-parser": "^1.4.3",
|
||||||
"cookie-session": "^1.3.1",
|
"cookie-session": "^1.3.1",
|
||||||
|
"emailjs": "^1.0.12",
|
||||||
"express": "^4.15.4",
|
"express": "^4.15.4",
|
||||||
"pg": "^7.3.0",
|
"pg": "^7.3.0",
|
||||||
"pug": "^2.0.0-rc.4",
|
"pug": "^2.0.0-rc.4",
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
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('model');
|
||||||
|
const User = require('auth/models');
|
||||||
|
const log = require('log');
|
||||||
|
|
||||||
|
model.reinitialize(() => {
|
||||||
|
|
||||||
|
log.debug('Database reinitialized');
|
||||||
|
process.exit(0);
|
||||||
|
|
||||||
|
});
|
|
@ -6,5 +6,13 @@ module.exports = {
|
||||||
'USERNAME': 'adejs',
|
'USERNAME': 'adejs',
|
||||||
'DBNAME': 'adejs',
|
'DBNAME': 'adejs',
|
||||||
'PASSWORD':'97Cnqw023V1G95fQUJR8H7gwvqUke4',
|
'PASSWORD':'97Cnqw023V1G95fQUJR8H7gwvqUke4',
|
||||||
|
},
|
||||||
|
'MAIL': {
|
||||||
|
'USERNAME': 'adejs',
|
||||||
|
'PASSWORD': 'ILm0iSIgYI18w41dJU3ll9Pe5w9qjJ',
|
||||||
|
'HOSTNAME': 'smtp.tforgione.fr',
|
||||||
|
'PORT': '587',
|
||||||
|
'FROM': 'ADEjs <adejs@tforgione.fr>',
|
||||||
|
'TLS': true,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
extends ./base.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
h1 Error 404
|
||||||
|
p.
|
||||||
|
This page does not exist.
|
|
@ -30,12 +30,12 @@ html
|
||||||
a.btn.btn-light(href=getUrl("login"), role='button')
|
a.btn.btn-light(href=getUrl("login"), role='button')
|
||||||
| Sign in
|
| Sign in
|
||||||
li.nav-item
|
li.nav-item
|
||||||
a.btn.btn-success(href='#', role='button')
|
a.btn.btn-success(href=getUrl("signup"), role='button')
|
||||||
| Sign up
|
| Sign up
|
||||||
else
|
else
|
||||||
li.nav-item
|
li.nav-item
|
||||||
a.nav-link(href='#')
|
a.nav-link(href='#')
|
||||||
| Username
|
| #{session.user._username.value}
|
||||||
li.nav-item
|
li.nav-item
|
||||||
a.btn.btn-light(href=getUrl("logout"), role='button')
|
a.btn.btn-light(href=getUrl("logout"), role='button')
|
||||||
| Log out
|
| Log out
|
||||||
|
|
|
@ -12,16 +12,29 @@ model.reinitialize(() => {
|
||||||
log.debug('Database reinitialized');
|
log.debug('Database reinitialized');
|
||||||
|
|
||||||
let user = new User();
|
let user = new User();
|
||||||
user.email = "toto";
|
user.username = "toto"
|
||||||
|
user.email = "toto@tforgione.fr";
|
||||||
user.password = "tata";
|
user.password = "tata";
|
||||||
user.resources = 23;
|
user.resources = 23;
|
||||||
user.project_id = 42;
|
user.project_id = 42;
|
||||||
user.save((err, u) => {
|
user.save((err, u) => {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
log.dberror(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log.debug('New user created');
|
log.debug('New user created');
|
||||||
|
|
||||||
// Test password
|
// Test password
|
||||||
User.getById(1, (err, user) => {
|
User.getById(1, (err, user) => {
|
||||||
|
|
||||||
|
if (err !== undefined) {
|
||||||
|
log.dberror("User is undefined");
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (model.PasswordField.testSync("tata", user.password)) {
|
if (model.PasswordField.testSync("tata", user.password)) {
|
||||||
log.debug("Password authentication succeed");
|
log.debug("Password authentication succeed");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -22,7 +22,7 @@ module.exports = function(app, controllersDir = __dirname + '/../controllers') {
|
||||||
|
|
||||||
for (let url of urls) {
|
for (let url of urls) {
|
||||||
|
|
||||||
app.get(url.url, ((url) => function(req, res, next) {
|
app[url.method.toLowerCase()](url.url, ((url) => function(req, res, next) {
|
||||||
|
|
||||||
let path = obj[url.view](req, res, function(template) {
|
let path = obj[url.view](req, res, function(template) {
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
let urls = {};
|
let urls = {};
|
||||||
|
|
||||||
class Url {
|
class Url {
|
||||||
constructor(url, view, name) {
|
constructor(url, view, name, method="GET") {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.view = view;
|
this.view = view;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.method = method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +20,8 @@ module.exports = function(req, res, next) {
|
||||||
|
|
||||||
module.exports.getUrl = getUrl;
|
module.exports.getUrl = getUrl;
|
||||||
|
|
||||||
module.exports.url = function(url, viewName, urlName) {
|
module.exports.url = function(url, viewName, urlName, method) {
|
||||||
let ret = new Url(url, viewName, urlName);
|
let ret = new Url(url, viewName, urlName, method);
|
||||||
urls[urlName] = ret;
|
urls[urlName] = ret;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
const config = require('settings/config');
|
||||||
|
const mail = require('emailjs');
|
||||||
|
|
||||||
|
module.exports = function(params, callback) {
|
||||||
|
let server = mail.server.connect({
|
||||||
|
user: config.MAIL.USERNAME,
|
||||||
|
password: config.MAIL.PASSWORD,
|
||||||
|
host: config.MAIL.HOSTNAME,
|
||||||
|
port: config.MAIL.PORT,
|
||||||
|
tls: config.MAIL.TLS
|
||||||
|
});
|
||||||
|
|
||||||
|
server.send(params, callback);
|
||||||
|
};
|
148
utils/model.js
148
utils/model.js
|
@ -5,6 +5,40 @@ const bc = require('bcryptjs');
|
||||||
|
|
||||||
let models = [];
|
let models = [];
|
||||||
|
|
||||||
|
function postgresToCamel(postgres) {
|
||||||
|
let ret = '';
|
||||||
|
for (let i = 0; i < postgres.length; i++) {
|
||||||
|
let c = postgres.charAt(i);
|
||||||
|
if (c === '_') {
|
||||||
|
i++;
|
||||||
|
ret += postgres.charAt(i).toUpperCase();
|
||||||
|
} else {
|
||||||
|
ret += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function camelToPostgres(camelCase) {
|
||||||
|
let ret = '';
|
||||||
|
for (let i = 0; i < camelCase.length; i++) {
|
||||||
|
let c = camelCase.charAt(i);
|
||||||
|
if (c === c.toUpperCase()) {
|
||||||
|
ret += '_' + c.toLowerCase()
|
||||||
|
} else {
|
||||||
|
ret += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function attributeToGetBy(attributeName) {
|
||||||
|
return 'getBy' +
|
||||||
|
attributeName.charAt(0).toUpperCase() + attributeName.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.BaseModel = class {
|
module.exports.BaseModel = class {
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
models.push(this);
|
models.push(this);
|
||||||
|
@ -19,13 +53,17 @@ module.exports.BaseModel = class {
|
||||||
|
|
||||||
reinitialize(callback = () => {}) {
|
reinitialize(callback = () => {}) {
|
||||||
log.debug('Reinitializing ' + this.name);
|
log.debug('Reinitializing ' + this.name);
|
||||||
db.query('DROP TABLE IF EXISTS ' + this.name + ' CASCADE')
|
let query =
|
||||||
|
"DROP TABLE IF EXISTS " +
|
||||||
|
camelToPostgres(this.name) + " CASCADE;";
|
||||||
|
|
||||||
|
db.query(query)
|
||||||
.then(res => this.createTable(callback))
|
.then(res => this.createTable(callback))
|
||||||
.catch(err => log.dberror(err));
|
.catch(err => log.dberror(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
createTable(callback) {
|
createTable(callback) {
|
||||||
let query = 'CREATE TABLE ' + this.name + '(\n';
|
let query = 'CREATE TABLE ' + camelToPostgres(this.name) + '(\n';
|
||||||
query += this.fields
|
query += this.fields
|
||||||
.map(f => '\t' + f.getCreationString())
|
.map(f => '\t' + f.getCreationString())
|
||||||
.join(',\n');
|
.join(',\n');
|
||||||
|
@ -35,7 +73,7 @@ module.exports.BaseModel = class {
|
||||||
|
|
||||||
db.query(query)
|
db.query(query)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
log.debug("Table " + this.name + " created")
|
log.debug("Table " + camelToPostgres(this.name) + " created")
|
||||||
callback();
|
callback();
|
||||||
})
|
})
|
||||||
.catch(err => log.dberror(err));
|
.catch(err => log.dberror(err));
|
||||||
|
@ -43,8 +81,30 @@ module.exports.BaseModel = class {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.BaseField = class {
|
module.exports.BaseField = class {
|
||||||
constructor(name) {
|
constructor(name, properties = {}) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.unique = properties.unique || false;
|
||||||
|
this.notNull = properties.notNull || false;
|
||||||
|
this.default = properties.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreationString() {
|
||||||
|
return [
|
||||||
|
camelToPostgres(this.name),
|
||||||
|
this.getPostgresType(),
|
||||||
|
this.getFormattedPropeties(),
|
||||||
|
].join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormattedPropeties() {
|
||||||
|
let properties = [];
|
||||||
|
if (this.unique) {
|
||||||
|
properties.push('UNIQUE');
|
||||||
|
}
|
||||||
|
if (this.notNull) {
|
||||||
|
properties.push('NOT NULL');
|
||||||
|
}
|
||||||
|
return properties.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
createMutators() {
|
createMutators() {
|
||||||
|
@ -73,43 +133,43 @@ module.exports.BaseField = class {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.SmallIntegerField = class extends module.exports.BaseField {
|
module.exports.SmallIntegerField = class extends module.exports.BaseField {
|
||||||
constructor(name) {
|
constructor(name, properties = {}) {
|
||||||
super(name);
|
super(name, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreationString() {
|
getPostgresType() {
|
||||||
return this.name + " SMALLINT";
|
return "SMALLINT";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.SerialField = class extends module.exports.BaseField {
|
module.exports.SerialField = class extends module.exports.BaseField {
|
||||||
constructor(name) {
|
constructor(name, properties = {}) {
|
||||||
super(name);
|
super(name, properties);
|
||||||
|
this.unique = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreationString() {
|
getPostgresType() {
|
||||||
return this.name + " SERIAL";
|
return "SERIAL";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.TextField = class extends module.exports.BaseField {
|
module.exports.TextField = class extends module.exports.BaseField {
|
||||||
constructor(name) {
|
constructor(name, properties = {}) {
|
||||||
super(name);
|
super(name, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreationString() {
|
getPostgresType() {
|
||||||
return this.name + " TEXT";
|
return "TEXT";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.PasswordField = class extends module.exports.BaseField {
|
module.exports.PasswordField = class extends module.exports.BaseField {
|
||||||
constructor(name) {
|
constructor(name, properties = {}) {
|
||||||
super(name);
|
super(name, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreationString() {
|
getPostgresType() {
|
||||||
return this.name + ' TEXT';
|
return 'TEXT';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,7 +181,6 @@ module.exports.PasswordField = class extends module.exports.BaseField {
|
||||||
changed: true,
|
changed: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,22 +189,41 @@ module.exports.PasswordField.testSync = function(password, hash) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports.BoolField = class extends module.exports.BaseField {
|
||||||
|
constructor(name, properties) {
|
||||||
|
super(name, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPostgresType() {
|
||||||
|
return 'BOOLEAN';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.createClass = function(model) {
|
module.exports.createClass = function(model) {
|
||||||
|
|
||||||
let ret = function(param) {
|
let ret = function(param) {
|
||||||
this._persisted = false;
|
this._persisted = false;
|
||||||
for (let field of model.fields) {
|
for (let field of model.fields) {
|
||||||
this['_' + field.name] = {
|
this['_' + field.name] = {
|
||||||
value: undefined,
|
value: field.default,
|
||||||
changed: false,
|
changed: true,
|
||||||
}
|
}
|
||||||
Object.defineProperty(this, field.name, field.createMutators());
|
Object.defineProperty(this, field.name, field.createMutators());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ret.getById = function(id, callback) {
|
for (let field of model.fields) {
|
||||||
|
if (!field.unique && field.name !== 'activationKey')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret[attributeToGetBy(field.name)] = function(value, callback) {
|
||||||
|
let query =
|
||||||
|
'SELECT * FROM ' + camelToPostgres(model.name) +
|
||||||
|
' WHERE ' + camelToPostgres(field.name) +
|
||||||
|
'=$1;';
|
||||||
|
|
||||||
db
|
db
|
||||||
.query('SELECT * FROM ' + model.name + ' WHERE id=$1;', [id])
|
.query(query, [value])
|
||||||
.then(res => {
|
.then(res => {
|
||||||
// Create the instance
|
// Create the instance
|
||||||
let instance = new ret();
|
let instance = new ret();
|
||||||
|
@ -154,12 +232,14 @@ module.exports.createClass = function(model) {
|
||||||
callback(undefined, instance);
|
callback(undefined, instance);
|
||||||
})
|
})
|
||||||
.catch(err => callback(err));
|
.catch(err => callback(err));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.prototype._setFromDbResult = function(res) {
|
ret.prototype._setFromDbResult = function(res) {
|
||||||
for (let field of model.fields) {
|
for (let field of model.fields) {
|
||||||
this['_' + field.name] = {
|
this['_' + field.name] = {
|
||||||
value: res.rows[0][field.name],
|
value: res.rows[0][camelToPostgres(field.name)],
|
||||||
changed: false,
|
changed: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -182,10 +262,10 @@ module.exports.createClass = function(model) {
|
||||||
|
|
||||||
if (!this._persisted) {
|
if (!this._persisted) {
|
||||||
// INSERT INTO
|
// INSERT INTO
|
||||||
let queryBegin = 'INSERT INTO ' + model.name + '('
|
let queryBegin = 'INSERT INTO ' + camelToPostgres(model.name) + '('
|
||||||
let queryEnd = ' VALUES (';
|
let queryEnd = ' VALUES (';
|
||||||
queryBegin += fieldsToSave
|
queryBegin += fieldsToSave
|
||||||
.map(f => f.field.name)
|
.map(f => camelToPostgres(f.field.name))
|
||||||
.join(',');
|
.join(',');
|
||||||
|
|
||||||
queryEnd += fieldsToSave
|
queryEnd += fieldsToSave
|
||||||
|
@ -208,16 +288,16 @@ module.exports.createClass = function(model) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// UPDATE FROM
|
// UPDATE FROM
|
||||||
let query = 'UPDATE FROM ' + model.name + ' SET ';
|
let query = 'UPDATE ' + camelToPostgres(model.name) + ' SET ';
|
||||||
|
|
||||||
query += fieldsToSave.map(f => {
|
query += fieldsToSave.map((f, i) => {
|
||||||
return f.field.name + '=' + f.value;
|
return camelToPostgres(f.field.name) + '=$' + (i+1);
|
||||||
}).join(',');
|
}).join(',');
|
||||||
|
|
||||||
query += ') RETURNING *;'
|
query += ' WHERE id=$' + (fieldsToSave.length + 1) + ' RETURNING *;'
|
||||||
|
|
||||||
db
|
db
|
||||||
.query(query, fieldsToSave.map(f => f.value))
|
.query(query, [...fieldsToSave.map(f => f.value), this.id])
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this._setFromDbResult(res);
|
this._setFromDbResult(res);
|
||||||
this._persisted = true;
|
this._persisted = true;
|
||||||
|
|
Loading…
Reference in New Issue