mirror of
https://github.com/xfarrow/blink
synced 2025-05-30 23:49:20 +02:00
code refactoring
This commit is contained in:
parent
bb64ccf7e3
commit
18be287a04
@ -15,7 +15,9 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const rateLimit = require('express-rate-limit');
|
const rateLimit = require('express-rate-limit');
|
||||||
const apiController = require('./controllers/api_controller.js');
|
const apiController = require('./controllers/api_controller.js'); // todo refactor
|
||||||
|
const personRoutes = require('./routes/person_routes.js');
|
||||||
|
const jwt_utils = require('./utils/jwt_utils.js');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
// Application configuration
|
// Application configuration
|
||||||
@ -29,15 +31,15 @@ app.use(rateLimit({
|
|||||||
})); // Apply the rate limiter middleware to all routes
|
})); // Apply the rate limiter middleware to all routes
|
||||||
|
|
||||||
const publicRoutes = express.Router();
|
const publicRoutes = express.Router();
|
||||||
publicRoutes.post('/register', apiController.registerPerson);
|
publicRoutes.post('/register', personRoutes.registerPerson);
|
||||||
publicRoutes.post('/login', apiController.login);
|
publicRoutes.post('/login', personRoutes.login);
|
||||||
|
|
||||||
const protectedRoutes = express.Router();
|
const protectedRoutes = express.Router();
|
||||||
protectedRoutes.use(apiController.verifyToken);
|
protectedRoutes.use(jwt_utils.verifyToken);
|
||||||
protectedRoutes.get('/person/myself', apiController.getMyself);
|
protectedRoutes.get('/person/myself', personRoutes.getMyself);
|
||||||
protectedRoutes.get('/person/:id', apiController.getPerson);
|
protectedRoutes.get('/person/:id', personRoutes.getPerson);
|
||||||
protectedRoutes.put('/person/:id', apiController.updatePerson);
|
protectedRoutes.put('/person/:id', personRoutes.updatePerson);
|
||||||
protectedRoutes.delete('/person/delete', apiController.deletePerson);
|
protectedRoutes.delete('/person/delete', personRoutes.deletePerson);
|
||||||
protectedRoutes.post('/organization/admin', apiController.addOrganizationAdmin);
|
protectedRoutes.post('/organization/admin', apiController.addOrganizationAdmin);
|
||||||
protectedRoutes.delete('/organization/removeadmin', apiController.removeOrganizationAdmin);
|
protectedRoutes.delete('/organization/removeadmin', apiController.removeOrganizationAdmin);
|
||||||
protectedRoutes.post('/organization', apiController.createOrganization);
|
protectedRoutes.post('/organization', apiController.createOrganization);
|
||||||
|
@ -19,259 +19,6 @@ const jwt = require('jsonwebtoken');
|
|||||||
|
|
||||||
// ======== BEGIN API ENDPOINTS ========
|
// ======== BEGIN API ENDPOINTS ========
|
||||||
|
|
||||||
/**
|
|
||||||
* POST Request
|
|
||||||
*
|
|
||||||
* Registers a Person
|
|
||||||
*
|
|
||||||
* Required field(s): name, email (valid ISO standard), password
|
|
||||||
*
|
|
||||||
* @returns The activationlink identifier
|
|
||||||
*/
|
|
||||||
async function registerPerson(req, res){
|
|
||||||
|
|
||||||
if (process.env.ALLOW_USER_REGISTRATION === 'false'){
|
|
||||||
return res.status(403).json({error : "Users cannot register on this server"});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the required fields are present before proceeding
|
|
||||||
if (!req.body.display_name || !req.body.email || !req.body.password) {
|
|
||||||
return res.status(400).json({ error : "Some or all required fields are missing"});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!validateEmail(req.body.email)){
|
|
||||||
return res.status(400).json({ error : "The email is not in a valid format"});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate activation link token
|
|
||||||
const activationLink = crypto.randomBytes(16).toString('hex');
|
|
||||||
|
|
||||||
// Hash provided password
|
|
||||||
const hashPasswordPromise = bcrypt.hash(req.body.password, 10);
|
|
||||||
|
|
||||||
try{
|
|
||||||
|
|
||||||
// Check whether e-mail exists already (enforced by database constraints)
|
|
||||||
const existingUser = await knex('Person')
|
|
||||||
.where('email', req.body.email)
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if(existingUser){
|
|
||||||
return res.status(409).json({ error: "E-mail already in use" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to insert either both in the "Person" table
|
|
||||||
// and in the "ActivationLink" one, or in neither
|
|
||||||
await knex.transaction(async (tr) => {
|
|
||||||
|
|
||||||
const personIdResult = await tr('Person')
|
|
||||||
.insert({
|
|
||||||
email: req.body.email,
|
|
||||||
password: await hashPasswordPromise,
|
|
||||||
display_name: req.body.display_name,
|
|
||||||
date_of_birth: req.body.date_of_birth,
|
|
||||||
available: req.body.available,
|
|
||||||
enabled: true,
|
|
||||||
place_of_living: req.body.place_of_living
|
|
||||||
})
|
|
||||||
.returning("id");
|
|
||||||
|
|
||||||
await tr('ActivationLink')
|
|
||||||
.insert({
|
|
||||||
person_id: personIdResult[0].id,
|
|
||||||
identifier: activationLink
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return res.status(200).json({ activationLink: activationLink });
|
|
||||||
}
|
|
||||||
catch (error){
|
|
||||||
console.error('Error registering person:', error);
|
|
||||||
res.status(500).json({error : "Internal server error"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POST Request
|
|
||||||
*
|
|
||||||
* Creates a token if the specified email
|
|
||||||
* and password are valid.
|
|
||||||
*
|
|
||||||
* Required field(s): email, password
|
|
||||||
*
|
|
||||||
* @returns The token
|
|
||||||
*/
|
|
||||||
async function login(req, res){
|
|
||||||
|
|
||||||
// Ensure that the required fields are present before proceeding
|
|
||||||
if (!req.body.email || !req.body.password) {
|
|
||||||
return res.status(400).json({error : "Invalid request"});
|
|
||||||
}
|
|
||||||
|
|
||||||
const person = await checkUserCredentials(req.body.email, req.body.password);
|
|
||||||
|
|
||||||
if (person){
|
|
||||||
const token = generateToken(person.id);
|
|
||||||
res.status(200).json({token: token });
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
res.status(401).json({error : "Unauthorized"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain a Person's details if the
|
|
||||||
* Person to retrieve is either myself or an
|
|
||||||
* enabled Person.
|
|
||||||
*
|
|
||||||
* Required field(s): none
|
|
||||||
*
|
|
||||||
* @returns The Person
|
|
||||||
*/
|
|
||||||
async function getPerson(req, res){
|
|
||||||
try {
|
|
||||||
const user = await knex('Person')
|
|
||||||
.select('*')
|
|
||||||
.where({ id: req.params.id })
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if(user){
|
|
||||||
// I am retrieving either myself or an enabled user
|
|
||||||
if(user.id == req.jwt.person_id || user.enabled){
|
|
||||||
delete user['password']; // remove password field for security reasons
|
|
||||||
return res.status(200).send(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.status(404).json({error: "Not found"});
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log("Error while getting person: " + error);
|
|
||||||
return res.status(500).json({error : "Internal server error"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* GET Request
|
|
||||||
*
|
|
||||||
* Get myself, from the JWT token
|
|
||||||
*
|
|
||||||
* @returns Person's details
|
|
||||||
*/
|
|
||||||
async function getMyself(req, res){
|
|
||||||
try{
|
|
||||||
const person = await knex('Person')
|
|
||||||
.select('*')
|
|
||||||
.where({ id: req.jwt.person_id })
|
|
||||||
.first();
|
|
||||||
|
|
||||||
console.log(req.jwt.person_id);
|
|
||||||
|
|
||||||
if(person){
|
|
||||||
delete person['password'];
|
|
||||||
return res.status(200).send(person);
|
|
||||||
}
|
|
||||||
return res.status(404).json({error: "Not found"});
|
|
||||||
}
|
|
||||||
catch (error){
|
|
||||||
console.log("Error while getting myself: " + error);
|
|
||||||
return res.status(500).json({error : "Internal server error"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PUT request
|
|
||||||
*
|
|
||||||
* Updates a Person's details. If some details are
|
|
||||||
* not present, they shall be ignored.
|
|
||||||
*
|
|
||||||
* Required field(s): none. Both old_password and
|
|
||||||
* new_password if updating the password.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
async function updatePerson(req, res){
|
|
||||||
|
|
||||||
if (req.jwt.person_id != req.params.id){
|
|
||||||
return res.status(403).json({ error : "Forbidden"});
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatePerson = {};
|
|
||||||
|
|
||||||
if(req.body.display_name){
|
|
||||||
updatePerson.display_name = req.body.display_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(req.body.date_of_birth){
|
|
||||||
if(isPostgresDateFormatValid(req.body.date_of_birth)){
|
|
||||||
updatePerson.date_of_birth = req.body.date_of_birth;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return res.status(400).json({ error : "Date of birth format not valid. Please specify a YYYY-MM-DD date"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(req.body.available){
|
|
||||||
updatePerson.available = req.body.available;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(req.body.place_of_living){
|
|
||||||
updatePerson.place_of_living = req.body.place_of_living;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are tying to change password, the old password must be provided
|
|
||||||
if(req.body.old_password && req.body.new_password){
|
|
||||||
const user = await knex('Person')
|
|
||||||
.select('password')
|
|
||||||
.where({ id: req.jwt.person_id })
|
|
||||||
.first();
|
|
||||||
const passwordMatches = await bcrypt.compare(req.body.old_password, user.password);
|
|
||||||
if(passwordMatches){
|
|
||||||
updatePerson.password = await bcrypt.hash(req.body.new_password, 10);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return res.status(401).json({ error : "Password verification failed"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(updatePerson).length === 0) {
|
|
||||||
return res.status(400).json({ error : "Bad request. No data to update"});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await knex('Person')
|
|
||||||
.where('id', req.params.id)
|
|
||||||
.update(updatePerson);
|
|
||||||
return res.status(200).json({ success : "true"});
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log("Error while updating a Person: " + error);
|
|
||||||
return res.status(500).json({ error : "Internal server error"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET Request
|
|
||||||
*
|
|
||||||
* Deletes a Person. An user can only delete
|
|
||||||
* themselves.
|
|
||||||
*
|
|
||||||
* Required field(s): none
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
async function deletePerson(req, res) {
|
|
||||||
try {
|
|
||||||
await knex('Person')
|
|
||||||
.where({id : req.jwt.person_id})
|
|
||||||
.del();
|
|
||||||
return res.status(200).json({success: true});
|
|
||||||
|
|
||||||
// TODO: Delete Organization if this user was its only administrator
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log("Error deleting a Person: " + error);
|
|
||||||
return res.status(500).json({error : "Internal server error"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST Request
|
* POST Request
|
||||||
*
|
*
|
||||||
@ -642,80 +389,10 @@ async function removeOrganizationAdmin(req, res){
|
|||||||
|
|
||||||
// ======== END API ENDPOINTS ========
|
// ======== END API ENDPOINTS ========
|
||||||
|
|
||||||
async function checkUserCredentials(email, password){
|
|
||||||
try {
|
|
||||||
const user = await knex('Person')
|
|
||||||
.where('email', email)
|
|
||||||
.where('enabled', true)
|
|
||||||
.select('*')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if(user){
|
|
||||||
const passwordMatches = await bcrypt.compare(password, user.password);
|
|
||||||
if (passwordMatches) {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateToken(person_id) {
|
|
||||||
// The payload the JWT will carry within itself
|
|
||||||
const payload = {
|
|
||||||
person_id: person_id
|
|
||||||
};
|
|
||||||
|
|
||||||
const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, {
|
|
||||||
expiresIn: '8h'
|
|
||||||
});
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Middlware
|
|
||||||
function verifyToken(req, res, next) {
|
|
||||||
const token = req.headers.authorization;
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
return res.status(401).send({error : 'No token provided'});
|
|
||||||
}
|
|
||||||
|
|
||||||
jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => {
|
|
||||||
if (err) {
|
|
||||||
return res.status(401).send({error : 'Failed to authenticate token'});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the token is valid, store the decoded data in the request object
|
|
||||||
req.jwt = decoded;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateEmail(email) {
|
|
||||||
const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
|
|
||||||
return regex.test(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPostgresDateFormatValid(dateString) {
|
|
||||||
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
|
||||||
return regex.test(dateString);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exporting a function
|
// Exporting a function
|
||||||
// means making a JavaScript function defined in one
|
// means making a JavaScript function defined in one
|
||||||
// module available for use in another module.
|
// module available for use in another module.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
registerPerson,
|
|
||||||
login,
|
|
||||||
getPerson,
|
|
||||||
getMyself,
|
|
||||||
updatePerson,
|
|
||||||
deletePerson,
|
|
||||||
verifyToken,
|
|
||||||
createOrganization,
|
createOrganization,
|
||||||
getOrganization,
|
getOrganization,
|
||||||
updateOrganization,
|
updateOrganization,
|
||||||
|
@ -0,0 +1,294 @@
|
|||||||
|
// Blink-specific modules
|
||||||
|
const validator = require('../utils/validation');
|
||||||
|
const knex = require('../utils/knex_config');
|
||||||
|
const jwt_utils = require('../utils/jwt_utils');
|
||||||
|
// Other modules
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST Request
|
||||||
|
*
|
||||||
|
* Registers a Person
|
||||||
|
*
|
||||||
|
* Required field(s): name, email (valid ISO standard), password
|
||||||
|
*
|
||||||
|
* @returns The activationlink identifier
|
||||||
|
*/
|
||||||
|
async function registerPerson(req, res){
|
||||||
|
|
||||||
|
if (process.env.ALLOW_USER_REGISTRATION === 'false'){
|
||||||
|
return res.status(403).json({error : "Users cannot register on this server"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the required fields are present before proceeding
|
||||||
|
if (!req.body.display_name || !req.body.email || !req.body.password) {
|
||||||
|
return res.status(400).json({ error : "Some or all required fields are missing"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!validator.validateEmail(req.body.email)){
|
||||||
|
return res.status(400).json({ error : "The email is not in a valid format"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate activation link token
|
||||||
|
const activationLink = crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
|
// Hash provided password
|
||||||
|
const hashPasswordPromise = bcrypt.hash(req.body.password, 10);
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
// Check whether e-mail exists already (enforced by database constraints)
|
||||||
|
const existingUser = await knex('Person')
|
||||||
|
.where('email', req.body.email)
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if(existingUser){
|
||||||
|
return res.status(409).json({ error: "E-mail already in use" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to insert either both in the "Person" table
|
||||||
|
// and in the "ActivationLink" one, or in neither
|
||||||
|
await knex.transaction(async (tr) => {
|
||||||
|
|
||||||
|
const personIdResult = await tr('Person')
|
||||||
|
.insert({
|
||||||
|
email: req.body.email,
|
||||||
|
password: await hashPasswordPromise,
|
||||||
|
display_name: req.body.display_name,
|
||||||
|
date_of_birth: req.body.date_of_birth,
|
||||||
|
available: req.body.available,
|
||||||
|
enabled: true,
|
||||||
|
place_of_living: req.body.place_of_living
|
||||||
|
})
|
||||||
|
.returning("id");
|
||||||
|
|
||||||
|
await tr('ActivationLink')
|
||||||
|
.insert({
|
||||||
|
person_id: personIdResult[0].id,
|
||||||
|
identifier: activationLink
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return res.status(200).json({ activationLink: activationLink });
|
||||||
|
}
|
||||||
|
catch (error){
|
||||||
|
console.error('Error registering person:', error);
|
||||||
|
res.status(500).json({error : "Internal server error"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST Request
|
||||||
|
*
|
||||||
|
* Creates a token if the specified email
|
||||||
|
* and password are valid.
|
||||||
|
*
|
||||||
|
* Required field(s): email, password
|
||||||
|
*
|
||||||
|
* @returns The token
|
||||||
|
*/
|
||||||
|
async function login(req, res){
|
||||||
|
|
||||||
|
// Ensure that the required fields are present before proceeding
|
||||||
|
if (!req.body.email || !req.body.password) {
|
||||||
|
return res.status(400).json({error : "Invalid request"});
|
||||||
|
}
|
||||||
|
|
||||||
|
const person = await checkUserCredentials(req.body.email, req.body.password);
|
||||||
|
|
||||||
|
if (person){
|
||||||
|
const token = jwt_utils.generateToken(person.id);
|
||||||
|
res.status(200).json({token: token });
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
res.status(401).json({error : "Unauthorized"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a Person's details if the
|
||||||
|
* Person to retrieve is either myself or an
|
||||||
|
* enabled Person.
|
||||||
|
*
|
||||||
|
* Required field(s): none
|
||||||
|
*
|
||||||
|
* @returns The Person
|
||||||
|
*/
|
||||||
|
async function getPerson(req, res){
|
||||||
|
try {
|
||||||
|
const user = await knex('Person')
|
||||||
|
.select('*')
|
||||||
|
.where({ id: req.params.id })
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if(user){
|
||||||
|
// I am retrieving either myself or an enabled user
|
||||||
|
if(user.id == req.jwt.person_id || user.enabled){
|
||||||
|
delete user['password']; // remove password field for security reasons
|
||||||
|
return res.status(200).send(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.status(404).json({error: "Not found"});
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log("Error while getting person: " + error);
|
||||||
|
return res.status(500).json({error : "Internal server error"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* GET Request
|
||||||
|
*
|
||||||
|
* Get myself, from the JWT token
|
||||||
|
*
|
||||||
|
* @returns Person's details
|
||||||
|
*/
|
||||||
|
async function getMyself(req, res){
|
||||||
|
try{
|
||||||
|
const person = await knex('Person')
|
||||||
|
.select('*')
|
||||||
|
.where({ id: req.jwt.person_id })
|
||||||
|
.first();
|
||||||
|
|
||||||
|
console.log(req.jwt.person_id);
|
||||||
|
|
||||||
|
if(person){
|
||||||
|
delete person['password'];
|
||||||
|
return res.status(200).send(person);
|
||||||
|
}
|
||||||
|
return res.status(404).json({error: "Not found"});
|
||||||
|
}
|
||||||
|
catch (error){
|
||||||
|
console.log("Error while getting myself: " + error);
|
||||||
|
return res.status(500).json({error : "Internal server error"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT request
|
||||||
|
*
|
||||||
|
* Updates a Person's details. If some details are
|
||||||
|
* not present, they shall be ignored.
|
||||||
|
*
|
||||||
|
* Required field(s): none. Both old_password and
|
||||||
|
* new_password if updating the password.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async function updatePerson(req, res){
|
||||||
|
|
||||||
|
if (req.jwt.person_id != req.params.id){
|
||||||
|
return res.status(403).json({ error : "Forbidden"});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePerson = {};
|
||||||
|
|
||||||
|
if(req.body.display_name){
|
||||||
|
updatePerson.display_name = req.body.display_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(req.body.date_of_birth){
|
||||||
|
if(validator.isPostgresDateFormatValid(req.body.date_of_birth)){
|
||||||
|
updatePerson.date_of_birth = req.body.date_of_birth;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return res.status(400).json({ error : "Date of birth format not valid. Please specify a YYYY-MM-DD date"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(req.body.available){
|
||||||
|
updatePerson.available = req.body.available;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(req.body.place_of_living){
|
||||||
|
updatePerson.place_of_living = req.body.place_of_living;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are tying to change password, the old password must be provided
|
||||||
|
if(req.body.old_password && req.body.new_password){
|
||||||
|
const user = await knex('Person')
|
||||||
|
.select('password')
|
||||||
|
.where({ id: req.jwt.person_id })
|
||||||
|
.first();
|
||||||
|
const passwordMatches = await bcrypt.compare(req.body.old_password, user.password);
|
||||||
|
if(passwordMatches){
|
||||||
|
updatePerson.password = await bcrypt.hash(req.body.new_password, 10);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return res.status(401).json({ error : "Password verification failed"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(updatePerson).length === 0) {
|
||||||
|
return res.status(400).json({ error : "Bad request. No data to update"});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await knex('Person')
|
||||||
|
.where('id', req.params.id)
|
||||||
|
.update(updatePerson);
|
||||||
|
return res.status(200).json({ success : "true"});
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log("Error while updating a Person: " + error);
|
||||||
|
return res.status(500).json({ error : "Internal server error"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET Request
|
||||||
|
*
|
||||||
|
* Deletes a Person. An user can only delete
|
||||||
|
* themselves.
|
||||||
|
*
|
||||||
|
* Required field(s): none
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async function deletePerson(req, res) {
|
||||||
|
try {
|
||||||
|
await knex('Person')
|
||||||
|
.where({id : req.jwt.person_id})
|
||||||
|
.del();
|
||||||
|
return res.status(200).json({success: true});
|
||||||
|
|
||||||
|
// TODO: Delete Organization if this user was its only administrator
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log("Error deleting a Person: " + error);
|
||||||
|
return res.status(500).json({error : "Internal server error"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkUserCredentials(email, password){
|
||||||
|
try {
|
||||||
|
const user = await knex('Person')
|
||||||
|
.where('email', email)
|
||||||
|
.where('enabled', true)
|
||||||
|
.select('*')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if(user){
|
||||||
|
const passwordMatches = await bcrypt.compare(password, user.password);
|
||||||
|
if (passwordMatches) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exporting a function
|
||||||
|
// means making a JavaScript function defined in one
|
||||||
|
// module available for use in another module.
|
||||||
|
module.exports = {
|
||||||
|
registerPerson,
|
||||||
|
login,
|
||||||
|
getPerson,
|
||||||
|
getMyself,
|
||||||
|
updatePerson,
|
||||||
|
deletePerson
|
||||||
|
};
|
37
backend/apis/nodejs/src/utils/jwt_utils.js
Normal file
37
backend/apis/nodejs/src/utils/jwt_utils.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
function generateToken(person_id) {
|
||||||
|
// The payload the JWT will carry within itself
|
||||||
|
const payload = {
|
||||||
|
person_id: person_id
|
||||||
|
};
|
||||||
|
|
||||||
|
const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, {
|
||||||
|
expiresIn: '8h'
|
||||||
|
});
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middlware
|
||||||
|
function verifyToken(req, res, next) {
|
||||||
|
const token = req.headers.authorization;
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return res.status(401).send({error : 'No token provided'});
|
||||||
|
}
|
||||||
|
|
||||||
|
jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(401).send({error : 'Failed to authenticate token'});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the token is valid, store the decoded data in the request object
|
||||||
|
req.jwt = decoded;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generateToken,
|
||||||
|
verifyToken
|
||||||
|
};
|
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Checks whether an e-mail is in a valid format
|
||||||
|
* @param {*} email email to validate
|
||||||
|
* @returns true or false
|
||||||
|
*/
|
||||||
|
function validateEmail(email) {
|
||||||
|
const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
|
||||||
|
return regex.test(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a date is in a correct Postgres
|
||||||
|
* format (YYYY-MM-DD)
|
||||||
|
* @param {*} dateString the date to validate
|
||||||
|
* @returns true or false
|
||||||
|
*/
|
||||||
|
function isPostgresDateFormatValid(dateString) {
|
||||||
|
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
||||||
|
return regex.test(dateString);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
validateEmail,
|
||||||
|
isPostgresDateFormatValid
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user