Add frontend to be served by backend.
This commit is contained in:
10
backend/public/index.html
Normal file
10
backend/public/index.html
Normal 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
16
backend/src/js/app.js
Normal 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'));
|
||||
6
backend/src/js/configuration.js
Normal file
6
backend/src/js/configuration.js
Normal 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;
|
||||
54
backend/src/js/controller/applicationCtrl.js
Normal file
54
backend/src/js/controller/applicationCtrl.js
Normal 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;
|
||||
5
backend/src/js/controller/templateCtrl.js
Normal file
5
backend/src/js/controller/templateCtrl.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const router = require('express').Router();
|
||||
|
||||
// Develop routes here
|
||||
|
||||
module.exports = router;
|
||||
20
backend/src/js/controller/userCtrl.js
Normal file
20
backend/src/js/controller/userCtrl.js
Normal 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;
|
||||
17
backend/src/js/filter/authenticationFilter.js
Normal file
17
backend/src/js/filter/authenticationFilter.js
Normal 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();
|
||||
}
|
||||
};
|
||||
76
backend/src/js/repository/mongoClient.js
Normal file
76
backend/src/js/repository/mongoClient.js
Normal 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;
|
||||
64
backend/src/js/repository/repository.js
Normal file
64
backend/src/js/repository/repository.js
Normal 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);
|
||||
}
|
||||
};
|
||||
27
backend/src/js/service/passwordService.js
Normal file
27
backend/src/js/service/passwordService.js
Normal 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;
|
||||
31
backend/src/js/service/tokenService.js
Normal file
31
backend/src/js/service/tokenService.js
Normal 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;
|
||||
42
backend/src/js/service/userService.js
Normal file
42
backend/src/js/service/userService.js
Normal 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;
|
||||
10
backend/src/resources/application.yml
Normal file
10
backend/src/resources/application.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
mongodb:
|
||||
url: 'localhost'
|
||||
port: 27017
|
||||
username: 'god'
|
||||
password: 'P@ssword'
|
||||
database: 'admin'
|
||||
security:
|
||||
jwt:
|
||||
secret: 5ubtcCCo7hWBqjNGtzzVKnLT1KxN9uS4D6kRZowCunZAYPmxtKy6mvgoxANe4WqLVfiVI7AZSVqZCtvlSWFwIsnXGH6lxeKG0U8Wu7Kw0jwfFOGLvlO8bXaB
|
||||
validity: 1h
|
||||
Reference in New Issue
Block a user