import fs from 'fs'; import path from 'path'; import { TcpCommunicator } from './tcp_communicator'; import { operationCodes } from '../network/operation_codes'; import { JsonManager } from './json_manager'; import { MemoryManager } from './memory_manager'; import { ParsedMessage } from "../network/message_handler"; export class DepartmentSharer { private userConfig: JsonManager; private applicationInfo: JsonManager; private memoryManager: MemoryManager; private departmentDirectory: string | null; private readonly clientPort: number; private isBusy: boolean = false; private tcpCommunicator: TcpCommunicator | null = null; private stopRequested: boolean = false; private intervalId: NodeJS.Timeout | null = null; constructor( userConfigPath: string, applicationInfoPath: string, memoryManagerPath: string, clientPort: number ) { this.userConfig = new JsonManager(userConfigPath); this.applicationInfo = new JsonManager(applicationInfoPath); this.memoryManager = new MemoryManager(memoryManagerPath); this.clientPort = clientPort; this.departmentDirectory = null; } // Start sharing files with the department every minute async start(): Promise { this.intervalId = setInterval(async () => { if (!this.isBusy || !this.stopRequested) { this.isBusy = true; this.log('Start successfully. Sharing files with the department.'); await this.shareFilesWithDepartment(); } if (global.gc) { global.gc(); } }, 10000); // 10-second interval for testing } // Share files with users in the same department private async shareFilesWithDepartment(): Promise { try { // Get the current user's department information const userInfo = await this.userConfig.readValue('user_info'); if (!userInfo || !userInfo.departmentId || !userInfo.name) { throw new Error('User information or department ID is missing in the configuration.'); } const departmentId = userInfo.departmentId; const userName = userInfo.name; // Get the list of active users from applicationInfo const activeUsersId = await this.applicationInfo.readValue('active_users_info'); if (!activeUsersId) { throw new Error('No active users found.'); } const activeUsers = await this.memoryManager.retrieveMetaInformation(activeUsersId); if (!activeUsers || activeUsers.length === 0) { throw new Error('No active users found.'); } // Filter users who belong to the same department const departmentUsers = activeUsers.filter((user: any) => user.user_info.departmentId === departmentId); if (departmentUsers.length === 0) { throw new Error('No users found in the same department.'); } // Get department directory info const departmentData = await this.applicationInfo.readValue('departmentDirectory'); if (!departmentData || !departmentData.path || !departmentData.id) { throw new Error('No department directory found.'); } this.departmentDirectory = departmentData.path; // Read files from the MemoryManager related to this department const departmentFiles = await this.memoryManager.retrieveMetaInformation(departmentData.id); if (!departmentFiles || !departmentFiles.structure) { throw new Error('No files found for this department in the memory manager.'); } // 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); if (!await this.tcpCommunicator.connect()) continue; // First clear the department directory; if (await this.clearDepartmentDirectory(userName)) { await this.sendFilesToUser(departmentFiles.structure, userName); } await this.tcpCommunicator.disconnect(); } } catch(error: any) { this.log(error.message, 'error'); } finally{ this.log('Department sharing completed.'); this.isBusy = false; } } // Clear the department directory for a user private async clearDepartmentDirectory(userName: string): Promise { if(!this.tcpCommunicator) return false; if (!await this.tcpCommunicator.sendMessage(operationCodes.CLEAR_DEPARTMENT, {userName: userName})) return false; const response = await this.waitForResponse(); if (!response || response.operationCode !== operationCodes.OK){ this.log('Failed to clear the department directory.', 'error'); return false; } return true; } // Send the files to a user in the department private async sendFilesToUser(files: { [key: string]: string }, userName: string): Promise { if(!this.tcpCommunicator) return; const unsentFiles = Object.keys(files); console.log(`\n\n${unsentFiles}\n\n`); for (const fileName of unsentFiles) { const filePath = files[fileName]; // Ensure the file exists before attempting to send if (!fs.existsSync(filePath)) { this.log(`File not found: ${filePath}`, 'error'); continue; } // Read the file content const fileContent = fs.readFileSync(filePath); // Get the relative path of the file (used in the meta info) if (!this.departmentDirectory) return; const relativeFilePath = path.relative(this.departmentDirectory, filePath); // Prepare the metaInfo (same structure as FileSharer) const metaInfo = { userName, relativeFilePath }; // Send the file if (!await this.tcpCommunicator.sendMessage(operationCodes.DEPARTMENT_FILE, metaInfo, Buffer.from(fileContent))) return; console.log(`\n\nSending file: ${fileName} to ${userName}\n\n`); const response = await this.waitForResponse(); if (!response || response.operationCode !== operationCodes.OK) { this.log(`Failed to send file: ${fileName}`, 'error'); return; } this.log(`File sent successfully: ${fileName} to ${userName}`); unsentFiles.splice(unsentFiles.indexOf(fileName), 1); await this.tcpCommunicator.disconnect(); } } async stop(): Promise { 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."); } private async waitForResponse(): Promise { return new Promise((resolve) => { const idResponseCheck = setInterval(async () => { if (!this.tcpCommunicator) return null; if (this.tcpCommunicator.hasResponseArrived()) { clearInterval(idResponseCheck); resolve(this.tcpCommunicator.getLastResult()); } }, 100); }); } // Unified logging function private log(message: string, level: 'log' | 'error' = 'log'): void { const prefix = '[DepartmentSharer]'; if (level === 'error') { console.error(`${prefix} ${message}`); } else { console.log(`${prefix} ${message}`); } } }