Add frontend to be served by backend.

This commit is contained in:
2020-12-28 15:19:09 +01:00
parent 94226c140e
commit 391be9b833
18 changed files with 4905 additions and 8 deletions

10
backend/public/index.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Vanilla JS frontend app.</title>
</head>
<body>
<div id="content"></div>
<script type="application/javascript" src="./bundle.js"></script>
</body>
</html>

16
backend/src/js/app.js Normal file
View File

@@ -0,0 +1,16 @@
const express = require('express');
const bodyParser = require('body-parser');
const applicationController = require('./controller/applicationCtrl');
const userController = require('./controller/userCtrl');
const port = 3000;
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static('backend/public'));
app.use('/apps', applicationController);
app.use('/users', userController);
app.listen(port, () => console.log('Mock is listening at port ', port, '\n'));

View File

@@ -0,0 +1,6 @@
const fs = require('fs');
const yaml = require('yaml');
const configurationFile = fs.readFileSync('backend/src/resources/application.yml', 'utf8');
const configuration = yaml.parse(configurationFile);
module.exports = configuration;

View File

@@ -0,0 +1,54 @@
const Repository = require('../repository/repository');
const authenticationFilter = require('../filter/authenticationFilter');
const express = require('express');
const router = express.Router();
const applicationRepository = new Repository('applications');
router.get('/', (request, response) => {
applicationRepository.find({}, results => {
response.json(results);
});
});
router.get('/:applicationId', (request, response) => {
applicationRepository.find({_id: request.params.applicationId}, (result) => {
response.json(result[0]);
});
});
router.post('/', (request, response) => {
applicationRepository.insert(request.body, (result) => {
response.json(result);
});
});
router.put('/:applicationId', (request, response) => {
const applicationId = request.params.applicationId;
applicationRepository.find({_id: applicationId}, entity => {
if (entity.length === 0) {
response.status(404).send();
} else {
const applicationToUpdate = request.body;
applicationToUpdate._id = applicationId;
applicationRepository.update(applicationToUpdate, () => {
response.status(204).send();
});
}
});
});
router.delete('/:applicationId', authenticationFilter, (request, response) => {
const applicationId = request.params.applicationId;
applicationRepository.find({_id: applicationId}, entity => {
if (entity.length === 0) {
response.status(404).send();
} else {
applicationRepository.delete(applicationId, () => {
response.status(204).send();
});
}
});
});
module.exports = router;

View File

@@ -0,0 +1,5 @@
const router = require('express').Router();
// Develop routes here
module.exports = router;

View File

@@ -0,0 +1,20 @@
const router = require('express').Router();
const tokenService = require('../service/tokenService');
const userService = require('../service/userService');
router.post('/login', (request, response) => {
const loginRequest = request.body;
if (!loginRequest) {
response.status(403).send();
} else {
userService.checkCredentials(loginRequest.login, loginRequest.password,
() => {
const tokenPayload = { login: loginRequest.login };
response.json(tokenService.build(tokenPayload));
},
() => response.status(403).send());
}
});
module.exports = router;

View File

@@ -0,0 +1,17 @@
const tokenService = require('../service/tokenService');
module.exports = (request, response, next) => {
const authenticationHeader = request.headers.authorization;
if (authenticationHeader) {
const token = authenticationHeader.split(' ')[1];
if (tokenService.isValid(token)) {
next();
} else {
response.status(403).send();
}
} else {
response.status(401).send();
}
};

View File

@@ -0,0 +1,76 @@
const mongodb = require('mongodb');
const configuration = require('../configuration');
const mongoConfig = configuration.mongodb;
function buildDatabaseUrl() {
const username = encodeURIComponent(mongoConfig.username);
const password = encodeURIComponent(mongoConfig.password);
const url = mongoConfig.url;
const port = mongoConfig.port;
const database = mongoConfig.database;
const test = `mongodb://${username}:${password}@${url}:${port}/${database}`;
console.log(test);
return test;
}
class MongoClient {
constructor() {
mongodb.MongoClient.connect(buildDatabaseUrl(), (error, client) => {
if (error !== null) {
throw new Error(`Unable de connect to Mongo database: ${error}`);
}
console.log('Connected successfuly to mongodb');
this.db = client.db(mongoConfig.database);
});
}
find(collectionName, query, onSuccess, onError) {
this.db.collection(collectionName).find(query).toArray()
.then(results => {
console.log(`Entities ${collectionName} founded.`);
onSuccess(results);
})
.catch(error => {
console.error(`Unable to find entities in collection ${collectionName}: ${error}`);
onError(error);
});
}
insert(collectionName, entity, callback) {
this.db.collection(collectionName).insertOne(entity, (error, result) => {
if (error !== null) {
throw new Error(`Unable to insert ${collectionName} entity: ${error}`);
}
console.log(`Entity ${collectionName} inserted.`);
// Return only the inserted document.
callback(result.ops[0]);
});
}
update(collectionName, entity, callback) {
this.db.collection(collectionName).updateOne({_id: mongodb.ObjectId(entity._id)}, {$set: entity}, {upsert: true}, (error) => {
if (error !== null) {
throw new Error(`Unable to update ${collectionName} entity: ${error}`);
}
console.log(`Entity ${collectionName} updated.`);
callback();
});
}
delete(collectionName, entityId, callback) {
this.db.collection(collectionName).deleteOne({_id: mongodb.ObjectId(entityId)}, (error) => {
if (error !== null) {
throw new Error(`Unable to delete ${collectionName} entity with id ${entityId}: ${error}`);
}
console.log(`Entity ${collectionName} with id ${entityId} deleted.`);
callback();
});
}
}
const singleton = new MongoClient();
module.exports = singleton;

View File

@@ -0,0 +1,64 @@
const mongoClient = require('./mongoClient');
const ObjectId = require('mongodb').ObjectId;
/**
* If entity in parameters has an attribute named "_id", this function converts it into a MongoDB {@code ObjectId}.
*/
function convertIdToMongodbFormat(entity) {
if (!!entity._id && !(entity._id instanceof ObjectId)) {
entity._id = ObjectId(entity._id);
}
}
module.exports = class Repository {
/**
* Creates a new repository which read and write into the {@code collectionName} collection in database.
* @param {String} collectionName
*/
constructor(collectionName) {
this.collectionName = collectionName;
this.mongoClient = mongoClient;
}
/**
* Returns the entities that matches criteria in {@code query}.
* @param {Object} query The query which contains criteria to find some entities.
* @param {Function} onSuccess The function to execute after getting entities.
* @param {Function} onError The function to execute if entity finding failed.
*/
find(query, onSuccess, onError) {
convertIdToMongodbFormat(query);
this.mongoClient.find(this.collectionName, query, onSuccess, onError);
}
/**
* Insert entity in database.
* @param {*} entity The entity to insert into database.
* @param {*} callback The function to execute after inserting entity.
*/
insert(entity, callback) {
this.mongoClient.insert(this.collectionName, entity, callback);
}
/**
* Update the whole entity in database.
* @param {*} entity The entity to update into database.
* @param {*} callback The function to execute after updating entity.
*/
update(entity, callback) {
convertIdToMongodbFormat(entity);
this.mongoClient.update(this.collectionName, entity, callback);
}
/**
* Delete entity in database.
* @param {*} entityId Entity to delete id.
* @param {*} callback The function to execute after deleting entity.
*/
delete(entityId, callback) {
if (!(entityId instanceof ObjectId)) {
entityId = ObjectId(entityId);
}
this.mongoClient.delete(this.collectionName, entityId, callback);
}
};

View File

@@ -0,0 +1,27 @@
const bcrypt = require('bcrypt');
const saltRounds = 10;
class PasswordService {
/**
* Hashes the password in parameters.
* @param {String} password The plain text password.
* @returns The hashed password.
*/
hashPassword(password) {
const salt = bcrypt.genSaltSync(saltRounds);
return bcrypt.hashSync(password, salt);
}
/**
* Checks if the {@code plainTextPassword} matches the hashed password.
* @param {String} plainTextPassword The plain text password.
* @param {String} hashedPassword The hashed password.
* @returns A boolean.
*/
areSamePasswords(plainTextPassword, hashedPassword) {
return bcrypt.compareSync(plainTextPassword, hashedPassword);
}
}
const singleton = new PasswordService();
module.exports = singleton;

View File

@@ -0,0 +1,31 @@
const jwt = require('jsonwebtoken');
const configuration = require('../configuration');
const securityConfig = configuration.security;
class TokenService {
/**
* Builds a JWT token.
* @param {Object} tokenPayload The JWT payload.
* @returns {String} The JWT token.
*/
build(tokenPayload) {
return jwt.sign(tokenPayload, securityConfig.jwt.secret, {expiresIn: securityConfig.jwt.validity});
}
/**
* Checks if the given token is valid, and provides from this application or not.
* @param {String} token The JWT token to check.
* @returns A boolean.
*/
isValid(token) {
try {
jwt.verify(token, securityConfig.jwt.secret);
} catch (exception) {
return false;
}
return true;
}
}
const singleton = new TokenService();
module.exports = singleton;

View File

@@ -0,0 +1,42 @@
const Repository = require('../repository/repository');
const passwordService = require('./passwordService');
const userRepository = new Repository('users');
class UserService {
/**
* Get a user from database by its login.
* @param {String} login User login.
* @param {Function} onSuccess Callback function to execute if a user exists with this login.
* @param {Function} onError Callback function to execute if not any user exists with this login.
*/
getUser(login, onSuccess, onError) {
userRepository.find({login: login}, results => onSuccess(results[0]), onError);
}
/**
* Checks if credentials matches to an existing user, that have this password.
* @param {String} login User login.
* @param {String} password User password, in plain text.
* @param {Function} onSuccess Callback function to execute if a user exists with this login.
* @param {Function} onError Callback function to execute if not any user exists with this login.
*/
checkCredentials(login, password, onSuccess, onError) {
this.getUser(
login,
dbUser => {
if (!!dbUser && passwordService.areSamePasswords(password, dbUser.password)) {
onSuccess();
} else {
onError();
}
},
// If login is incorrect, the "getUser" function will return "undefined".
// So if "user" is "undefined", this proofs that login is incorrect
onError
);
}
}
const singleton = new UserService();
module.exports = singleton;

View File

@@ -0,0 +1,10 @@
mongodb:
url: 'localhost'
port: 27017
username: 'god'
password: 'P@ssword'
database: 'admin'
security:
jwt:
secret: 5ubtcCCo7hWBqjNGtzzVKnLT1KxN9uS4D6kRZowCunZAYPmxtKy6mvgoxANe4WqLVfiVI7AZSVqZCtvlSWFwIsnXGH6lxeKG0U8Wu7Kw0jwfFOGLvlO8bXaB
validity: 1h