BACKEND DONE FOR ALL APPS

This commit is contained in:
andrei-mihnea-cerbu
2024-10-21 18:34:45 +03:00
parent 4157ca9e7d
commit ab1eaec413
349 changed files with 17199 additions and 14015 deletions
+205
View File
@@ -0,0 +1,205 @@
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 { parentPort } from 'worker_threads';
export class BackupManager {
private fileEncryptor: FileEncryptor | null = null;
private memoryManager: MemoryManager;
private applicationInfo: JsonManager;
private userConfig: JsonManager;
private readonly clientPort: number;
constructor(userConfigPath: string, applicationInfoPath: string, memoryManagerPath: string, clientPort: number) {
this.applicationInfo = new JsonManager(applicationInfoPath);
this.userConfig = new JsonManager(userConfigPath);
this.memoryManager = new MemoryManager(memoryManagerPath);
this.clientPort = clientPort;
}
async start(): Promise<void> {
setInterval(async () => {
await this.initialize(); // Re-run every minute
}, 60000); // 1 minute interval
}
// 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
}
// Calculate relative file path
const relativeFilePath = path.relative(backupDirectoryPath, filePath); // Get the relative file path
// Meta information to send
const metaInfo = {
userName, // Name of the user
relativeFilePath // Relative path to preserve the directory structure
};
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();
}
}
}
// 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' });
}
}
// 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> {
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
}
}, 100);
});
}
}