program finalizat

This commit is contained in:
andrei-mihnea-cerbu
2024-10-29 15:07:26 +02:00
parent ab1eaec413
commit 979539d3db
138 changed files with 14602 additions and 5068 deletions
+84
View File
@@ -0,0 +1,84 @@
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
import { TcpCommunicator } from "./tcp_communicator";
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
import { parentPort } from 'worker_threads';
import {ParsedMessage} from "../network/message_handler";
export class AnnouncementSender {
private applicationInfo: JsonManager;
private readonly clientPort: number;
private message: string = '';
private tcpCommunicator: TcpCommunicator | null = null;
constructor(applicationInfoPath: string, clientPort: number) {
this.applicationInfo = new JsonManager(applicationInfoPath);
this.clientPort = clientPort;
}
async start(message: string): Promise<void> {
console.log('AnnouncementWorker started.');
this.message = message;
try {
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
if (!activeUsersIp || !activeUsersIp.length) {
throw new Error('No active users found.');
}
for (const ip of activeUsersIp) {
const success = await this.sendAnnouncementToIp(ip);
if (!success) {
throw new Error(`Failed to send announcement to all users.`);
}
console.log(`Announcement sent and confirmed successfully from ${ip}`);
}
parentPort?.postMessage({ success: true, message: 'Announcement sent to all active users successfully.' });
} catch (error: any) {
console.error('Error in AnnouncementWorker:', error);
parentPort?.postMessage({ success: false, message: `A problem occurred: ${error.message}` });
}
console.log('AnnouncementWorker finished.');
}
private async sendAnnouncementToIp(ip: string): Promise<boolean> {
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
if (!await this.tcpCommunicator.connect()) {
console.log(`Skipping user at IP ${ip} - unable to connect.`);
return true;
}
// Prepare the message metadata
const metaInfo = { message: this.message };
// Send the announcement message
const messageSent = await this.tcpCommunicator.sendMessage(operationCodes.SEND_ANNOUNCEMENT, metaInfo);
if (!messageSent) {
await this.tcpCommunicator.disconnect();
return false;
}
// Await confirmation from the user
const response = await this.waitForResponse();
if (response?.operationCode === operationCodes.OK) {
await this.tcpCommunicator.disconnect();
return true;
}
// If confirmation is not OK, disconnect and halt
await this.tcpCommunicator.disconnect();
return false;
}
private async waitForResponse(): Promise<ParsedMessage | null> {
return new Promise((resolve) => {
const idResponseCheck = setInterval(async () => {
if (!this.tcpCommunicator) return null;
if (this.tcpCommunicator.hasResponseArrived()) {
clearInterval(idResponseCheck);
resolve(this.tcpCommunicator.getLastResult());
}
}, 100);
});
}
}
+155 -177
View File
@@ -1,203 +1,181 @@
import fs from 'fs';
import path from 'path';
import { FileEncryptor } from './file_encryptor'; // Assume the class is in this file
import { MemoryManager } from './memory_manager'; // Assume this handles memory-based storage
import { JsonManager } from './json_manager'; // Manages JSON-based configurations
import { TcpClient } from '../network/tcp/tcp_client' // Import your TcpClient class
import { operationCodes } from '../network/operation_codes';
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
import { TcpCommunicator } from "./tcp_communicator";
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
import { parentPort } from 'worker_threads';
import path from 'path';
import fs from 'fs';
import crypto from 'crypto';
import { ParsedMessage } from "../network/message_handler";
export class BackupManager {
private fileEncryptor: FileEncryptor | null = null;
private memoryManager: MemoryManager;
private applicationInfo: JsonManager;
export class BackupRetrievalWorker {
private userConfig: JsonManager;
private applicationInfo: JsonManager;
private readonly clientPort: number;
private readonly destinationPath: string;
private encryptionKey: Buffer | null = null;
private iv: Buffer | null = null;
private tcpCommunicator: TcpCommunicator | null = null;
constructor(userConfigPath: string, applicationInfoPath: string, memoryManagerPath: string, clientPort: number) {
this.applicationInfo = new JsonManager(applicationInfoPath);
constructor(userConfigPath: string, applicationInfoPath: string, clientPort: number, destinationPath: string) {
this.userConfig = new JsonManager(userConfigPath);
this.memoryManager = new MemoryManager(memoryManagerPath);
this.applicationInfo = new JsonManager(applicationInfoPath);
this.clientPort = clientPort;
this.destinationPath = destinationPath;
}
async start(): Promise<void> {
setInterval(async () => {
await this.initialize(); // Re-run every minute
}, 60000); // 1 minute interval
}
try {
const userInfo = await this.userConfig.readValue('user_info');
if (!userInfo || !userInfo.name) {
throw new Error('User information or name is missing.');
}
const userName = userInfo.name;
// Initialize the backup process: fetch data from the app info and memory
private async initialize(): Promise<void> {
// Initialize the encryption settings from UserConfig
const encryptionKeyData = await this.userConfig.readValue('encryption_key');
if (!encryptionKeyData || !encryptionKeyData.key || !encryptionKeyData.iv) {
console.error('Encryption key data is missing in user configuration.');
return;
}
// Create a new FileEncryptor with the retrieved key and IV
this.fileEncryptor = new FileEncryptor(encryptionKeyData.key, encryptionKeyData.iv);
// Get the name of the user
const userInfo = await this.userConfig.readValue('user_info');
if (!userInfo || !userInfo.name) {
console.error('User information is missing in user configuration.');
return;
}
const userName = userInfo.name;
// Get the backup directory information from ApplicationInfo
const backupDirectoryData = await this.applicationInfo.readValue('backupDirectory');
if (!backupDirectoryData || !backupDirectoryData.id || !backupDirectoryData.path) {
console.error('Backup directory information is missing in application info.');
return;
}
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
if (!activeUsersIp || !activeUsersIp.length) {
console.error('No active users found.');
return;
}
const backupDirectoryId = backupDirectoryData.id;
const backupDirectoryPath = backupDirectoryData.path;
// Get the file structure from MemoryManager
const directoryData = await this.memoryManager.retrieveMetaInformation(backupDirectoryId);
if (!directoryData || !directoryData.structure) {
console.error('Backup directory structure is missing in memory.');
return;
}
// Send files to the list of IPs
await this.sendFilesToUsers(directoryData.structure, userName, activeUsersIp, backupDirectoryPath);
}
// Method to encrypt a file and return the base64 string
private encryptFile(filePath: string): string {
if (!this.fileEncryptor) {
return filePath;
}
if (!fs.existsSync(filePath)) {
console.error(`File not found: ${filePath}`);
return '';
}
// Use FileEncryptor to encrypt the file and return the base64 string
return this.fileEncryptor.encryptFileToBase64(filePath);
}
// Send files to the list of users and remove successfully sent files from the list
private async sendFilesToUsers(fileStructure: { [key: string]: string }, userName: string, usersIp: string[], backupDirectoryPath: string): Promise<void> {
let unsentFiles = Object.keys(fileStructure); // Keep track of unsent files
for (const fileName of unsentFiles) {
const filePath = fileStructure[fileName];
const encryptedFileContent = this.encryptFile(filePath);
if (!encryptedFileContent) {
console.error(`Failed to encrypt file: ${fileName}`);
continue; // Skip to the next file
const encryptionData = await this.userConfig.readValue('encryption_key');
if (!encryptionData || !encryptionData.key || !encryptionData.iv) {
throw new Error('Encryption key or IV is missing.');
}
// Calculate relative file path
const relativeFilePath = path.relative(backupDirectoryPath, filePath); // Get the relative file path
this.encryptionKey = Buffer.from(encryptionData.key, 'base64');
this.iv = Buffer.from(encryptionData.iv, 'base64');
// Meta information to send
const metaInfo = {
userName, // Name of the user
relativeFilePath // Relative path to preserve the directory structure
};
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
if (!activeUsersIp || !activeUsersIp.length) {
parentPort?.postMessage({ success: false, message: 'No active users found.' });
return;
}
for (const ip of usersIp) {
const tcpClient = new TcpClient(this.clientPort);
tcpClient.openSocket(ip);
// Wait for AES key and send the file
try {
const sendSuccess = await this.waitForAesKeyAndSendFile(tcpClient, operationCodes.BACKUP_FILE, metaInfo, Buffer.from(encryptedFileContent, 'base64'));
if (sendSuccess) {
console.log(`Successfully sent file: ${fileName} to ${ip}`);
unsentFiles = unsentFiles.filter(f => f !== fileName); // Remove the file from the unsent list
tcpClient.closeSocket();
break; // Move to the next file after successful send
}
} catch (error) {
console.error(`Failed to send file: ${fileName} to ${ip}. Error: ${error}`);
tcpClient.closeSocket();
let backupSuccessful = true;
for (const ip of activeUsersIp) {
const success = await this.processBackupForIp(ip, userName);
if (!success) {
backupSuccessful = false;
parentPort?.postMessage({ success: false, message: 'Backup could not be completed due to an internal error.' });
break;
}
}
if (backupSuccessful) {
parentPort?.postMessage({ success: true, message: 'Backup successful.' });
}
} catch (error: any) {
console.error('Error in BackupRetrievalWorker:', error);
parentPort?.postMessage({ success: false, message: `Backup could not be completed due to an internal error: ${error.message}` });
}
}
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
if (!await this.tcpCommunicator.connect()) {
console.error(`Failed to connect to ${ip}`);
return false;
}
const backupExists = await this.checkIfBackupExists(userName);
if (!backupExists) {
console.log(`No backup found for user ${userName} on IP ${ip}`);
await this.tcpCommunicator.disconnect();
return true; // Skip user if no backup found, do not mark as error
}
const backupStructure = await this.requestBackupStructure(userName);
if (!backupStructure || Object.keys(backupStructure).length === 0) {
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
await this.tcpCommunicator.disconnect();
return true; // Skip user if no files found, do not mark as error
}
for (const relativeFilePath of Object.keys(backupStructure)) {
const fileRequestSuccess = await this.requestBackupFile(userName, relativeFilePath);
if (!fileRequestSuccess) {
console.error(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`);
await this.tcpCommunicator.disconnect();
return false; // Stop if any file fails to be retrieved
}
}
// If there are any unsent files, notify the parent process
if (unsentFiles.length > 0) {
parentPort?.postMessage({ success: false, message: 'Backup could not be completed for all files', unsentFiles });
} else {
parentPort?.postMessage({ success: true, message: 'Backup completed successfully' });
await this.tcpCommunicator.disconnect();
return true;
}
private async checkIfBackupExists(userName: string): Promise<boolean> {
if (!this.tcpCommunicator) return false;
const metaInfo = { name: userName };
if (!await this.tcpCommunicator.sendMessage(operationCodes.IS_BACKUP_CREATED, metaInfo)) return false;
const response = await this.waitForResponse();
return response?.metaInfo?.backupExists === true;
}
private async requestBackupStructure(userName: string): Promise<any> {
if (!this.tcpCommunicator) return false;
const metaInfo = { name: userName };
if (!await this.tcpCommunicator.sendMessage(operationCodes.GET_BACKUP_STRUCTURE, metaInfo)) return null;
const response = await this.waitForResponse();
return response?.metaInfo?.structure || null;
}
private async requestBackupFile(userName: string, relativeFilePath: string): Promise<boolean> {
if (!this.tcpCommunicator) return false;
const metaInfo = { name: userName, relativeFilePath };
if (!await this.tcpCommunicator.sendMessage(operationCodes.REQ_FILE_FROM_BACKUP, metaInfo)) return false;
const response = await this.waitForResponse();
if (response?.operationCode === operationCodes.OK && response.metaInfo && response.fileContent) {
return this.saveFile(response.metaInfo.relativeFilePath, response.fileContent.toString('base64'));
}
return false;
}
private saveFile(relativeFilePath: string, fileContent: string): boolean {
if (!this.encryptionKey || !this.iv) {
console.error('Encryption key or IV is not set.');
return false;
}
let encryptedBuffer: Buffer;
try {
encryptedBuffer = Buffer.from(fileContent, 'base64');
} catch (error) {
console.error('Error decoding base64 file content:', error);
return false;
}
let decryptedContent: Buffer;
try {
const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, this.iv);
decryptedContent = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]);
} catch (error) {
console.error('Error decrypting file:', error);
return false;
}
const fullFilePath = path.join(this.destinationPath, relativeFilePath);
try {
const dirPath = path.dirname(fullFilePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
fs.writeFileSync(fullFilePath, decryptedContent);
console.log(`File saved successfully: ${fullFilePath}`);
return true;
} catch (error: any) {
console.error(`Error saving file ${relativeFilePath}: ${error.message}`);
return false;
}
}
// Wait for AES key to be set, send the file, and wait for the response
private async waitForAesKeyAndSendFile(tcpClient: TcpClient, operationCode: string, metaInfo: { [key: string]: any }, fileContent: Buffer, checkInterval: number = 500, timeout: number = 10000): Promise<boolean> {
const startTime = Date.now();
return new Promise((resolve, reject) => {
const intervalId = setInterval(async () => {
const elapsedTime = Date.now() - startTime;
// Check if AES key is set, if timeout occurs, reject
if (!tcpClient.isAesKeySet()) {
if (elapsedTime > timeout) {
clearInterval(intervalId);
console.error('Timeout waiting for AES key.');
reject(new Error('Timeout waiting for AES key.'));
}
return; // Continue waiting for AES key
}
// AES key is set, send the message
clearInterval(intervalId);
try {
const success = await tcpClient.sendMessage(operationCode, metaInfo, fileContent);
if (!success) {
reject(new Error('Failed to send the file content.'));
return;
}
// Wait for a response after sending the message
const responseReceived = await this.waitForMessageResponse(tcpClient, timeout);
if (!responseReceived) {
reject(new Error('Timeout waiting for the message response.'));
return;
}
// Everything went fine
resolve(true);
} catch (error) {
reject(error);
}
}, checkInterval); // Check for AES key every `checkInterval`
});
}
// Method to wait for the response from the TCP client
private waitForMessageResponse(tcpClient: TcpClient, timeout: number = 10000): Promise<any> {
private async waitForResponse(): Promise<ParsedMessage | null> {
return new Promise((resolve) => {
const startTime = Date.now();
const intervalId = setInterval(() => {
const response = tcpClient.getLastResult();
const elapsedTime = Date.now() - startTime;
if (response) {
clearInterval(intervalId);
resolve(true);
} else if (elapsedTime > timeout) {
clearInterval(intervalId);
resolve(false); // No response after timeout
const idResponseCheck = setInterval(async () => {
if (!this.tcpCommunicator) return null;
if (this.tcpCommunicator.hasResponseArrived()) {
clearInterval(idResponseCheck);
resolve(this.tcpCommunicator.getLastResult());
}
}, 100);
});
+102 -154
View File
@@ -1,10 +1,11 @@
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
import { TcpClient } from '../network/tcp/tcp_client'; // Assuming this handles TCP client connections
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
import { TcpCommunicator } from "./tcp_communicator";
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
import { parentPort } from 'worker_threads';
import path from 'path';
import fs from 'fs';
import crypto from 'crypto'; // Import the crypto module for encryption and decryption
import crypto from 'crypto';
import { ParsedMessage } from "../network/message_handler"; // Import the crypto module for encryption and decryption
export class BackupRetrievalWorker {
private userConfig: JsonManager;
@@ -13,6 +14,7 @@ export class BackupRetrievalWorker {
private readonly destinationPath: string;
private encryptionKey: Buffer | null = null;
private iv: Buffer | null = null;
private tcpCommunicator: TcpCommunicator | null = null;
constructor(userConfigPath: string, applicationInfoPath: string, clientPort: number, destinationPath: string) {
this.userConfig = new JsonManager(userConfigPath);
@@ -22,203 +24,149 @@ export class BackupRetrievalWorker {
}
async start(): Promise<void> {
// Retrieve the necessary data from userConfig and applicationInfo
const userInfo = await this.userConfig.readValue('user_info');
if (!userInfo || !userInfo.name) {
console.error('User information or name is missing.');
return;
}
const userName = userInfo.name;
// Load encryption key and IV from userConfig
const encryptionData = await this.userConfig.readValue('encryption_key');
if (!encryptionData || !encryptionData.key || !encryptionData.iv) {
console.error('Encryption key or IV is missing.');
return;
}
// Convert encryption key and IV from base64 to buffer
this.encryptionKey = Buffer.from(encryptionData.key, 'base64');
this.iv = Buffer.from(encryptionData.iv, 'base64');
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
if (!activeUsersIp || !activeUsersIp.length) {
console.error('No active users found.');
return;
}
// Process each IP and request backup data
for (const ip of activeUsersIp) {
try {
const success = await this.processBackupForIp(ip, userName);
if (success) {
console.log(`Backup retrieved successfully from ${ip}`);
} else {
console.error(`Failed to retrieve backup from ${ip}`);
}
} catch (error) {
console.error(`Error processing backup from ${ip}: ${error}`);
try {
const userInfo = await this.userConfig.readValue('user_info');
if (!userInfo || !userInfo.name) {
throw new Error('User information or name is missing.');
}
}
const userName = userInfo.name;
// Notify the parent that the worker is done
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed.' });
const encryptionData = await this.userConfig.readValue('encryption_key');
if (!encryptionData || !encryptionData.key || !encryptionData.iv) {
throw new Error('Encryption key or IV is missing.');
}
this.encryptionKey = Buffer.from(encryptionData.key, 'base64');
this.iv = Buffer.from(encryptionData.iv, 'base64');
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
if (!activeUsersIp || !activeUsersIp.length) {
throw new Error('No active users found.');
}
for (const ip of activeUsersIp) {
const success = await this.processBackupForIp(ip, userName);
if (!success) {
throw new Error(`Failed to retrieve backup from ${ip}`);
}
console.log(`Backup retrieved successfully from ${ip}`);
}
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed successfully.' });
} catch (error: any) {
console.error('Error in BackupRetrievalWorker:', error);
parentPort?.postMessage({ success: false, message: `A problem occurred: ${error.message}` });
}
}
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
const tcpClient = new TcpClient(this.clientPort);
tcpClient.openSocket(ip);
try {
// Wait for the AES key to be set before continuing
const aesSet = await this.waitForAesKey(tcpClient);
if (!aesSet) {
console.error(`Timeout waiting for AES key on IP ${ip}`);
return false;
}
// Step 1: Check if a backup exists for the user on this IP
const backupExists = await this.checkIfBackupExists(tcpClient, userName);
if (!backupExists) {
console.log(`No backup found for user ${userName} on IP ${ip}`);
return false;
}
// Step 2: Request the structure of the backup directory
const backupStructure = await this.requestBackupStructure(tcpClient, userName);
if (!backupStructure || Object.keys(backupStructure).length === 0) {
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
return false;
}
// Step 3: Request and retrieve each file from the backup
for (const relativeFilePath of Object.keys(backupStructure)) {
const fileRequestSuccess = await this.requestBackupFile(tcpClient, userName, relativeFilePath);
if (!fileRequestSuccess) {
console.error(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`);
return false; // Stop if any file fails to be retrieved
}
}
return true; // All files retrieved successfully
} catch (error) {
console.error(`Error during backup processing for IP ${ip}: ${error}`);
return false;
} finally {
tcpClient.closeSocket(); // Ensure socket is closed
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
if (!await this.tcpCommunicator.connect()) {
return true;
}
const backupExists = await this.checkIfBackupExists(userName);
if (!backupExists) {
console.log(`No backup found for user ${userName} on IP ${ip}`);
await this.tcpCommunicator.disconnect();
return true;
}
const backupStructure = await this.requestBackupStructure(userName);
if (!backupStructure || Object.keys(backupStructure).length === 0) {
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
await this.tcpCommunicator.disconnect();
return true;
}
for (const relativeFilePath of Object.keys(backupStructure)) {
const fileRequestSuccess = await this.requestBackupFile(userName, relativeFilePath);
if (!fileRequestSuccess) {
await this.tcpCommunicator.disconnect();
throw new Error(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`);
}
}
await this.tcpCommunicator.disconnect();
return true;
}
// Wait for AES key to be set
private async waitForAesKey(tcpClient: TcpClient, timeout: number = 10000): Promise<boolean> {
const startTime = Date.now();
private async checkIfBackupExists(userName: string): Promise<boolean> {
if (!this.tcpCommunicator) return false;
return new Promise((resolve, reject) => {
const intervalId = setInterval(() => {
const elapsedTime = Date.now() - startTime;
if (tcpClient.isAesKeySet()) {
clearInterval(intervalId);
resolve(true); // AES key is set, we can proceed
} else if (elapsedTime > timeout) {
clearInterval(intervalId);
resolve(false); // Timeout reached, AES key not set
}
}, 500); // Check every 500ms
});
}
// Wait for a message response with timeout
private async waitForMessageResponse(tcpClient: TcpClient, timeout: number = 10000): Promise<any> {
const startTime = Date.now();
return new Promise((resolve) => {
const intervalId = setInterval(() => {
const elapsedTime = Date.now() - startTime;
const response = tcpClient.getLastResult();
if (response) {
clearInterval(intervalId);
resolve(response); // Return the response
} else if (elapsedTime > timeout) {
clearInterval(intervalId);
resolve(null); // Timeout, no response
}
}, 500); // Check every 500ms
});
}
// Check if the backup exists for the user on the remote IP
private async checkIfBackupExists(tcpClient: TcpClient, userName: string): Promise<boolean> {
const metaInfo = { name: userName };
await tcpClient.sendMessage(operationCodes.IS_BACKUP_CREATED, metaInfo);
if (!await this.tcpCommunicator.sendMessage(operationCodes.IS_BACKUP_CREATED, metaInfo)) return false;
const response = await this.waitForMessageResponse(tcpClient);
const response = await this.waitForResponse();
return response?.metaInfo?.backupExists === true;
}
// Request the backup structure from the remote IP
private async requestBackupStructure(tcpClient: TcpClient, userName: string): Promise<any> {
const metaInfo = { name: userName };
await tcpClient.sendMessage(operationCodes.GET_BACKUP_STRUCTURE, metaInfo);
private async requestBackupStructure(userName: string): Promise<any> {
if (!this.tcpCommunicator) return false;
const response = await this.waitForMessageResponse(tcpClient);
return response?.metaInfo?.structure ? response.metaInfo.structure : null;
const metaInfo = { name: userName };
if (!await this.tcpCommunicator.sendMessage(operationCodes.GET_BACKUP_STRUCTURE, metaInfo)) return null;
const response = await this.waitForResponse();
return response?.metaInfo?.structure || null;
}
// Request a file from the backup and wait for it to be decrypted and stored
private async requestBackupFile(tcpClient: TcpClient, userName: string, relativeFilePath: string): Promise<boolean> {
const metaInfo = { name: userName, relativeFilePath };
await tcpClient.sendMessage(operationCodes.REQ_FILE_FROM_BACKUP, metaInfo);
private async requestBackupFile(userName: string, relativeFilePath: string): Promise<boolean> {
if (!this.tcpCommunicator) return false;
const response = await this.waitForMessageResponse(tcpClient);
const metaInfo = { name: userName, relativeFilePath };
if (!await this.tcpCommunicator.sendMessage(operationCodes.REQ_FILE_FROM_BACKUP, metaInfo)) return false;
const response = await this.waitForResponse();
if (response?.operationCode === operationCodes.OK && response.metaInfo && response.fileContent) {
return this.saveFile(response.metaInfo.relativeFilePath, response.fileContent);
return this.saveFile(response.metaInfo.relativeFilePath, response.fileContent.toString('base64'));
}
return false;
}
// Decrypt the file content using AES-256-CBC and save the decrypted file
private saveFile(relativeFilePath: string, fileContent: string): boolean {
if (!this.encryptionKey || !this.iv) {
console.error('Encryption key or IV is not set.');
return false;
throw new Error('Encryption key or IV is not set.');
}
// Decode the base64-encoded file content into a buffer
let encryptedBuffer: Buffer;
try {
encryptedBuffer = Buffer.from(fileContent, 'base64');
} catch (error) {
console.error('Error decoding base64 file content:', error);
return false;
} catch (error: any) {
throw new Error(`Error decoding base64 file content: ${error.message}`);
}
// Decrypt the file content
let decryptedContent: Buffer;
try {
const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, this.iv);
decryptedContent = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]);
} catch (error) {
console.error('Error decrypting file:', error);
return false;
} catch (error: any) {
throw new Error(`Error decrypting file: ${error.message}`);
}
// Save the decrypted file content
const fullFilePath = path.join(this.destinationPath, relativeFilePath);
try {
const dirPath = path.dirname(fullFilePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true }); // Ensure directory exists
fs.mkdirSync(dirPath, { recursive: true });
}
fs.writeFileSync(fullFilePath, decryptedContent);
console.log(`File saved successfully: ${fullFilePath}`);
return true;
} catch (error: any) {
console.error(`Error saving file ${relativeFilePath}: ${error.message}`);
return false;
throw new Error(`Error saving file ${relativeFilePath}: ${error.message}`);
}
}
private async waitForResponse(): Promise<ParsedMessage | null> {
return new Promise((resolve) => {
const idResponseCheck = setInterval(async () => {
if (!this.tcpCommunicator) return null;
if (this.tcpCommunicator.hasResponseArrived()) {
clearInterval(idResponseCheck);
resolve(this.tcpCommunicator.getLastResult());
}
}, 100);
});
}
}
+57 -159
View File
@@ -1,9 +1,10 @@
import fs from 'fs';
import path from 'path';
import { TcpClient } from '../network/tcp/tcp_client'; // Assuming this class exists
import { TcpCommunicator } from './tcp_communicator'; // Using TcpCommunicator instead of TcpClient
import { operationCodes } from '../network/operation_codes';
import { JsonManager } from './json_manager'; // Manages JSON configurations
import { MemoryManager } from './memory_manager'; // Manages in-memory data structures
import { ParsedMessage } from "../network/message_handler";
export class DepartmentSharer {
private userConfig: JsonManager;
@@ -11,6 +12,8 @@ export class DepartmentSharer {
private memoryManager: MemoryManager; // To read the department files
private departmentDirectory: string | null;
private readonly clientPort: number;
private isBusy: boolean = false; // Busy flag to prevent simultaneous sharing
private tcpCommunicator: TcpCommunicator | null = null; // For each user connection
constructor(
userConfigPath: string,
@@ -20,7 +23,7 @@ export class DepartmentSharer {
) {
this.userConfig = new JsonManager(userConfigPath);
this.applicationInfo = new JsonManager(applicationInfoPath);
this.memoryManager = new MemoryManager(memoryManagerPath); // To retrieve files
this.memoryManager = new MemoryManager(memoryManagerPath);
this.clientPort = clientPort;
this.departmentDirectory = null;
}
@@ -28,7 +31,11 @@ export class DepartmentSharer {
// Start sharing files with the department every minute
async start(): Promise<void> {
setInterval(async () => {
await this.shareFilesWithDepartment(); // Retry every minute
if (!this.isBusy) {
this.isBusy = true;
await this.shareFilesWithDepartment();
this.isBusy = false;
}
}, 10000); // 10-second interval for testing
}
@@ -61,7 +68,6 @@ export class DepartmentSharer {
// Filter users who belong to the same department
const departmentUsers = activeUsers.filter((user: any) => user.user_info.departmentId === departmentId);
if (departmentUsers.length === 0) {
console.log('No users found in the same department.');
return;
@@ -83,15 +89,41 @@ export class DepartmentSharer {
return;
}
// Send each file to every department user
await this.shareFilesWithUsers(departmentUsers, departmentFiles.structure, userName);
// Iterate over all department users and perform the operations
for (const user of departmentUsers) {
const userIp = user.ip;
this.tcpCommunicator = new TcpCommunicator(userIp, this.clientPort);
// After sending all files, clear the department directory
await this.clearDepartmentDirectory(departmentUsers);
if(await this.tcpCommunicator.connect()) continue;
// First clear the department directory
const clearSuccess = await this.clearDepartmentDirectory();
if (clearSuccess) {
await this.sendFilesToUser(departmentFiles.structure, userName);
}
await this.tcpCommunicator.disconnect();
}
}
// Send the files to the users in the department
private async shareFilesWithUsers(users: any[], files: { [key: string]: string }, userName: string): Promise<void> {
// Clear the department directory for a user
private async clearDepartmentDirectory(): Promise<boolean> {
if(!this.tcpCommunicator) return false;
if (!await this.tcpCommunicator.sendMessage(operationCodes.CLEAR_DEPARTMENT)) return false;
const response = await this.waitForResponse();
if (!response || response.operationCode !== operationCodes.OK){
console.error('Failed to clear the department directory.');
return false;
}
return true;
}
// Send the files to a user in the department
private async sendFilesToUser(files: { [key: string]: string }, userName: string): Promise<void> {
if(!this.tcpCommunicator) return;
const unsentFiles = Object.keys(files); // Keep track of unsent files
for (const fileName of unsentFiles) {
@@ -116,163 +148,29 @@ export class DepartmentSharer {
relativeFilePath // Use the relative path to preserve directory structure
};
// Send the file to each user in the same department
for (const user of users) {
const userIp = user.ip;
const tcpClient = new TcpClient(this.clientPort);
// Send the file
if (!await this.tcpCommunicator.sendMessage(operationCodes.DEPARTMENT_FILE, metaInfo, Buffer.from(fileContent))) return;
// Open a connection to the user's IP
tcpClient.openSocket(userIp);
try {
// Wait for the AES key and send the file
const success = await this.waitForAesKeyAndSendFile(tcpClient, operationCodes.DEPARTMENT_FILE, metaInfo, fileContent);
if (success) {
console.log(`File successfully sent: ${fileName} to user ${userIp}`);
unsentFiles.splice(unsentFiles.indexOf(fileName), 1); // Remove successfully sent file
tcpClient.closeSocket(); // Close the connection after sending
break; // Move to the next file after a successful send
}
} catch (error) {
console.error(`Failed to send file: ${fileName} to IP: ${userIp}. Error: ${error}`);
tcpClient.closeSocket(); // Ensure socket is closed on error
}
const response = await this.waitForResponse();
if (!response || response.operationCode !== operationCodes.OK) {
console.error(`Failed to send file: ${fileName}`);
return;
}
}
if (unsentFiles.length > 0) {
console.log('Some files could not be sent, retrying later.');
} else {
console.log('All files shared successfully.');
unsentFiles.splice(unsentFiles.indexOf(fileName), 1);
await this.tcpCommunicator.disconnect();
}
}
// Clear the department directory for all users after sharing
private async clearDepartmentDirectory(users: any[]): Promise<void> {
for (const user of users) {
const userIp = user.ip;
const tcpClient = new TcpClient(this.clientPort);
// Open a connection to the user's IP
tcpClient.openSocket(userIp);
try {
const success = await this.waitForAesKeyAndSendClearDepartment(tcpClient, operationCodes.CLEAR_DEPARTMENT);
if (success) {
console.log(`Department directory cleared successfully for user ${userIp}`);
} else {
console.error(`Failed to clear department directory for user ${userIp}`);
}
} catch (error) {
console.error(`Error clearing department directory for user ${userIp}:`, error);
} finally {
tcpClient.closeSocket(); // Ensure the socket is closed
}
}
}
// Wait for AES key to be set, send the file, and wait for the response
private async waitForAesKeyAndSendFile(tcpClient: TcpClient, operationCode: string, metaInfo: { [key: string]: any }, fileContent: Buffer, checkInterval: number = 500, timeout: number = 10000): Promise<boolean> {
const startTime = Date.now();
return new Promise((resolve, reject) => {
const intervalId = setInterval(async () => {
const elapsedTime = Date.now() - startTime;
// Check if AES key is set, if timeout occurs, reject
if (!tcpClient.isAesKeySet()) {
if (elapsedTime > timeout) {
clearInterval(intervalId);
console.error('Timeout waiting for AES key.');
reject(new Error('Timeout waiting for AES key.'));
}
return; // Continue waiting for AES key
}
// AES key is set, send the file
clearInterval(intervalId);
try {
const success = await tcpClient.sendMessage(operationCode, metaInfo, fileContent);
if (!success) {
reject(new Error('Failed to send the file content.'));
return;
}
// Wait for a response after sending the message
const responseReceived = await this.waitForMessageResponse(tcpClient, timeout);
if (!responseReceived) {
reject(new Error('Timeout waiting for the message response.'));
return;
}
resolve(true); // Everything went fine
} catch (error) {
reject(error);
}
}, checkInterval); // Check for AES key every `checkInterval`
});
}
// Wait for AES key to be set, send the clear department message, and wait for the response
private async waitForAesKeyAndSendClearDepartment(tcpClient: TcpClient, operationCode: string, checkInterval: number = 500, timeout: number = 10000): Promise<boolean> {
const startTime = Date.now();
return new Promise((resolve, reject) => {
const intervalId = setInterval(async () => {
const elapsedTime = Date.now() - startTime;
// Check if AES key is set, if timeout occurs, reject
if (!tcpClient.isAesKeySet()) {
if (elapsedTime > timeout) {
clearInterval(intervalId);
console.error('Timeout waiting for AES key.');
reject(new Error('Timeout waiting for AES key.'));
}
return; // Continue waiting for AES key
}
// AES key is set, send the clear department message
clearInterval(intervalId);
try {
const success = await tcpClient.sendMessage(operationCode, {});
if (!success) {
reject(new Error('Failed to send the clear department operation.'));
return;
}
// Wait for a response after sending the message
const responseReceived = await this.waitForMessageResponse(tcpClient, timeout);
if (!responseReceived) {
reject(new Error('Timeout waiting for the message response.'));
return;
}
resolve(true); // Everything went fine
} catch (error) {
reject(error);
}
}, checkInterval); // Check for AES key every `checkInterval`
});
}
// Wait for the response from the TCP client
private waitForMessageResponse(tcpClient: TcpClient, timeout: number = 10000): Promise<boolean> {
private async waitForResponse(): Promise<ParsedMessage | null> {
return new Promise((resolve) => {
const startTime = Date.now();
const intervalId = setInterval(() => {
const response = tcpClient.getLastResult();
const elapsedTime = Date.now() - startTime;
if (response?.operationCode === operationCodes.OK) {
clearInterval(intervalId);
resolve(true); // Message successfully received
} else if (elapsedTime > timeout) {
clearInterval(intervalId);
resolve(false); // No response after timeout
const idResponseCheck = setInterval(async () => {
if (!this.tcpCommunicator) return null;
if (this.tcpCommunicator.hasResponseArrived()) {
clearInterval(idResponseCheck);
resolve(this.tcpCommunicator.getLastResult()); // Resolve the response or null if not available
}
}, 100); // Check every 100ms
}, 100); // Check every 100 milliseconds if the response has arrived
});
}
}
+46 -80
View File
@@ -1,9 +1,10 @@
import { QueueManager } from './queue_manager'; // Assume the QueueManager is in this path
import { TcpClient } from '../network/tcp/tcp_client'; // Import your TcpClient class
import { operationCodes } from '../network/operation_codes';
import {QueueManager} from './queue_manager';
import {TcpCommunicator} from "./tcp_communicator"; // Updated to use TcpCommunicator
import {operationCodes} from '../network/operation_codes';
import fs from "fs";
import path from "path";
import {compareFnFileItemTask, FileItemTask} from "../interfaces/file_item_task";
import {ParsedMessage} from "../network/message_handler";
interface FileSendTask {
ip: string;
@@ -14,45 +15,56 @@ interface FileSendTask {
export class FileSharer {
private queueManager: QueueManager<FileSendTask>;
private readonly clientPort: number;
private isBusy: boolean;
private tcpCommunicator: TcpCommunicator | null = null;
constructor(queueFilePath: string, clientPort: number) {
this.queueManager = new QueueManager<FileItemTask>(queueFilePath, compareFnFileItemTask);
this.clientPort = clientPort;
this.isBusy = false; // Initialize the busy flag
}
// Start processing the file queue
async start(): Promise<void> {
setInterval(async () => {
await this.processQueue(); // Process the queue at regular intervals
if (!this.isBusy) { // Check if the queue is already being processed
await this.processQueue(); // Process the queue at regular intervals
}
}, 10000); // 10 seconds interval
}
// Method to process the queue
private async processQueue(): Promise<void> {
if (this.isBusy) {
console.log("Queue is already being processed. Skipping this interval.");
return;
}
this.isBusy = true; // Set busy flag to true before starting
while (!this.queueManager.isEmpty()) {
const task = this.queueManager.peek();
if (task) {
console.log(task);
const success = await this.sendFile(task);
if (!success) {
console.error(`Failed to send file: ${task.path} to IP: ${task.ip}. Re-adding to queue.`);
this.queueManager.dequeue();
this.queueManager.enqueue(task); // Re-add to queue if failed
}
else{
} else {
console.log(`File sent successfully: ${task.path} to IP: ${task.ip}.`);
this.queueManager.dequeue();
}
}
}
this.isBusy = false; // Reset busy flag after the queue is processed
}
// Method to send the file to a specific IP using the TcpClient
// Method to send the file to a specific IP using TcpCommunicator
private async sendFile(task: FileSendTask): Promise<boolean> {
const { ip, path: filePath, userName } = task;
const {ip, path: filePath, userName} = task;
// Ensure the file exists before attempting to send
if (!fs.existsSync(filePath)) {
@@ -63,7 +75,7 @@ export class FileSharer {
// Read the file contents
const fileContent = fs.readFileSync(filePath);
// Extract the file name from the file path using path.basename (handles both Windows and Unix)
// Extract the file name from the file path using path.basename
const fileName = path.basename(filePath);
const metaInfo = {
@@ -71,82 +83,36 @@ export class FileSharer {
relativeFilePath: fileName, // Use the file name instead of the full path
};
const tcpClient = new TcpClient(this.clientPort);
tcpClient.openSocket(ip);
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
// Wait for the AES key and then send the file
try {
const sendSuccess = await this.waitForAesKeyAndSendFile(tcpClient, operationCodes.SHARE_FILE, metaInfo, fileContent, task);
tcpClient.closeSocket();
return sendSuccess;
} catch (error) {
console.error(`Error sending file: ${filePath} to IP: ${ip}. Error: ${error}`);
tcpClient.closeSocket();
if (!await this.tcpCommunicator.connect()) {
console.error(`Failed to connect to IP: ${ip}`);
return false;
}
console.log(`Sending file: ${filePath} to IP: ${ip}`);
if (!await this.tcpCommunicator.sendMessage(operationCodes.SHARE_FILE, metaInfo, fileContent)) return false;
const response = await this.waitForResponse();
if (!response || response.operationCode !== operationCodes.OK) {
console.error(`Failed to send file: ${filePath} to IP: ${ip}`);
return false;
}
await this.tcpCommunicator.disconnect();
return true;
}
// Wait for AES key to be set and send the file, handling the response
private async waitForAesKeyAndSendFile(tcpClient: TcpClient, operationCode: string, metaInfo: { [key: string]: any }, fileContent: Buffer, task: FileSendTask, checkInterval: number = 500, timeout: number = 10000): Promise<boolean> {
const startTime = Date.now();
return new Promise((resolve, reject) => {
const intervalId = setInterval(async () => {
const elapsedTime = Date.now() - startTime;
// Check if AES key is set, if timeout occurs, reject
if (!tcpClient.isAesKeySet()) {
if (elapsedTime > timeout) {
clearInterval(intervalId);
console.error('Timeout waiting for AES key.');
reject(new Error('Timeout waiting for AES key.'));
}
return; // Continue waiting for AES key
}
// AES key is set, send the message
clearInterval(intervalId);
try {
const success = await tcpClient.sendMessage(operationCode, metaInfo, fileContent);
if (!success) {
reject(new Error('Failed to send the file content.'));
return;
}
// Wait for a response after sending the message
const responseReceived = await this.waitForMessageResponse(tcpClient, timeout);
if (!responseReceived) {
reject(new Error('Timeout waiting for the message response.'));
return;
}
resolve(true); // Everything went fine
} catch (error) {
reject(error);
}
}, checkInterval); // Check for AES key every `checkInterval`
});
}
// Method to wait for the response from the TCP client
private waitForMessageResponse(tcpClient: TcpClient, timeout: number = 10000): Promise<boolean> {
private async waitForResponse(): Promise<ParsedMessage | null> {
return new Promise((resolve) => {
const startTime = Date.now();
const intervalId = setInterval(() => {
const response = tcpClient.getLastResult();
const elapsedTime = Date.now() - startTime;
if (response?.operationCode === operationCodes.OK) {
clearInterval(intervalId);
resolve(true); // Message successfully received
} else if (elapsedTime > timeout) {
clearInterval(intervalId);
resolve(false); // No response after timeout
const idResponseCheck = setInterval(async () => {
if (!this.tcpCommunicator) return null;
if (this.tcpCommunicator.hasResponseArrived()) {
clearInterval(idResponseCheck);
resolve(this.tcpCommunicator.getLastResult()); // Resolve the response or null if not available
}
}, 100); // Check every 100ms
}, 100); // Check every 100 milliseconds if the response has arrived
});
}
}
-76
View File
@@ -1,76 +0,0 @@
import { JsonManager } from './json_manager'; // Assuming you have this class for managing user/memory
import { WindowManager } from './window_manager'; // For managing app navigation
import { UdpClient } from '../network/udp/udp_client';
export class TaskScheduler {
private applicationInfo: JsonManager;
private windowManager: WindowManager;
private appStarted: boolean = false;
private intervalIds: NodeJS.Timeout[] = []; // Array to store interval IDs
constructor(applicationInfo: JsonManager, windowManager: WindowManager) {
this.applicationInfo = applicationInfo;
this.windowManager = windowManager;
}
// Function to schedule the UC check task with dynamic UDP client creation
public startUCCheck(udpPort: number, okPage: string, errorPage: string, interval: number = 30000) {
const intervalId = setInterval(async () => {
try {
const udpClient = new UdpClient(udpPort); // Create UdpClient with the provided port
const aliveClients = await udpClient.getAliveClients();
const storedIp = await this.applicationInfo.readValue('serverIp');
const foundClient = aliveClients.length > 0;
if (foundClient) {
const ipAddress = aliveClients[0]; // Just using the first alive client
if (!storedIp || storedIp !== ipAddress) {
await this.applicationInfo.writeValue('serverIp', ipAddress);
if (!this.appStarted) {
await this.windowManager.changeContent(okPage);
}
this.appStarted = true;
} else if (!this.appStarted) {
await this.windowManager.changeContent(okPage);
this.appStarted = true;
}
} else {
await this.windowManager.changeContent(errorPage);
}
} catch (err) {
console.error('Error checking UC:', err);
await this.windowManager.changeContent(errorPage);
}
}, interval);
this.intervalIds.push(intervalId);
}
// Function to schedule the IP lookup task, storing the active addresses in memory
public async startUserIPLookup(udpPort: number, interval: number = 30000) {
const serverIp = await this.applicationInfo.readValue('serverIp'); // Ensure it's awaited if it's an async function
const intervalId = setInterval(async () => {
try {
const udpClient = new UdpClient(udpPort); // Create a UDP client with the provided port
const activeIPs = await udpClient.getAliveClients(); // Get the list of active IPs
// Filter out the serverIp from the list of active clients
const filteredIPs = activeIPs.filter(ip => ip !== serverIp);
// Save the filtered IPs to 'users_ip'
await this.applicationInfo.writeValue('users_ip', filteredIPs);
} catch (err) {
console.error('Error during user IP lookup:', err);
}
}, interval);
this.intervalIds.push(intervalId); // Store the interval ID
}
// Function to stop all tasks (UC check, user worker, etc.)
public stopAllTasks() {
this.intervalIds.forEach(clearInterval); // Clear all intervals
this.intervalIds = []; // Reset the array
}
}
+15 -27
View File
@@ -2,6 +2,7 @@ import { JsonManager } from "./json_manager";
import { MemoryManager } from "./memory_manager";
import { operationCodes } from "../network/operation_codes";
import { TcpCommunicator } from "./tcp_communicator";
import {ParsedMessage} from "../network/message_handler";
export class UsersInfoFetcher {
private applicationInfo: JsonManager;
@@ -24,7 +25,7 @@ export class UsersInfoFetcher {
async start(): Promise<void> {
setInterval(async () => {
await this.initialize(); // Re-run every minute
}, 60000); // 1 minute interval
}, 5000); // 1 minute interval
}
// Initialize and fetch user IPs and process users info
@@ -57,16 +58,19 @@ export class UsersInfoFetcher {
continue;
}
if(!await this.tcpCommunicator?.sendMessage(operationCodes.GET_USER_INFORMATION)){
await this.tcpCommunicator.disconnect();
continue
}
// Wait for the response for 10 seconds
const response = await this.waitForResponse();
console.log(response);
// If a response is received and is successful, append it to usersInfo
if (response && response.metaInfo) {
console.log(`Response received from ${ip}:`, response);
usersInfo.push({
ip: ip,
user_info: response.metaInfo // Push the IP and user_info object into the array
user_info: response.metaInfo
});
}
@@ -76,31 +80,15 @@ export class UsersInfoFetcher {
await this.updateActiveUsers(usersInfo); // Update active users information in the memory
}
// Send the message once and wait for the response for a specific timeout
private async waitForResponse(timeout: number = 10000): Promise<any> {
// Send the message once
const status = await this.tcpCommunicator?.sendMessage(operationCodes.GET_USER_INFORMATION);
if (!status) {
return null;
}
// Wait for the response until the timeout
private async waitForResponse(): Promise<ParsedMessage | null> {
return new Promise((resolve) => {
const startTime = Date.now();
const checkResponseInterval = setInterval(async () => {
// Check if the message has been received
if (this.tcpCommunicator?.hasResponseArrived()) {
clearInterval(checkResponseInterval);
resolve(this.tcpCommunicator?.getLastResult()); // Return the response once it arrives
const idResponseCheck = setInterval(async () => {
if (!this.tcpCommunicator) return null;
if (this.tcpCommunicator.hasResponseArrived()) {
clearInterval(idResponseCheck);
resolve(this.tcpCommunicator.getLastResult()); // Resolve the response or null if not available
}
// If the timeout is reached, stop checking and resolve with null
if (Date.now() - startTime > timeout) {
clearInterval(checkResponseInterval);
resolve(null);
}
}, 100); // Check every 100ms if the response has arrived
}, 100); // Check every 100 milliseconds if the response has arrived
});
}
+68 -7
View File
@@ -13,10 +13,40 @@ export class WorkerManager {
this.workers = []; // Initialize the array to store workers
}
// Start the Connection Pool Worker
async startWatchersWorker(memoryManagerPath: string, applicationInfoPath: string): Promise<void> {
async startNetworkScannerWorker(udpPort: number, okPage: string, errorPage: string, applicationInfoPath: string): Promise<void> {
return new Promise((resolve, reject) => {
const worker = new Worker(path.join(this.pathToWorkerDir, 'watcher_worker.js'), {
const worker = new Worker(path.join(this.pathToWorkerDir, 'network_scanner_worker.js'), {
workerData: { udpPort, okPage, errorPage, applicationInfoPath }, // Pass necessary data to the worker
});
this.workers.push(worker); // Store the worker reference
worker.on('message', (data) => {
console.log(data);
if (data.type === 'changeContent') {
this.windowManager.changeContent(data.page);
}
});
worker.on('error', (err) => {
console.error('Network Scanner Worker error:', err);
worker.terminate();
this.removeWorker(worker);
reject(err); // Reject the promise if there's an error
});
worker.on('exit', (code) => {
console.log(`Network Scanner Worker exited with code ${code}`);
this.removeWorker(worker); // Remove worker reference when it exits
resolve(); // Resolve when the worker exits cleanly
});
});
}
// Start the Connection Pool Worker
async startDirectoriesWatchersWorker(memoryManagerPath: string, applicationInfoPath: string): Promise<void> {
return new Promise((resolve, reject) => {
const worker = new Worker(path.join(this.pathToWorkerDir, 'directories_watcher_worker.js'), {
workerData: { memoryManagerPath, applicationInfoPath }, // Pass the port to the worker
});
@@ -110,17 +140,18 @@ export class WorkerManager {
clientPort: number,
destinationPath: string
): Promise<void> {
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
const worker = new Worker(path.join(this.pathToWorkerDir, 'backup_retrieval_worker.js'), {
workerData: { userConfigPath, applicationInfoPath, clientPort, destinationPath }, // Pass parameters to the worker
});
this.workers.push(worker); // Store the worker reference
worker.on('message', (data) => {
worker.on('message', async (data) => {
console.log('Backup Retrieval Worker message:', data);
this.windowManager.changeContent('main_menu');
this.windowManager.showAlert(data.message);
await new Promise((resolve) => setTimeout(resolve, 3000)); // Wait for 1 second
await this.windowManager.changeContent('main_menu');
await this.windowManager.showAlert(data.message);
});
worker.on('error', (err) => {
@@ -138,6 +169,36 @@ export class WorkerManager {
});
}
// Method to start the Announcement Worker
async startAnnouncementWorker(applicationInfoPath: string, clientPort: number, message: string): Promise<void> {
return new Promise((resolve, reject) => {
const worker = new Worker(path.join(this.pathToWorkerDir, 'send_announcement_worker.js'), {
workerData: { applicationInfoPath, clientPort, message }, // Pass necessary data to the worker
});
this.workers.push(worker); // Store the worker reference
worker.on('message', async (data) => {
await new Promise((resolve) => setTimeout(resolve, 3000)); // Wait for 1 second
this.windowManager.changeContent('main_menu');
this.windowManager.showAlert(`${data.message}`)
});
worker.on('error', (err) => {
console.error('Announcement Worker error:', err);
worker.terminate();
this.removeWorker(worker);
reject(err); // Reject the promise if there's an error
});
worker.on('exit', (code) => {
console.log(`Announcement Worker exited with code ${code}`);
this.removeWorker(worker); // Remove worker reference when it exits
resolve(); // Resolve when the worker exits cleanly
});
});
}
// Close all running workers
closeAllWorkers(): void {
console.log('Terminating all running workers...');
+80 -16
View File
@@ -7,20 +7,20 @@ import {WorkerManager} from "../helpers/worker_manager";
import {DirectoryWatcher} from "../helpers/directory_watcher";
import {QueueManager} from "../helpers/queue_manager";
import {compareFnFileItemTask, FileItemTask} from "../interfaces/file_item_task";
import {TaskScheduler} from "../helpers/task_scheduler";
import {WindowManager} from "../helpers/window_manager";
import {JsonManager} from "../helpers/json_manager";
import {MemoryManager} from "../helpers/memory_manager";
import {TcpCommunicator} from "../helpers/tcp_communicator";
import {operationCodes} from "../network/operation_codes";
import os from "os";
// Load environment variables
dotenv.config({ path: path.join(__dirname, '..', '..', '.env') });
const UDP_PORT = process.env.UDP_PORT ? parseInt(process.env.UDP_PORT) : 41233;
const TCP_PORT = process.env.TCP_PORT ? parseInt(process.env.TCP_PORT) : 41234;
const HOST = process.env.HOST || '0.0.0.0';
const HOST = getLocalIp();
let mainWindow: BrowserWindow | null = null;
let windowManager: WindowManager | null = null;
@@ -29,7 +29,6 @@ let userConfig: JsonManager | null = null;
let applicationInfo: JsonManager | null = null;
let memoryManager: MemoryManager | null = null;
let workerManager: WorkerManager | null = null;
let taskScheduler: TaskScheduler | null = null;
let backupDirectoryManager: DirectoryWatcher | null = null;
let departmentShareManager: DirectoryWatcher | null = null;
let sendFileQueue: QueueManager<FileItemTask> | null = null;
@@ -62,11 +61,6 @@ async function cleanupAndExit() {
departmentShareManager.closeWatcher(); // Add this method to DirectoryWatcher to close the watcher
}
if(taskScheduler){
console.log('Stopping all tasks...');
taskScheduler.stopAllTasks()
}
if(workerManager){
console.log('Terminating all workers...');
workerManager.closeAllWorkers()
@@ -86,6 +80,21 @@ async function ensureDirectoryExists(dirPath: string): Promise<void> {
}
}
function getLocalIp() {
const interfaces = os.networkInterfaces();
for (let interfaceName in interfaces) {
const addresses = interfaces[interfaceName];
if(!addresses) continue;
for (let address of addresses) {
// Filter for IPv4 and ignore internal (127.0.0.1) addresses
if (address.family === 'IPv4' && !address.internal) {
return address.address;
}
}
}
return ''; // Fallback if no IP is found
}
app.whenReady().then(async () => {
const title = 'Application';
const mainScreen = require('electron').screen.getPrimaryDisplay();
@@ -103,6 +112,8 @@ app.whenReady().then(async () => {
},
});
mainWindow.removeMenu();
await ensureDirectoryExists(pathToJsons);
await ensureDirectoryExists(pathToClientsBackups);
@@ -112,14 +123,16 @@ app.whenReady().then(async () => {
memoryManager = new MemoryManager(path.join(pathToJsons, 'memory.json'));
sendFileQueue = new QueueManager(path.join(pathToJsons, 'sendFileTasks.json'), compareFnFileItemTask);
taskScheduler = new TaskScheduler(applicationInfo, windowManager);
workerManager = new WorkerManager(pathToWorkerDir, windowManager);
await userConfig.writeValue('app_type', 'ceo');
await applicationInfo.writeValue('users_ip', []);
await applicationInfo.writeValue('serverIp', '');
await applicationInfo.writeValue('announcement', '');
await memoryManager.resetFile();
workerManager.startWatchersWorker(path.join(pathToJsons, 'memory.json'), path.join(pathToJsons, 'application.json'));
workerManager.startNetworkScannerWorker(UDP_PORT, 'login', 'uc_not_found', path.join(pathToJsons, 'application.json'));
workerManager.startDirectoriesWatchersWorker(path.join(pathToJsons, 'memory.json'), path.join(pathToJsons, 'application.json'));
workerManager.startServersWorker(HOST, UDP_PORT, TCP_PORT);
workerManager.startResourceCoordinatorWorker(
path.join(pathToJsons, 'userConfig.json'),
@@ -129,10 +142,6 @@ app.whenReady().then(async () => {
TCP_PORT
);
taskScheduler.startUCCheck(UDP_PORT, 'login', 'uc_not_found');
taskScheduler.startUserIPLookup(UDP_PORT);
registerIPCHandlers();
await windowManager.changeContent('welcome');
@@ -156,6 +165,43 @@ app.on('before-quit', async () => {
// Register IPC handlers
function registerIPCHandlers() {
// ResetApplicationPreferences IPC Handlers
ipcMain.handle('reset-application-preferences', async () => {
if(applicationInfo) {
const serverIp = await applicationInfo.readValue('serverIp');
await applicationInfo.resetFile();
await applicationInfo.writeValue('serverIp', serverIp);
}
if(userConfig) {
await userConfig.resetFile();
await userConfig.writeValue('app_type', 'ceo');
}
if(memoryManager) {
await memoryManager.resetFile();
}
if(sendFileQueue) {
sendFileQueue.clearQueue();
}
if(workerManager) {
workerManager.closeAllWorkers();
await new Promise(resolve => setTimeout(resolve, 3000));
workerManager.startNetworkScannerWorker(UDP_PORT, 'login', 'uc_not_found', path.join(pathToJsons, 'application.json'));
workerManager.startDirectoriesWatchersWorker(path.join(pathToJsons, 'memory.json'), path.join(pathToJsons, 'application.json'));
workerManager.startServersWorker(HOST, UDP_PORT, TCP_PORT);
workerManager.startResourceCoordinatorWorker(
path.join(pathToJsons, 'userConfig.json'),
path.join(pathToJsons, 'application.json'),
path.join(pathToJsons, 'memory.json'),
path.join(pathToJsons, 'sendFileTasks.json'),
TCP_PORT
);
}
});
// Window Manager IPC Handlers
ipcMain.handle('show-alert', async (event: IpcMainInvokeEvent, message: string) => {
if (!windowManager) throw new Error('WindowManager is not initialized.');
@@ -251,7 +297,11 @@ function registerIPCHandlers() {
ipcMain.handle('reset-application-json-files', async () => {
if (!applicationInfo) throw new Error('ApplicationInfo is not initialized.');
return applicationInfo.resetFile();
const serverIp = await applicationInfo.readValue('serverIp');
await applicationInfo.resetFile();
if(serverIp) {
await applicationInfo.writeValue('serverIp', serverIp);
}
});
ipcMain.handle('remove-application-json-files-key', async (_event: IpcMainInvokeEvent, key: string) => {
@@ -280,13 +330,18 @@ function registerIPCHandlers() {
return memoryManager.removeMetaInformation(id);
});
ipcMain.handle('memory-reset', async () => {
if (!memoryManager) throw new Error('MemoryManager is not initialized.');
return memoryManager.resetFile();
});
// Queue IPC Handlers
ipcMain.handle('add-task-to-send-file-queue', async (event: IpcMainInvokeEvent, task: FileItemTask) => {
if (!sendFileQueue) throw new Error('SendFileQueue is not initialized.');
sendFileQueue.enqueue(task);
});
// BackupRetrievalWorker IPC Handler
// Workers IPC Handlers
ipcMain.handle('start-backup-retrieval', async (_event: IpcMainInvokeEvent, destinationPath: string) => {
if (!workerManager) throw new Error('WorkerManager is not initialized.');
return workerManager.startBackupRetrievalWorker(
@@ -296,4 +351,13 @@ function registerIPCHandlers() {
destinationPath
);
});
ipcMain.handle('start-announcement-worker', async (_event: IpcMainInvokeEvent, message: string) => {
if (!workerManager) throw new Error('WorkerManager is not initialized.');
return workerManager.startAnnouncementWorker(
path.join(pathToJsons, 'application.json'),
TCP_PORT,
message
);
});
}
+4 -2
View File
@@ -27,6 +27,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
readMemoryEntry: (id: string): Promise<any> => ipcRenderer.invoke('memory-read-entry', id),
updateMemoryEntry: (id: string, data: any): Promise<boolean> => ipcRenderer.invoke('memory-update-entry', id, data),
removeMemoryEntry: (id: string): Promise<boolean> => ipcRenderer.invoke('memory-remove-entry', id),
resetMemory: (): Promise<boolean> => ipcRenderer.invoke('memory-reset'),
// UI methods
showAlert: (message: string): Promise<void> => ipcRenderer.invoke('show-alert', message),
@@ -38,6 +39,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
// Queue methods
addTaskToSendFileQueue: (task: FileItemTask): Promise<void> => ipcRenderer.invoke('add-task-to-send-file-queue', task),
// BackupRetrievalWorker
startBackupRetrieval: (destinationPath: string): Promise<void> => ipcRenderer.invoke('start-backup-retrieval', destinationPath)
// Workers
startBackupRetrieval: (destinationPath: string): Promise<void> => ipcRenderer.invoke('start-backup-retrieval', destinationPath),
startAnnouncementWorker: (message: string): Promise<void> => ipcRenderer.invoke('start-announcement-worker', message),
});
+7 -3
View File
@@ -1,4 +1,5 @@
export let operationCodes = {
// General Operations
HEARTBEAT: 'HEARTBEAT',
ALIVE: 'ALIVE',
SET_PUBLIC_KEY: 'SET_PUBLIC_KEY',
@@ -9,20 +10,23 @@ export let operationCodes = {
END: 'END',
UNKNOWN_COMMAND: 'UNKNOWN_COMMAND',
// Auth Operations
LOGIN: 'LOGIN',
SIGN_UP: 'SIGN_UP',
RESET_PASSWORD: 'RESET_PASSWORD',
FIND_BY_EMAIL: 'FIND_BY_EMAIL',
MODIFY_USER: 'MODIFY_USER',
GET_DEPARTMENTS: 'GET_DEPARTMENTS',
CREATE_DEPARTMENT: 'CREATE_DEPARTMENT',
MODIFY_DEPARTMENT: 'MODIFY_DEPARTMENT',
DELETE_DEPARTMENT: 'DELETE_DEPARTMENT',
GET_USERS: 'GET_USERS',
DELETE_USER: 'DELETE_USER',
FIND_KEY_BY_USER_ID: 'FIND_KEY_BY_USER_ID',
FIND_BY_EMAIL: 'FIND_BY_EMAIL',
MODIFY_USER: 'MODIFY_USER',
DELETE_USER: 'DELETE_USER',
SEND_ANNOUNCEMENT: 'SEND_ANNOUNCEMENT',
GET_USER_INFORMATION: 'GET_USER_INFORMATION',
CLEAR_BACKUP: 'CLEAR_BACKUP',
@@ -12,19 +12,16 @@ export abstract class OperationBase implements OperationPlugin {
// Default handler for OK operation
public static handleOk(parsedMessage: ParsedMessage): ParsedMessage {
console.log('OK operation received');
return parsedMessage; // Typically, you would just acknowledge with OK, returning as-is
}
// Default handler for ERR operation
public static handleErr(parsedMessage: ParsedMessage): ParsedMessage {
console.log('ERR operation received: ', parsedMessage.metaInfo?.message || 'No error details provided');
return parsedMessage; // Typically, you would log the error and return
}
// Default handler for END operation
public static handleEnd(parsedMessage: ParsedMessage): ParsedMessage {
console.log('END operation received');
return {
operationCode: OperationBase.operationCodes.END,
metaInfo: { message: 'Connection ended.' },
@@ -137,8 +137,6 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
messageToProcess = this.decryptWithRsa(incomingMessage);
}
console.log(`\n\nComplete Message:\n${messageToProcess}\n\n`);
const result = this.operationHandler.handleOperation(messageToProcess);
if(result.operationCode === operationCodes.SET_AES_KEY){
-3
View File
@@ -80,9 +80,6 @@ export class TcpClient {
// Check if the message is received (based on if lastResult is available)
isMessageReceived(): boolean {
console.log('Is message received?');
console.log(this.lastResult);
console.log(this.lastResult !== null);
return this.lastResult !== null;
}
+1 -13
View File
@@ -23,16 +23,4 @@ const backupRetrievalWorker = new BackupRetrievalWorker(
);
// Start the backup retrieval process
backupRetrievalWorker.start()
.then(() => {
parentPort?.postMessage({
success: true,
message: 'Backup retrieval completed successfully.'
});
})
.catch((error: any) => {
parentPort?.postMessage({
success: false,
message: `Backup retrieval failed: ${error.message}`
});
});
backupRetrievalWorker.start();
@@ -18,7 +18,7 @@ const backupIntervalId = setInterval(async () => {
} else {
console.log('Retrying BackupDirectoryManager initialization...');
}
}, 60000); // Check every 60 seconds
}, 10000); // Check every 60 seconds
// Department share manager check loop
@@ -29,7 +29,7 @@ const departmentIntervalId = setInterval(async () => {
} else {
console.log('Retrying DepartmentShareManager initialization...');
}
}, 60000); // Check every 60 seconds
}, 10000); // Check every 60 seconds
// Department share manager check loop
const shareIntervalId = setInterval(async () => {
@@ -39,5 +39,5 @@ const shareIntervalId = setInterval(async () => {
} else {
console.log('Retrying DepartmentShareManager initialization...');
}
}, 60000); // Check every 60 seconds
}, 10000); // Check every 60 seconds
+94
View File
@@ -0,0 +1,94 @@
import { parentPort, workerData } from 'worker_threads';
import { JsonManager } from '../helpers/json_manager';
import { UdpClient } from '../network/udp/udp_client';
// Define the structure of workerData
interface WorkerData {
udpPort: number;
okPage: string;
errorPage: string;
applicationInfoPath: string;
}
// Extract the data passed to the worker
const { udpPort, okPage, errorPage, applicationInfoPath }: WorkerData = workerData;
// Create a JsonManager instance for application info
const applicationInfo = new JsonManager(applicationInfoPath);
let appStarted = false;
let intervalIds: NodeJS.Timeout[] = []; // Store interval IDs for future clearing
// Flags to prevent overlapping executions
let ucCheckBusy = false;
let ipLookupBusy = false;
// Function to schedule the UC check task with dynamic UDP client creation
function startUCCheck(udpPort: number, okPage: string, errorPage: string, interval: number = 5000): void {
const intervalId = setInterval(async () => {
if (ucCheckBusy) return; // If already running, skip this iteration
ucCheckBusy = true; // Mark as busy
try {
console.log('UC Check running...');
const udpClient = new UdpClient(udpPort); // Create UdpClient with the provided port
const aliveClients = await udpClient.getAliveClients();
const storedIp = await applicationInfo.readValue('serverIp');
const foundClient = aliveClients.length > 0;
if (foundClient) {
const ipAddress = aliveClients[0]; // Just using the first alive client
if (!storedIp || storedIp !== ipAddress) {
await applicationInfo.writeValue('serverIp', ipAddress);
if (!appStarted) {
parentPort?.postMessage({ type: 'changeContent', page: okPage });
}
appStarted = true;
} else if (!appStarted) {
parentPort?.postMessage({ type: 'changeContent', page: okPage });
appStarted = true;
}
} else {
parentPort?.postMessage({ type: 'changeContent', page: errorPage });
}
} catch (err) {
console.error('Error checking UC:', err);
parentPort?.postMessage({ type: 'changeContent', page: errorPage });
} finally {
ucCheckBusy = false; // Mark as not busy
}
}, interval);
intervalIds.push(intervalId);
}
// Function to schedule the IP lookup task, storing the active addresses in memory
function startUserIPLookup(udpPort: number, interval: number = 10000): void {
const intervalId = setInterval(async () => {
if (ipLookupBusy) return; // If already running, skip this iteration
ipLookupBusy = true; // Mark as busy
try {
console.log('IP Lookup running...');
const serverIp = await applicationInfo.readValue('serverIp');
const udpClient = new UdpClient(udpPort); // Create a UDP client with the provided port
const activeIPs = await udpClient.getAliveClients(); // Get the list of active IPs
// Filter out the serverIp from the list of active clients
const filteredIPs = activeIPs.filter(ip => ip !== serverIp);
// Save the filtered IPs to 'users_ip'
await applicationInfo.writeValue('users_ip', filteredIPs);
} catch (err) {
console.error('Error during user IP lookup:', err);
} finally {
ipLookupBusy = false; // Mark as not busy
}
}, interval);
intervalIds.push(intervalId);
}
// Start the UC Check and User IP Lookup tasks
startUCCheck(udpPort, okPage, errorPage);
startUserIPLookup(udpPort);
@@ -1,6 +1,6 @@
import { workerData } from 'worker_threads';
import { UsersInfoFetcher } from '../helpers/users_info_fetcher';
import {BackupManager} from "../helpers/backup_manager";
import {BackupRetrievalWorker} from "../helpers/backup_manager";
import {FileSharer} from "../helpers/file_sharer";
import {DepartmentSharer} from "../helpers/department_sharer";
@@ -16,7 +16,7 @@ usersInfoFetcher.start()
console.error('Error starting Users Info Fetcher:', error);
});
const backupManager = new BackupManager(
const backupManager = new BackupRetrievalWorker(
usersConfigPath,
applicationInfoPath,
memoryManagerPath,
@@ -0,0 +1,19 @@
import { workerData } from 'worker_threads';
import { AnnouncementSender} from "../helpers/announcement_sender";
// Destructure data passed from the main thread
const {
applicationInfoPath,
clientPort,
message
}: {
applicationInfoPath: string,
clientPort: number,
message: string
} = workerData;
// Initialize the AnnouncementWorker
const announcementWorker = new AnnouncementSender(applicationInfoPath, clientPort);
// Start the announcement process and handle results
announcementWorker.start(message);