BACKEND DONE FOR ALL APPS
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
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 { parentPort } from 'worker_threads';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import crypto from 'crypto'; // Import the crypto module for encryption and decryption
|
||||
|
||||
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;
|
||||
|
||||
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> {
|
||||
// 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}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the parent that the worker is done
|
||||
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed.' });
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for AES key to be set
|
||||
private async waitForAesKey(tcpClient: TcpClient, timeout: number = 10000): Promise<boolean> {
|
||||
const startTime = Date.now();
|
||||
|
||||
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);
|
||||
|
||||
const response = await this.waitForMessageResponse(tcpClient);
|
||||
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);
|
||||
|
||||
const response = await this.waitForMessageResponse(tcpClient);
|
||||
return response?.metaInfo?.structure ? 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);
|
||||
|
||||
const response = await this.waitForMessageResponse(tcpClient);
|
||||
if (response?.operationCode === operationCodes.OK && response.metaInfo && response.fileContent) {
|
||||
return this.saveFile(response.metaInfo.relativeFilePath, response.fileContent);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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.writeFileSync(fullFilePath, decryptedContent);
|
||||
console.log(`File saved successfully: ${fullFilePath}`);
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
console.error(`Error saving file ${relativeFilePath}: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user