SENDING THE FILE!
PlEASE WAIT
-
+
diff --git a/UC/backend/src/app.js b/UC/backend/src/app.js index c33ac73..911ae8f 100644 --- a/UC/backend/src/app.js +++ b/UC/backend/src/app.js @@ -6,7 +6,12 @@ const app = express(); const port = process.env.PORT || 5000; app.use(cors({ - allowedHeaders: ['Authorization', 'Content-Type'] // Add 'Authorization' to the list of allowed headers + origin: '*', // Allow all origins + methods: 'GET,POST,PUT,DELETE,PATCH', // Allow all methods + allowedHeaders: '*', // Allow all headers + credentials: true, // Enable credentials + preflightContinue: false, + optionsSuccessStatus: 204 // Some legacy browsers (IE11, various SmartTVs) choke on 204 })); app.use(json()); diff --git a/UC/backend/src/db/backup_schemes.json b/UC/backend/src/db/backup_schemes.json index 9e26dfe..2cb1939 100644 --- a/UC/backend/src/db/backup_schemes.json +++ b/UC/backend/src/db/backup_schemes.json @@ -1 +1,7 @@ -{} \ No newline at end of file +{ + "4659e71f-9bb4-4902-97d8-097efa138333": { + "ip": "192.168.0.195", + "directoryStructure": "{\"to_backup\":{\"files\":[\"fisier_random.txt\"],\"alte fis\":{\"files\":[\"alt fisier.txt\",\"fisier now.txt\",\"New Microsoft Excel Worksheet.xlsx\"]}}}", + "totalSize": 6197 + } +} \ No newline at end of file diff --git a/UC/backend/src/models/dirStructureModel.js b/UC/backend/src/models/dirStructureModel.js index 2c2d686..08625f7 100644 --- a/UC/backend/src/models/dirStructureModel.js +++ b/UC/backend/src/models/dirStructureModel.js @@ -10,13 +10,12 @@ const dirStructureModel = Joi.object({ 'string.ip': 'The IP address "{{#value}}" is not valid.', 'any.required': 'IP address is required.' }), - dir_config: Joi.string().required().messages({ + directoryStructure: Joi.string().required().messages({ 'any.required': 'Directory configuration is required', 'string.empty': 'Directory configuration must not be empty' }), - total_space: Joi.number().positive().required().messages({ + totalSize: Joi.number().required().messages({ 'any.required': 'Total space is required', - 'number.positive': 'Total space must be a positive number', 'number.base': 'Total space must be a number' }) }); diff --git a/UC/backend/src/routes/backup_schemes.js b/UC/backend/src/routes/backup_schemes.js index 526314e..73cf8e5 100644 --- a/UC/backend/src/routes/backup_schemes.js +++ b/UC/backend/src/routes/backup_schemes.js @@ -11,6 +11,7 @@ function validateBody(req, res, next) { } if(validationSchema !== undefined){ + console.log(req.body); const {error} = validationSchema.validate(req.body); if(error){ const errorMessage = error.details.map(detail => detail.message).join(', '); @@ -22,15 +23,16 @@ function validateBody(req, res, next) { router.patch('/', validateBody, (req, res) => { const backupSchemesDB = req.app.get('backupSchemesDB'); - const { id, ip, backup_schema, size } = req.body; + const { id, ip, directoryStructure, totalSize } = req.body; let backupSchemesJson = backupSchemesDB.readFile(); backupSchemesJson[id] = { ip: ip, - backup_schema: backup_schema, - size: size + directoryStructure: directoryStructure, + totalSize: totalSize } + backupSchemesDB.writeFile(backupSchemesJson); return res.status(httpStatus.OK).json({message: 'Backup schema updated'}); }); diff --git a/UC/backend/src/routes/users.js b/UC/backend/src/routes/users.js index 9e7e617..354d7c8 100644 --- a/UC/backend/src/routes/users.js +++ b/UC/backend/src/routes/users.js @@ -74,12 +74,12 @@ router.post('/login', validateBody, (req, res) => { const hashedPassword = crypto.createHash('sha256').update(password).digest('hex'); if(usersDB.readFile().length === 0){ - return res.status(httpStatus.NOT_FOUND).json({message: 'Invalid credentials.'}); + return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({message: 'Internal server error.'}); } if(usersDB.findIndexByKeyValueInArray('email', email) !== usersDB.findIndexByKeyValueInArray('password', hashedPassword)){ - return res.status(httpStatus.NOT_FOUND).json({message: 'Invalid credentials.'}); + return res.status(httpStatus.UNAUTHORIZED).json({message: 'Invalid credentials.'}); } const userIndex = usersDB.findIndexByKeyValueInArray('email', email); diff --git a/User/.idea/.name b/User/.idea/.name new file mode 100644 index 0000000..d3296ce --- /dev/null +++ b/User/.idea/.name @@ -0,0 +1 @@ +login.css \ No newline at end of file diff --git a/User/backupSchemes.json b/User/backupSchemes.json new file mode 100644 index 0000000..2cb1939 --- /dev/null +++ b/User/backupSchemes.json @@ -0,0 +1,7 @@ +{ + "4659e71f-9bb4-4902-97d8-097efa138333": { + "ip": "192.168.0.195", + "directoryStructure": "{\"to_backup\":{\"files\":[\"fisier_random.txt\"],\"alte fis\":{\"files\":[\"alt fisier.txt\",\"fisier now.txt\",\"New Microsoft Excel Worksheet.xlsx\"]}}}", + "totalSize": 6197 + } +} \ No newline at end of file diff --git a/User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/alte fis/New Microsoft Excel Worksheet.xlsx b/User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/alte fis/New Microsoft Excel Worksheet.xlsx new file mode 100644 index 0000000..a12bb7f Binary files /dev/null and b/User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/alte fis/New Microsoft Excel Worksheet.xlsx differ diff --git a/User/jobs/decrypt_files.js b/User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/alte fis/alt fisier.txt similarity index 100% rename from User/jobs/decrypt_files.js rename to User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/alte fis/alt fisier.txt diff --git a/User/jobs/receiver.js b/User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/alte fis/fisier now.txt similarity index 100% rename from User/jobs/receiver.js rename to User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/alte fis/fisier now.txt diff --git a/User/jobs/send_files.js b/User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/fisier_random.txt similarity index 100% rename from User/jobs/send_files.js rename to User/backup_directories/4659e71f-9bb4-4902-97d8-097efa138333/to_backup/fisier_random.txt diff --git a/User/dirBackup.json b/User/dirBackup.json new file mode 100644 index 0000000..580e37a --- /dev/null +++ b/User/dirBackup.json @@ -0,0 +1,18 @@ +{ + "path": "C:\\Users\\Andrei Cerbu\\Documents\\to_backup", + "structure": { + "to_backup": { + "files": [ + "fisier_random.txt" + ], + "alte fis": { + "files": [ + "alt fisier.txt", + "fisier now.txt", + "New Microsoft Excel Worksheet.xlsx" + ] + } + } + }, + "size": 6197 +} \ No newline at end of file diff --git a/User/helpers/http_status.js b/User/helpers/http_status.js new file mode 100644 index 0000000..b03796e --- /dev/null +++ b/User/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/User/helpers/lock_mechanism.js b/User/helpers/lock_mechanism.js new file mode 100644 index 0000000..2d55fc5 --- /dev/null +++ b/User/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/User/ipConfig.json b/User/ipConfig.json index 81d3eb7..51f378a 100644 --- a/User/ipConfig.json +++ b/User/ipConfig.json @@ -1 +1 @@ -ԗ۰s_(kK:Fn]C$ \ No newline at end of file +);'Fw-`e/dK \ No newline at end of file diff --git a/User/iv.key b/User/iv.key index dbf14ed..0d7c548 100644 --- a/User/iv.key +++ b/User/iv.key @@ -1 +1 @@ -李a%l:I \ No newline at end of file +i60@SLi \ No newline at end of file diff --git a/User/jobs/backup.js b/User/jobs/backup.js index e69de29..cc342c3 100644 --- a/User/jobs/backup.js +++ b/User/jobs/backup.js @@ -0,0 +1,217 @@ +const fs = require('fs').promises; // Ensure you use the promise-based API +const path = require('path'); +const { decryptFileInPlace, encryptFileInPlace, encryptFileWithKey, decryptFileWithKey} = require("../src/main/aes_encrypt"); +const {lockFile, unlockFile} = require("../helpers/lock_mechanism"); + +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 fs.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 fs.readdir(directoryPath); + + // Loop through each file/directory and delete them + for (const file of files) { + const currentPath = path.join(directoryPath, file); + const currentStats = await fs.stat(currentPath); + + if (currentStats.isDirectory()) { + // Recursive call for directories + await removeDirectory(currentPath); + } else { + // Delete file + await fs.unlink(currentPath); + } + } + + // Finally, delete the directory itself + await fs.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 unlockFile(configPath); + await encryptFileInPlace(configPath); +} + +async function processBackup() { + await decryptUserSystemConfig(); + const usersConfigPath = path.join(__dirname, '..', 'usersInSystem.json'); + const backupConfigPath = path.join(__dirname, '..', 'backupSchemes.json'); + const baseBackupDirPath = path.join(__dirname, '..', 'backup_directories'); + + try { + // Read and parse the users configuration + const usersData = await fs.readFile(usersConfigPath, 'utf8'); + const usersConfig = JSON.parse(usersData); + + // Read and parse the backup configuration + const backupData = await fs.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 fs.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 fs.mkdir(path.dirname(fullPath), { recursive: true }); + await fs.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 fs.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 decryptUserFilesToDirectory(destinationDir) { + await decryptUserSystemConfig(); // Ensure user config is decrypted + const usersConfigPath = path.join(__dirname, '..', 'usersInSystem.json'); + const usersConfig = JSON.parse(await fs.readFile(usersConfigPath, 'utf8')); + const sourceDir = path.join(__dirname, '..', 'backup_directories'); + + 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 fs.stat(filePath); + if (stats.isFile()) { + // Extract the part of the file path after the userId + const relativePath = path.relative(userDir, filePath); + + // Construct the destination file path + const destinationFilePath = path.join(destinationDir, userId, relativePath); + await fs.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 fs.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}`); + } + } + } + } catch (error) { + console.error(`Error processing decryption: ${error.message}`); + } +} + + +processBackup(); +decryptUserFilesToDirectory("C:\\Users\\Andrei Cerbu\\Documents\\decrypted"); diff --git a/User/jobs/backup_endpoints.js b/User/jobs/backup_endpoints.js new file mode 100644 index 0000000..ef20e54 --- /dev/null +++ b/User/jobs/backup_endpoints.js @@ -0,0 +1,79 @@ +const express = require('express'); +const fs = require('fs'); +const path = require('path'); +const Joi = require('joi'); +const fetch = require('node-fetch'); +const { httpStatus } = require('../helpers/http_status'); + +const app = express(); +app.use(express.json()); + +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' + }); + } +}); + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); diff --git a/User/jobs/fetcher.js b/User/jobs/fetcher.js index e69de29..f390bf3 100644 --- a/User/jobs/fetcher.js +++ b/User/jobs/fetcher.js @@ -0,0 +1,209 @@ +const fs = require('fs').promises; +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 fs.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 { + // 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']; + + // 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'); + await lockFile(filePath); + await fs.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 fs.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 fs.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 fs.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 fs.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 fs.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 fs.writeFile(path.join(__dirname, '..', 'dirBackup.json'), JSON.stringify(data, null, 2)); + await unlockFile(filePath); + } + + const configPath = path.join(__dirname, '..', 'dirBackup.json'); + 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); + } else { + console.log("No changes detected in the directory structure."); + } +} + +process.on('SIGINT', () => { + console.log('Process terminated'); + process.exit(0); +}); + +let serverIp = null +decryptAndGetServerIP(path.join(__dirname, '..', 'ipConfig.json')).then(ip => { + serverIp = ip; +}); + +setInterval(() => { + fetchUsersAndDepartments(serverIp); +}, 5000); + +setInterval(() => { + watchDirectoryChanges(serverIp); +}, 5000); + +setInterval(() => { + fetchBackupSchemes(serverIp); +}, 5000); + + +console.log("Service running. Press CTRL+C to stop."); diff --git a/User/loginData.json b/User/loginData.json index 4b94fa6..9456434 100644 Binary files a/User/loginData.json and b/User/loginData.json differ diff --git a/User/package-lock.json b/User/package-lock.json index b5b9d88..1459e85 100644 --- a/User/package-lock.json +++ b/User/package-lock.json @@ -9,10 +9,16 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "body-parser": "^1.20.2", "bootstrap": "^5.3.3", + "cors": "^2.8.5", "electron": "^29.1.5", "express": "^4.19.2", + "ip": "^2.0.1", + "joi": "^17.12.3", + "node-fetch": "^3.3.2", "nodemon": "^3.1.0", + "proper-lockfile": "^4.1.2", "toastify-js": "^1.12.0" }, "devDependencies": { @@ -39,6 +45,19 @@ "global-agent": "^3.0.0" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -49,6 +68,24 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -447,6 +484,26 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -747,6 +804,28 @@ "pend": "~1.2.0" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -788,6 +867,17 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1085,6 +1175,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1131,6 +1226,18 @@ "node": ">=0.12.0" } }, + "node_modules/joi": { + "version": "17.12.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.3.tgz", + "integrity": "sha512-2RRziagf555owrm9IRVtdKynOBeITiDpuZqIpgwqXShPncPKNiRQoiGsl/T8SQdq+8ugRzH2LqY67irr2y/d+g==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -1291,6 +1398,41 @@ "node": ">= 0.6" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/nodemon": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", @@ -1365,6 +1507,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -1455,6 +1605,16 @@ "node": ">=0.4.0" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1570,6 +1730,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "engines": { + "node": ">= 4" + } + }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -1742,6 +1910,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -1943,6 +2116,14 @@ "node": ">=8.0.0" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/User/package.json b/User/package.json index 195543c..2ab3b89 100644 --- a/User/package.json +++ b/User/package.json @@ -11,10 +11,16 @@ "author": "Cerbu Andrei - Mihnea", "license": "ISC", "dependencies": { + "body-parser": "^1.20.2", "bootstrap": "^5.3.3", + "cors": "^2.8.5", "electron": "^29.1.5", "express": "^4.19.2", + "ip": "^2.0.1", + "joi": "^17.12.3", + "node-fetch": "^3.3.2", "nodemon": "^3.1.0", + "proper-lockfile": "^4.1.2", "toastify-js": "^1.12.0" }, "devDependencies": { diff --git a/User/secret.key b/User/secret.key index c209bc5..5931e61 100644 --- a/User/secret.key +++ b/User/secret.key @@ -1 +1 @@ - 4~n`'TnoC:%:x \ No newline at end of file +`]'hźV8nkAVf7K \ No newline at end of file diff --git a/User/src/main/aes_encrypt.js b/User/src/main/aes_encrypt.js index aa33b9e..aaa5abe 100644 --- a/User/src/main/aes_encrypt.js +++ b/User/src/main/aes_encrypt.js @@ -6,7 +6,7 @@ async function readKeyFromFile(filePath) { try { await fs.promises.access(filePath, fs.constants.F_OK); return fs.promises.readFile(filePath); // Use asynchronous readFile - } catch(error) { + } catch (error) { console.error('Error reading key file:', error); return null; } @@ -70,7 +70,46 @@ async function decryptFileInPlace(filePath) { }); } +function cryptForKey(content, key, decrypt = false) { + const algorithm = 'aes-256-ctr'; + const secretKey = crypto.createHash('sha256').update(String(key)).digest('base64').substr(0, 32); + let cipher; + + if (decrypt) { + cipher = crypto.createDecipheriv(algorithm, secretKey, Buffer.alloc(16, 0)); // Using a zeroed IV for CTR + } else { + cipher = crypto.createCipheriv(algorithm, secretKey, Buffer.alloc(16, 0)); + } + + return Buffer.concat([cipher.update(content), cipher.final()]); +} + +// Encrypts a file in place with a given key +async function encryptFileWithKey(filePath, key) { + try { + const fileContent = await fs.promises.readFile(filePath); + const encryptedContent = cryptForKey(fileContent, key, false); + await fs.promises.writeFile(filePath, encryptedContent); + console.log(`File encrypted successfully: ${filePath}`); + } catch (error) { + console.error(`Error encrypting file: ${error.message}`); + } +} + +async function decryptFileWithKey(filePath, key) { + try { + const fileContent = await fs.promises.readFile(filePath); + const decryptedContent = cryptForKey(fileContent, key, true); + await fs.promises.writeFile(filePath, decryptedContent); + console.log(`File decrypted successfully: ${filePath}`); + } catch (error) { + console.error(`Error decrypting file: ${error.message}`); + } +} + module.exports = { encryptFileInPlace, - decryptFileInPlace + decryptFileInPlace, + encryptFileWithKey, + decryptFileWithKey, } diff --git a/User/src/main/main.js b/User/src/main/main.js index c77857f..d7cefd9 100644 --- a/User/src/main/main.js +++ b/User/src/main/main.js @@ -1,10 +1,11 @@ -const { app, BrowserWindow, screen, ipcMain, dialog } = require('electron'); +const {app, BrowserWindow, screen, ipcMain, dialog} = require('electron'); const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); -const { fork } = require('child_process'); +const {fork} = require('child_process'); const {encryptFileInPlace, decryptFileInPlace} = require('./aes_encrypt'); +const {lockFile, unlockFile} = require("../../helpers/lock_mechanism"); const isMac = process.platform === 'darwin'; let html_page = undefined; @@ -117,8 +118,8 @@ function showAlert(message) { if (alertWindow === undefined) { const title = 'Alert'; const mainScreen = screen.getPrimaryDisplay(); - const { width, height } = mainScreen.size; - createAlertWindow(title, width/4, height/4); + const {width, height} = mainScreen.size; + createAlertWindow(title, width / 4, height / 4); } alertWindow.webContents.once('dom-ready', () => { @@ -129,11 +130,11 @@ function showAlert(message) { app.whenReady().then(() => { const title = "Application"; const mainScreen = screen.getPrimaryDisplay(); - const { width, height } = mainScreen.size; + const {width, height} = mainScreen.size; createMainWindow(title, width / 1.5, height / 1.5); app.on('activate', () => { - if(BrowserWindow.getAllWindows().length === 0){ + if (BrowserWindow.getAllWindows().length === 0) { createMainWindow(title, width, height); } }); @@ -152,7 +153,7 @@ app.on('before-quit', () => { }); app.on('window-all-closed', () => { - if(!isMac){ + if (!isMac) { app.quit(); } }); @@ -160,40 +161,45 @@ app.on('window-all-closed', () => { ipcMain.handle('write-file', async (event, fileName, content) => { try { let filePath = path.join(__dirname, '..', '..', fileName); + await lockFile(filePath); await fs.promises.writeFile(filePath, content); await encryptFileInPlace(filePath); + await unlockFile(filePath) console.log(`File successfully written to ${filePath}`); - return { success: true }; + return {success: true}; } catch (error) { console.error('Failed to write file:', error); - return { success: false, error: error.message }; + return {success: false, error: error.message}; } }); ipcMain.handle('delete-file', async (event, fileName) => { try { let filePath = path.join(__dirname, '..', '..', fileName); - console.log(filePath); + await fs.promises.unlink(filePath); + console.log(`File ${filePath} successfully deleted`); - return { success: true }; + return {success: true}; } catch (error) { console.error('Failed to delete file:', error); - return { success: false, error: error.message }; + return {success: false, error: error.message}; } }); ipcMain.handle('read-file', async (event, fileName) => { try { let filePath = path.join(__dirname, '..', '..', fileName); + await lockFile(filePath); await decryptFileInPlace(filePath); const content = await fs.promises.readFile(filePath, 'utf-8'); await encryptFileInPlace(filePath); - return { success: true, content }; + await unlockFile(filePath); + return {success: true, content}; } catch (error) { console.error('Error reading file:', error); - return { success: false, error: error.message }; + return {success: false, error: error.message}; } }); @@ -229,7 +235,7 @@ ipcMain.handle('open-backup-dir-dialog', async (event) => { return true; } catch (error) { console.error('Error opening file dialog:', error); - return { error: error.message }; + return {error: error.message}; } }); @@ -253,7 +259,7 @@ ipcMain.handle('check-file-exists', async (event, fileName) => { } }); -ipcMain.handle('show-alert', async (event, message) =>{ +ipcMain.handle('show-alert', async (event, message) => { showAlert(message); }); @@ -265,10 +271,9 @@ ipcMain.on('close-alert-window', () => { }); //External processes - ipcMain.handle('start-fetcher', async (event, args) => { if (fetcherProcess === null) { - fetcherProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'fetcher.js'), args, { silent: false }); + fetcherProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'fetcher.js'), args, {silent: false}); fetcherProcess.on('exit', () => { fetcherProcess = null; // Optionally, notify the renderer process that the fetcher has finished @@ -276,46 +281,3 @@ ipcMain.handle('start-fetcher', async (event, args) => { } return true; // Indicate that the operation has started }); - -// Handler to start the backup process -ipcMain.handle('start-backup', async (event, args) => { - if (backupProcess === null) { // Should this be a unique variable for backupProcess instead? - backupProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'backup.js'), args, { silent: false }); - backupProcess.on('exit', () => { - backupProcess = null; - // Optionally, notify the renderer process that the backup has finished - }); - } - return true; // Indicate that the operation has started -}); - -// Handler to start the decrypt-files process -ipcMain.handle('start-decrypt-files', async (event, args) => { - const decryptFilesProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'decrypt_files.js'), args, { silent: false }); - decryptFilesProcess.on('exit', () => { - event.sender.send('decrypt-files-finished', true); // Notify renderer process - }); - return true; // Indicate that the operation has started -}); - -// Handler to start the send_files process -ipcMain.handle('start-send_files', async (event, args) => { - // This seems to duplicate the 'start-decrypt-files' process; assuming a different script is intended - const sendFilesProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'send_files.js'), args, { silent: false }); - sendFilesProcess.on('exit', () => { - event.sender.send('send-files-finished', true); // Notify renderer process - }); - return true; // Indicate that the operation has started -}); - -// Handler to start the receiver process -ipcMain.handle('start-receiver', async (event, args) => { - if (receiverProcess === null) { - receiverProcess = fork(path.join(__dirname, '..', '..', 'jobs', 'receiver.js'), args, { silent: false }); - receiverProcess.on('exit', () => { - receiverProcess = null; - // Optionally, notify the renderer process that the receiver has finished - }); - } - return true; // Indicate that the operation has started -}); diff --git a/User/src/main/preload.js b/User/src/main/preload.js index d259984..42e12c8 100644 --- a/User/src/main/preload.js +++ b/User/src/main/preload.js @@ -1,4 +1,4 @@ -const { contextBridge, ipcRenderer } = require('electron'); +const {contextBridge, ipcRenderer} = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { writeFile: (fileName, content) => ipcRenderer.invoke('write-file', fileName, content), diff --git a/User/src/renderer/css/alert_modal.css b/User/src/renderer/css/alert_modal.css index 7b2604c..3ee2c08 100644 --- a/User/src/renderer/css/alert_modal.css +++ b/User/src/renderer/css/alert_modal.css @@ -22,12 +22,12 @@ body, html { min-height: 100vh; } -h1{ +h1 { margin: 0; padding: 0; } -.main_component{ +.main_component { display: flex; flex-wrap: wrap; flex-direction: column; @@ -43,7 +43,7 @@ h1{ height: 80vh; } -.header{ +.header { color: #1B1A55; font-size: 0.8rem; text-align: center; @@ -61,7 +61,7 @@ button { cursor: pointer; } -button:hover{ +button:hover { filter: brightness(85%); } diff --git a/User/src/renderer/css/change_department.css b/User/src/renderer/css/change_department.css index d34a81d..bf02450 100644 --- a/User/src/renderer/css/change_department.css +++ b/User/src/renderer/css/change_department.css @@ -33,7 +33,7 @@ body, html { width: 25%; } -.department-form-title{ +.department-form-title { margin-bottom: 5vh; color: #FFFFFF; text-align: center; @@ -46,11 +46,11 @@ body, html { font-size: 5vh; } -.department-form-title hr{ +.department-form-title hr { width: 65%; } -.department-form-content{ +.department-form-content { display: flex; margin-left: 2rem; flex-direction: column; @@ -60,11 +60,11 @@ body, html { font-weight: bold; } -.department-form-content input{ +.department-form-content input { margin: 0.7rem; } -.department-form-footer{ +.department-form-footer { margin-top: 7vh; display: flex; flex-wrap: wrap; @@ -84,7 +84,7 @@ button { cursor: pointer; } -button:hover{ +button:hover { filter: brightness(85%); } diff --git a/User/src/renderer/css/ip_submit.css b/User/src/renderer/css/ip_submit.css index 1b40e46..09fbc06 100644 --- a/User/src/renderer/css/ip_submit.css +++ b/User/src/renderer/css/ip_submit.css @@ -34,23 +34,23 @@ body, html { width: 25%; } -.ip-form-title{ +.ip-form-title { margin: 0 0 5vh 0; text-align: center; } -.ip-form-title h2{ +.ip-form-title h2 { color: #FFFFFF; font-size: 5vh; margin: 0; padding: 0; } -.ip-form hr{ +.ip-form hr { width: 40%; } -.ip-form-content{ +.ip-form-content { display: flex; flex-wrap: wrap; justify-content: center; @@ -79,11 +79,11 @@ button { cursor: pointer; } -button:hover{ +button:hover { filter: brightness(85%); } -.ip-form-footer{ +.ip-form-footer { margin-top: 3vh; display: flex; flex-wrap: wrap; diff --git a/User/src/renderer/css/login.css b/User/src/renderer/css/login.css index 00530ae..2e9d6d5 100644 --- a/User/src/renderer/css/login.css +++ b/User/src/renderer/css/login.css @@ -16,7 +16,7 @@ body, html { .container { opacity: 0; - + display: flex; flex-direction: row; justify-content: space-evenly; @@ -24,7 +24,7 @@ body, html { min-height: 100vh; } -.header{ +.header { color: #1B1A55; font-size: 4vh; text-transform: uppercase; @@ -41,23 +41,23 @@ body, html { width: 25%; } -.login-form-title{ +.login-form-title { margin: 0 0 5vh 0; text-align: center; } -.login-form-title h2{ +.login-form-title h2 { color: #FFFFFF; font-size: 5vh; margin: 0; padding: 0; } -.login-form hr{ +.login-form hr { width: 40%; } -.login-form-content{ +.login-form-content { display: flex; flex-wrap: wrap; justify-content: center; @@ -86,11 +86,11 @@ button { cursor: pointer; } -button:hover{ +button:hover { filter: brightness(85%); } -.login-form-footer{ +.login-form-footer { margin-top: 3vh; display: flex; flex-wrap: wrap; diff --git a/User/src/renderer/css/main_menu.css b/User/src/renderer/css/main_menu.css index 54b52d9..60df4ae 100644 --- a/User/src/renderer/css/main_menu.css +++ b/User/src/renderer/css/main_menu.css @@ -95,7 +95,7 @@ input { text-align: center; } -.left_block{ +.left_block { display: flex; flex-direction: column; background-color: #535C91; @@ -106,29 +106,29 @@ input { color: white; } -.left_block_top{ +.left_block_top { display: flex; flex-direction: row; justify-content: space-between; text-align: left; } -.left_block_top h1{ +.left_block_top h1 { padding: 0; margin: 0; } -.left_block_top img{ +.left_block_top img { margin: 0 3vw 0 5vw; width: 8vw; height: 8vw; } -.left_block_content{ +.left_block_content { margin: 2rem 0 2rem 0; } -.left_block_buttons{ +.left_block_buttons { display: flex; flex-direction: row; align-content: center; @@ -136,7 +136,7 @@ input { justify-content: center; } -button{ +button { margin: 0 1rem 0 1rem; width: 15vw; height: 10vh; @@ -155,17 +155,17 @@ button{ transition: background-color 0.3s ease; } -button:hover{ +button:hover { filter: brightness(85%); } -.left_block_footer{ +.left_block_footer { display: flex; align-items: center; justify-content: end; } -.right_block{ +.right_block { display: flex; flex-direction: column; background-color: #535C91; @@ -176,7 +176,7 @@ button:hover{ color: white; } -.notifications{ +.notifications { display: flex; margin: 1rem 2rem 2rem 3rem; flex-direction: column; @@ -208,7 +208,7 @@ button:hover{ background: #555; /* Updated handle color on hover */ } -button[name="logout"]{ +button[name="logout"] { margin: 0; padding: 0; width: 10vw; @@ -216,17 +216,17 @@ button[name="logout"]{ background-color: #F44336; } -button[name="alert"]{ +button[name="alert"] { margin: 0.7rem; background-color: #F44336; } -button[name="notification"]{ +button[name="notification"] { margin: 0.7rem; background-color: #23BDEE; } -button[name="menu_button"]{ +button[name="menu_button"] { margin: 0.7rem; background-color: #1B1A55; } \ No newline at end of file diff --git a/User/src/renderer/css/profile.css b/User/src/renderer/css/profile.css index 3197e2e..5bbe960 100644 --- a/User/src/renderer/css/profile.css +++ b/User/src/renderer/css/profile.css @@ -25,7 +25,7 @@ body, html { text-align: center; /* Center the text for all child elements */ } -.header{ +.header { color: #1B1A55; font-size: 4vh; text-transform: uppercase; @@ -42,11 +42,11 @@ body, html { width: 25%; } -.profile-form-title{ +.profile-form-title { margin-bottom: 5vh; } -.profile-form hr{ +.profile-form hr { width: 40%; } @@ -58,13 +58,13 @@ body, html { font-size: 5vh; } -.profile-form-content{ +.profile-form-content { display: flex; flex-wrap: wrap; justify-content: center; } -.profile-form-footer{ +.profile-form-footer { margin-top: 7vh; display: flex; flex-direction: row; @@ -94,7 +94,7 @@ button { cursor: pointer; } -button:hover{ +button:hover { filter: brightness(85%); } diff --git a/User/src/renderer/css/sending_file_confirmation.css b/User/src/renderer/css/sending_file_confirmation.css index ea7177f..db2f969 100644 --- a/User/src/renderer/css/sending_file_confirmation.css +++ b/User/src/renderer/css/sending_file_confirmation.css @@ -25,7 +25,7 @@ body, html { text-align: center; /* Center the text for all child elements */ } -.header{ +.header { color: #1B1A55; font-size: 4vh; text-transform: uppercase; @@ -33,7 +33,7 @@ body, html { margin-bottom: 10rem; } -img{ - width:20%; +img { + width: 20%; height: 20%; } diff --git a/User/src/renderer/css/share_file.css b/User/src/renderer/css/share_file.css index 128b495..671485b 100644 --- a/User/src/renderer/css/share_file.css +++ b/User/src/renderer/css/share_file.css @@ -25,16 +25,16 @@ body, html { text-align: center; } -h1, h2{ +h1, h2 { padding: 0; margin: 0; } -h2{ +h2 { font-size: 1rem; } -.left_block{ +.left_block { display: flex; flex-direction: column; background-color: #535C91; @@ -45,25 +45,25 @@ h2{ color: white; } -.left_block_top{ +.left_block_top { display: flex; flex-direction: row; justify-content: space-between; } -.left_block_top_left{ +.left_block_top_left { display: flex; flex-direction: column; justify-content: start; text-align: left; } -.left_block_top_left hr{ +.left_block_top_left hr { margin: 0 0 1rem 0; width: 45%; } -.left_block_top_right{ +.left_block_top_right { width: 10vw; display: flex; @@ -77,14 +77,14 @@ h2{ border-radius: 1rem; } -.left_block_content{ +.left_block_content { display: flex; justify-content: start; align-items: center; margin: 2rem 0 2rem 0; } -.left_block_buttons{ +.left_block_buttons { display: flex; flex-direction: row; align-content: center; @@ -92,7 +92,7 @@ h2{ justify-content: center; } -button{ +button { margin: 0 1rem 0 1rem; width: 10vw; height: 5vh; @@ -111,17 +111,17 @@ button{ transition: background-color 0.3s ease; } -button:hover{ +button:hover { filter: brightness(85%); } -.left_block_footer{ +.left_block_footer { display: flex; align-items: center; justify-content: end; } -.right_block{ +.right_block { display: flex; flex-direction: column; background-color: #535C91; @@ -132,7 +132,7 @@ button:hover{ color: white; } -.choose_user_form_title{ +.choose_user_form_title { display: flex; flex-wrap: wrap; flex-direction: column; @@ -140,11 +140,11 @@ button:hover{ align-content: start; } -.choose_user_form_title hr{ +.choose_user_form_title hr { width: 80%; } -.choose_user_form_content{ +.choose_user_form_content { display: flex; margin: 1rem 7rem 2rem 0.5rem; flex-direction: column; @@ -176,15 +176,15 @@ button:hover{ background: #555; /* Updated handle color on hover */ } -button[name="back"]{ +button[name="back"] { background-color: #23BDEE; } -button[name="submit"]{ +button[name="submit"] { background-color: #F44336; } -button[name="select_file"]{ +button[name="select_file"] { margin: 0; width: 8vw; background-color: #1B1A55; diff --git a/User/src/renderer/css/sign_up_confirmation.css b/User/src/renderer/css/sign_up_confirmation.css index 183c9f9..dbd61b2 100644 --- a/User/src/renderer/css/sign_up_confirmation.css +++ b/User/src/renderer/css/sign_up_confirmation.css @@ -25,7 +25,7 @@ body, html { text-align: center; /* Center the text for all child elements */ } -.header{ +.header { color: #1B1A55; font-size: 4vh; text-transform: uppercase; diff --git a/User/src/renderer/css/sign_up_department.css b/User/src/renderer/css/sign_up_department.css index 009caab..6993c9c 100644 --- a/User/src/renderer/css/sign_up_department.css +++ b/User/src/renderer/css/sign_up_department.css @@ -25,7 +25,7 @@ body, html { text-align: center; } -.header{ +.header { color: #1B1A55; font-size: 4vh; text-transform: uppercase; @@ -42,7 +42,7 @@ body, html { width: 25%; } -.signup-form-title{ +.signup-form-title { margin-bottom: 5vh; } @@ -54,15 +54,15 @@ body, html { font-size: 5vh; } -.signup-form hr{ +.signup-form hr { width: 65%; } -input{ +input { margin: 0 0 1rem 0; } -.signup-form-content{ +.signup-form-content { display: flex; margin: 1rem 2rem 2rem 3rem; flex-direction: column; @@ -94,7 +94,7 @@ input{ background: #555; /* Updated handle color on hover */ } -.signup-form-footer{ +.signup-form-footer { margin-top: 5vh; display: flex; flex-wrap: wrap; @@ -114,7 +114,7 @@ button { cursor: pointer; } -button:hover{ +button:hover { filter: brightness(85%); } diff --git a/User/src/renderer/css/sing_up_profile.css b/User/src/renderer/css/sing_up_profile.css index 9fda52d..f882050 100644 --- a/User/src/renderer/css/sing_up_profile.css +++ b/User/src/renderer/css/sing_up_profile.css @@ -25,7 +25,7 @@ body, html { text-align: center; } -.header{ +.header { color: #1B1A55; font-size: 4vh; text-transform: uppercase; @@ -42,7 +42,7 @@ body, html { width: 25%; } -.signup-form-title{ +.signup-form-title { margin-bottom: 5vh; } @@ -54,7 +54,7 @@ body, html { font-size: 5vh; } -.signup-form hr{ +.signup-form hr { width: 40%; } @@ -70,7 +70,7 @@ input { border-radius: 10px; } -.signup-form-footer{ +.signup-form-footer { margin-top: 3vh; display: flex; flex-wrap: wrap; @@ -90,7 +90,7 @@ button { cursor: pointer; } -button:hover{ +button:hover { filter: brightness(85%); } diff --git a/User/src/renderer/html/alert_modal.html b/User/src/renderer/html/alert_modal.html index 340c97b..34cdd4f 100644 --- a/User/src/renderer/html/alert_modal.html +++ b/User/src/renderer/html/alert_modal.html @@ -2,20 +2,20 @@
- - + +
+
+
+