Files
FACULTATE-LICENTA/User/src/helpers/backup_manager.ts
T
2024-10-29 15:07:26 +02:00

184 lines
7.7 KiB
TypeScript

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 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, clientPort: number, destinationPath: string) {
this.userConfig = new JsonManager(userConfigPath);
this.applicationInfo = new JsonManager(applicationInfoPath);
this.clientPort = clientPort;
this.destinationPath = destinationPath;
}
async start(): Promise<void> {
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;
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) {
parentPort?.postMessage({ success: false, message: 'No active users found.' });
return;
}
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
}
}
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;
}
}
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);
});
}
}