215 lines
8.1 KiB
TypeScript
215 lines
8.1 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
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<boolean> {
|
|
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<void> {
|
|
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<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.");
|
|
}
|
|
|
|
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);
|
|
});
|
|
}
|
|
|
|
// 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}`);
|
|
}
|
|
}
|
|
}
|