1
0

Compare commits

...

2 Commits

Author SHA1 Message Date
James Ketrenos
d21946cb9e Added eslintrc
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
2022-04-07 14:25:37 -07:00
James Ketrenos
c7435c4bb1 eslinted everything and moved db around
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
2022-04-07 14:25:20 -07:00
17 changed files with 1698 additions and 500 deletions

26
.eslintrc.json Normal file
View File

@ -0,0 +1,26 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

View File

@ -26,20 +26,43 @@ const App = () => {
const [ csrfToken, setCsrfToken ] = useState(undefined);
useEffect(() => {
window.fetch(`${base}/api/v1/users/csrf`, {
method: 'GET',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
}).then((res) => {
return res.json();
}).then((data) => {
const effect = async () => {
const res = await window.fetch(`${base}/api/v1/users/csrf`, {
method: 'GET',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
});
const data = await res.json();
setCsrfToken(data.csrfToken);
});
}
effect();
}, []);
useEffect(() => {
if (csrfToken) {
return;
}
const effect = async () => {
const res = await window.fetch(`${base}/api/v1/users`, {
method: 'GET',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'CSRF-Token': csrfToken
},
});
const data = await res.json();
setUser(data);
};
effect();
}, [csrfToken, setUser]);
return (
<div className="App">
<GlobalContext.Provider value={{user, setUser, csrfToken }}>

16
server/.eslintrc.json Normal file
View File

@ -0,0 +1,16 @@
{
"env": {
"es2021": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"indent": [
"error",
2
]
}
}

View File

@ -1,73 +1,41 @@
"use strict";
'use strict';
process.env.TZ = "Etc/GMT";
process.env.TZ = 'Etc/GMT';
console.log("Loading Goodtimes");
console.log('Loading Goodtimes');
const express = require("express"),
bodyParser = require("body-parser"),
config = require("config"),
const express = require('express'),
bodyParser = require('body-parser'),
config = require('config'),
session = require('express-session'),
basePath = require("./basepath"),
cookieParser = require("cookie-parser"),
basePath = require('./basepath'),
cookieParser = require('cookie-parser'),
app = express(),
fs = require('fs'),
csrf = require('csurf');
csrf = require('csurf'),
http = require('http'),
{ goodTimesDB } = require('./db.js');
const server = require("http").createServer(app);
require('./console-line.js'); /* Monkey-patch console.log with line numbers */
/* App is behind an nginx proxy which we trust, so use the remote address
* set in the headers */
app.set('trust proxy', true);
app.use(session({
secret: 'm@g1x!',
resave: false,
saveUninitialized: false,
cookie: { secure: true }
}));
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(csrf({
cookie: true
}));
app.use(bodyParser.json());
//const ws = require('express-ws')(app, server);
require("./console-line.js"); /* Monkey-patch console.log with line numbers */
const frontendPath = config.get("frontendPath").replace(/\/$/, "") + "/",
serverConfig = config.get("server");
console.log("Hosting server from: " + basePath);
let userDB, groupDB;
app.use(bodyParser.json());
/* App is behind an nginx proxy which we trust, so use the remote address
* set in the headers */
app.set("trust proxy", true);
app.set("basePath", basePath);
app.use(basePath, require("./routes/basepath.js"));
/* Handle static files first so excessive logging doesn't occur */
app.use(basePath, express.static(frontendPath, { index: false }));
const index = require("./routes/index");
if (config.has("admin")) {
const admin = config.get("admin");
app.set("admin", admin);
}
/* Allow loading of the app w/out being logged in */
app.use(basePath, index);
/* Allow access to the 'users' API w/out being logged in */
/*
const users = require("./routes/users");
app.use(basePath + "api/v1/users", users.router);
*/
app.use(function(err, req, res, next) {
app.use(function (err, req, res) {
console.error(err.message);
res.status(err.status || 500).json({
message: err.message,
@ -75,55 +43,47 @@ app.use(function(err, req, res, next) {
});
});
app.use(`${basePath}api/v1/groups`, require("./routes/groups"));
app.use(`${basePath}api/v1/users`, require("./routes/users"));
/* Initialize the data base, configure routes, and open server */
goodTimesDB.init().then((db) => {
console.log('DB connected. Configuring routes.');
app.locals.db = db;
app.set('basePath', basePath);
app.use(`${basePath}api/v1/groups`, require('./routes/groups'));
app.use(`${basePath}api/v1/users`, require('./routes/users'));
}).then(() => {
const server = http.createServer(app),
serverConfig = config.get('server');
/* Declare the "catch all" index route last; the final route is a 404 dynamic router */
app.use(basePath, index);
console.log('Hosting server from: ' + basePath);
app.set('port', serverConfig.port);
/**
* Create HTTP server and listen for new connections
*/
app.set("port", serverConfig.port);
process.on('SIGINT', () => {
console.log("Gracefully shutting down from SIGINT (Ctrl-C) in 2 seconds");
setTimeout(() => process.exit(-1), 2000);
server.close(() => process.exit(1));
});
require("./db/groups").then(function(db) {
groupDB = db;
}).then(function() {
return require("./db/users").then(function(db) {
userDB = db;
process.on('SIGINT', () => {
console.log('Gracefully shutting down from SIGINT (Ctrl-C) in 2 seconds');
setTimeout(() => process.exit(-1), 2000);
server.close(() => process.exit(1));
});
}).then(function() {
console.log("DB connected. Opening server.");
server.listen(serverConfig.port, () => {
console.log(`http server listening on ${serverConfig.port}`);
});
}).catch(function(error) {
console.error(error);
process.exit(-1);
});
server.on("error", function(error) {
if (error.syscall !== "listen") {
throw error;
}
server.on('error', function (error) {
if (error.syscall !== 'listen') {
throw error;
}
// handle specific listen errors with friendly messages
switch (error.code) {
case "EACCES":
console.error(serverConfig.port + " requires elevated privileges");
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(serverConfig.port + ' requires elevated privileges');
process.exit(1);
break;
case "EADDRINUSE":
console.error(serverConfig.port + " is already in use");
case 'EADDRINUSE':
console.error(serverConfig.port + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
});
}
});
server.listen(serverConfig.port, () => {
console.log(`http server listening on ${serverConfig.port}`);
});
});

View File

@ -1,7 +1,7 @@
let basePath = process.env.REACT_APP_basePath;
basePath = "/" + basePath.replace(/^\/+/, "").replace(/\/+$/, "") + "/";
if (basePath == "//") {
basePath = "/";
basePath = '/' + basePath.replace(/^\/+/, '').replace(/\/+$/, '') + '/';
if (basePath == '//') {
basePath = '/';
}
console.log(`Using basepath ${basePath}`);

View File

@ -1,15 +1,9 @@
{
"db": {
"groups": {
"goodtimes": {
"dialect": "sqlite",
"storage": "../db/groups.db",
"logging" : false,
"timezone": "+00:00"
},
"users": {
"dialect": "sqlite",
"storage": "../db/users.db",
"logging" : false,
"storage": "../db/goodtimes.db",
"logging": false,
"timezone": "+00:00"
}
},

View File

@ -1,8 +1,8 @@
/* monkey-patch console.log to prefix with file/line-number */
if (process.env.LOG_LINE) {
let cwd = process.cwd(),
cwdRe = new RegExp("^[^/]*" + cwd.replace("/", "\\/") + "\/([^:]*:[0-9]*).*$");
[ "log", "warn", "error" ].forEach(function(method) {
cwdRe = new RegExp('^[^/]*' + cwd.replace('/', '\\/') + '\/([^:]*:[0-9]*).*$');
[ 'log', 'warn', 'error' ].forEach(function(method) {
console[method] = (function () {
let orig = console[method];
return function () {
@ -15,8 +15,8 @@ if (process.env.LOG_LINE) {
}
let err = getErrorObject(),
caller_line = err.stack.split("\n")[3],
args = [caller_line.replace(cwdRe, "$1 -")];
caller_line = err.stack.split('\n')[3],
args = [caller_line.replace(cwdRe, '$1 -')];
/* arguments.unshift() doesn't exist... */
for (var i = 0; i < arguments.length; i++) {

50
server/db/users.js → server/db.js Executable file → Normal file
View File

@ -1,15 +1,30 @@
"use strict";
'use strict';
const Sequelize = require('sequelize'),
config = require('config');
function init() {
const db = {
sequelize: new Sequelize(config.get("db.users")),
sequelize: new Sequelize(config.get('db.goodtimes')),
Sequelize: Sequelize
};
return db.sequelize.authenticate().then(function () {
const Group = db.sequelize.define('group', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
ownerId: Sequelize.INTEGER
}, {
timestamps: false,
classMethods: {
associate: function() {}
}
});
const User = db.sequelize.define('users', {
id: {
type: Sequelize.INTEGER,
@ -32,6 +47,7 @@ function init() {
timestamps: false
});
// eslint-disable-next-line no-unused-vars
const Authentication = db.sequelize.define('authentication', {
key: {
type: Sequelize.STRING,
@ -41,7 +57,7 @@ function init() {
issued: Sequelize.DATE,
type: {
type: Sequelize.ENUM,
values: [ 'account-setup', 'password-reset' ]
values: ['account-setup', 'password-reset']
},
userId: {
type: Sequelize.INTEGER,
@ -54,6 +70,28 @@ function init() {
}, {
timestamps: false
});
// eslint-disable-next-line no-unused-vars
const GroupUsers = db.sequelize.define('groupuser', {
groupId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: Group,
key: 'id'
}
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: User,
key: 'id'
}
}
}, {
timestamps: false
});
return db.sequelize.sync({
force: false
@ -61,11 +99,11 @@ function init() {
return db;
});
}).catch(function (error) {
console.log("ERROR: Failed to authenticate with USER DB");
console.log("ERROR: " + JSON.stringify(config.get("db"), null, 2));
console.log('ERROR: Failed to authenticate with GROUP DB');
console.log('ERROR: ' + JSON.stringify(config.get('db'), null, 2));
console.log(error);
throw error;
});
}
module.exports = init();
module.exports = { goodTimesDB: { init } };

View File

@ -1,58 +0,0 @@
"use strict";
const Sequelize = require('sequelize'),
config = require('config');
function init() {
const db = {
sequelize: new Sequelize(config.get("db.groups")),
Sequelize: Sequelize
};
return db.sequelize.authenticate().then(function () {
const Group = db.sequelize.define('group', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
ownerId: Sequelize.INTEGER
}, {
timestamps: false,
classMethods: {
associate: function() {}
}
});
const GroupUsers = db.sequelize.define('groupuser', {
groupId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: Group,
key: 'id'
}
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
}
}, {
timestamps: false
});
return db.sequelize.sync({
force: false
}).then(function () {
return db;
});
}).catch(function (error) {
console.log("ERROR: Failed to authenticate with GROUP DB");
console.log("ERROR: " + JSON.stringify(config.get("db"), null, 2));
console.log(error);
throw error;
});
}
module.exports = init();

View File

@ -1,61 +0,0 @@
"use strict";
const createTransport = require('nodemailer').createTransport,
{ timestamp } = require("./timestamp");
const transporter = createTransport({
host: 'email.ketrenos.com',
pool: true,
port: 25
});
function sendMail(to, subject, message, cc) {
let envelope = {
subject: subject,
from: 'Ketr.Ketran <james_ketran@ketrenos.com>',
to: to || '',
cc: cc || ''
};
/* If there isn't a To: but there is a Cc:, promote Cc: to To: */
if (!envelope.to && envelope.cc) {
envelope.to = envelope.cc;
delete envelope.cc;
}
envelope.text = message
envelope.html = message.replace(/\n/g, "<br>\n");
return new Promise(function (resolve, reject) {
let attempts = 10;
function attemptSend(envelope) {
/* Rate limit to ten per second */
transporter.sendMail(envelope, function (error, info) {
if (error) {
if (attempts) {
attempts--;
console.warn(timestamp() + " Unable to send mail. Trying again in 100ms (" + attempts + " attempts remain): ", error);
setTimeout(send.bind(undefined, envelope), 100);
} else {
console.error(timestamp() + " Error sending email: ", error)
return reject(error);
}
}
console.log(timestamp() + " Mail sent to: " + envelope.to);
return resolve(true);
});
}
attemptSend(envelope);
}).then(function(success) {
if (!success) {
console.error(timestamp() + " Mail not sent to: " + envelope.to);
}
});
}
module.exports = {
sendMail: sendMail
};

1401
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -38,5 +38,8 @@
"repository": {
"type": "git",
"url": "git@git.ketrenos.com:jketreno/goodtimes"
},
"devDependencies": {
"eslint": "^8.12.0"
}
}

View File

@ -1,33 +0,0 @@
"use strict";
const express = require("express"),
fs = require("fs"),
url = require("url");
const router = express.Router();
/* This router only handles HTML files and is used
* to replace BASEPATH */
router.get("/*", (req, res, next) => {
const parts = url.parse(req.url),
basePath = req.app.get("basePath");
if (!/^\/[^/]+\.html$/.exec(parts.pathname)) {
return next();
}
console.log("Attempting to parse 'frontend" + parts.pathname + "'");
/* Replace <script>'<base href="/BASEPATH/">';</script> in index.html with
* the basePath */
fs.readFile("frontend" + parts.pathname, "utf8", function(error, content) {
if (error) {
return next();
}
res.send(content.replace(
/<script>'<base href="BASEPATH">';<\/script>/,
"<base href='" + basePath + "'>"));
});
});
module.exports = router;

View File

@ -1,30 +1,10 @@
"use strict";
const express = require("express"),
const express = require('express'),
router = express.Router(),
crypto = require("crypto"),
{ readFile, writeFile } = require("fs").promises,
fs = require("fs"),
accessSync = fs.accessSync,
randomWords = require("random-words"),
equal = require("fast-deep-equal");
const debug = {
audio: false,
get: true,
set: true,
update: false
};
let groupDB;
require("../db/groups").then(function(db) {
groupDB = db;
});
crypto = require('crypto');
/* Simple NO-OP to set session cookie so user-id can use it as the
* index */
router.get("/", (req, res/*, next*/) => {
router.get('/', (req, res/*, next*/) => {
let userId;
if (!req.cookies.user) {
userId = crypto.randomBytes(16).toString('hex');
@ -38,19 +18,19 @@ router.get("/", (req, res/*, next*/) => {
return res.status(200).send({ user: userId });
});
router.post("/", async (req, res/*, next*/) => {
console.log(`POST /groups/`, req.session.userId);
router.get('/', async (req, res/*, next*/) => {
console.log('GET /groups/', req.session.userId);
return res.status(200).send(
[ {
id: 1,
ownerId: 1,
name: "Beer Tuesday",
name: 'Beer Tuesday',
nextEvent: Date.now() + 86400 * 14 * 1000 /* 2 weeks from now */
} ]
);
});
router.post("/:id", async (req, res/*, next*/) => {
router.post('/:id', async (req, res) => {
const { id } = req.params;
let userId;
@ -61,6 +41,10 @@ router.post("/:id", async (req, res/*, next*/) => {
userId = req.cookies.user;
}
const group = {
id: 1
};
if (id) {
console.log(`[${userId.substring(0,8)}]: Attempting load of ${id}`);
} else {

View File

@ -1,65 +0,0 @@
"use strict";
const express = require("express"),
fs = require("fs"),
url = require("url"),
config = require("config"),
basePath = require("../basepath");
const router = express.Router();
/* List of filename extensions we know are "potential" file extensions for
* assets we don"t want to return "index.html" for */
const extensions = [
"html", "js", "css", "eot", "gif", "ico", "jpeg", "jpg", "mp4",
"md", "ttf", "txt", "woff", "woff2", "yml", "svg"
];
/* Build the extension match RegExp from the list of extensions */
const extensionMatch = new RegExp("^.*?(" + extensions.join("|") + ")$", "i");
/* To handle dynamic routes, we return index.html to every request that
* gets this far -- so this needs to be the last route.
*
* However, that introduces site development problems when assets are
* referenced which don't yet exist (due to bugs, or sequence of adds) --
* the server would return HTML content instead of the 404.
*
* So, check to see if the requested path is for an asset with a recognized
* file extension.
*
* If so, 404 because the asset isn't there. otherwise assume it is a
* dynamic client side route and *then* return index.html.
*/
router.get("/*", function(req, res, next) {
const parts = url.parse(req.url);
/* If req.user isn't set yet (authentication hasn't happened) then
* only allow / to be loaded--everything else chains to the next
* handler */
if (!req.user &&
req.url !== "/") {
return next();
}
if (req.url == "/" || req.url.indexOf("/games") == 0 || !extensionMatch.exec(parts.pathname)) {
console.log("Returning index for " + req.url);
/* Replace <script>'<base href="BASEPATH">';</script> in index.html with
* the basePath */
const frontendPath = config.get("frontendPath").replace(/\/$/, "") + "/",
index = fs.readFileSync(frontendPath + "index.html", "utf8");
res.send(index.replace(
/<script>'<base href="BASEPATH">';<\/script>/,
"<base href='" + basePath + "'>"));
return;
}
console.log("Page not found: " + req.url);
return res.status(404).json({
message: "Page not found",
status: 404
});
});
module.exports = router;

View File

@ -1,31 +1,26 @@
"use strict";
'use strict';
const express = require("express"),
config = require("config"),
{ sendVerifyMail, sendPasswordChangedMail, sendVerifiedMail } = require("../lib/mail"),
crypto = require("crypto");
const express = require('express'),
{ sendVerifyMail, sendPasswordChangedMail, sendVerifiedMail } =
require('../lib/mail'),
crypto = require('crypto');
const router = express.Router();
const autoAuthenticate = 1;
let userDB;
require("../db/users.js").then(function(db) {
userDB = db;
});
router.get("/", function(req, res/*, next*/) {
console.log("/users/");
router.get('/', function(req, res/*, next*/) {
console.log('GET /users/');
return getSessionUser(req).then((user) => {
return res.status(200).send(user);
}).catch((error) => {
console.log("User not logged in: " + error);
console.log('User not logged in: ' + error);
return res.status(200).send({});
});
});
router.put("/password", function(req, res) {
console.log("/users/password");
router.put('/password', function(req, res) {
console.log('/users/password');
const db = req.app.locals.db;
const changes = {
currentPassword: req.query.c || req.body.c,
@ -33,21 +28,21 @@ router.put("/password", function(req, res) {
};
if (!changes.currentPassword || !changes.newPassword) {
return res.status(400).send("Missing current password and/or new password.");
return res.status(400).send('Missing current password and/or new password.');
}
if (changes.currentPassword == changes.newPassword) {
return res.status(400).send("Attempt to set new password to current password.");
return res.status(400).send('Attempt to set new password to current password.');
}
return getSessionUser(req).then(function(user) {
return userDB.sequelize.query("SELECT id FROM users " +
"WHERE uid=:username AND password=:password", {
return db.sequelize.query('SELECT id FROM users ' +
'WHERE uid=:username AND password=:password', {
replacements: {
username: user.username,
password: crypto.createHash('sha256').update(changes.currentPassword).digest('base64')
},
type: userDB.Sequelize.QueryTypes.SELECT,
type: db.Sequelize.QueryTypes.SELECT,
raw: true
}).then(function(users) {
if (users.length != 1) {
@ -57,34 +52,35 @@ router.put("/password", function(req, res) {
});
}).then(function(user) {
if (!user) {
console.log("Invalid password");
console.log('Invalid password');
/* Invalid password */
res.status(401).send("Invalid password");
res.status(401).send('Invalid password');
return null;
}
return userDB.sequelize.query("UPDATE users SET password=:password WHERE uid=:username", {
return db.sequelize.query('UPDATE users SET password=:password WHERE uid=:username', {
replacements: {
username: user.username,
password: crypto.createHash('sha256').update(changes.newPassword).digest('base64')
}
}).then(function() {
console.log("Password changed for user " + user.username + " to '" + changes.newPassword + "'.");
console.log('Password changed for user ' + user.username + ' to \'' + changes.newPassword + '\'.');
res.status(200).send(user);
user.id = req.session.userId;
return sendPasswordChangedMail(userDB, req, user);
return sendPasswordChangedMail(db, req, user);
});
});
});
router.get("/csrf", (req, res) => {
console.log("/users/csrf");
router.get('/csrf', (req, res) => {
console.log('/users/csrf');
res.json({ csrfToken: req.csrfToken() });
});
router.post("/signup", function(req, res) {
console.log("/users/signup");
router.post('/signup', function(req, res) {
console.log('/users/signup');
const db = req.app.locals.db;
const user = {
uid: req.body.email,
@ -100,7 +96,7 @@ router.post("/signup", function(req, res) {
|| !user.familyName
|| !user.firstName) {
return res.status(400).send({
message: `Missing email address, password, and/or name.`
message: 'Missing email address, password, and/or name.'
});
}
@ -109,18 +105,18 @@ router.post("/signup", function(req, res) {
user.md5 = crypto.createHash('md5')
.update(user.email).digest('base64');
return userDB.sequelize.query("SELECT * FROM users WHERE uid=:uid", {
return db.sequelize.query('SELECT * FROM users WHERE uid=:uid', {
replacements: user,
type: userDB.Sequelize.QueryTypes.SELECT,
type: db.Sequelize.QueryTypes.SELECT,
raw: true
}).then(async function(results) {
if (results.length != 0 && results[0].mailVerified) {
return res.status(400).send({
message: `Email address already used.`
message: 'Email address already used.'
});
}
let re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
let re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!re.exec(user.email)) {
const error = `Invalid email address: ${user.email}.`;
console.log(error);
@ -131,14 +127,14 @@ router.post("/signup", function(req, res) {
try {
if (results.length != 0) {
await userDB.sequelize.query("UPDATE users " +
"SET mailVerified=0");
await db.sequelize.query('UPDATE users ' +
'SET mailVerified=0');
req.session.userId = results[0].id;
} else {
let [, metadata] = await userDB.sequelize.query("INSERT INTO users " +
"(uid,firstName,familyName,password,email,memberSince," +
"authenticated,md5) " +
`VALUES(:uid,:firstName,:familyName,:password,` +
let [, metadata] = await db.sequelize.query('INSERT INTO users ' +
'(uid,firstName,familyName,password,email,memberSince,' +
'authenticated,md5) ' +
'VALUES(:uid,:firstName,:familyName,:password,' +
`:email,CURRENT_TIMESTAMP,${autoAuthenticate},:md5)`, {
replacements: user
});
@ -147,7 +143,7 @@ router.post("/signup", function(req, res) {
return getSessionUser(req).then(function(user) {
res.status(200).send(user);
user.id = req.session.userId;
return sendVerifyMail(userDB, req, user);
return sendVerifyMail(db, req, user);
});
} catch (error) {
console.error(error);
@ -156,34 +152,35 @@ router.post("/signup", function(req, res) {
});
const getSessionUser = function(req) {
const db = req.app.locals.db;
return Promise.resolve().then(function() {
if (!req.session || !req.session.userId) {
throw "Unauthorized. You must be logged in.";
throw 'Unauthorized. You must be logged in.';
}
let query = "SELECT " +
"uid AS username,firstName,familyName,mailVerified,authenticated,memberSince,email,md5 " +
"FROM users WHERE id=:id";
return userDB.sequelize.query(query, {
let query = 'SELECT ' +
'uid AS username,firstName,familyName,mailVerified,authenticated,memberSince,email,md5 ' +
'FROM users WHERE id=:id';
return db.sequelize.query(query, {
replacements: {
id: req.session.userId
},
type: userDB.Sequelize.QueryTypes.SELECT,
type: db.Sequelize.QueryTypes.SELECT,
raw: true
}).then(function(results) {
if (results.length != 1) {
throw "Invalid account.";
throw 'Invalid account.';
}
let user = results[0];
if (!user.mailVerified) {
user.restriction = user.restriction || "Email address not verified.";
user.restriction = user.restriction || 'Email address not verified.';
return user;
}
if (!user.authenticated) {
user.restriction = user.restriction || "Accout not authorized.";
user.restriction = user.restriction || 'Accout not authorized.';
return user;
}
@ -192,7 +189,7 @@ const getSessionUser = function(req) {
}).then(function(user) {
/* Strip out any fields that shouldn't be there. The allowed fields are: */
let allowed = [
"maintainer", "username", "firstName", "familyName", "mailVerified", "authenticated", "name", "email", "restriction", "md5"
'maintainer', 'username', 'firstName', 'familyName', 'mailVerified', 'authenticated', 'name', 'email', 'restriction', 'md5'
];
for (let field in user) {
if (allowed.indexOf(field) == -1) {
@ -201,114 +198,114 @@ const getSessionUser = function(req) {
}
return user;
});
}
};
router.post("/verify-email", async (req, res) => {
console.log("/users/verify-email");
router.post('/verify-email', async (req, res) => {
console.log('/users/verify-email');
const key = req.body.token;
const db = req.app.locals.db;
let results = await userDB.sequelize.query(
"SELECT * FROM authentications WHERE key=:key", {
replacements: { key },
type: userDB.sequelize.QueryTypes.SELECT
});
let results = await db.sequelize.query(
'SELECT * FROM authentications WHERE key=:key', {
replacements: { key },
type: db.sequelize.QueryTypes.SELECT
}
);
let token;
if (results.length == 0) {
console.log("Invalid key. Ignoring.");
console.log('Invalid key. Ignoring.');
return res.status(400).send({
message: `Invalid authentication token.`
message: 'Invalid authentication token.'
});
}
token = results[0];
console.log(token);
console.log("Matched token: " + JSON.stringify(token, null, 2));
console.log(token);
console.log('Matched token: ' + JSON.stringify(token, null, 2));
switch (token.type) {
case "account-setup":
return userDB.sequelize.query(
"UPDATE users SET mailVerified=1 WHERE id=:userId", {
replacements: token
})
.then(function () {
return userDB.sequelize.query(
"DELETE FROM authentications WHERE key=:key", {
replacements: { key }
});
})
.then(function () {
return userDB.sequelize.query(
"SELECT * FROM users WHERE id=:userId", {
replacements: token,
type: userDB.sequelize.QueryTypes.SELECT
});
})
.then(function (results) {
case 'account-setup':
return db.sequelize.query(
'UPDATE users SET mailVerified=1 WHERE id=:userId', {
replacements: token
}
).then(function () {
return db.sequelize.query(
'DELETE FROM authentications WHERE key=:key', {
replacements: { key }
}
);
}).then(function () {
return db.sequelize.query(
'SELECT * FROM users WHERE id=:userId', {
replacements: token,
type: db.sequelize.QueryTypes.SELECT
}
);
}).then(function (results) {
if (results.length == 0) {
return res.status(500).send({
message: `Internal authentication error.`
message: 'Internal authentication error.'
});
}
return results[0];
})
.then((user) => {
sendVerifiedMail(userDB, req, user);
}).then((user) => {
sendVerifiedMail(db, req, user);
req.session.userId = user.id;
}).then(function (user) {
return getSessionUser(req).then(function (user) {
}).then(() => {
return getSessionUser(req).then((user) => {
return res.status(200).send(user);
});
});
}
});
router.post("/signin", function(req, res) {
console.log("/users/signin");
router.post('/signin', function(req, res) {
console.log('/users/signin');
const db = req.app.locals.db;
let { email, password } = req.body;
if (!email || !password) {
return res.status(400).send({
message: `Missing email and/or password`
message: 'Missing email and/or password'
});
}
console.log("Looking up user in DB.");
console.log('Looking up user in DB.');
let query = "SELECT " +
"id,mailVerified,authenticated," +
"uid AS username," +
"familyName,firstName,email " +
"FROM users WHERE uid=:username AND password=:password";
return userDB.sequelize.query(query, {
let query = 'SELECT ' +
'id,mailVerified,authenticated,' +
'uid AS username,' +
'familyName,firstName,email ' +
'FROM users WHERE uid=:username AND password=:password';
return db.sequelize.query(query, {
replacements: {
username: email,
password: crypto.createHash('sha256').update(password).digest('base64')
},
type: userDB.Sequelize.QueryTypes.SELECT
})
.then(function(users) {
type: db.Sequelize.QueryTypes.SELECT
}).then(function(users) {
if (users.length != 1) {
return null;
}
let user = users[0];
req.session.userId = user.id;
return user;
})
.then(function(user) {
}).then(function(user) {
if (!user) {
console.log(email + " not found (or invalid password.)");
console.log(email + ' not found (or invalid password.)');
req.session.userId = null;
return res.status(401).send({
message: `Invalid sign in credentials`
message: 'Invalid sign in credentials'
});
}
let message = "Logged in as " + user.email + " (" + user.id + ")";
let message = 'Logged in as ' + user.email + ' (' + user.id + ')';
if (!user.mailVerified) {
console.log(message + ", who is not verified email.");
console.log(message + ', who is not verified email.');
} else if (!user.authenticated) {
console.log(message + ", who is not authenticated.");
console.log(message + ', who is not authenticated.');
} else {
console.log(message);
}
@ -316,15 +313,14 @@ router.post("/signin", function(req, res) {
return getSessionUser(req).then(function(user) {
return res.status(200).send(user);
});
})
.catch(function(error) {
}).catch(function(error) {
console.log(error);
return res.status(403).send(error);
});
});
router.post("/signout", (req, res) => {
console.log("/users/signout");
router.post('/signout', (req, res) => {
console.log('/users/signout');
if (req.session && req.session.userId) {
req.session.userId = null;
}

View File

@ -1,14 +1,12 @@
"use strict";
function twoDigit(number) {
return ("0" + number).slice(-2);
return ('0' + number).slice(-2);
}
function timestamp(date) {
date = date || new Date();
return [ date.getFullYear(), twoDigit(date.getMonth() + 1), twoDigit(date.getDate()) ].join("-") +
" " +
[ twoDigit(date.getHours()), twoDigit(date.getMinutes()), twoDigit(date.getSeconds()) ].join(":");
return [ date.getFullYear(), twoDigit(date.getMonth() + 1), twoDigit(date.getDate()) ].join('-') +
' ' +
[ twoDigit(date.getHours()), twoDigit(date.getMinutes()), twoDigit(date.getSeconds()) ].join(':');
}
module.exports = {