reconstruire UC inceput

This commit is contained in:
andrei-mihnea-cerbu
2024-04-03 04:07:20 +03:00
parent af8c295d75
commit 5e4a488bfd
36 changed files with 264 additions and 943 deletions
+45
View File
@@ -0,0 +1,45 @@
General Overview
-
The UC serves as the main logistic station for all the client/CEO applications which are used
inside the corporation. This UC stores
- credentials for the users and ceo;
- registered departments, as well security levels and the encrypting/decrypting keys for the files;
- backup schemes for each user, as well as the IP, as well as the IPs where each computer is located;
The main endpoints of the application are:
- <b>Users</b>:
- Can register/login in the system;
- Update the information (username, email, password);
- Change the working department (the client app will prompt the CEO password request to proceed);
- Retrieve the full list of departments for the sign-up / 'change department' procedure;
- Retrieve the full list of users and their IPs for sharing a file
- <b>CEO</b>:
- Can log in the system;
- Can change the email (the password can only be retrieved by the admin);
- Delete users from the system;
- Create / Delete departments, as well as managing the security levels of each department;
- Retrieve the full list of users and their IPs for sharing a file
- <b>Admin</b>:
- Reset the server to default state;
- Retrieve the CEO login information, as well as a list of all users in the system;
- <b>Backup Schemes</b>:
- At a request, will get the ID of a user, their IP and the backup directory structure to place it in the scheme,
as well as the data size of the backup
Other Features
-
The UC will also create a separate process which at a set interval of time will retrieve from the admin config, as
well as from the CEO config, the ID so that it will be used to authenticate the users at their endpoints.
+5 -8
View File
@@ -8,23 +8,21 @@ const port = process.env.PORT || 5000;
app.use(cors());
app.use(json());
app.locals.apiKey = 'uc_api'
const checkJson = require('./middlewares/checkJson');
const apiKeyValidation = require('./middlewares/apiKeyValidation');
const setAdminValues = require('./middlewares/setAdminValues');
app.use(apiKeyValidation)
app.use(checkJson)
app.use(setAdminValues);
const usersRouter = require('./routes/users');
const departmentsRouter = require('./routes/departments');
const adminRouter = require('./routes/admin');
const ceoRouter = require('./routes/ceo');
const backupSchemesRouter = require('./routes/backup_schemes');
app.use('/users', usersRouter);
app.use('/departments', departmentsRouter);
app.use('/admin', adminRouter);
app.use('/ceo', ceoRouter);
app.use('/backup_schemes', backupSchemesRouter);
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
@@ -32,8 +30,7 @@ app.listen(port, () => {
// Graceful shutdown function
function gracefulShutdown() {
console.log('\nGracefully shutting down from SIGINT (Ctrl-C)');
console.log('\nShutting down!');
process.exit(0);
}
-1
View File
@@ -1 +0,0 @@
{}
-4
View File
@@ -1,4 +0,0 @@
{
"adminKey": "9ded8e96a7a03fb7ce23a19b4f6a3d08ac42926aa96664af941e4d02e0b93f0d",
"ceoID": "a7635c7a-d6a0-43dc-8e24-552ab33239b8"
}
-12
View File
@@ -1,12 +0,0 @@
[
{
"id": "16183532-3090-4e24-8def-aa29f316c1c5",
"name": "Programatori",
"key": "66238d3abd92e43486a1f8526ff83646a2481b4fb669667daf81ecc71e8211e3"
},
{
"id": "897b883b-7638-452e-bed9-81e8da2ec0c3",
"name": "Contabili",
"key": "55956d2a1cad9e3952f3756ded721724f71baada95c9addbf46f72defa06d354"
}
]
-5
View File
@@ -1,5 +0,0 @@
{
"1": "f1444987-c0c2-4ce8-89e3-b21f302bff7f",
"2": "16183532-3090-4e24-8def-aa29f316c1c5",
"3": "897b883b-7638-452e-bed9-81e8da2ec0c3"
}
-16
View File
@@ -1,16 +0,0 @@
[
{
"id": "a7635c7a-d6a0-43dc-8e24-552ab33239b8",
"name": "CEO",
"email": "ceo@yourfirm.com",
"password": "405ee6885be5385223830874bd6dcfca",
"department": ""
},
{
"id": "ffc90aca-bb31-4641-9a43-30b343924e8a",
"name": "Andrei",
"email": "a@c.com",
"password": "5447045dc3cfaafdb49b365e31dad41eb88616b0b1449930e55fd3a70bd8cee4",
"department": "16183532-3090-4e24-8def-aa29f316c1c5"
}
]
@@ -1,8 +0,0 @@
class DepartmentAlreadyExistsError extends Error {
constructor(message = 'This department is already in system') {
super(message);
this.name = 'DepartmentAlreadyExistsError';
}
}
module.exports = DepartmentAlreadyExistsError
@@ -1,8 +0,0 @@
class DepartmentNotExistingError extends Error {
constructor(message = 'Department not found') {
super(message);
this.name = 'DepartmentNotExistingError';
}
}
module.exports = DepartmentNotExistingError;
@@ -1,8 +0,0 @@
class EmailAlreadyInSystemError extends Error {
constructor(message = "Email is already in system!") {
super(message);
this.name = 'EmailAlreadyInSystem';
}
}
module.exports = EmailAlreadyInSystemError
@@ -1,8 +0,0 @@
class ImproperDirStructureError extends Error {
constructor(message = 'the provided structure is not a valid JSON') {
super(message);
this.name = 'ImproperDirStructureError';
}
}
module.exports = ImproperDirStructureError
@@ -1,8 +0,0 @@
class InvalidDepartmentJsonError extends Error {
constructor(message = 'Invalid JSON format for security levels') {
super(message);
this.name = 'InvalidDepartmentJsonError';
}
}
module.exports=InvalidDepartmentJsonError;
@@ -1,8 +0,0 @@
class InvalidKeyErrorException extends Error {
constructor(message = 'Invalid key for this operation') {
super(message);
this.name = 'InvalidKeyErrorException';
}
}
module.exports=InvalidKeyErrorException;
@@ -1,8 +0,0 @@
class UserAlreadyExistsException extends Error {
constructor(message = 'User already exists in system') {
super(message);
this.name = 'UserAlreadyExistsException';
}
}
module.exports = UserAlreadyExistsException
@@ -1,8 +0,0 @@
class UserNotExistingError extends Error {
constructor(message = 'User not found in the system!') {
super(message);
this.name = 'UserNotExistingError';
}
}
module.exports = UserNotExistingError
@@ -1,26 +0,0 @@
const path = require("path");
const fs = require("fs");
function SetAdminValuesMiddleware(req, res, next){
try{
const configFilePath = path.join(__dirname, '..', 'db', 'config.json');
const config = JSON.parse(fs.readFileSync(configFilePath));
res.locals.adminKey = config.adminKey === undefined ? " " : config.adminKey;
res.locals.ceoID = config.ceoID === undefined ? " " : config.ceoID;
next();
fs.writeFileSync(configFilePath, JSON.stringify(
{
adminKey: res.locals.adminKey,
ceoID: res.locals.ceoID
}, null, 2));
return res;
} catch(error){
console.log(error.message);
return res;
}
}
module.exports = SetAdminValuesMiddleware;
+7 -140
View File
@@ -1,153 +1,20 @@
const express = require('express');
const fs = require('fs');
const crypto = require('crypto');
const {v4: uuidv4} = require("uuid");
const path = require("path");
const { sendResponse } = require('../helpers/responseHelper');
const {httpStatus, httpStatusMessages} = require('../helpers/httpResponses')
const {schemas} = require("../models/schemaMapper");
const InvalidKeyErrorException = require("../exceptions/invalidKeyErrorException");
const UserNotExistingError = require("../exceptions/userNotExistingError");
const router = express.Router();
router.get('/reset_key', (req, res) => {
const adminKey= crypto.randomBytes(32).toString('hex');
res.locals.adminKey = adminKey;
sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK], adminKey);
});
router.get('/reset', (req, res) => {
try {
const requestAdminKey = req.headers['admin-key'];
if (requestAdminKey !== res.locals.adminKey) {
throw new InvalidKeyErrorException();
}
fs.writeFileSync(path.join(__dirname, '..', 'db', 'users.json'), '[]');
fs.writeFileSync(path.join(__dirname, '..', 'db', 'departments.json'), '[]');
fs.writeFileSync(path.join(__dirname, '..', 'db', 'security_levels.json'), '{}');
fs.writeFileSync(path.join(__dirname, '..', 'db', 'backup_schemas.json'), '{}');
fs.writeFileSync(path.join(__dirname, '..', 'db', 'config.json'), JSON.stringify({
adminKey: ' ',
ceoID: ' '
}, null, 2));
const adminPassword = crypto.randomBytes(16).toString('hex'); // Generating random password
const ceoID = uuidv4();
res.locals.ceoID = ceoID;
const adminAccount = {
id: ceoID,
name: 'CEO',
email: 'ceo@yourfirm.com',
password: adminPassword
};
const usersData = [adminAccount];
fs.writeFileSync(path.join(__dirname, '..', 'db', 'users.json'),
JSON.stringify(usersData, null, 2));
sendResponse(res, httpStatus.CREATED, httpStatusMessages[httpStatus.CREATED]);
} catch (error) {
console.log(error.message);
if(error instanceof InvalidKeyErrorException){
return sendResponse(res, httpStatus.UNAUTHORIZED, error.message);
}
sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
});
const validateRegisterCeoBody = (req, res, next) => {
const { error, value } = schemas.ceoRegisterModelSchema.validate(req.body);
if (error) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
}
req.jsonModel = value;
function validateBody(req, res, next) {
next();
}
router.put('/change_admin_credentials', validateRegisterCeoBody, (req, res) => {
try {
const requestAdminKey = req.headers['admin-key'];
if (requestAdminKey !== res.locals.adminKey) {
throw new InvalidKeyErrorException();
}
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = JSON.parse(fs.readFileSync(usersFilePath, 'utf8'));
const adminIndex = usersData.findIndex(user => user.id === res.locals.ceoID);
if (adminIndex === -1) {
throw new UserNotExistingError();
}
const { name, email } = req.body; // Using req.body instead of req.jsonModel
const newPassword = crypto.randomBytes(16).toString('hex'); // Generate new random password
// Update the user data
usersData[adminIndex] = {
id: res.locals.ceoID,
name: name,
email: email,
password: newPassword
};
// Write the updated user data back to the file
fs.writeFileSync(usersFilePath, JSON.stringify(usersData, null, 2));
sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
} catch (error) {
console.log(error.message);
if (error instanceof InvalidKeyErrorException) {
return sendResponse(res, httpStatus.UNAUTHORIZED, error.message);
}
if (error instanceof UserNotExistingError) {
return sendResponse(res, httpStatus.NOT_FOUND, error.message);
}
sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR])
}
router.post('/reset_server', validateBody, (req, res) => {
// Handle reset server logic here
});
router.post('/validate_admin_password', (req, res) => {
try {
const requestAdminKey = req.headers['admin-key'];
if (requestAdminKey !== res.locals.adminKey) {
console.log(res.locals.adminKey);
throw new InvalidKeyErrorException();
}
if(req.body.password === undefined){
return sendResponse(res, httpStatus.BAD_REQUEST, httpStatusMessages[httpStatus.BAD_REQUEST]);
}
const password = req.body.password;
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = fs.readFileSync(usersFilePath, 'utf8');
const usersJson = JSON.parse(usersData);
const user = usersJson.find(user => user.id === res.locals.ceoID);
if(user.password !== password){
return sendResponse(res, httpStatus.UNAUTHORIZED, httpStatusMessages[httpStatus.UNAUTHORIZED]);
}
sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
} catch (error) {
console.log(error.message);
if(error instanceof InvalidKeyErrorException){
return sendResponse(res, httpStatus.UNAUTHORIZED, error.message);
}
sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
router.get('/ceo', validateBody, (req, res) => {
// Retrieve CEO info
});
router.use((req, res) => {
return sendResponse(res, httpStatus.NOT_FOUND, "Path not found");
router.get('/users', validateBody, (req, res) => {
// Retrieve all users in the system
});
module.exports = router;
+15
View File
@@ -0,0 +1,15 @@
const express = require('express');
const router = express.Router();
function validateBody(req, res, next) {
next();
}
router.patch('/', validateBody, (req, res) => {
const { id, ip, backup_schema, size } = req.body;
});
router.get('/', validateBody, (req, res) => {
});
module.exports = router;
+40
View File
@@ -0,0 +1,40 @@
const express = require('express');
const router = express.Router();
function validateBody(req, res, next) {
next();
}
router.post('/login', validateBody, (req, res) => {
const { email, password } = req.body;
});
router.get('/set_security_levels', validateBody, (req, res) => {
});
router.get('/users/delete/:id', validateBody, (req, res) => {
const { id } = req.params;
});
router.post('/departments', validateBody, (req, res) => {
const { name } = req.body;
// Generate key with crypto of 32Hex
});
router.get('/departments/:id', validateBody, (req, res) => {
const { id } = req.params;
});
router.put('/', validateBody, (req, res) => {
const { email } = req.body;
});
router.get('/', validateBody, (req, res) => {
// Send CEO info
});
router.get('/get_decrypt_keys', (req, res) => {
// Get all departments
});
module.exports = router;
-224
View File
@@ -1,224 +0,0 @@
const express = require('express');
const fs = require('fs');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const crypto = require('crypto');
const { sendResponse } = require('../helpers/responseHelper');
const {httpStatus, httpStatusMessages} = require('../helpers/httpResponses')
const {schemas} = require("../models/schemaMapper");
const DepartmentAlreadyExistsError = require("../exceptions/departmentAlreadyExistsError");
const InvalidDepartmentJsonError = require("../exceptions/invalidDepartmentJsonError");
const DepartmentNotExistingError = require("../exceptions/departmentNotExistingError");
const router = express.Router();
router.get('/', (req, res) => {
try {
const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json');
const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8');
const departments = JSON.parse(departmentsData);
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK], departments);
} catch (error) {
console.error(`Error: ${error.message}`);
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
});
function generateAESKey() {
return crypto.randomBytes(32).toString('hex');
}
router.post('/', validateRegisterBody, createDepartment, addSecurityLevel, (req, res) => {
return res;
});
function validateRegisterBody(req, res, next) {
const { error, value } = schemas.departmentRegisterModelSchema.validate(req.body);
if (error) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
}
req.jsonModel = value;
next();
}
function createDepartment(req, res, next) {
try {
const { name } = req.jsonModel;
const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json');
const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8');
const departments = JSON.parse(departmentsData);
const existingDepartment = departments.find(department => department.name === name);
if (existingDepartment) {
throw new DepartmentAlreadyExistsError();
}
const departmentId = uuidv4();
const aesKey = generateAESKey();
const newDepartment = {
id: departmentId,
name: name,
key: aesKey
};
departments.push(newDepartment);
fs.writeFileSync(departmentsFilePath, JSON.stringify(departments, null, 2));
req.departmentId = departmentId;
next();
return res;
} catch (error) {
console.error(`Error: ${error.message}`);
if (error instanceof DepartmentAlreadyExistsError) {
return sendResponse(res, httpStatus.CONFLICT, error.message);
}
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
}
function addSecurityLevel(req, res) {
try {
const { departmentId } = req;
const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json');
const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8');
const securityLevels = JSON.parse(securityLevelsData);
const nextIndex = Object.keys(securityLevels).length + 1;
securityLevels[nextIndex] = departmentId;
fs.writeFileSync(securityLevelsFilePath, JSON.stringify(securityLevels, null, 2));
return sendResponse(res, httpStatus.CREATED, httpStatusMessages[httpStatus.CREATED]);
} catch (error) {
console.error(`Error: ${error.message}`);
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
}
router.put('/', validateDepartmentsJson, validateSecurityLevels, updateSecurityLevels);
function validateDepartmentsJson(req, res, next) {
try {
for (const key in req.body) {
if (!Number.isInteger(parseInt(key)) || typeof req.body[key] !== 'string') {
throw new InvalidDepartmentJsonError();
}
}
next();
} catch (error) {
console.error(`Error: ${error.message}`);
return sendResponse(res, httpStatus.BAD_REQUEST, error.message);
}
}
function validateSecurityLevels(req, res, next) {
try {
const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json');
const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8');
const securityLevels = JSON.parse(securityLevelsData);
if (Object.keys(req.body).length !== Object.keys(securityLevels).length) {
throw new InvalidDepartmentJsonError('Number of keys does not match the database');
}
const encounteredValues = new Set();
const securityLevelValues = Object.values(securityLevels);
for (const key in req.body) {
const requestBodyValue = req.body[key];
if (!securityLevels.hasOwnProperty(key)) {
throw new InvalidDepartmentJsonError('Invalid key found in request body');
}
const securityLevelValue = securityLevels[key];
if (!securityLevelValues.includes(securityLevelValue)) {
throw new InvalidDepartmentJsonError('Values do not match the database');
}
if (encounteredValues.has(requestBodyValue)) {
throw new InvalidDepartmentJsonError('Duplicate values found in request body');
}
encounteredValues.add(requestBodyValue);
}
next();
return res;
} catch (error) {
console.error(`Error: ${error.message}`);
return sendResponse(res, httpStatus.BAD_REQUEST, error.message);
}
}
function updateSecurityLevels(req, res) {
try {
const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json');
fs.writeFileSync(securityLevelsFilePath, JSON.stringify(req.body, null, 2));
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
} catch (error) {
console.error(`Error: ${error.message}`);
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
}
router.delete('/:id', async (req, res) => {
const id = req.params.id;
try {
deleteDepartment(id, res);
} catch (error) {
console.error(`Error: ${error.message}`);
if (error instanceof DepartmentNotExistingError) {
return sendResponse(res, httpStatus.NOT_FOUND, error.message);
}
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
});
function deleteDepartment(id, res) {
const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json');
const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8');
const departmentsJson = JSON.parse(departmentsData);
const index = departmentsJson.findIndex(department => department.id === id);
if (index === -1) {
throw new DepartmentNotExistingError();
}
departmentsJson.splice(index, 1);
fs.writeFileSync(departmentsFilePath, JSON.stringify(departmentsJson, null, 2));
const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json');
const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8');
const securityLevelsJson = JSON.parse(securityLevelsData);
const securityIndex = securityLevelsJson.findIndex(level => level.id === id);
if (securityIndex !== -1) {
securityLevelsJson.splice(securityIndex, 1);
fs.writeFileSync(securityLevelsFilePath, JSON.stringify(securityLevelsJson, null, 2));
}
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
}
router.use((req, res) => {
return sendResponse(res, httpStatus.NOT_FOUND, "Path not found");
});
module.exports = router;
+13 -338
View File
@@ -1,359 +1,34 @@
const express = require('express');
const fs = require('fs');
const path = require('path');
const {v4: uuidv4, validate} = require('uuid');
const crypto = require('crypto');
const {sendResponse} = require('../helpers/responseHelper');
const {httpStatus, httpStatusMessages} = require('../helpers/httpResponses')
const {schemas} = require("../models/schemaMapper");
const UserAlreadyExistsException = require('../exceptions/userAlreadyExistsError');
const UserNotExistingError = require("../exceptions/userNotExistingError");
const ImproperDirStructureError = require("../exceptions/improperDirStructureError");
const EmailAlreadyInSystemError = require("../exceptions/emailAlreadyInSystemError");
const {HttpStatusCode} = require("axios");
const DepartmentAlreadyExistsError = require("../exceptions/departmentAlreadyExistsError");
const DepartmentNotExistingError = require("../exceptions/departmentNotExistingError");
const router = express.Router();
router.get('/', async (req, res) => {
try {
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = fs.readFileSync(usersFilePath, 'utf8');
const usersJson = JSON.parse(usersData);
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK], usersJson)
} catch (error) {
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR])
}
});
const validateRegisterBody = (req, res, next) => {
const {error, value} = schemas.usersRegisterModelSchema.validate(req.body);
if (error) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
}
req.jsonModel = value;
function validateBody(req, res, next) {
next();
}
router.post('/register', validateRegisterBody, async (req, res) => {
try {
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = fs.readFileSync(usersFilePath, 'utf8');
const usersJson = JSON.parse(usersData);
const {name, email, password, department} = req.jsonModel;
const existingUser = usersJson.find(user => user.email === email);
if (existingUser) {
throw new UserAlreadyExistsException();
}
const userId = uuidv4();
const hashedPassword = crypto.createHash('sha256').update(password).digest('hex');
const newUser = {
id: userId,
name: name,
email: email,
password: hashedPassword,
department: department
};
usersJson.push(newUser);
fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2));
return sendResponse(res, httpStatus.CREATED, httpStatusMessages[httpStatus.CREATED]);
} catch (error) {
console.error(`Error: ${error.message}`);
if (error instanceof UserAlreadyExistsException) {
return sendResponse(res, httpStatus.CONFLICT, error.message);
}
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
router.post('/register', validateBody, (req, res) => {
const { name, email, password, department } = req.body;
});
const validateLoginBody = (req, res, next) => {
const {error, value} = schemas.usersLoginModelSchema.validate(req.body);
if (error) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
}
req.jsonModel = value;
next();
}
router.post('/login', validateLoginBody, async (req, res) => {
try {
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = fs.readFileSync(usersFilePath, 'utf8');
const usersJson = JSON.parse(usersData);
const {email, password} = req.jsonModel;
let userIndex = usersJson.findIndex(user => user.email === email);
if (userIndex === -1) {
throw new UserNotExistingError('Invalid email');
}
let hashedPassword = '';
if(usersJson[userIndex].id === res.locals.ceoID) {
hashedPassword = password;
}
else{
hashedPassword = crypto.createHash('sha256').update(password).digest('hex');
}
console.log(email);
console.log(password);
userIndex = usersJson.findIndex(user => user.email === email && user.password === hashedPassword);
console.log(userIndex);
if (userIndex === -1) {
throw new UserNotExistingError('Invalid email or password');
}
const user = usersJson[userIndex];
return sendResponse(res, httpStatus.OK, {
id: user.id,
name: user.name,
email: email,
password: password,
department: user.department
});
} catch (error) {
console.error(`Error: ${error.message}`);
if (error instanceof UserNotExistingError) {
return sendResponse(res, httpStatus.UNAUTHORIZED, error.message);
}
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
router.post('/login', validateBody, (req, res) => {
const { email, password } = req.body;
});
const validateEmailValidationBody = (req, res, next) => {
const {error, value} = schemas.emailVerificationSchema.validate(req.body);
if (error) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
}
req.jsonModel = value;
next();
}
router.post('/validate_email', validateEmailValidationBody, (req, res) => {
try {
const {email} = req.jsonModel;
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = fs.readFileSync(usersFilePath, 'utf8');
const usersJson = JSON.parse(usersData);
if (usersJson.some(user => user.email === email)) {
throw new EmailAlreadyInSystemError();
}
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
} catch (error) {
console.error(`Error: ${error.message}`);
if (error instanceof EmailAlreadyInSystemError) {
return sendResponse(res, httpStatus.CONFLICT, error.message);
}
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
router.post('/email-validation', validateBody, (req, res) => {
const { email } = req.body;
});
const validateDirStructureBody = (req, res, next) => {
const {error, value} = schemas.dirStructureModelSchema.validate(req.body);
if (error) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
}
req.jsonModel = value;
next();
};
router.put('/dir_structure', validateDirStructureBody, (req, res) => {
try {
const {id, dir_config, total_space} = req.jsonModel;
const backupFilePath = path.join(__dirname, '..', 'db', 'backup_schemas.json');
let backupArray = [];
if (fs.existsSync(backupFilePath)) {
const backupData = fs.readFileSync(backupFilePath, 'utf8');
try {
backupArray = JSON.parse(backupData);
} catch (jsonParseError) {
throw new ImproperDirStructureError();
}
}
const existingIndex = backupArray.findIndex(item => item.id === id);
if (existingIndex !== -1) {
backupArray[existingIndex] = {
id: id,
dir_config: dir_config,
total_space: total_space
};
} else {
backupArray.push({
id: id,
dir_config: dir_config,
total_space: total_space
});
}
fs.writeFileSync(backupFilePath, JSON.stringify(backupArray, null, 2));
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
} catch (error) {
console.error(`Error: ${error.message}`);
if (error instanceof ImproperDirStructureError) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.message);
}
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
router.get('/', (req, res) => {
});
const validateUserDepartmentPatch = (req, res, next) => {
const {error, value} = schemas.userDepartmentPatchSchema.validate(req.body);
if (error) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
}
req.jsonModel = value;
next();
};
router.patch('/:id', validateUserDepartmentPatch, async (req, res) => {
const id = req.params.id;
console.log('esti aici');
try {
const department_id = req.jsonModel.department;
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = fs.readFileSync(usersFilePath, 'utf8');
const usersJson = JSON.parse(usersData);
const index = usersJson.findIndex(user => user.id === id);
if (index === -1) {
throw new UserNotExistingError();
}
const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json');
const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8');
const departments = JSON.parse(departmentsData);
console.log(departments);
const indexDepartment = departments.findIndex(department => department.id === department_id);
if (indexDepartment === -1) {
throw new DepartmentNotExistingError();
}
usersJson[index].department = department_id;
fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2));
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
}catch(error){
console.log(error);
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
})
router.delete('/:id', async (req, res) => {
const id = req.params.id;
try {
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = fs.readFileSync(usersFilePath, 'utf8');
const usersJson = JSON.parse(usersData);
const index = usersJson.findIndex(user => user.id === id);
if (index === -1) {
throw new UserNotExistingError();
}
usersJson.splice(index, 1);
fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2));
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
} catch (error) {
console.error(`Error: ${error.message}`);
if (error instanceof UserNotExistingError) {
return sendResponse(res, httpStatus.NOT_FOUND, error.message);
}
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
router.put('/', validateBody, (req, res) => {
const { name, email, password } = req.body;
});
const validateModifyUserBody = (req, res, next) => {
const {error, value} = schemas.usersModifyModelSchema.validate(req.body);
if (error) {
return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
}
req.jsonModel = value;
next();
};
router.put('/:id', validateModifyUserBody, async (req, res) => {
const id = req.params.id;
try {
const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
const usersData = fs.readFileSync(usersFilePath, 'utf8');
const usersJson = JSON.parse(usersData);
const {name, email, password} = req.jsonModel;
if (usersJson.some(user => user.email === email && user.id !== id)) {
throw new EmailAlreadyInSystemError();
}
let index = usersJson.findIndex(user => user.id === id);
if (index === -1) {
throw new UserNotExistingError();
}
const hashedPassword = crypto.createHash('sha256').update(password).digest('hex');
usersJson[index] = {
id: id,
name: name,
email: email,
password: hashedPassword
}
fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2));
return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
} catch (error) {
console.error(`Error: ${error.message}`);
if (error instanceof EmailAlreadyInSystemError) {
return sendResponse(res, httpStatus.CONFLICT, error.message);
}
if (error instanceof UserNotExistingError) {
return sendResponse(res, httpStatus.NOT_FOUND, error.message);
}
return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
}
router.patch('/change-department', validateBody, (req, res) => {
const { id, department } = req.body;
});
router.use((req, res) => {
return sendResponse(res, httpStatus.NOT_FOUND, "Path not found");
router.get('/get_decrypt_keys', (req, res) => {
});
module.exports = router;
+28 -1
View File
@@ -1,4 +1,31 @@
General overview
-
For the use of the admin console, start with 'npm run start' the UI frontend.
To see/modify the initial login credentials check the /config/config.json file.
WITHOUT SETTING THE CONFIG FILE FOR THE SERVER (API KEY) THE SERVER WILL NOT START!
WITHOUT SETTING THE CONFIG FILE, THE SERVER WILL NOT WORK AS INTENDED!
(click on the 'Reset UC' when first configuring the server to bring the
UC at the initial state)
Because the client and CEO apps are dependent on the Central Unit (UC), we strongly
suggest you to make backup at a regular interval in case of hardware failing you will
only have to deploy the server with the configurations already existing.
Important aspects:
-
- The starting/stopping of the UC is handled by this 'Admin Console'. The server will not
work if the server which servers as the 'Admin Console' is not running.
- The main server will run at the port 5000, so make sure to configure your routers to
enable routing to the server;
- The client and CEO backup system of their applications will need access at ports 5001 and 5002,
so also make sure to open those ports as well;
- If the CEO wants to change the email address linked to the account, his password will be changed
to a low-risk password, so make sure after any release to specify the account information;
Closing thoughts
-
We really hope that you will enjoy our application and for any issues, feel free to contact us at
'username@ourdomain.com' for troubleshooting and other problems.
+23 -1
View File
@@ -7,6 +7,26 @@ const cookieParser = require('cookie-parser');
const checkSession = require('./middlewares/checkSession');
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
function shutdown() {
console.log('Received kill signal, shutting down gracefully.');
const serverProcess = app.get('serverProcess');
if (serverProcess && typeof serverProcess.kill === 'function') {
console.log('Shutting down server process...');
serverProcess.kill(); // Send SIGTERM to server process
serverProcess.on('exit', () => {
console.log('Server process terminated.');
process.exit(0); // Exit main process cleanly
});
} else {
console.log('No server process or cannot be killed, exiting.');
process.exit(0); // Exit main process cleanly
}
}
const app = express()
app.set('serverProcess', null);
@@ -18,7 +38,7 @@ app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'public', 'html')));
app.use('/css', express.static(path.join(__dirname, 'public', 'css')));
app.use('/js', express.static(path.join(__dirname, 'public', 'js')));
app.use('/images', express.static(path.join(__dirname, 'public', 'assets')));
app.use('/images', express.static(path.join(__dirname, 'public', 'images')));
app.use(checkSession);
@@ -31,3 +51,5 @@ const PORT = process.env.PORT || 80;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
+2
View File
@@ -15,6 +15,8 @@ body, html {
}
.container {
opacity: 0;
display: flex;
flex-direction: row;
justify-content: space-evenly;
+2 -71
View File
@@ -13,78 +13,9 @@ body, html {
background-repeat: no-repeat;
}
.overlay {
display: none;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.85);
z-index: 2;
cursor: pointer;
}
.ceo-validation-form {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
padding: 20px;
background: #1B1A55;
border-radius: 10px;
cursor: default;
}
.ceo-validation-form h2 {
text-align: center;
color: white;
}
.form-actions {
text-align: center;
padding-top: 20px;
}
.form-actions button {
padding: 10px 20px;
margin: 0 10px;
border: none;
border-radius: 5px;
cursor: pointer;
}
input {
text-align: center;
color: white;
font-weight: bold;
font-size: 1.2rem;
width: 80%;
padding: 1rem;
margin: 0.7rem;
background-color: #1B1A55;
border-radius: 10px;
}
#back_button {
background-color: #f44336;
width: 35%;
height: auto;
color: white;
}
#submit_button {
background-color: #4CAF50;
width: 35%;
height: auto;
color: white;
}
.container {
opacity: 0;
display: flex;
flex-direction: row;
justify-content: space-evenly;
+25
View File
@@ -0,0 +1,25 @@
.fade-in {
animation: fadeInAnimation 0.5s ease-in forwards;
}
.fade-out {
animation: fadeOutAnimation 0.5s ease-out forwards;
}
@keyframes fadeInAnimation {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeOutAnimation {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
+19 -1
View File
@@ -5,10 +5,28 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/images/hard-disk.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="/css/login.css">
<link rel="stylesheet" type="text/css" href="/css/transition.css">
<script src="/js/login.js"></script>
<script>
function fadeIn() {
document.querySelector('.container').classList.remove('fade-out');
document.querySelector('.container').classList.add('fade-in');
}
function fadeOut(destination) {
const container = document.querySelector('.container');
container.classList.remove('fade-in');
container.classList.add('fade-out');
container.addEventListener('animationend', () => {
window.location.href = destination;
});
}
</script>
<title>Login</title>
</head>
<body>
<body onload="fadeIn()">
<div class="container">
<div class="header">
<h1>DO WE KNOW</h1>
+21 -2
View File
@@ -5,12 +5,28 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/images/hard-disk.ico" type="image/x-icon">
<link rel="stylesheet" href="/css/main_menu.css">
<link rel="stylesheet" href="/css/transition.css">
<script src="/js/main_menu.js"></script>
<script>
function fadeIn() {
document.querySelector('.container').classList.remove('fade-out');
document.querySelector('.container').classList.add('fade-in');
}
function fadeOut() {
document.querySelector('.container').classList.remove('fade-in');
document.querySelector('.container').classList.add('fade-out');
// Redirect after fadeOut animation completes (adjust timeout as needed)
setTimeout(() => {
window.location.href = 'your_redirect_url.html';
}, 500);
}
</script>
<title>Main Page</title>
</head>
<body>
<body onload="fadeIn()">
<div class="container">
<div class="main_block">
<div class="header">
@@ -28,7 +44,10 @@
</div>
<div class="content_buttons">
<button id="ceo_info" name="menu_button">Get CEO Info</button>
<button id="set_conf" name="menu_button">Set Server Conf File</button>
<button id="users_info" name="menu_button">Get All Users</button>
</div>
<div class="content_buttons">
<button id="reset_uc" name="menu_button">Reset UC</button>
</div>
</div>
<div class="footer">

Before

Width:  |  Height:  |  Size: 298 KiB

After

Width:  |  Height:  |  Size: 298 KiB

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

+1 -1
View File
@@ -26,7 +26,7 @@ document.addEventListener('DOMContentLoaded', function () {
document.cookie = `email=${result.user.email};`;
document.cookie = `name=${result.user.name};`;
window.location.href = '/';
fadeOut('/');
} else {
alert('Incorrect credentials. Please try again.');
}
+9 -4
View File
@@ -28,7 +28,8 @@ document.addEventListener('DOMContentLoaded', function() {
const startUcButton = document.getElementById('start_uc');
const stopUcButton = document.getElementById('stop_uc');
const ceoInfoButton = document.getElementById('ceo_info');
const setConfButton = document.getElementById('set_conf');
const usersInfoButton = document.getElementById('users_info');
const resetUcButton = document.getElementById('reset_uc');
const logoutButton = document.getElementById('logout');
startUcButton.addEventListener('click', () => {
@@ -63,11 +64,15 @@ document.addEventListener('DOMContentLoaded', function() {
console.log('Ceo Info Button pressed');
})
setConfButton.addEventListener('click', () => {
usersInfoButton.addEventListener('click', () => {
console.log('Users Info Button pressed');
})
resetUcButton.addEventListener('click', () => {
console.log('Set Conf Button pressed');
})
logoutButton.addEventListener('click', function() {
logoutButton.addEventListener('click', () => {
console.log('Logout button was clicked');
const deleteCookie = (name) => {
@@ -77,6 +82,6 @@ document.addEventListener('DOMContentLoaded', function() {
deleteCookie('email');
deleteCookie('name');
window.location.href = '/login';
fadeOut('/login');
});
});
+9 -17
View File
@@ -1,11 +1,9 @@
const express = require('express');
const path = require('path');
const fs = require('fs').promises;
const { fork } = require('child_process');
const httpStatus = require('../helpers/status_codes');
const router = express.Router();
const backendPath = path.join(__dirname, '..', '..', 'backend');
router.get('/', (req, res) => {
@@ -15,44 +13,38 @@ router.get('/', (req, res) => {
router.post('/server', async (req, res) => {
const action = req.body.action;
let serverProcess = req.app.get('serverProcess');
const configFilePath = path.join(backendPath, 'config', 'config.json');
try {
await fs.access(configFilePath);
} catch (error) {
if(action === 'start'){
return res.status(httpStatus.BAD_REQUEST).send({ message: 'Config file is missing, cannot start server.' });
}
}
if (action === 'start') {
if (serverProcess) {
return res.status(httpStatus.BAD_REQUEST).send({message: 'Server is already running.'});
}
const serverPath = path.join(backendPath, 'src', 'app.js');
serverProcess = fork(serverPath);
serverProcess = fork(serverPath, [], { stdio: 'inherit' }); // Add stdio: 'inherit' to see child process logs in the parent process console
serverProcess.on('message', (msg) => {
console.log('Message from server:', msg);
});
serverProcess.on('close', (code) => {
console.log(`Server process exited with code ${code}`);
serverProcess.on('close', (code, signal) => {
console.log(`Server process exited with code ${code} and signal ${signal}`);
req.app.set('serverProcess', null);
});
req.app.set('serverProcess', serverProcess); // Update the serverProcess in app
req.app.set('serverProcess', serverProcess); // Update the serverProcess in the app
res.status(httpStatus.OK).json({message: 'Server starting...'});
} else if (action === 'stop') {
if (!serverProcess) {
return res.status(httpStatus.BAD_REQUEST).json({message: 'Server is not running.'});
}
serverProcess.kill();
req.app.set('serverProcess', null);
serverProcess.kill('SIGTERM');
res.status(httpStatus.OK).json({message: 'Server stopping...'});
} else {
res.status(httpStatus.BAD_REQUEST).json({message: 'Invalid action.'});
}
});
router.post('ceo_credentials', (req, res) => {
});
module.exports = router;
-7
View File
@@ -1,7 +0,0 @@
{
"id": "ffc90aca-bb31-4641-9a43-30b343924e8a",
"name": "Andrei",
"email": "a@c.com",
"password": "andreicerbu",
"department": "16183532-3090-4e24-8def-aa29f316c1c5"
}