Files
FACULTATE-LICENTA/User/src/helpers/backup_manager.ts
T
2025-02-04 20:08:56 +02:00

175 lines
5.3 KiB
TypeScript

import fs from 'fs'
import path from 'path'
import { FileEncryptor } from './file_encryptor'
import { TcpCommunicator } from './tcp_communicator'
import { operationCodes } from '../network/operation_codes'
import { ParsedMessage } from '../network/message_handler'
import { Database } from '../database/database'
import { NetworkUserScheme } from '../database/schemes/network_scheme'
export class BackupManager {
private fileEncryptor: FileEncryptor | null = null
private readonly db: Database
private readonly clientPort: number
private isBusy: boolean = false
private intervalId: NodeJS.Timeout | null = null
private stopRequested: boolean = false
constructor(pathToDatabaseFile: string, clientPort: number) {
this.db = new Database(pathToDatabaseFile)
this.clientPort = clientPort
}
async start(): Promise<void> {
this.intervalId = setInterval(async () => {
if (!this.isBusy || !this.stopRequested) {
this.isBusy = true
this.log('Start successfully. Backup files to users.')
await this.initialize()
}
if (global.gc) {
global.gc()
}
}, 10000) // 10-second interval for testing
}
private async initialize(): Promise<void> {
this.isBusy = true
try {
const data = await this.db.read()
const userInfo = data.app_config.user_info
const encryptionKey = data.app_config.encryption_key
const usersIp = data.network.usersInLan.map((user: NetworkUserScheme) => user.ip)
const backupDirectoryData = data.local_resources.directory_schemes.backup
this.fileEncryptor = new FileEncryptor(encryptionKey.key, encryptionKey.iv)
await this.sendFilesToUsers(
userInfo.name,
usersIp,
backupDirectoryData.structure,
backupDirectoryData.path,
)
} catch (error: any) {
this.log(`Error in initialize process: ${error.message}`, 'error')
} finally {
this.log('Backup process completed.')
this.isBusy = false
}
}
private encryptFile(filePath: string): string {
if (!this.fileEncryptor) {
return filePath
}
if (!fs.existsSync(filePath)) {
this.log(`File not found: ${filePath}`, 'error')
return ''
}
return this.fileEncryptor.encryptFileToBase64(filePath)
}
private async sendFilesToUsers(
userName: string,
usersIp: string[],
fileStructure: { [key: string]: string },
backupDirectoryPath: string,
): Promise<void> {
let unsentFiles = Object.keys(fileStructure)
for (const fileName of unsentFiles) {
const filePath = fileStructure[fileName]
const encryptedFileContent = this.encryptFile(filePath)
if (!encryptedFileContent) {
this.log(`Failed to encrypt file: ${fileName}`, 'error')
continue
}
const relativeFilePath = path.relative(backupDirectoryPath, filePath)
const metaInfo = { userName, relativeFilePath }
for (const ip of usersIp) {
const tcpCommunicator = new TcpCommunicator(ip, this.clientPort)
try {
await tcpCommunicator.connect()
this.log(`Connected to ${ip}`)
const sendSuccess = await tcpCommunicator.sendMessage(
operationCodes.BACKUP_FILE,
metaInfo,
Buffer.from(encryptedFileContent, 'base64'),
)
if (!sendSuccess) {
throw new Error('Failed to send file content.')
}
const responseReceived = await this.waitForResponse(tcpCommunicator)
if (!responseReceived) {
throw new Error('Timeout waiting for the message response.')
}
this.log(`Successfully sent file: ${fileName} to ${ip}`)
unsentFiles = unsentFiles.filter((f) => f !== fileName)
break
} catch (error) {
this.log(`Failed to send file: ${fileName} to ${ip}. Error: ${error}`, 'error')
} finally {
await tcpCommunicator.disconnect()
this.log(`Disconnected from ${ip}`)
}
}
}
if (unsentFiles.length > 0) {
process.send?.({ type: 'log', message: 'Backup could not be completed for all files' })
} else {
process.send?.({ type: 'log', message: 'Backup completed successfully' })
}
}
private async waitForResponse(tcpCommunicator: TcpCommunicator): Promise<ParsedMessage | null> {
return new Promise((resolve) => {
const idResponseCheck = setInterval(() => {
if (!tcpCommunicator) return null
if (tcpCommunicator.hasResponseArrived()) {
clearInterval(idResponseCheck)
resolve(tcpCommunicator.getLastResult())
}
}, 100)
})
}
async stop(): Promise<void> {
this.stopRequested = true // Signal that stop is requested
if (this.intervalId) {
clearInterval(this.intervalId)
this.intervalId = null
}
// Wait for any ongoing process to complete if busy
while (this.isBusy) {
await new Promise((resolve) => setTimeout(resolve, 100))
}
console.log('[BackupManager] Stopped successfully.')
}
// Unified logging function
private log(message: string, level: 'log' | 'error' | 'warn' = 'log'): void {
const prefix = '[BackupManager]'
if (level === 'error') {
console.error(`${prefix} ${message}`)
} else if (level === 'warn') {
console.warn(`${prefix} ${message}`)
} else {
console.log(`${prefix} ${message}`)
}
}
}