diff --git a/CEO/.idea/jsLibraryMappings.xml b/CEO/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..d23208f --- /dev/null +++ b/CEO/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CEO/backupSchemes.json b/CEO/backupSchemes.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/CEO/backupSchemes.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/CEO/helpers/http_status.js b/CEO/helpers/http_status.js new file mode 100644 index 0000000..b03796e --- /dev/null +++ b/CEO/helpers/http_status.js @@ -0,0 +1,36 @@ +const httpStatus = { + // Informational + CONTINUE: 100, + SWITCHING_PROTOCOLS: 101, + PROCESSING: 102, + + // Success + OK: 200, + CREATED: 201, + ACCEPTED: 202, + NO_CONTENT: 204, + + // Redirection + MOVED_PERMANENTLY: 301, + FOUND: 302, + SEE_OTHER: 303, + NOT_MODIFIED: 304, + TEMPORARY_REDIRECT: 307, + + // Client Error + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + FORBIDDEN: 403, + NOT_FOUND: 404, + METHOD_NOT_ALLOWED: 405, + CONFLICT: 409, + GONE: 410, + UNSUPPORTED_MEDIA_TYPE: 415, + + // Server Error + INTERNAL_SERVER_ERROR: 500, + NOT_IMPLEMENTED: 501, + SERVICE_UNAVAILABLE: 503 +}; + +module.exports = { httpStatus }; diff --git a/CEO/helpers/lock_mechanism.js b/CEO/helpers/lock_mechanism.js new file mode 100644 index 0000000..2d55fc5 --- /dev/null +++ b/CEO/helpers/lock_mechanism.js @@ -0,0 +1,33 @@ +const lockfile = require('proper-lockfile'); + +async function lockFile(filePath) { + try { + await lockfile.lock(filePath, { + realpath: false, + retries: { + retries: 10, // Number of retries + factor: 2, // The exponential factor + minTimeout: 1000, // The number of milliseconds before starting the first retry + maxTimeout: 5000, // The maximum number of milliseconds between two retries + randomize: true, // Randomizes the timeouts by multiplying with a factor between 1 to 2 + } + }); + console.log(`File locked: ${filePath}`); + } catch (error) { + console.error(`Error locking file ${filePath}: ${error.message}`); + throw error; // Propagate the error if unable to lock after retries + } +} + +async function unlockFile(filePath) { + try { + await lockfile.unlock(filePath); + console.log(`File unlocked: ${filePath}`); + } catch (error) { + console.error(`Error unlocking file ${filePath}: ${error.message}`); + // Decide whether to throw the error or not, based on your error handling strategy + throw error; + } +} + +module.exports = {lockFile, unlockFile} \ No newline at end of file diff --git a/CEO/ipConfig.json b/CEO/ipConfig.json new file mode 100644 index 0000000..adbb4e0 --- /dev/null +++ b/CEO/ipConfig.json @@ -0,0 +1 @@ +/ =9 uDH[ \ No newline at end of file diff --git a/CEO/iv.key b/CEO/iv.key new file mode 100644 index 0000000..a8859e3 --- /dev/null +++ b/CEO/iv.key @@ -0,0 +1 @@ +DZb /@ \ No newline at end of file diff --git a/CEO/jobs/backup.js b/CEO/jobs/backup.js new file mode 100644 index 0000000..3a0c8ae --- /dev/null +++ b/CEO/jobs/backup.js @@ -0,0 +1,365 @@ +const fsPromises = require('fs').promises; // Ensure you use the promise-based API +const fs = require('fs'); +const path = require('path'); +const { decryptFileInPlace, encryptFileInPlace, encryptFileWithKey, decryptFileWithKey} = require("../src/main/aes_encrypt"); +const {lockFile, unlockFile} = require("../helpers/lock_mechanism"); + +async function decryptAndGetServerIP(filePath) { + try { + await lockFile(filePath); + await decryptFileInPlace(filePath); + + const fileContent = await fs.promises.readFile(filePath, 'utf8'); + const jsonData = JSON.parse(fileContent); + const serverIP = jsonData.ip; + + await encryptFileInPlace(filePath) + await unlockFile(filePath); + return serverIP; + } catch (error) { + console.error('An error occurred:', error); + return null; + } +} + +function extractFilePaths(directoryStructure) { + let paths = []; + const traverse = (dir, currentPath) => { + Object.keys(dir).forEach(key => { + if (key === 'files') { + dir[key].forEach(file => paths.push(path.join(currentPath, file))); + } else { + traverse(dir[key], path.join(currentPath, key)); + } + }); + }; + traverse(directoryStructure, ''); + return paths; +} + +async function fetchFiles(ip, filePaths) { + let results = {}; + + for (const filePath of filePaths) { + const url = `http://${ip}:3000/file_path`; + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ filePath }) + }); + + if (!response.ok) throw new Error(`HTTP status ${response.status}`); + + const arrayBuffer = await response.arrayBuffer(); // Fetch the response as an ArrayBuffer + results[filePath] = Buffer.from(arrayBuffer); + } catch (error) { + console.error(`Error fetching ${filePath}:`, error); + results[filePath] = null; + } + } + + return results; +} + +async function removeDirectory(directoryPath) { + try { + // Check if the directory exists + const stats = await fsPromises.stat(directoryPath); + if (!stats.isDirectory()) { + console.log('The specified path is not a directory.'); + return; + } + + // Read all the contents of the directory + const files = await fsPromises.readdir(directoryPath); + + // Loop through each file/directory and delete them + for (const file of files) { + const currentPath = path.join(directoryPath, file); + const currentStats = await fsPromises.stat(currentPath); + + if (currentStats.isDirectory()) { + // Recursive call for directories + await removeDirectory(currentPath); + } else { + await lockFile(currentPath); + await unlockFile(currentPath); + await fsPromises.unlink(currentPath); + } + } + + // Finally, delete the directory itself + await fsPromises.rmdir(directoryPath); + //console.log(`Directory removed: ${directoryPath}`); + } catch (error) { + console.error(`Error removing directory: ${error.message}`); + } +} + +async function decryptUserSystemConfig() { + const configPath = path.join(__dirname, '..', 'usersInSystem.json'); + await lockFile(configPath); + await decryptFileInPlace(configPath); +} + +async function encryptUserSystemConfig(){ + const configPath = path.join(__dirname, '..', 'usersInSystem.json'); + await encryptFileInPlace(configPath); + await unlockFile(configPath); +} + +async function processBackup() { + const usersConfigPath = path.join(__dirname, '..', 'usersInSystem.json'); + const backupConfigPath = path.join(__dirname, '..', 'backupSchemes.json'); + const baseBackupDirPath = path.join(__dirname, '..', 'backup_directories'); + if(!fs.existsSync(backupConfigPath) || !fs.existsSync(usersConfigPath)){ + console.log('Can\'t start backup system: necessary files missing!'); + return; + } + + await decryptUserSystemConfig(); + + try { + // Read and parse the users configuration + const usersData = await fsPromises.readFile(usersConfigPath, 'utf8'); + const usersConfig = JSON.parse(usersData); + + // Read and parse the backup configuration + const backupData = await fsPromises.readFile(backupConfigPath, 'utf8'); + const jsonData = JSON.parse(backupData); + + for (const key in jsonData) { + const userBackupDir = path.join(baseBackupDirPath, key); + await removeDirectory(userBackupDir); + + const node = jsonData[key]; + const directoryStructure = JSON.parse(node.directoryStructure); + const filePaths = extractFilePaths(directoryStructure); + const files = await fetchFiles(node.ip, filePaths); + + await fsPromises.mkdir(userBackupDir, { recursive: true }); + + // Find the encryption key for the user's department + let encryptionKey = ''; + for (const dept in usersConfig) { + if (usersConfig[dept].users.includes(key)) { + encryptionKey = usersConfig[dept].key; + break; + } + } + + for (const file in files) { + if (files[file]) { + const fullPath = path.join(userBackupDir, file); + await fsPromises.mkdir(path.dirname(fullPath), { recursive: true }); + await fsPromises.writeFile(fullPath, files[file]); // Write the binary data + + // Encrypt the file in place after writing it + if (encryptionKey) { + await encryptFileWithKey(fullPath, encryptionKey); + } + } + } + } + } catch (error) { + console.error('Failed to read or process the JSON file:', error); + } + + await encryptUserSystemConfig(); +} + +async function getAllFilePaths(dirPath) { + let filePaths = []; + async function recurse(currentPath) { + const entries = await fsPromises.readdir(currentPath, { withFileTypes: true }); + // Create promises for each entry and process them in parallel + const entryPromises = entries.map(async (entry) => { + const resolvedPath = path.join(currentPath, entry.name); + if (entry.isDirectory()) { + // If it's a directory, recurse into it + await recurse(resolvedPath); + } else { + // If it's a file, add it to the file paths array + filePaths.push(resolvedPath); + } + }); + // Wait for all promises to complete + await Promise.all(entryPromises); + } + await recurse(dirPath); + return filePaths; +} + +async function decryptBackupFilesToDirectory(destinationDir) { + await decryptUserSystemConfig(); + const usersConfigPath = path.join(__dirname, '..', 'usersInSystem.json'); + + if(!fs.existsSync(usersConfigPath)){ + return false; + } + + const usersConfig = JSON.parse(await fsPromises.readFile(usersConfigPath, 'utf8')); + const sourceDir = path.join(__dirname, '..', 'backup_directories'); + + await removeDirectory(destinationDir); + + try { + for (const department in usersConfig) { + const { key, users } = usersConfig[department]; + for (const userId of users) { + const userDir = path.join(sourceDir, userId); + try { + const files = await getAllFilePaths(userDir); + for (const filePath of files) { + const stats = await fsPromises.stat(filePath); + if (stats.isFile()) { + const relativePath = path.relative(userDir, filePath); + + // Construct the destination file path + const destinationFilePath = path.join(destinationDir, userId, relativePath); + await fsPromises.mkdir(path.dirname(destinationFilePath), { recursive: true }); + await lockFile(filePath); + + // Decrypt the file in its original location + await decryptFileWithKey(filePath, key); + + // Copy the decrypted file to the destination directory + await fsPromises.copyFile(filePath, destinationFilePath); + console.log(`Decrypted file copied to: ${destinationFilePath}`); + + // Re-encrypt the file in its original location (optional) + await encryptFileWithKey(filePath, key); + await unlockFile(filePath); + } + } + } catch (error) { + console.error(`Error processing files for user ${userId}: ${error.message}`); + await encryptUserSystemConfig(); + return false; + } + } + } + } catch (error) { + console.error(`Error processing decryption: ${error.message}`); + await encryptUserSystemConfig(); + return false; + } + + await encryptUserSystemConfig(); + return true; +} + +async function decryptDepartmentFilesToDirectory() { + const dirDepartmentPath = path.join(__dirname, '..', 'dirDepartment.json'); + const loginDataPath = path.join(__dirname, '..', 'loginData.json'); + const usersConfigPath = path.join(__dirname, '..', 'usersInSystem.json'); + const sourceDir = path.join(__dirname, '..', 'backup_directories'); + + if (!fs.existsSync(usersConfigPath) || !fs.existsSync(loginDataPath) || !fs.existsSync(dirDepartmentPath)) { + console.error("Missing one or more essential configuration files."); + return false; + } + + await lockFile(loginDataPath); + await lockFile(usersConfigPath); + + await decryptFileInPlace(loginDataPath); + await decryptFileInPlace(usersConfigPath); + + const { path: destinationDir } = JSON.parse(await fsPromises.readFile(dirDepartmentPath, 'utf8')); + const { department } = JSON.parse(await fsPromises.readFile(loginDataPath, 'utf8')); + const usersConfig = JSON.parse(await fsPromises.readFile(usersConfigPath, 'utf8')); + + // Check if department exists in users configuration + if (!usersConfig[department]) { + console.error(`No configuration found for department: ${department}`); + return false; + } + + const { key, users } = usersConfig[department]; + + await encryptFileInPlace(loginDataPath); + await encryptFileInPlace(usersConfigPath); + + await unlockFile(loginDataPath); + await unlockFile(usersConfigPath); + + await removeDirectory(destinationDir); + + for (const userId of users) { + const userDir = path.join(sourceDir, userId); + + try { + const files = await getAllFilePaths(userDir); + console.log(`Processing files for user ${userId}: ${files}`); + + for (const filePath of files) { + const stats = await fsPromises.stat(filePath); + if (stats.isFile()) { + const relativePath = path.relative(userDir, filePath); + + // Construct the destination file path + const destinationFilePath = path.join(destinationDir, userId, relativePath); + await fsPromises.mkdir(path.dirname(destinationFilePath), { recursive: true }); + + // Decrypt the file in its original location + await decryptFileWithKey(filePath, key); + + // Copy the decrypted file to the destination directory + await fsPromises.copyFile(filePath, destinationFilePath); + console.log(`Decrypted file copied to: ${destinationFilePath}`); + + // Re-encrypt the file in its original location (optional) + await encryptFileWithKey(filePath, key); + } + } + } catch (error) { + console.error(`Error processing files for user ${userId}: ${error.message}`); + return false; + } + } + + console.log("All files processed successfully."); + return true; +} + +let serverIp = null; +decryptAndGetServerIP(path.join(__dirname, '..', 'ipConfig.json')).then(ip => { + serverIp = ip; +}); + +const timeoutInterval = 7 * 60 * 1000 //minutes * seconds * miliseconds + +setInterval(async () => { + await decryptDepartmentFilesToDirectory(); +}, timeoutInterval); + +console.log('backup.js process started!'); +process.on('SIGINT', async () => { + console.log('Shutdown signal received in \'backup.js\'. Cleaning up...'); + + try { + console.log('Performing cleanup tasks...'); + console.log('Cleanup completed successfully.'); + } catch (error) { + console.error('An error occurred during cleanup:', error); + } finally { + console.log('Process terminated'); + process.exit(0); + } +}); + +process.on('message', async (message) => { + if (message.type === 'startBackup') { + console.log('BackupProcess: Received command to start backup.'); + await processBackup() + } + if(message.type === 'decryptBackup'){ + console.log(`BackupProcess: Received command to decrypt backup to ${message.decryptDestPath}`); + console.log(message.decryptDestPath); + await decryptBackupFilesToDirectory(message.decryptDestPath); + } +}); \ No newline at end of file diff --git a/CEO/jobs/external_endpoints.js b/CEO/jobs/external_endpoints.js new file mode 100644 index 0000000..8b12e01 --- /dev/null +++ b/CEO/jobs/external_endpoints.js @@ -0,0 +1,187 @@ +const express = require('express'); +const fs = require('fs'); +const multer = require('multer'); +const fsExtra = require('fs-extra'); +const path = require('path'); +const Joi = require('joi'); +const { httpStatus } = require('../helpers/http_status'); +const {lockFile, unlockFile} = require("../helpers/lock_mechanism"); + +const app = express(); +app.use(express.json()); +app.use('/share_file', express.raw({ type: 'application/octet-stream', limit: 'Infinity' })); + +const server = require('http').createServer(app); +const connections = []; + +// Store active connections to close them on shutdown +server.on('connection', (conn) => { + connections.push(conn); + conn.on('close', () => { + connections.splice(connections.indexOf(conn), 1); + }); +}); + +const filePathSchema = Joi.object({ + filePath: Joi.string().required() +}); + +async function getBaseDirectory() { + const filePath = path.join(__dirname, '..', 'dirBackup.json'); + try { + const data = await fs.promises.readFile(filePath, 'utf8'); + const jsonData = JSON.parse(data); + return jsonData.path; + } catch (error) { + console.error('Error reading the base directory:', error); + throw error; + } +} + +app.post('/file_path', async (req, res) => { + const { error, value } = filePathSchema.validate(req.body); + if (error) { + return res.status(httpStatus.BAD_REQUEST).json({ + message: 'Invalid input', + data: error.details + }); + } + + try { + const basePath = await getBaseDirectory(); + let { filePath } = value; + const baseDirName = path.basename(basePath); + if (filePath.startsWith(baseDirName + path.sep)) { + filePath = filePath.slice(baseDirName.length + 1); + } + const fullPath = path.resolve(basePath, filePath); + + fs.stat(fullPath, (err, stats) => { + if (err) { + if (err.code === 'ENOENT') { + return res.status(httpStatus.NOT_FOUND).json({ + message: 'File not found' + }); + } + return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ + message: 'Error accessing the file' + }); + } + + if (!stats.isFile()) { + return res.status(httpStatus.BAD_REQUEST).json({ + message: 'Path is not a file' + }); + } + + // Stream the file as binary data + res.writeHead(httpStatus.OK, { + 'Content-Type': 'application/octet-stream', + 'Content-Disposition': `attachment; filename="${path.basename(fullPath)}"` + }); + const fileStream = fs.createReadStream(fullPath); + fileStream.pipe(res); + }); + } catch (error) { + return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ + message: 'Error processing request' + }); + } +}); + + + +async function addFilePathToJson(filePath) { + const jsonFilePath = path.join(__dirname, '..', 'filesReceived.json'); + + try { + let data = { receivedFiles: [] }; + if (await fsExtra.pathExists(jsonFilePath)) { + await lockFile(jsonFilePath); + data = await fsExtra.readJson(jsonFilePath); + }else{ + await lockFile(jsonFilePath); + } + + data.receivedFiles.push(filePath); + + await fsExtra.writeJson(jsonFilePath, data, { spaces: 2 }); + await unlockFile(jsonFilePath); + console.log('File path added successfully.'); + } catch (error) { + console.error('Error updating JSON file:', error); + throw error; // Rethrow the error for further handling if necessary + } +} + +const shareFileSchema = Joi.object({ + idUser: Joi.string().required(), + nameOfFile: Joi.string().required(), + sizeOfFile: Joi.number().required() +}); + +const storage = multer.diskStorage({ + destination: function (req, file, cb) { + const { error, value } = shareFileSchema.validate(req.body); + if (error) { + cb(error, undefined); + return; + } + + const baseDir = path.join(__dirname, '..', 'uploads', value.idUser); // Temp storage location + fsExtra.ensureDirSync(baseDir); + cb(null, baseDir); + }, + filename: function (req, file, cb) { + cb(null, req.body.nameOfFile); // Using directly assuming it has been validated already + } +}); + +const upload = multer({ storage: storage }).single('file'); + +app.post('/upload', (req, res) => { + upload(req, res, async (err) => { + if (err instanceof multer.MulterError) { + res.status(httpStatus.INTERNAL_SERVER_ERROR).send(`Multer error: ${err.message}`); + return; + } else if (err) { + res.status(httpStatus.BAD_REQUEST).send(`Validation error: ${err.message}`); + return; + } + + try { + const { idUser, nameOfFile } = req.body; + + const dirConfig = await fsExtra.readJson(path.join(__dirname, '..', 'dirShare.json')); + const baseDir = dirConfig.path; + + const finalPath = path.join(baseDir, idUser, nameOfFile); + + await fsExtra.move(req.file.path, finalPath, { overwrite: true }); + await addFilePathToJson(finalPath); + + res.status(httpStatus.OK).json({message: 'Upload successful.'}) + } catch (error) { + console.error('Failed to move file:', error); + res.status(httpStatus.INTERNAL_SERVER_ERROR).json({message: 'Server error while moving the file.'}); + } + }); +}); + + + + +console.log('external_endpoints.json started.'); +const PORT = process.env.PORT || 3000; +server.listen(PORT, () => console.log(`Server running on port ${PORT}`)); + +process.on('SIGINT', () => { + console.log('SIGINT signal received. Shutting down gracefully.'); + server.close(() => { + console.log('Server closed.'); + // Ensure all connections are closed + connections.forEach(conn => conn.end()); + setTimeout(() => connections.forEach(conn => conn.destroy()), 5000); + process.exit(0); + }); +}); diff --git a/CEO/jobs/fetcher.js b/CEO/jobs/fetcher.js new file mode 100644 index 0000000..9d91cc2 --- /dev/null +++ b/CEO/jobs/fetcher.js @@ -0,0 +1,234 @@ +const fsPromises = require('fs').promises; +const fs = require('fs'); +const path = require('path'); +const ip = require('ip'); +const {encryptFileInPlace, decryptFileInPlace} = require('../src/main/aes_encrypt'); +const {lockFile, unlockFile} = require("../helpers/lock_mechanism"); + +async function fetchBackupSchemes(serverIp) { + // Fetching backup schemes from the server + const depResponse = await fetch(`http://${serverIp}/backup_schemes`, { + method: 'GET', + headers: {'x-api-key': 'uc_api'} + }); + const backupSchemesJson = await depResponse.json(); + const backupSchemes = backupSchemesJson['data']; + + // Specify the path where the backup schemes will be saved + const destPath = path.join(__dirname, '..', 'backupSchemes.json'); + + // Write the fetched backup schemes to the specified file + try { + await lockFile(destPath); + await fsPromises.writeFile(destPath, JSON.stringify(backupSchemes, null, 2)); // Use null, 2 for pretty formatting + await unlockFile(destPath); + console.log('Backup schemes saved successfully to', destPath); + } catch (error) { + console.error('Failed to save backup schemes:', error); + } +} + +async function fetchUsersAndDepartments(serverIp) { + try { + console.log('se asteapta depart'); + // Fetch departments + const depResponse = await fetch(`http://${serverIp}/users/departments`, { + method: 'GET', + headers: {'x-api-key': 'uc_api'} + }); + + const departmentsJson = await depResponse.json(); + const departments = departmentsJson['data']; + + console.log('se asteapta users') + // Fetch users + const userResponse = await fetch(`http://${serverIp}/users`, { + method: 'GET', + headers: {'x-api-key': 'uc_api'} + }); + const usersJson = await userResponse.json(); + const users = usersJson['data']; + + // Combine the JSON data + const combinedData = Object.values(departments).reduce((acc, {name, key}) => { + acc[name] = { + key, + users: users.filter(user => user.department === name).map(user => user.id) + }; + return acc; + }, {}); + + // Write combined data to a file + const filePath = path.join(__dirname, '..', 'usersInSystem.json'); + if(!fs.existsSync(filePath)){ + await fsPromises.writeFile(filePath, ''); + } + await lockFile(filePath); + await fsPromises.writeFile(filePath, JSON.stringify(combinedData, null, 2), 'utf8'); + + await encryptFileInPlace(filePath); + await unlockFile(filePath); + console.log('Data has been encrypted and saved.'); + } catch (error) { + console.error('Failed to fetch or process data:', error); + } +} + +async function decryptAndGetServerIP(filePath) { + try { + await lockFile(filePath); + await decryptFileInPlace(filePath); + + const fileContent = await fsPromises.readFile(filePath, 'utf8'); + const jsonData = JSON.parse(fileContent); + const serverIP = jsonData.ip; + + await encryptFileInPlace(filePath) + await unlockFile(filePath); + return serverIP; + } catch (error) { + console.error('An error occurred:', error); + return null; + } +} + +async function getDirectoryStructure(dirPath) { + const baseName = path.basename(dirPath); + const entries = await fsPromises.readdir(dirPath, {withFileTypes: true}); + const result = {}; + result[baseName] = {files: []}; + let totalSize = 0; + + for (let entry of entries) { + const entryPath = path.join(dirPath, entry.name); + if (entry.isDirectory()) { + // Recursively get structure for subdirectories + const {structure, size: subSize} = await getDirectoryStructure(entryPath); + result[baseName][entry.name] = structure[Object.keys(structure)[0]]; // Object.keys to fetch the first key name + totalSize += subSize; + } else { + // Add file name to the 'files' array and calculate total size + const stats = await fsPromises.stat(entryPath); + result[baseName].files.push(entry.name); + totalSize += stats.size; + } + } + return {structure: result, size: totalSize}; +} + +async function createBackupScheme(serverIp, structure, size) { + let filePath = path.join(__dirname, '..', 'loginData.json'); + await lockFile(filePath); + await decryptFileInPlace(filePath); + + const userContent = await fsPromises.readFile(filePath, 'utf8'); + const userJson = JSON.parse(userContent); + + await encryptFileInPlace(filePath) + await unlockFile(filePath); + + // Find the IP address of the machine + const machineIP = ip.address(); + + // Prepare the data to be sent + const dataToSend = { + id: userJson.id, + ip: machineIP, + directoryStructure: JSON.stringify(structure), + totalSize: size + }; + + const response = await fetch(`http://${serverIp}/backup_schemes`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'uc_api' + }, + body: JSON.stringify(dataToSend) + }); + + if (response.ok) { + console.log("Backup dir structure submitted.") + } else { + const data = await response.json() + console.log(data.message); + } +} + +async function watchDirectoryChanges(serverIp) { + async function readBackupConfig(filePath) { + await lockFile(filePath); + const fileContent = await fsPromises.readFile(filePath, 'utf8'); + await unlockFile(filePath); + return JSON.parse(fileContent); + } + + async function updateBackupConfig(filePath, structure, size) { + const data = { + path: filePath, + structure: structure, + size: size + }; + await lockFile(filePath); + await fsPromises.writeFile(path.join(__dirname, '..', 'dirBackup.json'), JSON.stringify(data, null, 2)); + await unlockFile(filePath); + } + + const configPath = path.join(__dirname, '..', 'dirBackup.json'); + if(!fs.existsSync(configPath)){ + console.log('backupDir file doesn\'t exists'); + return; + } + const backupConfig = await readBackupConfig(configPath); + const storedStructure = backupConfig.structure; + + const {structure: currentStructure, size: currentSize} = await getDirectoryStructure(backupConfig.path); + + if (JSON.stringify(storedStructure) !== JSON.stringify(currentStructure)) { + console.log("Directory structure has changed. Updating backup..."); + await createBackupScheme(serverIp, currentStructure, currentSize); + + await updateBackupConfig(backupConfig.path, currentStructure, currentSize); + console.log('FetcherProcess: start backup.'); + process.send({type: 'startBackup'}); + } else { + console.log("No changes detected in the directory structure."); + } +} + +let serverIp = null +decryptAndGetServerIP(path.join(__dirname, '..', 'ipConfig.json')).then(ip => { + serverIp = ip; +}); + +const timeoutInterval = 6 * 60 * 1000 //minutes * seconds * miliseconds + +setInterval(() => { + fetchUsersAndDepartments(serverIp); +}, timeoutInterval); + +setInterval(() => { + watchDirectoryChanges(serverIp); +}, timeoutInterval); + +setInterval(() => { + fetchBackupSchemes(serverIp); +}, timeoutInterval); + + +console.log("fetcher.js process started."); + +process.on('SIGINT', async () => { + console.log('Shutdown signal received in \'fetcher.js\'. Cleaning up...'); + + try { + console.log('Performing cleanup tasks...'); + console.log('Cleanup completed successfully.'); + } catch (error) { + console.error('An error occurred during cleanup:', error); + } finally { + console.log('Process terminated'); + process.exit(0); + } +}); + diff --git a/CEO/jobs/send_file.js b/CEO/jobs/send_file.js new file mode 100644 index 0000000..b37520f --- /dev/null +++ b/CEO/jobs/send_file.js @@ -0,0 +1,90 @@ +const fs = require('fs'); +const path = require('path'); +const axios = require('axios'); +const FormData = require('form-data'); +const {lockFile, unlockFile} = require("../helpers/lock_mechanism"); +const {decryptFileInPlace, encryptFileInPlace} = require("../src/main/aes_encrypt"); + +async function decryptAndGetServerIP(filePath) { + try { + await lockFile(filePath); + await decryptFileInPlace(filePath); + + const fileContent = await fs.promises.readFile(filePath, 'utf8'); + const jsonData = JSON.parse(fileContent); + const serverIP = jsonData.ip; + + await encryptFileInPlace(filePath) + await unlockFile(filePath); + return serverIP; + } catch (error) { + console.error('An error occurred:', error); + return null; + } +} + +async function extractId() { + try { + // Path to the JSON file + const jsonFilePath = path.join(__dirname, '..', 'loginData.json'); + + // Read and parse the JSON file + const jsonData = await fs.promises.readFile(jsonFilePath, 'utf-8'); + const { id } = JSON.parse(jsonData); + + return id; + } catch (error) { + console.error('Failed to read file or parse data:', error); + throw error; + } +} + +async function uploadFile(filePath, serverIp) { + try { + const idUser = await extractId(); // Fetch ID from JSON + const nameOfFile = path.basename(filePath); // Extract the file name from the path + + // Ensure the file exists and get its size + if (!fs.existsSync(filePath)) { + console.error("File does not exist: " + filePath); + process.exit(1); + } + const fileSizeInBytes = fs.statSync(filePath).size; + + const form = new FormData(); + form.append('idUser', idUser); + form.append('nameOfFile', nameOfFile); + form.append('sizeOfFile', fileSizeInBytes); + form.append('file', fs.createReadStream(filePath)); + + const response = await axios.post(`http://${serverIp}/upload`, form, { + headers: { + ...form.getHeaders(), + }, + }); + + console.log('File uploaded successfully:', response.data); + process.exit(0); // Exit with status 0 (success) + } catch (error) { + console.error('Error uploading file:', error.message); + process.exit(1); // Exit with status 1 (error) + } +} + +// Command-line argument for file path +if (process.argv.length < 3) { + console.log('Usage: node + - Setup Completion + Sending file
-

SENDING THE FILE!

+

DECRYPTING BACKUP!

PlEASE WAIT

Description of GIF diff --git a/CEO/src/renderer/html/ip_submit.html b/CEO/src/renderer/html/ip_submit.html new file mode 100644 index 0000000..e4c7f0d --- /dev/null +++ b/CEO/src/renderer/html/ip_submit.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + IP Submit + + +
+
+
+

IP Config

+
+
+
+ +
+ +
+
+ + diff --git a/CEO/src/renderer/html/login.html b/CEO/src/renderer/html/login.html index 13753ba..335c50d 100644 --- a/CEO/src/renderer/html/login.html +++ b/CEO/src/renderer/html/login.html @@ -4,12 +4,15 @@ + + + Login - +

DO WE KNOW

@@ -25,7 +28,6 @@
diff --git a/CEO/src/renderer/html/main_menu.html b/CEO/src/renderer/html/main_menu.html index f4e4e94..282b56d 100644 --- a/CEO/src/renderer/html/main_menu.html +++ b/CEO/src/renderer/html/main_menu.html @@ -4,15 +4,17 @@ + + Main Page - +
@@ -26,15 +28,19 @@
+ + +
+
-
-
- + +
+ - +