overall v1
This commit is contained in:
@@ -1,84 +0,0 @@
|
|||||||
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 {ParsedMessage} from "../network/message_handler";
|
|
||||||
|
|
||||||
export class AnnouncementSender {
|
|
||||||
private applicationInfo: JsonManager;
|
|
||||||
private readonly clientPort: number;
|
|
||||||
private message: string = '';
|
|
||||||
private tcpCommunicator: TcpCommunicator | null = null;
|
|
||||||
|
|
||||||
constructor(applicationInfoPath: string, clientPort: number) {
|
|
||||||
this.applicationInfo = new JsonManager(applicationInfoPath);
|
|
||||||
this.clientPort = clientPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
async start(message: string): Promise<void> {
|
|
||||||
console.log('AnnouncementWorker started.');
|
|
||||||
this.message = message;
|
|
||||||
try {
|
|
||||||
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
|
|
||||||
if (!activeUsersIp || !activeUsersIp.length) {
|
|
||||||
throw new Error('No active users found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const ip of activeUsersIp) {
|
|
||||||
const success = await this.sendAnnouncementToIp(ip);
|
|
||||||
if (!success) {
|
|
||||||
throw new Error(`Failed to send announcement to all users.`);
|
|
||||||
}
|
|
||||||
console.log(`Announcement sent and confirmed successfully from ${ip}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
parentPort?.postMessage({ success: true, message: 'Announcement sent to all active users successfully.' });
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Error in AnnouncementWorker:', error);
|
|
||||||
parentPort?.postMessage({ success: false, message: `A problem occurred: ${error.message}` });
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('AnnouncementWorker finished.');
|
|
||||||
}
|
|
||||||
|
|
||||||
private async sendAnnouncementToIp(ip: string): Promise<boolean> {
|
|
||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
|
||||||
console.log(`Skipping user at IP ${ip} - unable to connect.`);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the message metadata
|
|
||||||
const metaInfo = { message: this.message };
|
|
||||||
|
|
||||||
// Send the announcement message
|
|
||||||
const messageSent = await this.tcpCommunicator.sendMessage(operationCodes.SEND_ANNOUNCEMENT, metaInfo);
|
|
||||||
if (!messageSent) {
|
|
||||||
await this.tcpCommunicator.disconnect();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Await confirmation from the user
|
|
||||||
const response = await this.waitForResponse();
|
|
||||||
if (response?.operationCode === operationCodes.OK) {
|
|
||||||
await this.tcpCommunicator.disconnect();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If confirmation is not OK, disconnect and halt
|
|
||||||
await this.tcpCommunicator.disconnect();
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
|
import { JsonManager } from './json_manager';
|
||||||
import { TcpCommunicator } from "./tcp_communicator";
|
import { TcpCommunicator } from "./tcp_communicator";
|
||||||
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
|
import { operationCodes } from '../network/operation_codes';
|
||||||
import { parentPort } from 'worker_threads';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
@@ -15,15 +14,30 @@ export class BackupRetrievalWorker {
|
|||||||
private encryptionKey: Buffer | null = null;
|
private encryptionKey: Buffer | null = null;
|
||||||
private iv: Buffer | null = null;
|
private iv: Buffer | null = null;
|
||||||
private tcpCommunicator: TcpCommunicator | null = null;
|
private tcpCommunicator: TcpCommunicator | null = null;
|
||||||
|
private isBusy: boolean;
|
||||||
|
private lastProcessedUserIndex: number;
|
||||||
|
|
||||||
constructor(userConfigPath: string, applicationInfoPath: string, clientPort: number, destinationPath: string) {
|
constructor(userConfigPath: string, applicationInfoPath: string, clientPort: number, destinationPath: string) {
|
||||||
this.userConfig = new JsonManager(userConfigPath);
|
this.userConfig = new JsonManager(userConfigPath);
|
||||||
this.applicationInfo = new JsonManager(applicationInfoPath);
|
this.applicationInfo = new JsonManager(applicationInfoPath);
|
||||||
this.clientPort = clientPort;
|
this.clientPort = clientPort;
|
||||||
this.destinationPath = destinationPath;
|
this.destinationPath = destinationPath;
|
||||||
|
this.isBusy = false;
|
||||||
|
this.lastProcessedUserIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
|
setInterval(async () => {
|
||||||
|
if (!this.isBusy) {
|
||||||
|
this.log('Start successfully. Processing backup tasks.');
|
||||||
|
await this.processBackupTasks();
|
||||||
|
}
|
||||||
|
}, 10000); // Retry every 10 seconds if there's an error
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processBackupTasks(): Promise<void> {
|
||||||
|
this.isBusy = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userInfo = await this.userConfig.readValue('user_info');
|
const userInfo = await this.userConfig.readValue('user_info');
|
||||||
if (!userInfo || !userInfo.name) {
|
if (!userInfo || !userInfo.name) {
|
||||||
@@ -41,46 +55,50 @@ export class BackupRetrievalWorker {
|
|||||||
|
|
||||||
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
|
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
|
||||||
if (!activeUsersIp || !activeUsersIp.length) {
|
if (!activeUsersIp || !activeUsersIp.length) {
|
||||||
parentPort?.postMessage({ success: false, message: 'No active users found.' });
|
this.isBusy = false;
|
||||||
return;
|
throw new Error('No active users found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process each user, starting from the last processed index
|
||||||
let backupSuccessful = true;
|
let backupSuccessful = true;
|
||||||
for (const ip of activeUsersIp) {
|
for (let i = this.lastProcessedUserIndex; i < activeUsersIp.length; i++) {
|
||||||
|
const ip = activeUsersIp[i];
|
||||||
const success = await this.processBackupForIp(ip, userName);
|
const success = await this.processBackupForIp(ip, userName);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
backupSuccessful = false;
|
backupSuccessful = false;
|
||||||
parentPort?.postMessage({ success: false, message: 'Backup could not be completed due to an internal error.' });
|
this.lastProcessedUserIndex = i; // Remember where it stopped
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backupSuccessful) {
|
if (backupSuccessful) {
|
||||||
parentPort?.postMessage({ success: true, message: 'Backup successful.' });
|
this.log('Backup retrieval completed successfully for all users.');
|
||||||
|
this.lastProcessedUserIndex = 0; // Reset index to start from the beginning next cycle
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error in BackupRetrievalWorker:', error);
|
this.log(error.message, 'error');
|
||||||
parentPort?.postMessage({ success: false, message: `Backup could not be completed due to an internal error: ${error.message}` });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isBusy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
|
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
|
||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
if (!await this.tcpCommunicator.connect()) {
|
||||||
console.error(`Failed to connect to ${ip}`);
|
this.log(`Failed to connect to ${ip}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backupExists = await this.checkIfBackupExists(userName);
|
const backupExists = await this.checkIfBackupExists(userName);
|
||||||
if (!backupExists) {
|
if (!backupExists) {
|
||||||
console.log(`No backup found for user ${userName} on IP ${ip}`);
|
this.log(`No backup found for user ${userName} on IP ${ip}`);
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return true; // Skip user if no backup found, do not mark as error
|
return true; // Skip user if no backup found, do not mark as error
|
||||||
}
|
}
|
||||||
|
|
||||||
const backupStructure = await this.requestBackupStructure(userName);
|
const backupStructure = await this.requestBackupStructure(userName);
|
||||||
if (!backupStructure || Object.keys(backupStructure).length === 0) {
|
if (!backupStructure || Object.keys(backupStructure).length === 0) {
|
||||||
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
|
this.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return true; // Skip user if no files found, do not mark as error
|
return true; // Skip user if no files found, do not mark as error
|
||||||
}
|
}
|
||||||
@@ -88,7 +106,7 @@ export class BackupRetrievalWorker {
|
|||||||
for (const relativeFilePath of Object.keys(backupStructure)) {
|
for (const relativeFilePath of Object.keys(backupStructure)) {
|
||||||
const fileRequestSuccess = await this.requestBackupFile(userName, relativeFilePath);
|
const fileRequestSuccess = await this.requestBackupFile(userName, relativeFilePath);
|
||||||
if (!fileRequestSuccess) {
|
if (!fileRequestSuccess) {
|
||||||
console.error(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`);
|
this.log(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`, 'error');
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return false; // Stop if any file fails to be retrieved
|
return false; // Stop if any file fails to be retrieved
|
||||||
}
|
}
|
||||||
@@ -133,7 +151,7 @@ export class BackupRetrievalWorker {
|
|||||||
|
|
||||||
private saveFile(relativeFilePath: string, fileContent: string): boolean {
|
private saveFile(relativeFilePath: string, fileContent: string): boolean {
|
||||||
if (!this.encryptionKey || !this.iv) {
|
if (!this.encryptionKey || !this.iv) {
|
||||||
console.error('Encryption key or IV is not set.');
|
this.log('Encryption key or IV is not set.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +159,7 @@ export class BackupRetrievalWorker {
|
|||||||
try {
|
try {
|
||||||
encryptedBuffer = Buffer.from(fileContent, 'base64');
|
encryptedBuffer = Buffer.from(fileContent, 'base64');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error decoding base64 file content:', error);
|
this.log(`Error decoding base64 file content: ${error}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +168,7 @@ export class BackupRetrievalWorker {
|
|||||||
const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, this.iv);
|
const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, this.iv);
|
||||||
decryptedContent = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]);
|
decryptedContent = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error decrypting file:', error);
|
this.log(`Error decrypting file: ${error}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,10 +179,10 @@ export class BackupRetrievalWorker {
|
|||||||
fs.mkdirSync(dirPath, { recursive: true });
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
fs.writeFileSync(fullFilePath, decryptedContent);
|
fs.writeFileSync(fullFilePath, decryptedContent);
|
||||||
console.log(`File saved successfully: ${fullFilePath}`);
|
this.log(`File saved successfully: ${fullFilePath}`);
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(`Error saving file ${relativeFilePath}: ${error.message}`);
|
this.log(`Error saving file ${relativeFilePath}: ${error.message}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +195,17 @@ export class BackupRetrievalWorker {
|
|||||||
clearInterval(idResponseCheck);
|
clearInterval(idResponseCheck);
|
||||||
resolve(this.tcpCommunicator.getLastResult());
|
resolve(this.tcpCommunicator.getLastResult());
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100); // Check every 100 milliseconds
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified logging function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const prefix = '[BackupRetrievalWorker]';
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
|
import { JsonManager } from './json_manager';
|
||||||
import { TcpCommunicator } from "./tcp_communicator";
|
import { TcpCommunicator } from "./tcp_communicator";
|
||||||
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
|
import { operationCodes } from '../network/operation_codes';
|
||||||
import { parentPort } from 'worker_threads';
|
import { parentPort } from 'worker_threads';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { ParsedMessage } from "../network/message_handler"; // Import the crypto module for encryption and decryption
|
import { ParsedMessage } from "../network/message_handler";
|
||||||
|
|
||||||
export class BackupRetrievalWorker {
|
export class BackupRetrievalWorker {
|
||||||
private userConfig: JsonManager;
|
private userConfig: JsonManager;
|
||||||
@@ -23,6 +23,16 @@ export class BackupRetrievalWorker {
|
|||||||
this.destinationPath = destinationPath;
|
this.destinationPath = destinationPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logging helper function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const prefix = '[BackupRetrievalWorker]';
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userInfo = await this.userConfig.readValue('user_info');
|
const userInfo = await this.userConfig.readValue('user_info');
|
||||||
@@ -49,12 +59,12 @@ export class BackupRetrievalWorker {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(`Failed to retrieve backup from ${ip}`);
|
throw new Error(`Failed to retrieve backup from ${ip}`);
|
||||||
}
|
}
|
||||||
console.log(`Backup retrieved successfully from ${ip}`);
|
this.log(`Backup retrieved successfully from ${ip}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed successfully.' });
|
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed successfully.' });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error in BackupRetrievalWorker:', error);
|
this.log(`Error in BackupRetrievalWorker: ${error.message}`, 'error');
|
||||||
parentPort?.postMessage({ success: false, message: `A problem occurred: ${error.message}` });
|
parentPort?.postMessage({ success: false, message: `A problem occurred: ${error.message}` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,19 +72,20 @@ export class BackupRetrievalWorker {
|
|||||||
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
|
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
|
||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
if (!await this.tcpCommunicator.connect()) {
|
||||||
|
this.log(`Failed to connect to ${ip}`, 'error');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backupExists = await this.checkIfBackupExists(userName);
|
const backupExists = await this.checkIfBackupExists(userName);
|
||||||
if (!backupExists) {
|
if (!backupExists) {
|
||||||
console.log(`No backup found for user ${userName} on IP ${ip}`);
|
this.log(`No backup found for user ${userName} on IP ${ip}`);
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backupStructure = await this.requestBackupStructure(userName);
|
const backupStructure = await this.requestBackupStructure(userName);
|
||||||
if (!backupStructure || Object.keys(backupStructure).length === 0) {
|
if (!backupStructure || Object.keys(backupStructure).length === 0) {
|
||||||
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
|
this.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -151,7 +162,7 @@ export class BackupRetrievalWorker {
|
|||||||
fs.mkdirSync(dirPath, { recursive: true });
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
fs.writeFileSync(fullFilePath, decryptedContent);
|
fs.writeFileSync(fullFilePath, decryptedContent);
|
||||||
console.log(`File saved successfully: ${fullFilePath}`);
|
this.log(`File saved successfully: ${fullFilePath}`);
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
throw new Error(`Error saving file ${relativeFilePath}: ${error.message}`);
|
throw new Error(`Error saving file ${relativeFilePath}: ${error.message}`);
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { TcpCommunicator } from './tcp_communicator'; // Using TcpCommunicator instead of TcpClient
|
import { TcpCommunicator } from './tcp_communicator';
|
||||||
import { operationCodes } from '../network/operation_codes';
|
import { operationCodes } from '../network/operation_codes';
|
||||||
import { JsonManager } from './json_manager'; // Manages JSON configurations
|
import { JsonManager } from './json_manager';
|
||||||
import { MemoryManager } from './memory_manager'; // Manages in-memory data structures
|
import { MemoryManager } from './memory_manager';
|
||||||
import { ParsedMessage } from "../network/message_handler";
|
import { ParsedMessage } from "../network/message_handler";
|
||||||
|
|
||||||
export class DepartmentSharer {
|
export class DepartmentSharer {
|
||||||
private userConfig: JsonManager;
|
private userConfig: JsonManager;
|
||||||
private applicationInfo: JsonManager;
|
private applicationInfo: JsonManager;
|
||||||
private memoryManager: MemoryManager; // To read the department files
|
private memoryManager: MemoryManager;
|
||||||
private departmentDirectory: string | null;
|
private departmentDirectory: string | null;
|
||||||
private readonly clientPort: number;
|
private readonly clientPort: number;
|
||||||
private isBusy: boolean = false; // Busy flag to prevent simultaneous sharing
|
private isBusy: boolean = false;
|
||||||
private tcpCommunicator: TcpCommunicator | null = null; // For each user connection
|
private tcpCommunicator: TcpCommunicator | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
userConfigPath: string,
|
userConfigPath: string,
|
||||||
@@ -32,21 +32,20 @@ export class DepartmentSharer {
|
|||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
if (!this.isBusy) {
|
if (!this.isBusy) {
|
||||||
this.isBusy = true;
|
this.log('Start successfully. Sharing files with the department.');
|
||||||
await this.shareFilesWithDepartment();
|
await this.shareFilesWithDepartment();
|
||||||
this.isBusy = false;
|
|
||||||
}
|
}
|
||||||
}, 10000); // 10-second interval for testing
|
}, 10000); // 10-second interval for testing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Share files with users in the same department
|
// Share files with users in the same department
|
||||||
private async shareFilesWithDepartment(): Promise<void> {
|
private async shareFilesWithDepartment(): Promise<void> {
|
||||||
console.log('\n\nStarting Department Share Process\n\n');
|
this.isBusy = true;
|
||||||
|
|
||||||
// Get the current user's department information
|
// Get the current user's department information
|
||||||
const userInfo = await this.userConfig.readValue('user_info');
|
const userInfo = await this.userConfig.readValue('user_info');
|
||||||
if (!userInfo || !userInfo.departmentId || !userInfo.name) {
|
if (!userInfo || !userInfo.departmentId || !userInfo.name) {
|
||||||
console.error('User information or department ID is missing in the configuration.');
|
this.log('User information or department ID is missing in the configuration.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,27 +55,27 @@ export class DepartmentSharer {
|
|||||||
// Get the list of active users from applicationInfo
|
// Get the list of active users from applicationInfo
|
||||||
const activeUsersId = await this.applicationInfo.readValue('active_users_info');
|
const activeUsersId = await this.applicationInfo.readValue('active_users_info');
|
||||||
if (!activeUsersId) {
|
if (!activeUsersId) {
|
||||||
console.error('No active users found.');
|
this.log('No active users found.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeUsers = await this.memoryManager.retrieveMetaInformation(activeUsersId);
|
const activeUsers = await this.memoryManager.retrieveMetaInformation(activeUsersId);
|
||||||
if (!activeUsers || activeUsers.length === 0) {
|
if (!activeUsers || activeUsers.length === 0) {
|
||||||
console.error('No active users found.');
|
this.log('No active users found.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter users who belong to the same department
|
// Filter users who belong to the same department
|
||||||
const departmentUsers = activeUsers.filter((user: any) => user.user_info.departmentId === departmentId);
|
const departmentUsers = activeUsers.filter((user: any) => user.user_info.departmentId === departmentId);
|
||||||
if (departmentUsers.length === 0) {
|
if (departmentUsers.length === 0) {
|
||||||
console.log('No users found in the same department.');
|
this.log('No users found in the same department.', 'log');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get department directory info
|
// Get department directory info
|
||||||
const departmentData = await this.applicationInfo.readValue('departmentDirectory');
|
const departmentData = await this.applicationInfo.readValue('departmentDirectory');
|
||||||
if (!departmentData || !departmentData.path || !departmentData.id) {
|
if (!departmentData || !departmentData.path || !departmentData.id) {
|
||||||
console.error('No department directory found.');
|
this.log('No department directory found.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +84,7 @@ export class DepartmentSharer {
|
|||||||
// Read files from the MemoryManager related to this department
|
// Read files from the MemoryManager related to this department
|
||||||
const departmentFiles = await this.memoryManager.retrieveMetaInformation(departmentData.id);
|
const departmentFiles = await this.memoryManager.retrieveMetaInformation(departmentData.id);
|
||||||
if (!departmentFiles || !departmentFiles.structure) {
|
if (!departmentFiles || !departmentFiles.structure) {
|
||||||
console.error('No files found for this department in the memory manager.');
|
this.log('No files found for this department in the memory manager.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +113,7 @@ export class DepartmentSharer {
|
|||||||
const response = await this.waitForResponse();
|
const response = await this.waitForResponse();
|
||||||
|
|
||||||
if (!response || response.operationCode !== operationCodes.OK){
|
if (!response || response.operationCode !== operationCodes.OK){
|
||||||
console.error('Failed to clear the department directory.');
|
this.log('Failed to clear the department directory.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,14 +123,14 @@ export class DepartmentSharer {
|
|||||||
// Send the files to a user in the department
|
// Send the files to a user in the department
|
||||||
private async sendFilesToUser(files: { [key: string]: string }, userName: string): Promise<void> {
|
private async sendFilesToUser(files: { [key: string]: string }, userName: string): Promise<void> {
|
||||||
if(!this.tcpCommunicator) return;
|
if(!this.tcpCommunicator) return;
|
||||||
const unsentFiles = Object.keys(files); // Keep track of unsent files
|
const unsentFiles = Object.keys(files);
|
||||||
|
|
||||||
for (const fileName of unsentFiles) {
|
for (const fileName of unsentFiles) {
|
||||||
const filePath = files[fileName];
|
const filePath = files[fileName];
|
||||||
|
|
||||||
// Ensure the file exists before attempting to send
|
// Ensure the file exists before attempting to send
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
console.error(`File not found: ${filePath}`);
|
this.log(`File not found: ${filePath}`, 'error');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,8 +143,8 @@ export class DepartmentSharer {
|
|||||||
|
|
||||||
// Prepare the metaInfo (same structure as FileSharer)
|
// Prepare the metaInfo (same structure as FileSharer)
|
||||||
const metaInfo = {
|
const metaInfo = {
|
||||||
userName, // Sender's username
|
userName,
|
||||||
relativeFilePath // Use the relative path to preserve directory structure
|
relativeFilePath
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send the file
|
// Send the file
|
||||||
@@ -153,7 +152,7 @@ export class DepartmentSharer {
|
|||||||
|
|
||||||
const response = await this.waitForResponse();
|
const response = await this.waitForResponse();
|
||||||
if (!response || response.operationCode !== operationCodes.OK) {
|
if (!response || response.operationCode !== operationCodes.OK) {
|
||||||
console.error(`Failed to send file: ${fileName}`);
|
this.log(`Failed to send file: ${fileName}`, 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,9 +167,19 @@ export class DepartmentSharer {
|
|||||||
if (!this.tcpCommunicator) return null;
|
if (!this.tcpCommunicator) return null;
|
||||||
if (this.tcpCommunicator.hasResponseArrived()) {
|
if (this.tcpCommunicator.hasResponseArrived()) {
|
||||||
clearInterval(idResponseCheck);
|
clearInterval(idResponseCheck);
|
||||||
resolve(this.tcpCommunicator.getLastResult()); // Resolve the response or null if not available
|
resolve(this.tcpCommunicator.getLastResult());
|
||||||
}
|
}
|
||||||
}, 100); // Check every 100 milliseconds if the response has arrived
|
}, 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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ export class DirectoryWatcher {
|
|||||||
private applicationInfo: JsonManager;
|
private applicationInfo: JsonManager;
|
||||||
private memoryManager: MemoryManager;
|
private memoryManager: MemoryManager;
|
||||||
private readonly sourceKey: string;
|
private readonly sourceKey: string;
|
||||||
private directoryWatcher: FSWatcher | null; // To store the watcher reference
|
private directoryWatcher: FSWatcher | null;
|
||||||
private totalSize: number; // To store total directory size
|
private totalSize: number;
|
||||||
|
private isBusy: boolean;
|
||||||
|
|
||||||
constructor(memoryManagerPath: string, applicationInfoPath: string, sourceKey: string) {
|
constructor(memoryManagerPath: string, applicationInfoPath: string, sourceKey: string) {
|
||||||
this.sourceKey = sourceKey;
|
this.sourceKey = sourceKey;
|
||||||
@@ -19,14 +20,28 @@ export class DirectoryWatcher {
|
|||||||
this.memoryManager = new MemoryManager(memoryManagerPath);
|
this.memoryManager = new MemoryManager(memoryManagerPath);
|
||||||
this.directoryMemoryId = '';
|
this.directoryMemoryId = '';
|
||||||
this.directoryPath = '';
|
this.directoryPath = '';
|
||||||
this.directoryWatcher = null; // Initialize with no watcher
|
this.directoryWatcher = null;
|
||||||
this.totalSize = 0; // Initialize size with zero
|
this.totalSize = 0;
|
||||||
|
this.isBusy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the watcher with a busy flag to prevent overlapping operations
|
||||||
|
async start(): Promise<void> {
|
||||||
|
setInterval(async () => {
|
||||||
|
if (!this.isBusy) {
|
||||||
|
this.isBusy = true;
|
||||||
|
const initialized = await this.initialize();
|
||||||
|
if (initialized) this.log('Directory watcher started successfully.');
|
||||||
|
this.isBusy = false;
|
||||||
|
}
|
||||||
|
}, 10000); // 10-second interval for testing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to initialize and validate the backup directory
|
// Method to initialize and validate the backup directory
|
||||||
async initialize(): Promise<boolean> {
|
async initialize(): Promise<boolean> {
|
||||||
const directoryData = await this.applicationInfo.readValue(this.sourceKey);
|
const directoryData = await this.applicationInfo.readValue(this.sourceKey);
|
||||||
if (!directoryData) {
|
if (!directoryData) {
|
||||||
|
this.log('Directory data not found in application info.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +49,7 @@ export class DirectoryWatcher {
|
|||||||
this.directoryMemoryId = directoryData.id;
|
this.directoryMemoryId = directoryData.id;
|
||||||
|
|
||||||
if (!this.directoryMemoryId || !this.directoryPath) {
|
if (!this.directoryMemoryId || !this.directoryPath) {
|
||||||
console.error('Components of entry in \'DirectoryWatcher\' not found.');
|
this.log('Components of entry in \'DirectoryWatcher\' not found.', 'error');
|
||||||
await this.applicationInfo.removeValue(this.sourceKey);
|
await this.applicationInfo.removeValue(this.sourceKey);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -65,7 +80,7 @@ export class DirectoryWatcher {
|
|||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const fullPath = path.join(dirPath, item.name);
|
const fullPath = path.join(dirPath, item.name);
|
||||||
const stats = await fs.stat(fullPath); // Get stats for each item
|
const stats = await fs.stat(fullPath);
|
||||||
|
|
||||||
if (item.isDirectory()) {
|
if (item.isDirectory()) {
|
||||||
// If it's a directory, recursively build its structure and accumulate size
|
// If it's a directory, recursively build its structure and accumulate size
|
||||||
@@ -75,7 +90,7 @@ export class DirectoryWatcher {
|
|||||||
} else if (item.isFile()) {
|
} else if (item.isFile()) {
|
||||||
// If it's a file, store its full path and accumulate size
|
// If it's a file, store its full path and accumulate size
|
||||||
directoryScheme[item.name] = fullPath;
|
directoryScheme[item.name] = fullPath;
|
||||||
totalSize += stats.size; // Add file size
|
totalSize += stats.size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +100,8 @@ export class DirectoryWatcher {
|
|||||||
// Restart the directory watcher, ensuring any previous watcher is closed
|
// Restart the directory watcher, ensuring any previous watcher is closed
|
||||||
private restartWatcher(): void {
|
private restartWatcher(): void {
|
||||||
if (this.directoryWatcher) {
|
if (this.directoryWatcher) {
|
||||||
console.log('Stopping existing watcher...');
|
this.log('Stopping existing watcher...');
|
||||||
this.directoryWatcher.close(); // Stop the existing watcher
|
this.directoryWatcher.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.startDirectoryWatcher();
|
this.startDirectoryWatcher();
|
||||||
@@ -100,7 +115,7 @@ export class DirectoryWatcher {
|
|||||||
|
|
||||||
this.directoryWatcher = watch(this.directoryPath, { recursive: true }, async (eventType, filename) => {
|
this.directoryWatcher = watch(this.directoryPath, { recursive: true }, async (eventType, filename) => {
|
||||||
if (filename) {
|
if (filename) {
|
||||||
console.log(`File change detected: ${eventType} - ${filename}`);
|
this.log(`File change detected: ${eventType} - ${filename}`);
|
||||||
// Rebuild the directory scheme and update memory
|
// Rebuild the directory scheme and update memory
|
||||||
const result = await this.buildDirectoryScheme(this.directoryPath);
|
const result = await this.buildDirectoryScheme(this.directoryPath);
|
||||||
this.directoryScheme = result.structure;
|
this.directoryScheme = result.structure;
|
||||||
@@ -111,19 +126,34 @@ export class DirectoryWatcher {
|
|||||||
totalSize: this.totalSize,
|
totalSize: this.totalSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Directory structure and size updated in memory.');
|
this.log('Directory structure and size updated in memory.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Watching for changes in: ${this.directoryPath}`);
|
this.log(`Watching for changes in: ${this.directoryPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the directory watcher
|
// Close the directory watcher
|
||||||
public closeWatcher(): void {
|
public closeWatcher(): void {
|
||||||
if (this.directoryWatcher) {
|
if (this.directoryWatcher) {
|
||||||
console.log(`Stopping watcher for ${this.directoryPath}`);
|
this.log(`Stopping watcher for ${this.directoryPath}`);
|
||||||
this.directoryWatcher.close();
|
this.directoryWatcher.close();
|
||||||
this.directoryWatcher = null; // Clear the reference after closing
|
this.directoryWatcher = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified logging function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const sourcePrefix = `[DirectoryWatcher] {${this.capitalize(this.sourceKey)}}`;
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${sourcePrefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${sourcePrefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capitalize the first letter of the sourceKey
|
||||||
|
private capitalize(str: string): string {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {QueueManager} from './queue_manager';
|
|
||||||
import {TcpCommunicator} from "./tcp_communicator"; // Updated to use TcpCommunicator
|
|
||||||
import {operationCodes} from '../network/operation_codes';
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import {compareFnFileItemTask, FileItemTask} from "../interfaces/file_item_task";
|
import { QueueManager } from './queue_manager';
|
||||||
import {ParsedMessage} from "../network/message_handler";
|
import { TcpCommunicator } from "./tcp_communicator";
|
||||||
|
import { operationCodes } from '../network/operation_codes';
|
||||||
|
import { compareFnFileItemTask, FileItemTask } from "../interfaces/file_item_task";
|
||||||
|
import { ParsedMessage } from "../network/message_handler";
|
||||||
|
|
||||||
interface FileSendTask {
|
interface FileSendTask {
|
||||||
ip: string;
|
ip: string;
|
||||||
@@ -28,7 +28,9 @@ export class FileSharer {
|
|||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
if (!this.isBusy) { // Check if the queue is already being processed
|
if (!this.isBusy) { // Check if the queue is already being processed
|
||||||
|
this.log("Start successfully. Processing the queue.");
|
||||||
await this.processQueue(); // Process the queue at regular intervals
|
await this.processQueue(); // Process the queue at regular intervals
|
||||||
|
this.log("Queue processing completed.");
|
||||||
}
|
}
|
||||||
}, 10000); // 10 seconds interval
|
}, 10000); // 10 seconds interval
|
||||||
}
|
}
|
||||||
@@ -36,7 +38,7 @@ export class FileSharer {
|
|||||||
// Method to process the queue
|
// Method to process the queue
|
||||||
private async processQueue(): Promise<void> {
|
private async processQueue(): Promise<void> {
|
||||||
if (this.isBusy) {
|
if (this.isBusy) {
|
||||||
console.log("Queue is already being processed. Skipping this interval.");
|
this.log("Queue is already being processed. Skipping this interval.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,15 +48,15 @@ export class FileSharer {
|
|||||||
const task = this.queueManager.peek();
|
const task = this.queueManager.peek();
|
||||||
|
|
||||||
if (task) {
|
if (task) {
|
||||||
console.log(task);
|
this.log(`Processing task for file: ${task.path} to IP: ${task.ip}`);
|
||||||
const success = await this.sendFile(task);
|
const success = await this.sendFile(task);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
console.error(`Failed to send file: ${task.path} to IP: ${task.ip}. Re-adding to queue.`);
|
this.log(`Failed to send file: ${task.path} to IP: ${task.ip}. Re-adding to queue.`, 'error');
|
||||||
this.queueManager.dequeue();
|
this.queueManager.dequeue();
|
||||||
this.queueManager.enqueue(task); // Re-add to queue if failed
|
this.queueManager.enqueue(task); // Re-add to queue if failed
|
||||||
} else {
|
} else {
|
||||||
console.log(`File sent successfully: ${task.path} to IP: ${task.ip}.`);
|
this.log(`File sent successfully: ${task.path} to IP: ${task.ip}`);
|
||||||
this.queueManager.dequeue();
|
this.queueManager.dequeue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,11 +66,11 @@ export class FileSharer {
|
|||||||
|
|
||||||
// Method to send the file to a specific IP using TcpCommunicator
|
// Method to send the file to a specific IP using TcpCommunicator
|
||||||
private async sendFile(task: FileSendTask): Promise<boolean> {
|
private async sendFile(task: FileSendTask): Promise<boolean> {
|
||||||
const {ip, path: filePath, userName} = task;
|
const { ip, path: filePath, userName } = task;
|
||||||
|
|
||||||
// Ensure the file exists before attempting to send
|
// Ensure the file exists before attempting to send
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
console.error(`File not found: ${filePath}`);
|
this.log(`File not found: ${filePath}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,17 +88,17 @@ export class FileSharer {
|
|||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
||||||
|
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
if (!await this.tcpCommunicator.connect()) {
|
||||||
console.error(`Failed to connect to IP: ${ip}`);
|
this.log(`Failed to connect to IP: ${ip}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Sending file: ${filePath} to IP: ${ip}`);
|
this.log(`Sending file: ${filePath} to IP: ${ip}`);
|
||||||
|
|
||||||
if (!await this.tcpCommunicator.sendMessage(operationCodes.SHARE_FILE, metaInfo, fileContent)) return false;
|
if (!await this.tcpCommunicator.sendMessage(operationCodes.SHARE_FILE, metaInfo, fileContent)) return false;
|
||||||
|
|
||||||
const response = await this.waitForResponse();
|
const response = await this.waitForResponse();
|
||||||
if (!response || response.operationCode !== operationCodes.OK) {
|
if (!response || response.operationCode !== operationCodes.OK) {
|
||||||
console.error(`Failed to send file: ${filePath} to IP: ${ip}`);
|
this.log(`Failed to send file: ${filePath} to IP: ${ip}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,4 +117,14 @@ export class FileSharer {
|
|||||||
}, 100); // Check every 100 milliseconds if the response has arrived
|
}, 100); // Check every 100 milliseconds if the response has arrived
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified logging function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const prefix = '[FileSharer]';
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
import {JsonManager} from "./json_manager";
|
||||||
|
import {UdpClient} from "../network/udp/udp_client";
|
||||||
|
import {parentPort} from "worker_threads";
|
||||||
|
import {TcpCommunicator} from "./tcp_communicator";
|
||||||
|
import {operationCodes} from "../network/operation_codes";
|
||||||
|
import {ParsedMessage} from "../network/message_handler";
|
||||||
|
|
||||||
|
export class NetworkScanner {
|
||||||
|
private applicationInfo: JsonManager;
|
||||||
|
private userConfig: JsonManager;
|
||||||
|
private readonly udpPort: number;
|
||||||
|
private readonly tcpPort: number;
|
||||||
|
private readonly okPage: string;
|
||||||
|
private readonly errorPage: string;
|
||||||
|
private readonly databaseResetPage: string;
|
||||||
|
private appStarted = false;
|
||||||
|
private intervalIds: NodeJS.Timeout[] = [];
|
||||||
|
|
||||||
|
// Flags to prevent overlapping executions
|
||||||
|
private ucCheckBusy = false;
|
||||||
|
private ipLookupBusy = false;
|
||||||
|
private sendLoginBusy = false;
|
||||||
|
|
||||||
|
constructor(applicationInfoPath: string, userConfigPath: string, udpPort: number, tcpPort: number, okPage: string, errorPage: string, databaseResetPage: string) {
|
||||||
|
this.applicationInfo = new JsonManager(applicationInfoPath);
|
||||||
|
this.userConfig = new JsonManager(userConfigPath);
|
||||||
|
this.udpPort = udpPort;
|
||||||
|
this.tcpPort = tcpPort;
|
||||||
|
this.okPage = okPage;
|
||||||
|
this.errorPage = errorPage;
|
||||||
|
this.databaseResetPage = databaseResetPage;
|
||||||
|
|
||||||
|
// Start tasks
|
||||||
|
this.startUCCheck();
|
||||||
|
this.startUserIPLookup();
|
||||||
|
this.sendLoginRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log helper function for consistent logging format
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log', methodName: string = ''): void {
|
||||||
|
const prefix = `[NetworkScanner${methodName ? `.${methodName}` : ''}]`;
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UC Check Task
|
||||||
|
private startUCCheck(interval: number = 5000): void {
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (this.ucCheckBusy) return;
|
||||||
|
this.ucCheckBusy = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.log('UC Check running...', 'log', 'startUCCheck');
|
||||||
|
const udpClient = new UdpClient(this.udpPort);
|
||||||
|
const aliveClients = await udpClient.getAliveClients();
|
||||||
|
const storedIp = await this.applicationInfo.readValue('serverIp');
|
||||||
|
const foundClient = aliveClients.length > 0;
|
||||||
|
|
||||||
|
if (foundClient) {
|
||||||
|
const ipAddress = aliveClients[0]; // Use the first alive client
|
||||||
|
|
||||||
|
if (!storedIp || storedIp !== ipAddress) {
|
||||||
|
await this.applicationInfo.writeValue('serverIp', ipAddress);
|
||||||
|
if (!this.appStarted) {
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.okPage });
|
||||||
|
}
|
||||||
|
this.appStarted = true;
|
||||||
|
} else if (!this.appStarted) {
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.okPage });
|
||||||
|
this.appStarted = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.errorPage });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.log(`Error checking UC: ${err}`, 'error', 'startUCCheck');
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.errorPage });
|
||||||
|
} finally {
|
||||||
|
this.ucCheckBusy = false;
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
|
||||||
|
this.intervalIds.push(intervalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP Lookup Task
|
||||||
|
private startUserIPLookup(interval: number = 10000): void {
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (this.ipLookupBusy) return;
|
||||||
|
this.ipLookupBusy = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.log('IP Lookup running...', 'log', 'startUserIPLookup');
|
||||||
|
const serverIp = await this.applicationInfo.readValue('serverIp');
|
||||||
|
const udpClient = new UdpClient(this.udpPort);
|
||||||
|
const activeIPs = await udpClient.getAliveClients();
|
||||||
|
const filteredIPs = activeIPs.filter(ip => ip !== serverIp);
|
||||||
|
|
||||||
|
// Save the filtered IPs to 'users_ip'
|
||||||
|
await this.applicationInfo.writeValue('users_ip', filteredIPs);
|
||||||
|
} catch (err) {
|
||||||
|
this.log(`Error during user IP lookup: ${err}`, 'error', 'startUserIPLookup');
|
||||||
|
} finally {
|
||||||
|
this.ipLookupBusy = false;
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
|
||||||
|
this.intervalIds.push(intervalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login Request Task
|
||||||
|
private sendLoginRequest(interval: number = 5000): void {
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (this.sendLoginBusy || this.appStarted) return;
|
||||||
|
this.sendLoginBusy = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userInfo = await this.userConfig.readValue('user_info');
|
||||||
|
if (!userInfo || !userInfo.email || !userInfo.password) {
|
||||||
|
this.log("Email or password not found in user config.", 'error', 'sendLoginRequest');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const app_type = await this.userConfig.readValue('app_type');
|
||||||
|
const email = userInfo.email;
|
||||||
|
const password = userInfo.password;
|
||||||
|
const serverIp = await this.applicationInfo.readValue('serverIp');
|
||||||
|
|
||||||
|
if (!serverIp) {
|
||||||
|
this.log("Server IP not found in application info.", 'error', 'sendLoginRequest');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tcpCommunicator = new TcpCommunicator(serverIp, this.tcpPort);
|
||||||
|
|
||||||
|
if (!await tcpCommunicator.connect()) {
|
||||||
|
this.log("Failed to connect to the server.", 'error', 'sendLoginRequest');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaInfo = { email, password, app_type };
|
||||||
|
if (!await tcpCommunicator.sendMessage(operationCodes.LOGIN, metaInfo)) {
|
||||||
|
this.log("Failed to send login request.", 'error', 'sendLoginRequest');
|
||||||
|
await tcpCommunicator.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.waitForResponse(tcpCommunicator);
|
||||||
|
if (response?.operationCode !== operationCodes.OK) {
|
||||||
|
await this.userConfig.resetFile();
|
||||||
|
await this.userConfig.writeValue('app_type', app_type);
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.databaseResetPage });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.log(`Error during login request: ${err}`, 'error', 'sendLoginRequest');
|
||||||
|
} finally {
|
||||||
|
this.sendLoginBusy = false;
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
|
||||||
|
this.intervalIds.push(intervalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to wait for a response from the TCP communicator
|
||||||
|
private async waitForResponse(tcpCommunicator: TcpCommunicator): Promise<ParsedMessage | null> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const checkInterval = setInterval(() => {
|
||||||
|
if (tcpCommunicator.hasResponseArrived()) {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
resolve(tcpCommunicator.getLastResult());
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to stop all intervals (for cleanup if needed)
|
||||||
|
public stopAllIntervals(): void {
|
||||||
|
for (const id of this.intervalIds) {
|
||||||
|
clearInterval(id);
|
||||||
|
}
|
||||||
|
this.log("All intervals have been stopped.", 'log', 'stopAllIntervals');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import { JsonManager } from "./json_manager";
|
|||||||
import { MemoryManager } from "./memory_manager";
|
import { MemoryManager } from "./memory_manager";
|
||||||
import { operationCodes } from "../network/operation_codes";
|
import { operationCodes } from "../network/operation_codes";
|
||||||
import { TcpCommunicator } from "./tcp_communicator";
|
import { TcpCommunicator } from "./tcp_communicator";
|
||||||
import {ParsedMessage} from "../network/message_handler";
|
import { ParsedMessage } from "../network/message_handler";
|
||||||
|
|
||||||
export class UsersInfoFetcher {
|
export class UsersInfoFetcher {
|
||||||
private applicationInfo: JsonManager;
|
private applicationInfo: JsonManager;
|
||||||
@@ -32,7 +32,7 @@ export class UsersInfoFetcher {
|
|||||||
private async initialize() {
|
private async initialize() {
|
||||||
const usersIps = await this.applicationInfo.readValue('users_ip');
|
const usersIps = await this.applicationInfo.readValue('users_ip');
|
||||||
if (!usersIps) {
|
if (!usersIps) {
|
||||||
console.error('No IP addresses found in users_ip');
|
this.log('No IP addresses found in users_ip', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,13 +54,13 @@ export class UsersInfoFetcher {
|
|||||||
for (const ip of usersIps) {
|
for (const ip of usersIps) {
|
||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
if (!await this.tcpCommunicator.connect()) {
|
||||||
console.error(`Failed to open connection for IP: ${ip}`);
|
this.log(`Failed to open connection for IP: ${ip}`, 'error');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await this.tcpCommunicator?.sendMessage(operationCodes.GET_USER_INFORMATION)){
|
if(!await this.tcpCommunicator?.sendMessage(operationCodes.GET_USER_INFORMATION)){
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the response for 10 seconds
|
// Wait for the response for 10 seconds
|
||||||
@@ -96,4 +96,14 @@ export class UsersInfoFetcher {
|
|||||||
private async updateActiveUsers(userInfo: any[]) {
|
private async updateActiveUsers(userInfo: any[]) {
|
||||||
await this.memoryManager.updateMetaInformation(this.memoryId, userInfo); // Update active users in memory
|
await this.memoryManager.updateMetaInformation(this.memoryId, userInfo); // Update active users in memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified logging function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const prefix = '[UsersInfoFetcher]';
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import path from 'path';
|
|||||||
export class WindowManager {
|
export class WindowManager {
|
||||||
private readonly mainWindow: BrowserWindow;
|
private readonly mainWindow: BrowserWindow;
|
||||||
private readonly pathToPagesDir: string;
|
private readonly pathToPagesDir: string;
|
||||||
|
private announcementWindow: BrowserWindow | null = null;
|
||||||
|
|
||||||
constructor(mainWindow: BrowserWindow, pathToPagesDir: string) {
|
constructor(mainWindow: BrowserWindow, pathToPagesDir: string) {
|
||||||
this.pathToPagesDir = pathToPagesDir;
|
this.pathToPagesDir = pathToPagesDir;
|
||||||
@@ -89,4 +90,46 @@ export class WindowManager {
|
|||||||
return undefined; // Return undefined if no file was selected
|
return undefined; // Return undefined if no file was selected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method to display an announcement in a new window
|
||||||
|
async displayAnnouncement(): Promise<void> {
|
||||||
|
if (this.announcementWindow) {
|
||||||
|
// If the window is already open, focus it
|
||||||
|
this.announcementWindow.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainScreen = require('electron').screen.getPrimaryDisplay();
|
||||||
|
const { width, height } = mainScreen.size;
|
||||||
|
|
||||||
|
// Initialize the announcement window
|
||||||
|
this.announcementWindow = new BrowserWindow({
|
||||||
|
width: width / 3,
|
||||||
|
height: height / 2,
|
||||||
|
resizable: false,
|
||||||
|
title: 'Announcement',
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, '..', 'main', 'preload.js'),
|
||||||
|
contextIsolation: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.announcementWindow.removeMenu();
|
||||||
|
|
||||||
|
// Load the announcement page
|
||||||
|
const announcementPath = path.join(this.pathToPagesDir, 'announcement.html');
|
||||||
|
await this.announcementWindow.loadFile(announcementPath);
|
||||||
|
|
||||||
|
// Handle window close
|
||||||
|
this.announcementWindow.on('closed', () => {
|
||||||
|
this.announcementWindow = null; // Clean up the reference
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeAnnouncementWindow(): Promise<void> {
|
||||||
|
if (this.announcementWindow) {
|
||||||
|
this.announcementWindow.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,16 +13,15 @@ export class WorkerManager {
|
|||||||
this.workers = []; // Initialize the array to store workers
|
this.workers = []; // Initialize the array to store workers
|
||||||
}
|
}
|
||||||
|
|
||||||
async startNetworkScannerWorker(udpPort: number, okPage: string, errorPage: string, applicationInfoPath: string): Promise<void> {
|
async startNetworkScannerWorker(udpPort: number, tcpPort: number, okPage: string, errorPage: string, databaseResetPage: string, userConfigPath: string, applicationInfoPath: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const worker = new Worker(path.join(this.pathToWorkerDir, 'network_scanner_worker.js'), {
|
const worker = new Worker(path.join(this.pathToWorkerDir, 'network_scanner_worker.js'), {
|
||||||
workerData: { udpPort, okPage, errorPage, applicationInfoPath }, // Pass necessary data to the worker
|
workerData: { udpPort, tcpPort, okPage, errorPage, databaseResetPage, userConfigPath, applicationInfoPath }, // Pass necessary data to the worker
|
||||||
});
|
});
|
||||||
|
|
||||||
this.workers.push(worker); // Store the worker reference
|
this.workers.push(worker); // Store the worker reference
|
||||||
|
|
||||||
worker.on('message', (data) => {
|
worker.on('message', (data) => {
|
||||||
console.log(data);
|
|
||||||
if (data.type === 'changeContent') {
|
if (data.type === 'changeContent') {
|
||||||
this.windowManager.changeContent(data.page);
|
this.windowManager.changeContent(data.page);
|
||||||
}
|
}
|
||||||
@@ -43,7 +42,7 @@ export class WorkerManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the Connection Pool Worker
|
// Start the Directories Watcher Worker
|
||||||
async startDirectoriesWatchersWorker(memoryManagerPath: string, applicationInfoPath: string): Promise<void> {
|
async startDirectoriesWatchersWorker(memoryManagerPath: string, applicationInfoPath: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const worker = new Worker(path.join(this.pathToWorkerDir, 'directories_watcher_worker.js'), {
|
const worker = new Worker(path.join(this.pathToWorkerDir, 'directories_watcher_worker.js'), {
|
||||||
@@ -53,18 +52,18 @@ export class WorkerManager {
|
|||||||
this.workers.push(worker); // Store the worker reference
|
this.workers.push(worker); // Store the worker reference
|
||||||
|
|
||||||
worker.on('message', (data) => {
|
worker.on('message', (data) => {
|
||||||
console.log('Connection Pool Worker message:', data);
|
console.log('DirectoriesWatcher message:', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('error', (err) => {
|
worker.on('error', (err) => {
|
||||||
console.error('Connection Pool Worker error:', err);
|
console.error('DirectoriesWatcher error:', err);
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
this.removeWorker(worker);
|
this.removeWorker(worker);
|
||||||
reject(err); // Reject the promise if there's an error
|
reject(err); // Reject the promise if there's an error
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('exit', (code) => {
|
worker.on('exit', (code) => {
|
||||||
console.log(`Connection Pool Worker exited with code ${code}`);
|
console.log(`DirectoriesWatcher exited with code ${code}`);
|
||||||
this.removeWorker(worker); // Remove worker reference when it exits
|
this.removeWorker(worker); // Remove worker reference when it exits
|
||||||
resolve(); // Resolve when the worker exits cleanly
|
resolve(); // Resolve when the worker exits cleanly
|
||||||
});
|
});
|
||||||
@@ -169,36 +168,6 @@ export class WorkerManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to start the Announcement Worker
|
|
||||||
async startAnnouncementWorker(applicationInfoPath: string, clientPort: number, message: string): Promise<void> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const worker = new Worker(path.join(this.pathToWorkerDir, 'send_announcement_worker.js'), {
|
|
||||||
workerData: { applicationInfoPath, clientPort, message }, // Pass necessary data to the worker
|
|
||||||
});
|
|
||||||
|
|
||||||
this.workers.push(worker); // Store the worker reference
|
|
||||||
|
|
||||||
worker.on('message', async (data) => {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000)); // Wait for 1 second
|
|
||||||
this.windowManager.changeContent('main_menu');
|
|
||||||
this.windowManager.showAlert(`${data.message}`)
|
|
||||||
});
|
|
||||||
|
|
||||||
worker.on('error', (err) => {
|
|
||||||
console.error('Announcement Worker error:', err);
|
|
||||||
worker.terminate();
|
|
||||||
this.removeWorker(worker);
|
|
||||||
reject(err); // Reject the promise if there's an error
|
|
||||||
});
|
|
||||||
|
|
||||||
worker.on('exit', (code) => {
|
|
||||||
console.log(`Announcement Worker exited with code ${code}`);
|
|
||||||
this.removeWorker(worker); // Remove worker reference when it exits
|
|
||||||
resolve(); // Resolve when the worker exits cleanly
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close all running workers
|
// Close all running workers
|
||||||
closeAllWorkers(): void {
|
closeAllWorkers(): void {
|
||||||
console.log('Terminating all running workers...');
|
console.log('Terminating all running workers...');
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export let operationCodes = {
|
|||||||
|
|
||||||
GET_USERS: 'GET_USERS',
|
GET_USERS: 'GET_USERS',
|
||||||
FIND_KEY_BY_USER_ID: 'FIND_KEY_BY_USER_ID',
|
FIND_KEY_BY_USER_ID: 'FIND_KEY_BY_USER_ID',
|
||||||
DELETE_USER: 'DELETE_USER',
|
|
||||||
SEND_ANNOUNCEMENT: 'SEND_ANNOUNCEMENT',
|
SEND_ANNOUNCEMENT: 'SEND_ANNOUNCEMENT',
|
||||||
|
|
||||||
GET_USER_INFORMATION: 'GET_USER_INFORMATION',
|
GET_USER_INFORMATION: 'GET_USER_INFORMATION',
|
||||||
|
|||||||
@@ -2,15 +2,17 @@ import { ParsedMessage } from '../message_handler';
|
|||||||
import { OperationBase } from '../operations_base/operation_base';
|
import { OperationBase } from '../operations_base/operation_base';
|
||||||
import { OperationHandler } from '../operations_base/operation_handler';
|
import { OperationHandler } from '../operations_base/operation_handler';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { FileEncryptor } from '../../helpers/file_encryptor';
|
|
||||||
|
|
||||||
const LOCK_FILE_EXTENSION = '.lock';
|
const LOCK_FILE_EXTENSION = '.lock';
|
||||||
|
|
||||||
export class UserToUserOperations extends OperationBase {
|
export class UserToUserOperations extends OperationBase {
|
||||||
public static readonly operationCodes = {
|
public static readonly operationCodes = {
|
||||||
...OperationBase.operationCodes, // Inherit common operation codes (OK, ERR, END)
|
...OperationBase.operationCodes, // Inherit common operation codes (OK, ERR, END)
|
||||||
|
SEND_ANNOUNCEMENT: 'SEND_ANNOUNCEMENT',
|
||||||
GET_USER_INFORMATION: 'GET_USER_INFORMATION',
|
GET_USER_INFORMATION: 'GET_USER_INFORMATION',
|
||||||
|
RESET_DATABASE: 'RESET_DATABASE',
|
||||||
BACKUP_FILE: 'BACKUP_FILE',
|
BACKUP_FILE: 'BACKUP_FILE',
|
||||||
CLEAR_BACKUP: 'CLEAR_BACKUP',
|
CLEAR_BACKUP: 'CLEAR_BACKUP',
|
||||||
SHARE_FILE: 'SHARE_FILE',
|
SHARE_FILE: 'SHARE_FILE',
|
||||||
@@ -32,7 +34,7 @@ export class UserToUserOperations extends OperationBase {
|
|||||||
|
|
||||||
// Read JSON file with a lock mechanism
|
// Read JSON file with a lock mechanism
|
||||||
static readJsonSync(filePath: string): any {
|
static readJsonSync(filePath: string): any {
|
||||||
const lockFilePath = `${filePath}${LOCK_FILE_EXTENSION}`;
|
const lockFilePath = `${filePath}.${LOCK_FILE_EXTENSION}`;
|
||||||
const absolutePath = path.resolve(filePath);
|
const absolutePath = path.resolve(filePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -65,6 +67,146 @@ export class UserToUserOperations extends OperationBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static writeJsonSync(filePath: string, data: any): boolean {
|
||||||
|
const lockFilePath = `${filePath}.${LOCK_FILE_EXTENSION}`;
|
||||||
|
const absolutePath = path.resolve(filePath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Loop until the lock file is removed by another process
|
||||||
|
while (fs.existsSync(lockFilePath)) {
|
||||||
|
console.log(`Waiting for lock file to be released: ${lockFilePath}`);
|
||||||
|
UserToUserOperations.sleep(100); // Use the sleep utility to pause
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create lock file to signal this process is working on the file
|
||||||
|
fs.writeFileSync(lockFilePath, ''); // Create the lock file
|
||||||
|
|
||||||
|
// Write the data to the JSON file
|
||||||
|
fs.writeFileSync(absolutePath, JSON.stringify(data, null, 2), 'utf-8');
|
||||||
|
console.log(`Data written successfully to ${absolutePath}`);
|
||||||
|
|
||||||
|
// Once processing is done, delete the lock file
|
||||||
|
fs.unlinkSync(lockFilePath); // Remove the lock file
|
||||||
|
|
||||||
|
return true; // Indicate successful write
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error writing JSON to ${filePath}:`, error);
|
||||||
|
|
||||||
|
// Ensure the lock file is removed even in case of an error
|
||||||
|
if (fs.existsSync(lockFilePath)) {
|
||||||
|
fs.unlinkSync(lockFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // Indicate failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static hasEnoughDiskSpace(directory: string, requiredPercentage: number): boolean {
|
||||||
|
try {
|
||||||
|
let availableSpace = 0;
|
||||||
|
let totalSpace = 0;
|
||||||
|
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
// Windows
|
||||||
|
const output = execSync(`wmic logicaldisk where "DeviceID='${directory[0]}:'" get FreeSpace,Size`).toString();
|
||||||
|
const lines = output.trim().split('\n');
|
||||||
|
const [freeSpaceStr, totalSpaceStr] = lines[1].trim().split(/\s+/);
|
||||||
|
availableSpace = parseInt(freeSpaceStr, 10); // Available space in bytes
|
||||||
|
totalSpace = parseInt(totalSpaceStr, 10); // Total space in bytes
|
||||||
|
} else {
|
||||||
|
// Unix-based (Linux/macOS)
|
||||||
|
const output = execSync(`df -k "${directory}"`).toString();
|
||||||
|
const lines = output.trim().split('\n');
|
||||||
|
const parts = lines[lines.length - 1].split(/\s+/);
|
||||||
|
const availableSpaceInKb = parseInt(parts[3], 10); // Available space in KB
|
||||||
|
const totalSpaceInKb = parseInt(parts[1], 10); // Total space in KB
|
||||||
|
availableSpace = availableSpaceInKb * 1024;
|
||||||
|
totalSpace = totalSpaceInKb * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate available space as a percentage of the total space
|
||||||
|
const availablePercentage = (availableSpace / totalSpace) * 100;
|
||||||
|
|
||||||
|
// Return true if the available percentage is greater than or equal to the required percentage
|
||||||
|
return availablePercentage >= requiredPercentage;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error checking disk space: ${error}`);
|
||||||
|
return false; // Return false if there's an error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static handleResetDatabase(parsedMessage: ParsedMessage): ParsedMessage {
|
||||||
|
// Path to the application.json file
|
||||||
|
const pathToApplicationJson = path.join(__dirname, '..', '..', 'json_files', 'application.json');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Read the existing data from application.json
|
||||||
|
const appData = UserToUserOperations.readJsonSync(pathToApplicationJson) || {};
|
||||||
|
|
||||||
|
// Update the reset_application_preferences field to true
|
||||||
|
appData.reset_application_preferences = true;
|
||||||
|
|
||||||
|
// Write the updated data back to application.json
|
||||||
|
const success = UserToUserOperations.writeJsonSync(pathToApplicationJson, appData);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
console.log(`Application preferences reset successfully.`);
|
||||||
|
return {
|
||||||
|
operationCode: UserToUserOperations.operationCodes.OK,
|
||||||
|
metaInfo: { message: 'Application preferences reset successfully.' },
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw new Error("Failed to write to application.json");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`Error resetting application preferences: ${error.message}`);
|
||||||
|
return {
|
||||||
|
operationCode: UserToUserOperations.operationCodes.ERR,
|
||||||
|
metaInfo: { message: `Error resetting application preferences: ${error.message}` },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static handleSendAnnouncement(parsedMessage: ParsedMessage): ParsedMessage {
|
||||||
|
// Ensure the message is available in metaInfo
|
||||||
|
if (!parsedMessage.metaInfo || !parsedMessage.metaInfo.message) {
|
||||||
|
return {
|
||||||
|
operationCode: UserToUserOperations.operationCodes.ERR,
|
||||||
|
metaInfo: { message: 'Missing announcement message in meta information.' },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const announcementMessage = parsedMessage.metaInfo.message;
|
||||||
|
|
||||||
|
// Path to the application.json file
|
||||||
|
const pathToApplicationJson = path.join(__dirname, '..', '..', 'json_files', 'application.json');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Read the existing data from application.json
|
||||||
|
const appData = UserToUserOperations.readJsonSync(pathToApplicationJson) || {};
|
||||||
|
|
||||||
|
// Update the announcement field with the new message
|
||||||
|
appData.announcement = announcementMessage;
|
||||||
|
|
||||||
|
// Write the updated data back to application.json
|
||||||
|
fs.writeFileSync(pathToApplicationJson, JSON.stringify(appData, null, 2), 'utf-8');
|
||||||
|
|
||||||
|
console.log(`Announcement message saved successfully: ${announcementMessage}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
operationCode: UserToUserOperations.operationCodes.OK,
|
||||||
|
metaInfo: { message: 'Announcement message saved successfully.' },
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`Error saving announcement message: ${error.message}`);
|
||||||
|
return {
|
||||||
|
operationCode: UserToUserOperations.operationCodes.ERR,
|
||||||
|
metaInfo: { message: `Error saving announcement message: ${error.message}` },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle user information retrieval
|
// Handle user information retrieval
|
||||||
public static handleGetUserInformation(parsedMessage: ParsedMessage): ParsedMessage {
|
public static handleGetUserInformation(parsedMessage: ParsedMessage): ParsedMessage {
|
||||||
const pathToUserJson = path.join(__dirname, '..', '..', 'json_files', 'userConfig.json');
|
const pathToUserJson = path.join(__dirname, '..', '..', 'json_files', 'userConfig.json');
|
||||||
@@ -109,6 +251,15 @@ export class UserToUserOperations extends OperationBase {
|
|||||||
const fullFilePath = path.join(baseBackupDir, userName, relativeFilePath);
|
const fullFilePath = path.join(baseBackupDir, userName, relativeFilePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Check if there is enough disk space
|
||||||
|
const requiredPercentage = 25;
|
||||||
|
if (!UserToUserOperations.hasEnoughDiskSpace(baseBackupDir, requiredPercentage)) {
|
||||||
|
return {
|
||||||
|
operationCode: UserToUserOperations.operationCodes.ERR,
|
||||||
|
metaInfo: { message: 'Insufficient disk space for backup.' },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the directory structure exists (create directories if they don't exist)
|
// Ensure the directory structure exists (create directories if they don't exist)
|
||||||
const dirPath = path.dirname(fullFilePath);
|
const dirPath = path.dirname(fullFilePath);
|
||||||
if (!fs.existsSync(dirPath)) {
|
if (!fs.existsSync(dirPath)) {
|
||||||
@@ -262,7 +413,7 @@ export class UserToUserOperations extends OperationBase {
|
|||||||
|
|
||||||
// Get the share directory path
|
// Get the share directory path
|
||||||
const baseDepartmentDir = appInfo.departmentDirectory.path;
|
const baseDepartmentDir = appInfo.departmentDirectory.path;
|
||||||
const userDepartmentDir = path.join(baseDepartmentDir, userName);
|
const userDepartmentDir = path.join(baseDepartmentDir, '..', 'DEPARTMENT_FILES', userName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if the user's backup directory exists
|
// Check if the user's backup directory exists
|
||||||
@@ -325,7 +476,7 @@ export class UserToUserOperations extends OperationBase {
|
|||||||
const departmentDirectory = appInfo.departmentDirectory.path;
|
const departmentDirectory = appInfo.departmentDirectory.path;
|
||||||
|
|
||||||
// Full path where the file will be stored (under the user's directory in the shared folder)
|
// Full path where the file will be stored (under the user's directory in the shared folder)
|
||||||
const fullFilePath = path.join(departmentDirectory, userName, relativeFilePath);
|
const fullFilePath = path.join(departmentDirectory, '..', 'DEPARTMENT_FILES', userName, relativeFilePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Ensure the directory structure exists (create directories if they don't exist)
|
// Ensure the directory structure exists (create directories if they don't exist)
|
||||||
@@ -484,6 +635,8 @@ export class UserToUserOperations extends OperationBase {
|
|||||||
// Register user-to-user operations with the OperationHandler
|
// Register user-to-user operations with the OperationHandler
|
||||||
public register(operationHandler: OperationHandler): void {
|
public register(operationHandler: OperationHandler): void {
|
||||||
// Register specific handlers for user-to-user operations
|
// Register specific handlers for user-to-user operations
|
||||||
|
operationHandler.registerHandler(UserToUserOperations.operationCodes.RESET_DATABASE, UserToUserOperations.handleResetDatabase);
|
||||||
|
operationHandler.registerHandler(UserToUserOperations.operationCodes.SEND_ANNOUNCEMENT, UserToUserOperations.handleSendAnnouncement);
|
||||||
operationHandler.registerHandler(UserToUserOperations.operationCodes.GET_USER_INFORMATION, UserToUserOperations.handleGetUserInformation);
|
operationHandler.registerHandler(UserToUserOperations.operationCodes.GET_USER_INFORMATION, UserToUserOperations.handleGetUserInformation);
|
||||||
operationHandler.registerHandler(UserToUserOperations.operationCodes.BACKUP_FILE, UserToUserOperations.handleBackupFile);
|
operationHandler.registerHandler(UserToUserOperations.operationCodes.BACKUP_FILE, UserToUserOperations.handleBackupFile);
|
||||||
operationHandler.registerHandler(UserToUserOperations.operationCodes.CLEAR_BACKUP, UserToUserOperations.handleClearBackup);
|
operationHandler.registerHandler(UserToUserOperations.operationCodes.CLEAR_BACKUP, UserToUserOperations.handleClearBackup);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Socket } from 'net';
|
import { Socket } from 'net';
|
||||||
import { createCipheriv, createDecipheriv } from 'crypto';
|
import { createCipheriv, createDecipheriv, constants, publicDecrypt } from 'crypto';
|
||||||
import { MessageHandler, ParsedMessage } from '../message_handler';
|
import {MessageHandler, ParsedMessage} from '../message_handler';
|
||||||
import { SocketCommunicatorBase } from './socket_communicator_base';
|
import { SocketCommunicatorBase } from './socket_communicator_base';
|
||||||
import { OperationHandler } from '../operations_base/operation_handler';
|
import { OperationHandler } from '../operations_base/operation_handler';
|
||||||
import {constants, publicDecrypt} from "node:crypto";
|
import { operationCodes } from '../operation_codes';
|
||||||
import {operationCodes} from "../operation_codes";
|
|
||||||
|
|
||||||
const END_OF_MESSAGE = '<EOM>'; // Define a unique marker for end of message
|
const END_OF_MESSAGE = '<EOM>'; // Unique marker for the end of message
|
||||||
|
const CHUNK_SIZE = 1024; // Define chunk size
|
||||||
|
|
||||||
export class TcpClientCommunicator extends SocketCommunicatorBase {
|
export class TcpClientCommunicator extends SocketCommunicatorBase {
|
||||||
private readonly socket: Socket;
|
private readonly socket: Socket;
|
||||||
@@ -15,30 +15,28 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
|
|||||||
private messageBuffer: string;
|
private messageBuffer: string;
|
||||||
private serverPublicKey: string | null;
|
private serverPublicKey: string | null;
|
||||||
private isAesKeySetFlag: boolean;
|
private isAesKeySetFlag: boolean;
|
||||||
|
private chunkBuffers: { [messageId: string]: string[] }; // Buffer for reassembling chunks
|
||||||
|
|
||||||
constructor(socket: Socket, ip: string, port: number, operationHandler: OperationHandler) {
|
constructor(socket: Socket, ip: string, port: number, operationHandler: OperationHandler) {
|
||||||
super(ip, port, operationHandler);
|
super(ip, port, operationHandler); // Call parent constructor
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.aesKey = null;
|
this.aesKey = null;
|
||||||
this.aesIv = null;
|
this.aesIv = null;
|
||||||
this.serverPublicKey = null;
|
this.serverPublicKey = null;
|
||||||
this.messageBuffer = ''; // Buffer for message reassembly
|
this.messageBuffer = ''; // Buffer for message reassembly
|
||||||
this.isAesKeySetFlag = false;
|
this.isAesKeySetFlag = false;
|
||||||
|
this.chunkBuffers = {}; // Initialize chunk buffer for reassembling messages
|
||||||
}
|
}
|
||||||
|
|
||||||
setServerPublicKey(publicKey: string): void {
|
setServerPublicKey(publicKey: string): void {
|
||||||
this.serverPublicKey = publicKey;
|
this.serverPublicKey = publicKey;
|
||||||
console.log('Server public key set.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the AES key when received
|
|
||||||
setAesKey(aesKey: string, aesIv: string): void {
|
setAesKey(aesKey: string, aesIv: string): void {
|
||||||
this.aesKey = Buffer.from(aesKey, 'base64');
|
this.aesKey = Buffer.from(aesKey, 'base64');
|
||||||
this.aesIv = Buffer.from(aesIv, 'base64');
|
this.aesIv = Buffer.from(aesIv, 'base64');
|
||||||
console.log('AES key set.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt a message with AES
|
|
||||||
private encryptWithAes(message: string): string {
|
private encryptWithAes(message: string): string {
|
||||||
if (!this.aesKey || !this.aesIv) {
|
if (!this.aesKey || !this.aesIv) {
|
||||||
throw new Error('AES key or IV is not set.');
|
throw new Error('AES key or IV is not set.');
|
||||||
@@ -65,62 +63,48 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const encryptedMessage = Buffer.from(message.toString(), 'base64');
|
const encryptedMessage = Buffer.from(message.toString(), 'base64');
|
||||||
// Decrypt the message using the server's public key
|
|
||||||
const decrypted = publicDecrypt(
|
const decrypted = publicDecrypt(
|
||||||
{
|
{
|
||||||
key: this.serverPublicKey,
|
key: this.serverPublicKey,
|
||||||
padding: constants.RSA_PKCS1_PADDING, // Matching padding for decryption
|
padding: constants.RSA_PKCS1_PADDING,
|
||||||
},
|
},
|
||||||
encryptedMessage
|
encryptedMessage
|
||||||
);
|
);
|
||||||
return decrypted.toString('utf-8');
|
return decrypted.toString('utf-8');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('RSA decryption failed:', error);
|
|
||||||
throw new Error('Failed to decrypt RSA message.');
|
throw new Error('Failed to decrypt RSA message.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle incoming chunks of data
|
|
||||||
async handleIncomingChunk(data: Buffer): Promise<void> {
|
|
||||||
const incomingMessage = data.toString();
|
|
||||||
this.messageBuffer += incomingMessage;
|
|
||||||
|
|
||||||
// Check if the message ends with END_OF_MESSAGE
|
|
||||||
if (this.messageBuffer.endsWith(END_OF_MESSAGE)) {
|
|
||||||
// Remove the END_OF_MESSAGE marker and process the message
|
|
||||||
const completeMessage = this.messageBuffer.slice(0, -END_OF_MESSAGE.length);
|
|
||||||
|
|
||||||
this.handleIncomingMessage(completeMessage);
|
|
||||||
|
|
||||||
// Clear the message buffer after processing
|
|
||||||
this.messageBuffer = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a chunked message over the socket
|
|
||||||
async sendChunkedMessage(operationCode: string, metaInfo?: { [key: string]: any }, fileContent?: Buffer): Promise<void> {
|
async sendChunkedMessage(operationCode: string, metaInfo?: { [key: string]: any }, fileContent?: Buffer): Promise<void> {
|
||||||
const message = MessageHandler.formatMessage(operationCode, metaInfo, fileContent);
|
const message = MessageHandler.formatMessage(operationCode, metaInfo, fileContent);
|
||||||
let outgoingMessage: string;
|
const outgoingMessage = this.encryptWithAes(message);
|
||||||
|
|
||||||
// Encrypt the message with AES if available
|
const totalChunks = Math.ceil(outgoingMessage.length / CHUNK_SIZE);
|
||||||
if (this.aesKey && this.aesIv) {
|
const messageId = Date.now().toString();
|
||||||
outgoingMessage = this.encryptWithAes(message);
|
|
||||||
} else {
|
for (let i = 0; i < totalChunks; i++) {
|
||||||
outgoingMessage = message // Send plain text if AES is not set
|
const chunk = outgoingMessage.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE);
|
||||||
|
const chunkHeader = JSON.stringify({
|
||||||
|
messageId,
|
||||||
|
sequenceNumber: i,
|
||||||
|
totalChunks,
|
||||||
|
});
|
||||||
|
|
||||||
|
const chunkWithHeader = `${chunkHeader}|${chunk}`;
|
||||||
|
|
||||||
|
await this.writeToSocket(chunkWithHeader);
|
||||||
|
|
||||||
|
if (i === totalChunks - 1) {
|
||||||
|
await this.writeToSocket(END_OF_MESSAGE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the end marker to the message
|
|
||||||
outgoingMessage += END_OF_MESSAGE;
|
|
||||||
|
|
||||||
await this.writeToSocket(outgoingMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write message to socket
|
|
||||||
private writeToSocket(message: string): Promise<void> {
|
private writeToSocket(message: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.socket.write(message, (err: any) => {
|
this.socket.write(message, (err: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error sending message over TCP:', err);
|
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
@@ -128,37 +112,69 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle incoming message (decrypted if AES is set)
|
async handleIncomingChunk(data: Buffer): Promise<void> {
|
||||||
|
const incomingMessage = data.toString();
|
||||||
|
this.messageBuffer += incomingMessage;
|
||||||
|
|
||||||
|
if (this.messageBuffer.includes(END_OF_MESSAGE)) {
|
||||||
|
const messages = this.messageBuffer.split(END_OF_MESSAGE);
|
||||||
|
|
||||||
|
for (let i = 0; i < messages.length - 1; i++) {
|
||||||
|
const completeMessage = messages[i];
|
||||||
|
if (completeMessage) {
|
||||||
|
this.processCompleteMessage(completeMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messageBuffer = messages[messages.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private processCompleteMessage(completeMessage: string): void {
|
||||||
|
const [headerJson, chunkContent] = completeMessage.split('|');
|
||||||
|
const header = JSON.parse(headerJson);
|
||||||
|
|
||||||
|
if (!this.chunkBuffers[header.messageId]) {
|
||||||
|
this.chunkBuffers[header.messageId] = new Array(header.totalChunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chunkBuffers[header.messageId][header.sequenceNumber] = chunkContent;
|
||||||
|
|
||||||
|
if (this.chunkBuffers[header.messageId].every((chunk) => chunk !== undefined)) {
|
||||||
|
const fullMessage = this.chunkBuffers[header.messageId].join('');
|
||||||
|
this.handleIncomingMessage(fullMessage);
|
||||||
|
delete this.chunkBuffers[header.messageId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleIncomingMessage(incomingMessage: string): void {
|
handleIncomingMessage(incomingMessage: string): void {
|
||||||
let messageToProcess = incomingMessage;
|
let messageToProcess = incomingMessage;
|
||||||
if (this.aesKey && this.aesIv) {
|
if (this.aesKey && this.aesIv) {
|
||||||
messageToProcess = this.decryptWithAes(incomingMessage);
|
messageToProcess = this.decryptWithAes(incomingMessage);
|
||||||
}else if(this.serverPublicKey){
|
} else if (this.serverPublicKey) {
|
||||||
messageToProcess = this.decryptWithRsa(incomingMessage);
|
messageToProcess = this.decryptWithRsa(incomingMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = this.operationHandler.handleOperation(messageToProcess);
|
const result = this.operationHandler.handleOperation(messageToProcess);
|
||||||
|
|
||||||
if(result.operationCode === operationCodes.SET_AES_KEY){
|
if (result.operationCode === operationCodes.SET_AES_KEY) {
|
||||||
this.isAesKeySetFlag = true;
|
this.isAesKeySetFlag = true;
|
||||||
this.setAesKey(result.metaInfo?.aesKey, result.metaInfo?.aesIv);
|
this.setAesKey(result.metaInfo?.aesKey, result.metaInfo?.aesIv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result.operationCode === operationCodes.SET_PUBLIC_KEY) {
|
if (result.operationCode === operationCodes.SET_PUBLIC_KEY) {
|
||||||
this.setServerPublicKey(result.metaInfo?.publicKey);
|
this.setServerPublicKey(result.metaInfo?.publicKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handlerResult = result
|
this.handlerResult = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if AES key is set
|
|
||||||
isAesKeySet(): boolean {
|
isAesKeySet(): boolean {
|
||||||
return this.isAesKeySetFlag;
|
return this.isAesKeySetFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get handler result for operation handling
|
|
||||||
getHandlerResult(): ParsedMessage | null {
|
getHandlerResult(): ParsedMessage | null {
|
||||||
return this.handlerResult;
|
return this.handlerResult;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Socket } from 'net';
|
import { Socket } from 'net';
|
||||||
import { privateEncrypt, privateDecrypt, generateKeyPairSync, createCipheriv, createDecipheriv, randomBytes } from 'crypto';
|
import { privateEncrypt, generateKeyPairSync, createCipheriv, createDecipheriv, randomBytes, constants } from 'crypto';
|
||||||
import { MessageHandler, ParsedMessage } from '../message_handler';
|
import { MessageHandler } from '../message_handler';
|
||||||
import { SocketCommunicatorBase } from './socket_communicator_base';
|
import { SocketCommunicatorBase } from './socket_communicator_base';
|
||||||
import { OperationHandler } from '../operations_base/operation_handler';
|
import { OperationHandler } from '../operations_base/operation_handler';
|
||||||
import {constants} from "node:crypto";
|
|
||||||
|
|
||||||
const END_OF_MESSAGE = '<EOM>'; // Define a unique marker for end of message
|
const END_OF_MESSAGE = '<EOM>'; // Define a unique marker for end of message
|
||||||
|
const CHUNK_SIZE = 1024; // Define chunk size
|
||||||
|
|
||||||
export class TcpServerCommunicator extends SocketCommunicatorBase {
|
export class TcpServerCommunicator extends SocketCommunicatorBase {
|
||||||
private readonly socket: Socket;
|
private readonly socket: Socket;
|
||||||
@@ -14,6 +14,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
|
|||||||
private aesKey: Buffer | null;
|
private aesKey: Buffer | null;
|
||||||
private aesIv: Buffer | null;
|
private aesIv: Buffer | null;
|
||||||
private messageBuffer: string;
|
private messageBuffer: string;
|
||||||
|
private chunkBuffers: { [messageId: string]: string[] }; // Buffer for reassembling chunks
|
||||||
|
|
||||||
constructor(socket: Socket, ip: string, port: number, operationHandler: OperationHandler) {
|
constructor(socket: Socket, ip: string, port: number, operationHandler: OperationHandler) {
|
||||||
super(ip, port, operationHandler);
|
super(ip, port, operationHandler);
|
||||||
@@ -23,6 +24,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
|
|||||||
this.aesKey = null;
|
this.aesKey = null;
|
||||||
this.aesIv = null;
|
this.aesIv = null;
|
||||||
this.messageBuffer = ''; // Initialize the message buffer
|
this.messageBuffer = ''; // Initialize the message buffer
|
||||||
|
this.chunkBuffers = {}; // Buffer for reassembling incoming messages
|
||||||
this.generateKeyPair(); // Generate RSA key pair for encryption
|
this.generateKeyPair(); // Generate RSA key pair for encryption
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +37,6 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
|
|||||||
});
|
});
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
console.log('RSA key pair generated.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the server's public key to the client
|
// Send the server's public key to the client
|
||||||
@@ -44,9 +45,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
|
|||||||
throw new Error('Public key is not available. Please generate RSA key pair.');
|
throw new Error('Public key is not available. Please generate RSA key pair.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = MessageHandler.formatMessage('SET_PUBLIC_KEY', { publicKey: this.publicKey });
|
await this.sendChunkedMessage('SET_PUBLIC_KEY', { publicKey: this.publicKey });
|
||||||
await this.writeToSocket(message + END_OF_MESSAGE);
|
|
||||||
console.log('Public key sent to client.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate AES key and IV, then send them to the client
|
// Generate AES key and IV, then send them to the client
|
||||||
@@ -57,16 +56,11 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
|
|||||||
const aesKeyBase64 = this.aesKey.toString('base64');
|
const aesKeyBase64 = this.aesKey.toString('base64');
|
||||||
const aesIvBase64 = this.aesIv.toString('base64');
|
const aesIvBase64 = this.aesIv.toString('base64');
|
||||||
|
|
||||||
const formattedMessage = MessageHandler.formatMessage('SET_AES_KEY', { aesKey: aesKeyBase64, aesIv: aesIvBase64 });
|
await this.sendChunkedMessage('SET_AES_KEY', { aesKey: aesKeyBase64, aesIv: aesIvBase64 });
|
||||||
const encryptedMessage = this.encryptWithRsa(Buffer.from(formattedMessage)).toString('base64');
|
|
||||||
|
|
||||||
await this.writeToSocket(encryptedMessage + END_OF_MESSAGE);
|
|
||||||
console.log('AES key and IV sent to client.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt a message with the server's private key (RSA encryption)
|
// Encrypt a message with the server's private key (RSA encryption)
|
||||||
private encryptWithRsa(message: Buffer): Buffer {
|
private encryptWithRsa(message: string): string {
|
||||||
const bufferMessage = Buffer.isBuffer(message) ? message : Buffer.from(message);
|
|
||||||
if (!this.privateKey) throw new Error('Server private key not set.');
|
if (!this.privateKey) throw new Error('Server private key not set.');
|
||||||
|
|
||||||
return privateEncrypt(
|
return privateEncrypt(
|
||||||
@@ -74,8 +68,8 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
|
|||||||
key: this.privateKey,
|
key: this.privateKey,
|
||||||
padding: constants.RSA_PKCS1_PADDING, // PKCS1 padding
|
padding: constants.RSA_PKCS1_PADDING, // PKCS1 padding
|
||||||
},
|
},
|
||||||
bufferMessage
|
Buffer.from(message)
|
||||||
);
|
).toString('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt AES-encrypted messages
|
// Decrypt AES-encrypted messages
|
||||||
@@ -106,17 +100,34 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
|
|||||||
const incomingMessage = data.toString();
|
const incomingMessage = data.toString();
|
||||||
this.messageBuffer += incomingMessage;
|
this.messageBuffer += incomingMessage;
|
||||||
|
|
||||||
// Check if the message ends with END_OF_MESSAGE
|
if (this.messageBuffer.includes(END_OF_MESSAGE)) {
|
||||||
if (this.messageBuffer.endsWith(END_OF_MESSAGE)) {
|
const messages = this.messageBuffer.split(END_OF_MESSAGE);
|
||||||
// Remove the END_OF_MESSAGE marker and process the message
|
|
||||||
const completeMessage = this.messageBuffer.slice(0, -END_OF_MESSAGE.length);
|
|
||||||
|
|
||||||
console.log(`\n\nComplete Message:\n${completeMessage}\n\n`);
|
for (let i = 0; i < messages.length - 1; i++) {
|
||||||
|
const completeMessage = messages[i];
|
||||||
|
if (completeMessage) {
|
||||||
|
this.processCompleteMessage(completeMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.handleIncomingMessage(completeMessage);
|
this.messageBuffer = messages[messages.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the message buffer after processing
|
private processCompleteMessage(completeMessage: string): void {
|
||||||
this.messageBuffer = '';
|
const [headerJson, chunkContent] = completeMessage.split('|');
|
||||||
|
const header = JSON.parse(headerJson);
|
||||||
|
|
||||||
|
if (!this.chunkBuffers[header.messageId]) {
|
||||||
|
this.chunkBuffers[header.messageId] = new Array(header.totalChunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chunkBuffers[header.messageId][header.sequenceNumber] = chunkContent;
|
||||||
|
|
||||||
|
if (this.chunkBuffers[header.messageId].every((chunk) => chunk !== undefined)) {
|
||||||
|
const fullMessage = this.chunkBuffers[header.messageId].join('');
|
||||||
|
this.handleIncomingMessage(fullMessage);
|
||||||
|
delete this.chunkBuffers[header.messageId];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,14 +136,36 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
|
|||||||
const message = MessageHandler.formatMessage(operationCode, metaInfo, fileContent);
|
const message = MessageHandler.formatMessage(operationCode, metaInfo, fileContent);
|
||||||
let outgoingMessage: string;
|
let outgoingMessage: string;
|
||||||
|
|
||||||
if (this.aesKey && this.aesIv) {
|
switch(operationCode) {
|
||||||
outgoingMessage = this.encryptWithAes(message);
|
case 'SET_PUBLIC_KEY':
|
||||||
} else {
|
outgoingMessage = message;
|
||||||
outgoingMessage = message;
|
break;
|
||||||
|
case 'SET_AES_KEY':
|
||||||
|
outgoingMessage = this.encryptWithRsa(message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
outgoingMessage = this.encryptWithAes(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
outgoingMessage += END_OF_MESSAGE; // Append end-of-message marker
|
const totalChunks = Math.ceil(outgoingMessage.length / CHUNK_SIZE);
|
||||||
await this.writeToSocket(outgoingMessage);
|
const messageId = Date.now().toString();
|
||||||
|
|
||||||
|
for (let i = 0; i < totalChunks; i++) {
|
||||||
|
const chunk = outgoingMessage.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE);
|
||||||
|
const chunkHeader = JSON.stringify({
|
||||||
|
messageId,
|
||||||
|
sequenceNumber: i,
|
||||||
|
totalChunks,
|
||||||
|
});
|
||||||
|
|
||||||
|
const chunkWithHeader = `${chunkHeader}|${chunk}`;
|
||||||
|
|
||||||
|
await this.writeToSocket(chunkWithHeader);
|
||||||
|
|
||||||
|
if (i === totalChunks - 1) {
|
||||||
|
await this.writeToSocket(END_OF_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle incoming message (decrypt with AES if available)
|
// Handle incoming message (decrypt with AES if available)
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ export class TcpClient {
|
|||||||
this.socket = new net.Socket();
|
this.socket = new net.Socket();
|
||||||
|
|
||||||
this.socket.connect(this.tcp_port, ip, () => {
|
this.socket.connect(this.tcp_port, ip, () => {
|
||||||
console.log(`Client connected to server at ${ip}:${this.tcp_port}`);
|
//console.log(`Client connected to server at ${ip}:${this.tcp_port}`);
|
||||||
this.communicator = new TcpClientCommunicator(this.socket as Socket, ip, this.tcp_port, this.operationHandler);
|
this.communicator = new TcpClientCommunicator(this.socket as Socket, ip, this.tcp_port, this.operationHandler);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('error', (err) => {
|
this.socket.on('error', (err) => {
|
||||||
console.error(`Connection error to server at ${ip}:${this.tcp_port}: ${err.message}`);
|
//console.error(`Connection error to server at ${ip}:${this.tcp_port}: ${err.message}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('data', async (data: Buffer) => {
|
this.socket.on('data', async (data: Buffer) => {
|
||||||
@@ -45,7 +45,7 @@ export class TcpClient {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('close', () => {
|
this.socket.on('close', () => {
|
||||||
console.log(`Connection closed: ${ip}:${this.tcp_port}`);
|
//console.log(`Connection closed: ${ip}:${this.tcp_port}`);
|
||||||
this.lastResult = null; // Clear the last result on socket close
|
this.lastResult = null; // Clear the last result on socket close
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -57,14 +57,14 @@ export class TcpClient {
|
|||||||
this.socket = null;
|
this.socket = null;
|
||||||
this.communicator = null;
|
this.communicator = null;
|
||||||
this.lastResult = null; // Clear the last result on close
|
this.lastResult = null; // Clear the last result on close
|
||||||
console.log('Client socket connection closed.');
|
//console.log('Client socket connection closed.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a message with operationCode, metaInfo, and fileContent in chunks
|
// Send a message with operationCode, metaInfo, and fileContent in chunks
|
||||||
async sendMessage(operationCode: string, metaInfo?: { [key: string]: any }, fileContent?: Buffer): Promise<boolean> {
|
async sendMessage(operationCode: string, metaInfo?: { [key: string]: any }, fileContent?: Buffer): Promise<boolean> {
|
||||||
if (!this.communicator || !this.isAesKeySet()) {
|
if (!this.communicator || !this.isAesKeySet()) {
|
||||||
console.error('Communicator not initialized or AES key not set.');
|
//console.error('Communicator not initialized or AES key not set.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export class TcpServer {
|
|||||||
const port = socket.remotePort || 0;
|
const port = socket.remotePort || 0;
|
||||||
|
|
||||||
const clientId = `${ip}:${port}`; // Use IP and port to identify the client
|
const clientId = `${ip}:${port}`; // Use IP and port to identify the client
|
||||||
console.log(`Client connected: ${clientId}`);
|
//console.log(`Client connected: ${clientId}`);
|
||||||
|
|
||||||
const tcpCommunicator = new TcpServerCommunicator(socket, ip, port, this.operationHandler);
|
const tcpCommunicator = new TcpServerCommunicator(socket, ip, port, this.operationHandler);
|
||||||
this.connectionManager.addConnection(ip, port, tcpCommunicator);
|
this.connectionManager.addConnection(ip, port, tcpCommunicator);
|
||||||
@@ -44,9 +44,8 @@ export class TcpServer {
|
|||||||
tcpCommunicator.generateKeyPair();
|
tcpCommunicator.generateKeyPair();
|
||||||
tcpCommunicator.sendPublicKey()
|
tcpCommunicator.sendPublicKey()
|
||||||
.then(() => tcpCommunicator.sendAesKey())
|
.then(() => tcpCommunicator.sendAesKey())
|
||||||
.then(() => console.log('Public key and AES key sent successfully.'))
|
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(`Error during key exchange with client ${clientId}:`, err);
|
//console.error(`Error during key exchange with client ${clientId}:`, err);
|
||||||
socket.end(); // Close the connection in case of any error
|
socket.end(); // Close the connection in case of any error
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,13 +56,13 @@ export class TcpServer {
|
|||||||
|
|
||||||
// Handle client disconnect
|
// Handle client disconnect
|
||||||
socket.on('end', () => {
|
socket.on('end', () => {
|
||||||
console.log(`Client disconnected: ${clientId}`);
|
//console.log(`Client disconnected: ${clientId}`);
|
||||||
this.connectionManager.removeCommunicator(ip, port);
|
this.connectionManager.removeCommunicator(ip, port);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle socket errors
|
// Handle socket errors
|
||||||
socket.on('error', (err: Error) => {
|
socket.on('error', (err: Error) => {
|
||||||
console.error(`Error from client ${clientId}: ${err.message}`);
|
//console.error(`Error from client ${clientId}: ${err.message}`);
|
||||||
this.connectionManager.removeCommunicator(ip, port);
|
this.connectionManager.removeCommunicator(ip, port);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -86,7 +85,7 @@ export class TcpServer {
|
|||||||
// Retrieve the communicator associated with this connection
|
// Retrieve the communicator associated with this connection
|
||||||
const communicator = this.connectionManager.getCommunicator(ip, port) as TcpServerCommunicator;
|
const communicator = this.connectionManager.getCommunicator(ip, port) as TcpServerCommunicator;
|
||||||
if (!communicator) {
|
if (!communicator) {
|
||||||
console.error(`No communicator found for ${clientId}`);
|
//console.error(`No communicator found for ${clientId}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,9 +101,9 @@ export class TcpServer {
|
|||||||
handlerResult.metaInfo,
|
handlerResult.metaInfo,
|
||||||
handlerResult.fileContent
|
handlerResult.fileContent
|
||||||
);
|
);
|
||||||
console.log(`Response sent to ${clientId}`);
|
//console.log(`Response sent to ${clientId}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Failed to send response to ${clientId}:`, err);
|
//console.error(`Failed to send response to ${clientId}:`, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class UdpServer {
|
|||||||
const ip = rinfo.address;
|
const ip = rinfo.address;
|
||||||
const port = rinfo.port;
|
const port = rinfo.port;
|
||||||
|
|
||||||
console.log(`Received message from ${ip}:${port}`);
|
//console.log(`Received message from ${ip}:${port}`);
|
||||||
|
|
||||||
// Create a temporary communicator for the incoming message
|
// Create a temporary communicator for the incoming message
|
||||||
const communicator = new UdpSocketCommunicator(this.udpServer, ip, port, this.operationHandler);
|
const communicator = new UdpSocketCommunicator(this.udpServer, ip, port, this.operationHandler);
|
||||||
@@ -46,8 +46,6 @@ export class UdpServer {
|
|||||||
if (communicatorResult) {
|
if (communicatorResult) {
|
||||||
// Send response back to the client using the temporary communicator
|
// Send response back to the client using the temporary communicator
|
||||||
await communicator.sendMessage(communicatorResult.operationCode, communicatorResult.metaInfo);
|
await communicator.sendMessage(communicatorResult.operationCode, communicatorResult.metaInfo);
|
||||||
} else {
|
|
||||||
console.error(`No handler result for ${ip}:${port}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,37 +7,11 @@ const {
|
|||||||
} = workerData;
|
} = workerData;
|
||||||
|
|
||||||
const backupDirectoryManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'backupDirectory');
|
const backupDirectoryManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'backupDirectory');
|
||||||
|
backupDirectoryManager.start();
|
||||||
|
|
||||||
const departmentShareManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'departmentDirectory');
|
const departmentShareManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'departmentDirectory');
|
||||||
|
departmentShareManager.start();
|
||||||
|
|
||||||
const shareFileManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'shareDirectory');
|
const shareFileManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'shareDirectory');
|
||||||
|
shareFileManager.start();
|
||||||
// Backup directory manager check loop
|
|
||||||
const backupIntervalId = setInterval(async () => {
|
|
||||||
if (await backupDirectoryManager.initialize()) {
|
|
||||||
clearInterval(backupIntervalId); // Stop the loop once initialized
|
|
||||||
console.log('BackupDirectoryManager successfully initialized.');
|
|
||||||
} else {
|
|
||||||
console.log('Retrying BackupDirectoryManager initialization...');
|
|
||||||
}
|
|
||||||
}, 10000); // Check every 60 seconds
|
|
||||||
|
|
||||||
|
|
||||||
// Department share manager check loop
|
|
||||||
const departmentIntervalId = setInterval(async () => {
|
|
||||||
if (await departmentShareManager.initialize()) {
|
|
||||||
clearInterval(departmentIntervalId); // Stop the loop once initialized
|
|
||||||
console.log('DepartmentShareManager successfully initialized.');
|
|
||||||
} else {
|
|
||||||
console.log('Retrying DepartmentShareManager initialization...');
|
|
||||||
}
|
|
||||||
}, 10000); // Check every 60 seconds
|
|
||||||
|
|
||||||
// Department share manager check loop
|
|
||||||
const shareIntervalId = setInterval(async () => {
|
|
||||||
if (await shareFileManager.initialize()) {
|
|
||||||
clearInterval(shareIntervalId); // Stop the loop once initialized
|
|
||||||
console.log('DepartmentShareManager successfully initialized.');
|
|
||||||
} else {
|
|
||||||
console.log('Retrying DepartmentShareManager initialization...');
|
|
||||||
}
|
|
||||||
}, 10000); // Check every 60 seconds
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,94 +1,19 @@
|
|||||||
import { parentPort, workerData } from 'worker_threads';
|
import { parentPort, workerData } from 'worker_threads';
|
||||||
import { JsonManager } from '../helpers/json_manager';
|
import { NetworkScanner } from '../helpers/network_scanner';
|
||||||
import { UdpClient } from '../network/udp/udp_client';
|
|
||||||
|
|
||||||
// Define the structure of workerData
|
// Define the structure of workerData
|
||||||
interface WorkerData {
|
interface WorkerData {
|
||||||
udpPort: number;
|
udpPort: number;
|
||||||
|
tcpPort: number;
|
||||||
okPage: string;
|
okPage: string;
|
||||||
errorPage: string;
|
errorPage: string;
|
||||||
|
databaseResetPage: string;
|
||||||
|
userConfigPath: string;
|
||||||
applicationInfoPath: string;
|
applicationInfoPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the data passed to the worker
|
// Extract the data passed to the worker
|
||||||
const { udpPort, okPage, errorPage, applicationInfoPath }: WorkerData = workerData;
|
const { udpPort, tcpPort, okPage, errorPage, databaseResetPage, userConfigPath, applicationInfoPath }: WorkerData = workerData;
|
||||||
|
|
||||||
// Create a JsonManager instance for application info
|
// Start the NetworkScanner instance
|
||||||
const applicationInfo = new JsonManager(applicationInfoPath);
|
const networkScanner = new NetworkScanner(applicationInfoPath, userConfigPath, udpPort, tcpPort, okPage, errorPage, databaseResetPage);
|
||||||
let appStarted = false;
|
|
||||||
let intervalIds: NodeJS.Timeout[] = []; // Store interval IDs for future clearing
|
|
||||||
|
|
||||||
// Flags to prevent overlapping executions
|
|
||||||
let ucCheckBusy = false;
|
|
||||||
let ipLookupBusy = false;
|
|
||||||
|
|
||||||
// Function to schedule the UC check task with dynamic UDP client creation
|
|
||||||
function startUCCheck(udpPort: number, okPage: string, errorPage: string, interval: number = 5000): void {
|
|
||||||
const intervalId = setInterval(async () => {
|
|
||||||
if (ucCheckBusy) return; // If already running, skip this iteration
|
|
||||||
ucCheckBusy = true; // Mark as busy
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('UC Check running...');
|
|
||||||
const udpClient = new UdpClient(udpPort); // Create UdpClient with the provided port
|
|
||||||
const aliveClients = await udpClient.getAliveClients();
|
|
||||||
const storedIp = await applicationInfo.readValue('serverIp');
|
|
||||||
const foundClient = aliveClients.length > 0;
|
|
||||||
|
|
||||||
if (foundClient) {
|
|
||||||
const ipAddress = aliveClients[0]; // Just using the first alive client
|
|
||||||
|
|
||||||
if (!storedIp || storedIp !== ipAddress) {
|
|
||||||
await applicationInfo.writeValue('serverIp', ipAddress);
|
|
||||||
if (!appStarted) {
|
|
||||||
parentPort?.postMessage({ type: 'changeContent', page: okPage });
|
|
||||||
}
|
|
||||||
appStarted = true;
|
|
||||||
} else if (!appStarted) {
|
|
||||||
parentPort?.postMessage({ type: 'changeContent', page: okPage });
|
|
||||||
appStarted = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parentPort?.postMessage({ type: 'changeContent', page: errorPage });
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error checking UC:', err);
|
|
||||||
parentPort?.postMessage({ type: 'changeContent', page: errorPage });
|
|
||||||
} finally {
|
|
||||||
ucCheckBusy = false; // Mark as not busy
|
|
||||||
}
|
|
||||||
}, interval);
|
|
||||||
|
|
||||||
intervalIds.push(intervalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to schedule the IP lookup task, storing the active addresses in memory
|
|
||||||
function startUserIPLookup(udpPort: number, interval: number = 10000): void {
|
|
||||||
const intervalId = setInterval(async () => {
|
|
||||||
if (ipLookupBusy) return; // If already running, skip this iteration
|
|
||||||
ipLookupBusy = true; // Mark as busy
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('IP Lookup running...');
|
|
||||||
const serverIp = await applicationInfo.readValue('serverIp');
|
|
||||||
const udpClient = new UdpClient(udpPort); // Create a UDP client with the provided port
|
|
||||||
const activeIPs = await udpClient.getAliveClients(); // Get the list of active IPs
|
|
||||||
|
|
||||||
// Filter out the serverIp from the list of active clients
|
|
||||||
const filteredIPs = activeIPs.filter(ip => ip !== serverIp);
|
|
||||||
|
|
||||||
// Save the filtered IPs to 'users_ip'
|
|
||||||
await applicationInfo.writeValue('users_ip', filteredIPs);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error during user IP lookup:', err);
|
|
||||||
} finally {
|
|
||||||
ipLookupBusy = false; // Mark as not busy
|
|
||||||
}
|
|
||||||
}, interval);
|
|
||||||
|
|
||||||
intervalIds.push(intervalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the UC Check and User IP Lookup tasks
|
|
||||||
startUCCheck(udpPort, okPage, errorPage);
|
|
||||||
startUserIPLookup(udpPort);
|
|
||||||
|
|||||||
@@ -8,45 +8,15 @@ import {DepartmentSharer} from "../helpers/department_sharer";
|
|||||||
const { usersConfigPath, applicationInfoPath, memoryManagerPath, queueManagerPath, tcpPort } = workerData;
|
const { usersConfigPath, applicationInfoPath, memoryManagerPath, queueManagerPath, tcpPort } = workerData;
|
||||||
|
|
||||||
const usersInfoFetcher = new UsersInfoFetcher(applicationInfoPath, memoryManagerPath, tcpPort);
|
const usersInfoFetcher = new UsersInfoFetcher(applicationInfoPath, memoryManagerPath, tcpPort);
|
||||||
usersInfoFetcher.start()
|
usersInfoFetcher.start();
|
||||||
.then(() => {
|
|
||||||
console.log('Users Info Fetcher started successfully');
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error('Error starting Users Info Fetcher:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
const backupManager = new BackupRetrievalWorker(
|
const backupManager = new BackupRetrievalWorker(usersConfigPath, applicationInfoPath, memoryManagerPath, tcpPort);
|
||||||
usersConfigPath,
|
backupManager.start();
|
||||||
applicationInfoPath,
|
|
||||||
memoryManagerPath,
|
|
||||||
tcpPort
|
|
||||||
);
|
|
||||||
|
|
||||||
backupManager.start()
|
|
||||||
.then(() => {
|
|
||||||
console.log('Backup Manager started successfully');
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error('Error starting Backup Manager:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
const fileSharer = new FileSharer(queueManagerPath, tcpPort);
|
const fileSharer = new FileSharer(queueManagerPath, tcpPort);
|
||||||
fileSharer.start()
|
fileSharer.start();
|
||||||
.then(() => {
|
|
||||||
console.log('File Sharer started successfully');
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error('Error starting File Sharer:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const departmentSharer = new DepartmentSharer(usersConfigPath, applicationInfoPath, memoryManagerPath, tcpPort);
|
const departmentSharer = new DepartmentSharer(usersConfigPath, applicationInfoPath, memoryManagerPath, tcpPort);
|
||||||
departmentSharer.start()
|
departmentSharer.start();
|
||||||
.then(() => {
|
|
||||||
console.log('Department Sharer started successfully');
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error('Error starting Department Sharer:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { workerData } from 'worker_threads';
|
|
||||||
import { AnnouncementSender} from "../helpers/announcement_sender";
|
|
||||||
|
|
||||||
// Destructure data passed from the main thread
|
|
||||||
const {
|
|
||||||
applicationInfoPath,
|
|
||||||
clientPort,
|
|
||||||
message
|
|
||||||
}: {
|
|
||||||
applicationInfoPath: string,
|
|
||||||
clientPort: number,
|
|
||||||
message: string
|
|
||||||
} = workerData;
|
|
||||||
|
|
||||||
// Initialize the AnnouncementWorker
|
|
||||||
const announcementWorker = new AnnouncementSender(applicationInfoPath, clientPort);
|
|
||||||
|
|
||||||
// Start the announcement process and handle results
|
|
||||||
announcementWorker.start(message);
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
|
import { JsonManager } from './json_manager';
|
||||||
import { TcpCommunicator } from "./tcp_communicator";
|
import { TcpCommunicator } from "./tcp_communicator";
|
||||||
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
|
import { operationCodes } from '../network/operation_codes';
|
||||||
import { parentPort } from 'worker_threads';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
@@ -15,15 +14,30 @@ export class BackupRetrievalWorker {
|
|||||||
private encryptionKey: Buffer | null = null;
|
private encryptionKey: Buffer | null = null;
|
||||||
private iv: Buffer | null = null;
|
private iv: Buffer | null = null;
|
||||||
private tcpCommunicator: TcpCommunicator | null = null;
|
private tcpCommunicator: TcpCommunicator | null = null;
|
||||||
|
private isBusy: boolean;
|
||||||
|
private lastProcessedUserIndex: number;
|
||||||
|
|
||||||
constructor(userConfigPath: string, applicationInfoPath: string, clientPort: number, destinationPath: string) {
|
constructor(userConfigPath: string, applicationInfoPath: string, clientPort: number, destinationPath: string) {
|
||||||
this.userConfig = new JsonManager(userConfigPath);
|
this.userConfig = new JsonManager(userConfigPath);
|
||||||
this.applicationInfo = new JsonManager(applicationInfoPath);
|
this.applicationInfo = new JsonManager(applicationInfoPath);
|
||||||
this.clientPort = clientPort;
|
this.clientPort = clientPort;
|
||||||
this.destinationPath = destinationPath;
|
this.destinationPath = destinationPath;
|
||||||
|
this.isBusy = false;
|
||||||
|
this.lastProcessedUserIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
|
setInterval(async () => {
|
||||||
|
if (!this.isBusy) {
|
||||||
|
this.log('Start successfully. Processing backup tasks.');
|
||||||
|
await this.processBackupTasks();
|
||||||
|
}
|
||||||
|
}, 10000); // Retry every 10 seconds if there's an error
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processBackupTasks(): Promise<void> {
|
||||||
|
this.isBusy = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userInfo = await this.userConfig.readValue('user_info');
|
const userInfo = await this.userConfig.readValue('user_info');
|
||||||
if (!userInfo || !userInfo.name) {
|
if (!userInfo || !userInfo.name) {
|
||||||
@@ -41,46 +55,50 @@ export class BackupRetrievalWorker {
|
|||||||
|
|
||||||
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
|
const activeUsersIp = await this.applicationInfo.readValue('users_ip');
|
||||||
if (!activeUsersIp || !activeUsersIp.length) {
|
if (!activeUsersIp || !activeUsersIp.length) {
|
||||||
parentPort?.postMessage({ success: false, message: 'No active users found.' });
|
this.isBusy = false;
|
||||||
return;
|
throw new Error('No active users found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process each user, starting from the last processed index
|
||||||
let backupSuccessful = true;
|
let backupSuccessful = true;
|
||||||
for (const ip of activeUsersIp) {
|
for (let i = this.lastProcessedUserIndex; i < activeUsersIp.length; i++) {
|
||||||
|
const ip = activeUsersIp[i];
|
||||||
const success = await this.processBackupForIp(ip, userName);
|
const success = await this.processBackupForIp(ip, userName);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
backupSuccessful = false;
|
backupSuccessful = false;
|
||||||
parentPort?.postMessage({ success: false, message: 'Backup could not be completed due to an internal error.' });
|
this.lastProcessedUserIndex = i; // Remember where it stopped
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backupSuccessful) {
|
if (backupSuccessful) {
|
||||||
parentPort?.postMessage({ success: true, message: 'Backup successful.' });
|
this.log('Backup retrieval completed successfully for all users.');
|
||||||
|
this.lastProcessedUserIndex = 0; // Reset index to start from the beginning next cycle
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error in BackupRetrievalWorker:', error);
|
this.log(error.message, 'error');
|
||||||
parentPort?.postMessage({ success: false, message: `Backup could not be completed due to an internal error: ${error.message}` });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isBusy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
|
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
|
||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
if (!await this.tcpCommunicator.connect()) {
|
||||||
console.error(`Failed to connect to ${ip}`);
|
this.log(`Failed to connect to ${ip}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backupExists = await this.checkIfBackupExists(userName);
|
const backupExists = await this.checkIfBackupExists(userName);
|
||||||
if (!backupExists) {
|
if (!backupExists) {
|
||||||
console.log(`No backup found for user ${userName} on IP ${ip}`);
|
this.log(`No backup found for user ${userName} on IP ${ip}`);
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return true; // Skip user if no backup found, do not mark as error
|
return true; // Skip user if no backup found, do not mark as error
|
||||||
}
|
}
|
||||||
|
|
||||||
const backupStructure = await this.requestBackupStructure(userName);
|
const backupStructure = await this.requestBackupStructure(userName);
|
||||||
if (!backupStructure || Object.keys(backupStructure).length === 0) {
|
if (!backupStructure || Object.keys(backupStructure).length === 0) {
|
||||||
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
|
this.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return true; // Skip user if no files found, do not mark as error
|
return true; // Skip user if no files found, do not mark as error
|
||||||
}
|
}
|
||||||
@@ -88,7 +106,7 @@ export class BackupRetrievalWorker {
|
|||||||
for (const relativeFilePath of Object.keys(backupStructure)) {
|
for (const relativeFilePath of Object.keys(backupStructure)) {
|
||||||
const fileRequestSuccess = await this.requestBackupFile(userName, relativeFilePath);
|
const fileRequestSuccess = await this.requestBackupFile(userName, relativeFilePath);
|
||||||
if (!fileRequestSuccess) {
|
if (!fileRequestSuccess) {
|
||||||
console.error(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`);
|
this.log(`Failed to retrieve file ${relativeFilePath} from backup for user ${userName} on IP ${ip}`, 'error');
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return false; // Stop if any file fails to be retrieved
|
return false; // Stop if any file fails to be retrieved
|
||||||
}
|
}
|
||||||
@@ -133,7 +151,7 @@ export class BackupRetrievalWorker {
|
|||||||
|
|
||||||
private saveFile(relativeFilePath: string, fileContent: string): boolean {
|
private saveFile(relativeFilePath: string, fileContent: string): boolean {
|
||||||
if (!this.encryptionKey || !this.iv) {
|
if (!this.encryptionKey || !this.iv) {
|
||||||
console.error('Encryption key or IV is not set.');
|
this.log('Encryption key or IV is not set.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +159,7 @@ export class BackupRetrievalWorker {
|
|||||||
try {
|
try {
|
||||||
encryptedBuffer = Buffer.from(fileContent, 'base64');
|
encryptedBuffer = Buffer.from(fileContent, 'base64');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error decoding base64 file content:', error);
|
this.log(`Error decoding base64 file content: ${error}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +168,7 @@ export class BackupRetrievalWorker {
|
|||||||
const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, this.iv);
|
const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, this.iv);
|
||||||
decryptedContent = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]);
|
decryptedContent = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error decrypting file:', error);
|
this.log(`Error decrypting file: ${error}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,10 +179,10 @@ export class BackupRetrievalWorker {
|
|||||||
fs.mkdirSync(dirPath, { recursive: true });
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
fs.writeFileSync(fullFilePath, decryptedContent);
|
fs.writeFileSync(fullFilePath, decryptedContent);
|
||||||
console.log(`File saved successfully: ${fullFilePath}`);
|
this.log(`File saved successfully: ${fullFilePath}`);
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(`Error saving file ${relativeFilePath}: ${error.message}`);
|
this.log(`Error saving file ${relativeFilePath}: ${error.message}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +195,17 @@ export class BackupRetrievalWorker {
|
|||||||
clearInterval(idResponseCheck);
|
clearInterval(idResponseCheck);
|
||||||
resolve(this.tcpCommunicator.getLastResult());
|
resolve(this.tcpCommunicator.getLastResult());
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100); // Check every 100 milliseconds
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified logging function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const prefix = '[BackupRetrievalWorker]';
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { JsonManager } from './json_manager'; // Assuming this manages JSON configurations
|
import { JsonManager } from './json_manager';
|
||||||
import { TcpCommunicator } from "./tcp_communicator";
|
import { TcpCommunicator } from "./tcp_communicator";
|
||||||
import { operationCodes } from '../network/operation_codes'; // Assuming this holds operation codes
|
import { operationCodes } from '../network/operation_codes';
|
||||||
import { parentPort } from 'worker_threads';
|
import { parentPort } from 'worker_threads';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { ParsedMessage } from "../network/message_handler"; // Import the crypto module for encryption and decryption
|
import { ParsedMessage } from "../network/message_handler";
|
||||||
|
|
||||||
export class BackupRetrievalWorker {
|
export class BackupRetrievalWorker {
|
||||||
private userConfig: JsonManager;
|
private userConfig: JsonManager;
|
||||||
@@ -23,6 +23,16 @@ export class BackupRetrievalWorker {
|
|||||||
this.destinationPath = destinationPath;
|
this.destinationPath = destinationPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logging helper function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const prefix = '[BackupRetrievalWorker]';
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userInfo = await this.userConfig.readValue('user_info');
|
const userInfo = await this.userConfig.readValue('user_info');
|
||||||
@@ -49,12 +59,12 @@ export class BackupRetrievalWorker {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(`Failed to retrieve backup from ${ip}`);
|
throw new Error(`Failed to retrieve backup from ${ip}`);
|
||||||
}
|
}
|
||||||
console.log(`Backup retrieved successfully from ${ip}`);
|
this.log(`Backup retrieved successfully from ${ip}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed successfully.' });
|
parentPort?.postMessage({ success: true, message: 'Backup retrieval completed successfully.' });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error in BackupRetrievalWorker:', error);
|
this.log(`Error in BackupRetrievalWorker: ${error.message}`, 'error');
|
||||||
parentPort?.postMessage({ success: false, message: `A problem occurred: ${error.message}` });
|
parentPort?.postMessage({ success: false, message: `A problem occurred: ${error.message}` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,19 +72,20 @@ export class BackupRetrievalWorker {
|
|||||||
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
|
private async processBackupForIp(ip: string, userName: string): Promise<boolean> {
|
||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
if (!await this.tcpCommunicator.connect()) {
|
||||||
|
this.log(`Failed to connect to ${ip}`, 'error');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backupExists = await this.checkIfBackupExists(userName);
|
const backupExists = await this.checkIfBackupExists(userName);
|
||||||
if (!backupExists) {
|
if (!backupExists) {
|
||||||
console.log(`No backup found for user ${userName} on IP ${ip}`);
|
this.log(`No backup found for user ${userName} on IP ${ip}`);
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backupStructure = await this.requestBackupStructure(userName);
|
const backupStructure = await this.requestBackupStructure(userName);
|
||||||
if (!backupStructure || Object.keys(backupStructure).length === 0) {
|
if (!backupStructure || Object.keys(backupStructure).length === 0) {
|
||||||
console.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
|
this.log(`No files found in backup structure for user ${userName} on IP ${ip}`);
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -151,7 +162,7 @@ export class BackupRetrievalWorker {
|
|||||||
fs.mkdirSync(dirPath, { recursive: true });
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
fs.writeFileSync(fullFilePath, decryptedContent);
|
fs.writeFileSync(fullFilePath, decryptedContent);
|
||||||
console.log(`File saved successfully: ${fullFilePath}`);
|
this.log(`File saved successfully: ${fullFilePath}`);
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
throw new Error(`Error saving file ${relativeFilePath}: ${error.message}`);
|
throw new Error(`Error saving file ${relativeFilePath}: ${error.message}`);
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { TcpCommunicator } from './tcp_communicator'; // Using TcpCommunicator instead of TcpClient
|
import { TcpCommunicator } from './tcp_communicator';
|
||||||
import { operationCodes } from '../network/operation_codes';
|
import { operationCodes } from '../network/operation_codes';
|
||||||
import { JsonManager } from './json_manager'; // Manages JSON configurations
|
import { JsonManager } from './json_manager';
|
||||||
import { MemoryManager } from './memory_manager'; // Manages in-memory data structures
|
import { MemoryManager } from './memory_manager';
|
||||||
import { ParsedMessage } from "../network/message_handler";
|
import { ParsedMessage } from "../network/message_handler";
|
||||||
|
|
||||||
export class DepartmentSharer {
|
export class DepartmentSharer {
|
||||||
private userConfig: JsonManager;
|
private userConfig: JsonManager;
|
||||||
private applicationInfo: JsonManager;
|
private applicationInfo: JsonManager;
|
||||||
private memoryManager: MemoryManager; // To read the department files
|
private memoryManager: MemoryManager;
|
||||||
private departmentDirectory: string | null;
|
private departmentDirectory: string | null;
|
||||||
private readonly clientPort: number;
|
private readonly clientPort: number;
|
||||||
private isBusy: boolean = false; // Busy flag to prevent simultaneous sharing
|
private isBusy: boolean = false;
|
||||||
private tcpCommunicator: TcpCommunicator | null = null; // For each user connection
|
private tcpCommunicator: TcpCommunicator | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
userConfigPath: string,
|
userConfigPath: string,
|
||||||
@@ -32,21 +32,20 @@ export class DepartmentSharer {
|
|||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
if (!this.isBusy) {
|
if (!this.isBusy) {
|
||||||
this.isBusy = true;
|
this.log('Start successfully. Sharing files with the department.');
|
||||||
await this.shareFilesWithDepartment();
|
await this.shareFilesWithDepartment();
|
||||||
this.isBusy = false;
|
|
||||||
}
|
}
|
||||||
}, 10000); // 10-second interval for testing
|
}, 10000); // 10-second interval for testing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Share files with users in the same department
|
// Share files with users in the same department
|
||||||
private async shareFilesWithDepartment(): Promise<void> {
|
private async shareFilesWithDepartment(): Promise<void> {
|
||||||
console.log('\n\nStarting Department Share Process\n\n');
|
this.isBusy = true;
|
||||||
|
|
||||||
// Get the current user's department information
|
// Get the current user's department information
|
||||||
const userInfo = await this.userConfig.readValue('user_info');
|
const userInfo = await this.userConfig.readValue('user_info');
|
||||||
if (!userInfo || !userInfo.departmentId || !userInfo.name) {
|
if (!userInfo || !userInfo.departmentId || !userInfo.name) {
|
||||||
console.error('User information or department ID is missing in the configuration.');
|
this.log('User information or department ID is missing in the configuration.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,27 +55,27 @@ export class DepartmentSharer {
|
|||||||
// Get the list of active users from applicationInfo
|
// Get the list of active users from applicationInfo
|
||||||
const activeUsersId = await this.applicationInfo.readValue('active_users_info');
|
const activeUsersId = await this.applicationInfo.readValue('active_users_info');
|
||||||
if (!activeUsersId) {
|
if (!activeUsersId) {
|
||||||
console.error('No active users found.');
|
this.log('No active users found.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeUsers = await this.memoryManager.retrieveMetaInformation(activeUsersId);
|
const activeUsers = await this.memoryManager.retrieveMetaInformation(activeUsersId);
|
||||||
if (!activeUsers || activeUsers.length === 0) {
|
if (!activeUsers || activeUsers.length === 0) {
|
||||||
console.error('No active users found.');
|
this.log('No active users found.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter users who belong to the same department
|
// Filter users who belong to the same department
|
||||||
const departmentUsers = activeUsers.filter((user: any) => user.user_info.departmentId === departmentId);
|
const departmentUsers = activeUsers.filter((user: any) => user.user_info.departmentId === departmentId);
|
||||||
if (departmentUsers.length === 0) {
|
if (departmentUsers.length === 0) {
|
||||||
console.log('No users found in the same department.');
|
this.log('No users found in the same department.', 'log');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get department directory info
|
// Get department directory info
|
||||||
const departmentData = await this.applicationInfo.readValue('departmentDirectory');
|
const departmentData = await this.applicationInfo.readValue('departmentDirectory');
|
||||||
if (!departmentData || !departmentData.path || !departmentData.id) {
|
if (!departmentData || !departmentData.path || !departmentData.id) {
|
||||||
console.error('No department directory found.');
|
this.log('No department directory found.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +84,7 @@ export class DepartmentSharer {
|
|||||||
// Read files from the MemoryManager related to this department
|
// Read files from the MemoryManager related to this department
|
||||||
const departmentFiles = await this.memoryManager.retrieveMetaInformation(departmentData.id);
|
const departmentFiles = await this.memoryManager.retrieveMetaInformation(departmentData.id);
|
||||||
if (!departmentFiles || !departmentFiles.structure) {
|
if (!departmentFiles || !departmentFiles.structure) {
|
||||||
console.error('No files found for this department in the memory manager.');
|
this.log('No files found for this department in the memory manager.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +113,7 @@ export class DepartmentSharer {
|
|||||||
const response = await this.waitForResponse();
|
const response = await this.waitForResponse();
|
||||||
|
|
||||||
if (!response || response.operationCode !== operationCodes.OK){
|
if (!response || response.operationCode !== operationCodes.OK){
|
||||||
console.error('Failed to clear the department directory.');
|
this.log('Failed to clear the department directory.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,14 +123,14 @@ export class DepartmentSharer {
|
|||||||
// Send the files to a user in the department
|
// Send the files to a user in the department
|
||||||
private async sendFilesToUser(files: { [key: string]: string }, userName: string): Promise<void> {
|
private async sendFilesToUser(files: { [key: string]: string }, userName: string): Promise<void> {
|
||||||
if(!this.tcpCommunicator) return;
|
if(!this.tcpCommunicator) return;
|
||||||
const unsentFiles = Object.keys(files); // Keep track of unsent files
|
const unsentFiles = Object.keys(files);
|
||||||
|
|
||||||
for (const fileName of unsentFiles) {
|
for (const fileName of unsentFiles) {
|
||||||
const filePath = files[fileName];
|
const filePath = files[fileName];
|
||||||
|
|
||||||
// Ensure the file exists before attempting to send
|
// Ensure the file exists before attempting to send
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
console.error(`File not found: ${filePath}`);
|
this.log(`File not found: ${filePath}`, 'error');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,8 +143,8 @@ export class DepartmentSharer {
|
|||||||
|
|
||||||
// Prepare the metaInfo (same structure as FileSharer)
|
// Prepare the metaInfo (same structure as FileSharer)
|
||||||
const metaInfo = {
|
const metaInfo = {
|
||||||
userName, // Sender's username
|
userName,
|
||||||
relativeFilePath // Use the relative path to preserve directory structure
|
relativeFilePath
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send the file
|
// Send the file
|
||||||
@@ -153,7 +152,7 @@ export class DepartmentSharer {
|
|||||||
|
|
||||||
const response = await this.waitForResponse();
|
const response = await this.waitForResponse();
|
||||||
if (!response || response.operationCode !== operationCodes.OK) {
|
if (!response || response.operationCode !== operationCodes.OK) {
|
||||||
console.error(`Failed to send file: ${fileName}`);
|
this.log(`Failed to send file: ${fileName}`, 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,9 +167,19 @@ export class DepartmentSharer {
|
|||||||
if (!this.tcpCommunicator) return null;
|
if (!this.tcpCommunicator) return null;
|
||||||
if (this.tcpCommunicator.hasResponseArrived()) {
|
if (this.tcpCommunicator.hasResponseArrived()) {
|
||||||
clearInterval(idResponseCheck);
|
clearInterval(idResponseCheck);
|
||||||
resolve(this.tcpCommunicator.getLastResult()); // Resolve the response or null if not available
|
resolve(this.tcpCommunicator.getLastResult());
|
||||||
}
|
}
|
||||||
}, 100); // Check every 100 milliseconds if the response has arrived
|
}, 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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ export class DirectoryWatcher {
|
|||||||
private applicationInfo: JsonManager;
|
private applicationInfo: JsonManager;
|
||||||
private memoryManager: MemoryManager;
|
private memoryManager: MemoryManager;
|
||||||
private readonly sourceKey: string;
|
private readonly sourceKey: string;
|
||||||
private directoryWatcher: FSWatcher | null; // To store the watcher reference
|
private directoryWatcher: FSWatcher | null;
|
||||||
private totalSize: number; // To store total directory size
|
private totalSize: number;
|
||||||
|
private isBusy: boolean;
|
||||||
|
|
||||||
constructor(memoryManagerPath: string, applicationInfoPath: string, sourceKey: string) {
|
constructor(memoryManagerPath: string, applicationInfoPath: string, sourceKey: string) {
|
||||||
this.sourceKey = sourceKey;
|
this.sourceKey = sourceKey;
|
||||||
@@ -19,14 +20,28 @@ export class DirectoryWatcher {
|
|||||||
this.memoryManager = new MemoryManager(memoryManagerPath);
|
this.memoryManager = new MemoryManager(memoryManagerPath);
|
||||||
this.directoryMemoryId = '';
|
this.directoryMemoryId = '';
|
||||||
this.directoryPath = '';
|
this.directoryPath = '';
|
||||||
this.directoryWatcher = null; // Initialize with no watcher
|
this.directoryWatcher = null;
|
||||||
this.totalSize = 0; // Initialize size with zero
|
this.totalSize = 0;
|
||||||
|
this.isBusy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the watcher with a busy flag to prevent overlapping operations
|
||||||
|
async start(): Promise<void> {
|
||||||
|
setInterval(async () => {
|
||||||
|
if (!this.isBusy) {
|
||||||
|
this.isBusy = true;
|
||||||
|
const initialized = await this.initialize();
|
||||||
|
if (initialized) this.log('Directory watcher started successfully.');
|
||||||
|
this.isBusy = false;
|
||||||
|
}
|
||||||
|
}, 10000); // 10-second interval for testing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to initialize and validate the backup directory
|
// Method to initialize and validate the backup directory
|
||||||
async initialize(): Promise<boolean> {
|
async initialize(): Promise<boolean> {
|
||||||
const directoryData = await this.applicationInfo.readValue(this.sourceKey);
|
const directoryData = await this.applicationInfo.readValue(this.sourceKey);
|
||||||
if (!directoryData) {
|
if (!directoryData) {
|
||||||
|
this.log('Directory data not found in application info.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +49,7 @@ export class DirectoryWatcher {
|
|||||||
this.directoryMemoryId = directoryData.id;
|
this.directoryMemoryId = directoryData.id;
|
||||||
|
|
||||||
if (!this.directoryMemoryId || !this.directoryPath) {
|
if (!this.directoryMemoryId || !this.directoryPath) {
|
||||||
console.error('Components of entry in \'DirectoryWatcher\' not found.');
|
this.log('Components of entry in \'DirectoryWatcher\' not found.', 'error');
|
||||||
await this.applicationInfo.removeValue(this.sourceKey);
|
await this.applicationInfo.removeValue(this.sourceKey);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -65,7 +80,7 @@ export class DirectoryWatcher {
|
|||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const fullPath = path.join(dirPath, item.name);
|
const fullPath = path.join(dirPath, item.name);
|
||||||
const stats = await fs.stat(fullPath); // Get stats for each item
|
const stats = await fs.stat(fullPath);
|
||||||
|
|
||||||
if (item.isDirectory()) {
|
if (item.isDirectory()) {
|
||||||
// If it's a directory, recursively build its structure and accumulate size
|
// If it's a directory, recursively build its structure and accumulate size
|
||||||
@@ -75,7 +90,7 @@ export class DirectoryWatcher {
|
|||||||
} else if (item.isFile()) {
|
} else if (item.isFile()) {
|
||||||
// If it's a file, store its full path and accumulate size
|
// If it's a file, store its full path and accumulate size
|
||||||
directoryScheme[item.name] = fullPath;
|
directoryScheme[item.name] = fullPath;
|
||||||
totalSize += stats.size; // Add file size
|
totalSize += stats.size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +100,8 @@ export class DirectoryWatcher {
|
|||||||
// Restart the directory watcher, ensuring any previous watcher is closed
|
// Restart the directory watcher, ensuring any previous watcher is closed
|
||||||
private restartWatcher(): void {
|
private restartWatcher(): void {
|
||||||
if (this.directoryWatcher) {
|
if (this.directoryWatcher) {
|
||||||
console.log('Stopping existing watcher...');
|
this.log('Stopping existing watcher...');
|
||||||
this.directoryWatcher.close(); // Stop the existing watcher
|
this.directoryWatcher.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.startDirectoryWatcher();
|
this.startDirectoryWatcher();
|
||||||
@@ -100,7 +115,7 @@ export class DirectoryWatcher {
|
|||||||
|
|
||||||
this.directoryWatcher = watch(this.directoryPath, { recursive: true }, async (eventType, filename) => {
|
this.directoryWatcher = watch(this.directoryPath, { recursive: true }, async (eventType, filename) => {
|
||||||
if (filename) {
|
if (filename) {
|
||||||
console.log(`File change detected: ${eventType} - ${filename}`);
|
this.log(`File change detected: ${eventType} - ${filename}`);
|
||||||
// Rebuild the directory scheme and update memory
|
// Rebuild the directory scheme and update memory
|
||||||
const result = await this.buildDirectoryScheme(this.directoryPath);
|
const result = await this.buildDirectoryScheme(this.directoryPath);
|
||||||
this.directoryScheme = result.structure;
|
this.directoryScheme = result.structure;
|
||||||
@@ -111,19 +126,34 @@ export class DirectoryWatcher {
|
|||||||
totalSize: this.totalSize,
|
totalSize: this.totalSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Directory structure and size updated in memory.');
|
this.log('Directory structure and size updated in memory.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Watching for changes in: ${this.directoryPath}`);
|
this.log(`Watching for changes in: ${this.directoryPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the directory watcher
|
// Close the directory watcher
|
||||||
public closeWatcher(): void {
|
public closeWatcher(): void {
|
||||||
if (this.directoryWatcher) {
|
if (this.directoryWatcher) {
|
||||||
console.log(`Stopping watcher for ${this.directoryPath}`);
|
this.log(`Stopping watcher for ${this.directoryPath}`);
|
||||||
this.directoryWatcher.close();
|
this.directoryWatcher.close();
|
||||||
this.directoryWatcher = null; // Clear the reference after closing
|
this.directoryWatcher = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified logging function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const sourcePrefix = `[DirectoryWatcher] {${this.capitalize(this.sourceKey)}}`;
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${sourcePrefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${sourcePrefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capitalize the first letter of the sourceKey
|
||||||
|
private capitalize(str: string): string {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {QueueManager} from './queue_manager';
|
|
||||||
import {TcpCommunicator} from "./tcp_communicator"; // Updated to use TcpCommunicator
|
|
||||||
import {operationCodes} from '../network/operation_codes';
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import {compareFnFileItemTask, FileItemTask} from "../interfaces/file_item_task";
|
import { QueueManager } from './queue_manager';
|
||||||
import {ParsedMessage} from "../network/message_handler";
|
import { TcpCommunicator } from "./tcp_communicator";
|
||||||
|
import { operationCodes } from '../network/operation_codes';
|
||||||
|
import { compareFnFileItemTask, FileItemTask } from "../interfaces/file_item_task";
|
||||||
|
import { ParsedMessage } from "../network/message_handler";
|
||||||
|
|
||||||
interface FileSendTask {
|
interface FileSendTask {
|
||||||
ip: string;
|
ip: string;
|
||||||
@@ -28,7 +28,9 @@ export class FileSharer {
|
|||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
if (!this.isBusy) { // Check if the queue is already being processed
|
if (!this.isBusy) { // Check if the queue is already being processed
|
||||||
|
this.log("Start successfully. Processing the queue.");
|
||||||
await this.processQueue(); // Process the queue at regular intervals
|
await this.processQueue(); // Process the queue at regular intervals
|
||||||
|
this.log("Queue processing completed.");
|
||||||
}
|
}
|
||||||
}, 10000); // 10 seconds interval
|
}, 10000); // 10 seconds interval
|
||||||
}
|
}
|
||||||
@@ -36,7 +38,7 @@ export class FileSharer {
|
|||||||
// Method to process the queue
|
// Method to process the queue
|
||||||
private async processQueue(): Promise<void> {
|
private async processQueue(): Promise<void> {
|
||||||
if (this.isBusy) {
|
if (this.isBusy) {
|
||||||
console.log("Queue is already being processed. Skipping this interval.");
|
this.log("Queue is already being processed. Skipping this interval.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,15 +48,15 @@ export class FileSharer {
|
|||||||
const task = this.queueManager.peek();
|
const task = this.queueManager.peek();
|
||||||
|
|
||||||
if (task) {
|
if (task) {
|
||||||
console.log(task);
|
this.log(`Processing task for file: ${task.path} to IP: ${task.ip}`);
|
||||||
const success = await this.sendFile(task);
|
const success = await this.sendFile(task);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
console.error(`Failed to send file: ${task.path} to IP: ${task.ip}. Re-adding to queue.`);
|
this.log(`Failed to send file: ${task.path} to IP: ${task.ip}. Re-adding to queue.`, 'error');
|
||||||
this.queueManager.dequeue();
|
this.queueManager.dequeue();
|
||||||
this.queueManager.enqueue(task); // Re-add to queue if failed
|
this.queueManager.enqueue(task); // Re-add to queue if failed
|
||||||
} else {
|
} else {
|
||||||
console.log(`File sent successfully: ${task.path} to IP: ${task.ip}.`);
|
this.log(`File sent successfully: ${task.path} to IP: ${task.ip}`);
|
||||||
this.queueManager.dequeue();
|
this.queueManager.dequeue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,11 +66,11 @@ export class FileSharer {
|
|||||||
|
|
||||||
// Method to send the file to a specific IP using TcpCommunicator
|
// Method to send the file to a specific IP using TcpCommunicator
|
||||||
private async sendFile(task: FileSendTask): Promise<boolean> {
|
private async sendFile(task: FileSendTask): Promise<boolean> {
|
||||||
const {ip, path: filePath, userName} = task;
|
const { ip, path: filePath, userName } = task;
|
||||||
|
|
||||||
// Ensure the file exists before attempting to send
|
// Ensure the file exists before attempting to send
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
console.error(`File not found: ${filePath}`);
|
this.log(`File not found: ${filePath}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,17 +88,17 @@ export class FileSharer {
|
|||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
||||||
|
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
if (!await this.tcpCommunicator.connect()) {
|
||||||
console.error(`Failed to connect to IP: ${ip}`);
|
this.log(`Failed to connect to IP: ${ip}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Sending file: ${filePath} to IP: ${ip}`);
|
this.log(`Sending file: ${filePath} to IP: ${ip}`);
|
||||||
|
|
||||||
if (!await this.tcpCommunicator.sendMessage(operationCodes.SHARE_FILE, metaInfo, fileContent)) return false;
|
if (!await this.tcpCommunicator.sendMessage(operationCodes.SHARE_FILE, metaInfo, fileContent)) return false;
|
||||||
|
|
||||||
const response = await this.waitForResponse();
|
const response = await this.waitForResponse();
|
||||||
if (!response || response.operationCode !== operationCodes.OK) {
|
if (!response || response.operationCode !== operationCodes.OK) {
|
||||||
console.error(`Failed to send file: ${filePath} to IP: ${ip}`);
|
this.log(`Failed to send file: ${filePath} to IP: ${ip}`, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,4 +117,14 @@ export class FileSharer {
|
|||||||
}, 100); // Check every 100 milliseconds if the response has arrived
|
}, 100); // Check every 100 milliseconds if the response has arrived
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified logging function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const prefix = '[FileSharer]';
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
import {JsonManager} from "./json_manager";
|
||||||
|
import {UdpClient} from "../network/udp/udp_client";
|
||||||
|
import {parentPort} from "worker_threads";
|
||||||
|
import {TcpCommunicator} from "./tcp_communicator";
|
||||||
|
import {operationCodes} from "../network/operation_codes";
|
||||||
|
import {ParsedMessage} from "../network/message_handler";
|
||||||
|
|
||||||
|
export class NetworkScanner {
|
||||||
|
private applicationInfo: JsonManager;
|
||||||
|
private userConfig: JsonManager;
|
||||||
|
private readonly udpPort: number;
|
||||||
|
private readonly tcpPort: number;
|
||||||
|
private readonly okPage: string;
|
||||||
|
private readonly errorPage: string;
|
||||||
|
private readonly databaseResetPage: string;
|
||||||
|
private appStarted = false;
|
||||||
|
private intervalIds: NodeJS.Timeout[] = [];
|
||||||
|
|
||||||
|
// Flags to prevent overlapping executions
|
||||||
|
private ucCheckBusy = false;
|
||||||
|
private ipLookupBusy = false;
|
||||||
|
private sendLoginBusy = false;
|
||||||
|
|
||||||
|
constructor(applicationInfoPath: string, userConfigPath: string, udpPort: number, tcpPort: number, okPage: string, errorPage: string, databaseResetPage: string) {
|
||||||
|
this.applicationInfo = new JsonManager(applicationInfoPath);
|
||||||
|
this.userConfig = new JsonManager(userConfigPath);
|
||||||
|
this.udpPort = udpPort;
|
||||||
|
this.tcpPort = tcpPort;
|
||||||
|
this.okPage = okPage;
|
||||||
|
this.errorPage = errorPage;
|
||||||
|
this.databaseResetPage = databaseResetPage;
|
||||||
|
|
||||||
|
// Start tasks
|
||||||
|
this.startUCCheck();
|
||||||
|
this.startUserIPLookup();
|
||||||
|
this.sendLoginRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log helper function for consistent logging format
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log', methodName: string = ''): void {
|
||||||
|
const prefix = `[NetworkScanner${methodName ? `.${methodName}` : ''}]`;
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UC Check Task
|
||||||
|
private startUCCheck(interval: number = 5000): void {
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (this.ucCheckBusy) return;
|
||||||
|
this.ucCheckBusy = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.log('UC Check running...', 'log', 'startUCCheck');
|
||||||
|
const udpClient = new UdpClient(this.udpPort);
|
||||||
|
const aliveClients = await udpClient.getAliveClients();
|
||||||
|
const storedIp = await this.applicationInfo.readValue('serverIp');
|
||||||
|
const foundClient = aliveClients.length > 0;
|
||||||
|
|
||||||
|
if (foundClient) {
|
||||||
|
const ipAddress = aliveClients[0]; // Use the first alive client
|
||||||
|
|
||||||
|
if (!storedIp || storedIp !== ipAddress) {
|
||||||
|
await this.applicationInfo.writeValue('serverIp', ipAddress);
|
||||||
|
if (!this.appStarted) {
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.okPage });
|
||||||
|
}
|
||||||
|
this.appStarted = true;
|
||||||
|
} else if (!this.appStarted) {
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.okPage });
|
||||||
|
this.appStarted = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.errorPage });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.log(`Error checking UC: ${err}`, 'error', 'startUCCheck');
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.errorPage });
|
||||||
|
} finally {
|
||||||
|
this.ucCheckBusy = false;
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
|
||||||
|
this.intervalIds.push(intervalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP Lookup Task
|
||||||
|
private startUserIPLookup(interval: number = 10000): void {
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (this.ipLookupBusy) return;
|
||||||
|
this.ipLookupBusy = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.log('IP Lookup running...', 'log', 'startUserIPLookup');
|
||||||
|
const serverIp = await this.applicationInfo.readValue('serverIp');
|
||||||
|
const udpClient = new UdpClient(this.udpPort);
|
||||||
|
const activeIPs = await udpClient.getAliveClients();
|
||||||
|
const filteredIPs = activeIPs.filter(ip => ip !== serverIp);
|
||||||
|
|
||||||
|
// Save the filtered IPs to 'users_ip'
|
||||||
|
await this.applicationInfo.writeValue('users_ip', filteredIPs);
|
||||||
|
} catch (err) {
|
||||||
|
this.log(`Error during user IP lookup: ${err}`, 'error', 'startUserIPLookup');
|
||||||
|
} finally {
|
||||||
|
this.ipLookupBusy = false;
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
|
||||||
|
this.intervalIds.push(intervalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login Request Task
|
||||||
|
private sendLoginRequest(interval: number = 5000): void {
|
||||||
|
const intervalId = setInterval(async () => {
|
||||||
|
if (this.sendLoginBusy || this.appStarted) return;
|
||||||
|
this.sendLoginBusy = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userInfo = await this.userConfig.readValue('user_info');
|
||||||
|
if (!userInfo || !userInfo.email || !userInfo.password) {
|
||||||
|
this.log("Email or password not found in user config.", 'error', 'sendLoginRequest');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const app_type = await this.userConfig.readValue('app_type');
|
||||||
|
const email = userInfo.email;
|
||||||
|
const password = userInfo.password;
|
||||||
|
const serverIp = await this.applicationInfo.readValue('serverIp');
|
||||||
|
|
||||||
|
if (!serverIp) {
|
||||||
|
this.log("Server IP not found in application info.", 'error', 'sendLoginRequest');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tcpCommunicator = new TcpCommunicator(serverIp, this.tcpPort);
|
||||||
|
|
||||||
|
if (!await tcpCommunicator.connect()) {
|
||||||
|
this.log("Failed to connect to the server.", 'error', 'sendLoginRequest');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaInfo = { email, password, app_type };
|
||||||
|
if (!await tcpCommunicator.sendMessage(operationCodes.LOGIN, metaInfo)) {
|
||||||
|
this.log("Failed to send login request.", 'error', 'sendLoginRequest');
|
||||||
|
await tcpCommunicator.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.waitForResponse(tcpCommunicator);
|
||||||
|
if (response?.operationCode !== operationCodes.OK) {
|
||||||
|
await this.userConfig.resetFile();
|
||||||
|
await this.userConfig.writeValue('app_type', app_type);
|
||||||
|
parentPort?.postMessage({ type: 'changeContent', page: this.databaseResetPage });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.log(`Error during login request: ${err}`, 'error', 'sendLoginRequest');
|
||||||
|
} finally {
|
||||||
|
this.sendLoginBusy = false;
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
|
||||||
|
this.intervalIds.push(intervalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to wait for a response from the TCP communicator
|
||||||
|
private async waitForResponse(tcpCommunicator: TcpCommunicator): Promise<ParsedMessage | null> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const checkInterval = setInterval(() => {
|
||||||
|
if (tcpCommunicator.hasResponseArrived()) {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
resolve(tcpCommunicator.getLastResult());
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to stop all intervals (for cleanup if needed)
|
||||||
|
public stopAllIntervals(): void {
|
||||||
|
for (const id of this.intervalIds) {
|
||||||
|
clearInterval(id);
|
||||||
|
}
|
||||||
|
this.log("All intervals have been stopped.", 'log', 'stopAllIntervals');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import { JsonManager } from "./json_manager";
|
|||||||
import { MemoryManager } from "./memory_manager";
|
import { MemoryManager } from "./memory_manager";
|
||||||
import { operationCodes } from "../network/operation_codes";
|
import { operationCodes } from "../network/operation_codes";
|
||||||
import { TcpCommunicator } from "./tcp_communicator";
|
import { TcpCommunicator } from "./tcp_communicator";
|
||||||
import {ParsedMessage} from "../network/message_handler";
|
import { ParsedMessage } from "../network/message_handler";
|
||||||
|
|
||||||
export class UsersInfoFetcher {
|
export class UsersInfoFetcher {
|
||||||
private applicationInfo: JsonManager;
|
private applicationInfo: JsonManager;
|
||||||
@@ -32,7 +32,7 @@ export class UsersInfoFetcher {
|
|||||||
private async initialize() {
|
private async initialize() {
|
||||||
const usersIps = await this.applicationInfo.readValue('users_ip');
|
const usersIps = await this.applicationInfo.readValue('users_ip');
|
||||||
if (!usersIps) {
|
if (!usersIps) {
|
||||||
console.error('No IP addresses found in users_ip');
|
this.log('No IP addresses found in users_ip', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,13 +54,13 @@ export class UsersInfoFetcher {
|
|||||||
for (const ip of usersIps) {
|
for (const ip of usersIps) {
|
||||||
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
this.tcpCommunicator = new TcpCommunicator(ip, this.clientPort);
|
||||||
if (!await this.tcpCommunicator.connect()) {
|
if (!await this.tcpCommunicator.connect()) {
|
||||||
console.error(`Failed to open connection for IP: ${ip}`);
|
this.log(`Failed to open connection for IP: ${ip}`, 'error');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await this.tcpCommunicator?.sendMessage(operationCodes.GET_USER_INFORMATION)){
|
if(!await this.tcpCommunicator?.sendMessage(operationCodes.GET_USER_INFORMATION)){
|
||||||
await this.tcpCommunicator.disconnect();
|
await this.tcpCommunicator.disconnect();
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the response for 10 seconds
|
// Wait for the response for 10 seconds
|
||||||
@@ -96,4 +96,14 @@ export class UsersInfoFetcher {
|
|||||||
private async updateActiveUsers(userInfo: any[]) {
|
private async updateActiveUsers(userInfo: any[]) {
|
||||||
await this.memoryManager.updateMetaInformation(this.memoryId, userInfo); // Update active users in memory
|
await this.memoryManager.updateMetaInformation(this.memoryId, userInfo); // Update active users in memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified logging function
|
||||||
|
private log(message: string, level: 'log' | 'error' = 'log'): void {
|
||||||
|
const prefix = '[UsersInfoFetcher]';
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(`${prefix} ${message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`${prefix} ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export class WorkerManager {
|
|||||||
this.workers.push(worker); // Store the worker reference
|
this.workers.push(worker); // Store the worker reference
|
||||||
|
|
||||||
worker.on('message', (data) => {
|
worker.on('message', (data) => {
|
||||||
console.log(data);
|
|
||||||
if (data.type === 'changeContent') {
|
if (data.type === 'changeContent') {
|
||||||
this.windowManager.changeContent(data.page);
|
this.windowManager.changeContent(data.page);
|
||||||
}
|
}
|
||||||
@@ -43,7 +42,7 @@ export class WorkerManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the Connection Pool Worker
|
// Start the Directories Watcher Worker
|
||||||
async startDirectoriesWatchersWorker(memoryManagerPath: string, applicationInfoPath: string): Promise<void> {
|
async startDirectoriesWatchersWorker(memoryManagerPath: string, applicationInfoPath: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const worker = new Worker(path.join(this.pathToWorkerDir, 'directories_watcher_worker.js'), {
|
const worker = new Worker(path.join(this.pathToWorkerDir, 'directories_watcher_worker.js'), {
|
||||||
@@ -53,18 +52,18 @@ export class WorkerManager {
|
|||||||
this.workers.push(worker); // Store the worker reference
|
this.workers.push(worker); // Store the worker reference
|
||||||
|
|
||||||
worker.on('message', (data) => {
|
worker.on('message', (data) => {
|
||||||
console.log('Connection Pool Worker message:', data);
|
console.log('DirectoriesWatcher message:', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('error', (err) => {
|
worker.on('error', (err) => {
|
||||||
console.error('Connection Pool Worker error:', err);
|
console.error('DirectoriesWatcher error:', err);
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
this.removeWorker(worker);
|
this.removeWorker(worker);
|
||||||
reject(err); // Reject the promise if there's an error
|
reject(err); // Reject the promise if there's an error
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('exit', (code) => {
|
worker.on('exit', (code) => {
|
||||||
console.log(`Connection Pool Worker exited with code ${code}`);
|
console.log(`DirectoriesWatcher exited with code ${code}`);
|
||||||
this.removeWorker(worker); // Remove worker reference when it exits
|
this.removeWorker(worker); // Remove worker reference when it exits
|
||||||
resolve(); // Resolve when the worker exits cleanly
|
resolve(); // Resolve when the worker exits cleanly
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ app.whenReady().then(async () => {
|
|||||||
workerManager.startNetworkScannerWorker(UDP_PORT, TCP_PORT, 'login', 'uc_not_found', 'reset_database', path.join(pathToJsons, 'userConfig.json'), path.join(pathToJsons, 'application.json'));
|
workerManager.startNetworkScannerWorker(UDP_PORT, TCP_PORT, 'login', 'uc_not_found', 'reset_database', path.join(pathToJsons, 'userConfig.json'), path.join(pathToJsons, 'application.json'));
|
||||||
workerManager.startDirectoriesWatchersWorker(path.join(pathToJsons, 'memory.json'), path.join(pathToJsons, 'application.json'));
|
workerManager.startDirectoriesWatchersWorker(path.join(pathToJsons, 'memory.json'), path.join(pathToJsons, 'application.json'));
|
||||||
workerManager.startServersWorker(HOST, UDP_PORT, TCP_PORT);
|
workerManager.startServersWorker(HOST, UDP_PORT, TCP_PORT);
|
||||||
|
|
||||||
workerManager.startResourceCoordinatorWorker(
|
workerManager.startResourceCoordinatorWorker(
|
||||||
path.join(pathToJsons, 'userConfig.json'),
|
path.join(pathToJsons, 'userConfig.json'),
|
||||||
path.join(pathToJsons, 'application.json'),
|
path.join(pathToJsons, 'application.json'),
|
||||||
@@ -219,6 +220,11 @@ function registerIPCHandlers() {
|
|||||||
return await windowManager.showFileInExplorer(path);
|
return await windowManager.showFileInExplorer(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('close-announcement-window', async (_event: IpcMainInvokeEvent) => {
|
||||||
|
if (!windowManager) throw new Error('WindowManager is not initialized.');
|
||||||
|
return await windowManager.closeAnnouncementWindow();
|
||||||
|
});
|
||||||
|
|
||||||
// TcpMethods IPC Handlers
|
// TcpMethods IPC Handlers
|
||||||
ipcMain.handle('open-socket', async (_event: IpcMainInvokeEvent) => {
|
ipcMain.handle('open-socket', async (_event: IpcMainInvokeEvent) => {
|
||||||
if (!applicationInfo) throw new Error('TcpMethods is not initialized.');
|
if (!applicationInfo) throw new Error('TcpMethods is not initialized.');
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
selectDirectory: (): Promise<string | undefined> => ipcRenderer.invoke('select-directory'),
|
selectDirectory: (): Promise<string | undefined> => ipcRenderer.invoke('select-directory'),
|
||||||
selectFile: (): Promise<string | undefined> => ipcRenderer.invoke('select-file'),
|
selectFile: (): Promise<string | undefined> => ipcRenderer.invoke('select-file'),
|
||||||
showFileInExplorer: (path: string): Promise<void> => ipcRenderer.invoke('show-file-in-explorer', path),
|
showFileInExplorer: (path: string): Promise<void> => ipcRenderer.invoke('show-file-in-explorer', path),
|
||||||
|
closeAnnouncementWindow: (): Promise<void> => ipcRenderer.invoke('close-announcement-window'),
|
||||||
|
|
||||||
// Queue methods
|
// Queue methods
|
||||||
addTaskToSendFileQueue: (task: FileItemTask): Promise<void> => ipcRenderer.invoke('add-task-to-send-file-queue', task),
|
addTaskToSendFileQueue: (task: FileItemTask): Promise<void> => ipcRenderer.invoke('add-task-to-send-file-queue', task),
|
||||||
|
|||||||
@@ -7,37 +7,11 @@ const {
|
|||||||
} = workerData;
|
} = workerData;
|
||||||
|
|
||||||
const backupDirectoryManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'backupDirectory');
|
const backupDirectoryManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'backupDirectory');
|
||||||
|
backupDirectoryManager.start();
|
||||||
|
|
||||||
const departmentShareManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'departmentDirectory');
|
const departmentShareManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'departmentDirectory');
|
||||||
|
departmentShareManager.start();
|
||||||
|
|
||||||
const shareFileManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'shareDirectory');
|
const shareFileManager = new DirectoryWatcher(memoryManagerPath, applicationInfoPath, 'shareDirectory');
|
||||||
|
shareFileManager.start();
|
||||||
// Backup directory manager check loop
|
|
||||||
const backupIntervalId = setInterval(async () => {
|
|
||||||
if (await backupDirectoryManager.initialize()) {
|
|
||||||
clearInterval(backupIntervalId); // Stop the loop once initialized
|
|
||||||
console.log('BackupDirectoryManager successfully initialized.');
|
|
||||||
} else {
|
|
||||||
console.log('Retrying BackupDirectoryManager initialization...');
|
|
||||||
}
|
|
||||||
}, 10000); // Check every 60 seconds
|
|
||||||
|
|
||||||
|
|
||||||
// Department share manager check loop
|
|
||||||
const departmentIntervalId = setInterval(async () => {
|
|
||||||
if (await departmentShareManager.initialize()) {
|
|
||||||
clearInterval(departmentIntervalId); // Stop the loop once initialized
|
|
||||||
console.log('DepartmentShareManager successfully initialized.');
|
|
||||||
} else {
|
|
||||||
console.log('Retrying DepartmentShareManager initialization...');
|
|
||||||
}
|
|
||||||
}, 10000); // Check every 60 seconds
|
|
||||||
|
|
||||||
// Department share manager check loop
|
|
||||||
const shareIntervalId = setInterval(async () => {
|
|
||||||
if (await shareFileManager.initialize()) {
|
|
||||||
clearInterval(shareIntervalId); // Stop the loop once initialized
|
|
||||||
console.log('DepartmentShareManager successfully initialized.');
|
|
||||||
} else {
|
|
||||||
console.log('Retrying DepartmentShareManager initialization...');
|
|
||||||
}
|
|
||||||
}, 10000); // Check every 60 seconds
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import {parentPort, workerData} from 'worker_threads';
|
import { parentPort, workerData } from 'worker_threads';
|
||||||
import {JsonManager} from '../helpers/json_manager';
|
import { NetworkScanner } from '../helpers/network_scanner';
|
||||||
import {UdpClient} from '../network/udp/udp_client';
|
|
||||||
import {TcpCommunicator} from "../helpers/tcp_communicator";
|
|
||||||
import {operationCodes} from "../network/operation_codes";
|
|
||||||
import {ParsedMessage} from "../network/message_handler";
|
|
||||||
|
|
||||||
// Define the structure of workerData
|
// Define the structure of workerData
|
||||||
interface WorkerData {
|
interface WorkerData {
|
||||||
udpPort: number;
|
udpPort: number;
|
||||||
tcpPort: number
|
tcpPort: number;
|
||||||
okPage: string;
|
okPage: string;
|
||||||
errorPage: string;
|
errorPage: string;
|
||||||
databaseResetPage: string;
|
databaseResetPage: string;
|
||||||
@@ -17,151 +13,7 @@ interface WorkerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract the data passed to the worker
|
// Extract the data passed to the worker
|
||||||
const {udpPort, tcpPort, okPage, errorPage, databaseResetPage, userConfigPath, applicationInfoPath}: WorkerData = workerData;
|
const { udpPort, tcpPort, okPage, errorPage, databaseResetPage, userConfigPath, applicationInfoPath }: WorkerData = workerData;
|
||||||
|
|
||||||
// Create a JsonManager instance for application info
|
// Start the NetworkScanner instance
|
||||||
const applicationInfo = new JsonManager(applicationInfoPath);
|
const networkScanner = new NetworkScanner(applicationInfoPath, userConfigPath, udpPort, tcpPort, okPage, errorPage, databaseResetPage);
|
||||||
const userConfig = new JsonManager(userConfigPath);
|
|
||||||
|
|
||||||
let appStarted = false;
|
|
||||||
let intervalIds: NodeJS.Timeout[] = []; // Store interval IDs for future clearing
|
|
||||||
|
|
||||||
// Flags to prevent overlapping executions
|
|
||||||
let ucCheckBusy = false;
|
|
||||||
let ipLookupBusy = false;
|
|
||||||
let sendLoginBusy = false;
|
|
||||||
|
|
||||||
// Function to schedule the UC check task with dynamic UDP client creation
|
|
||||||
function startUCCheck(udpPort: number, okPage: string, errorPage: string, interval: number = 5000): void {
|
|
||||||
const intervalId = setInterval(async () => {
|
|
||||||
if (ucCheckBusy) return; // If already running, skip this iteration
|
|
||||||
ucCheckBusy = true; // Mark as busy
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('UC Check running...');
|
|
||||||
const udpClient = new UdpClient(udpPort); // Create UdpClient with the provided port
|
|
||||||
const aliveClients = await udpClient.getAliveClients();
|
|
||||||
const storedIp = await applicationInfo.readValue('serverIp');
|
|
||||||
const foundClient = aliveClients.length > 0;
|
|
||||||
|
|
||||||
if (foundClient) {
|
|
||||||
const ipAddress = aliveClients[0]; // Just using the first alive client
|
|
||||||
|
|
||||||
if (!storedIp || storedIp !== ipAddress) {
|
|
||||||
await applicationInfo.writeValue('serverIp', ipAddress);
|
|
||||||
if (!appStarted) {
|
|
||||||
parentPort?.postMessage({type: 'changeContent', page: okPage});
|
|
||||||
}
|
|
||||||
appStarted = true;
|
|
||||||
} else if (!appStarted) {
|
|
||||||
parentPort?.postMessage({type: 'changeContent', page: okPage});
|
|
||||||
appStarted = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parentPort?.postMessage({type: 'changeContent', page: errorPage});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error checking UC:', err);
|
|
||||||
parentPort?.postMessage({type: 'changeContent', page: errorPage});
|
|
||||||
} finally {
|
|
||||||
ucCheckBusy = false; // Mark as not busy
|
|
||||||
}
|
|
||||||
}, interval);
|
|
||||||
|
|
||||||
intervalIds.push(intervalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to schedule the IP lookup task, storing the active addresses in memory
|
|
||||||
function startUserIPLookup(udpPort: number, interval: number = 10000): void {
|
|
||||||
const intervalId = setInterval(async () => {
|
|
||||||
if (ipLookupBusy) return; // If already running, skip this iteration
|
|
||||||
ipLookupBusy = true; // Mark as busy
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('IP Lookup running...');
|
|
||||||
const serverIp = await applicationInfo.readValue('serverIp');
|
|
||||||
const udpClient = new UdpClient(udpPort); // Create a UDP client with the provided port
|
|
||||||
const activeIPs = await udpClient.getAliveClients(); // Get the list of active IPs
|
|
||||||
|
|
||||||
// Filter out the serverIp from the list of active clients
|
|
||||||
const filteredIPs = activeIPs.filter(ip => ip !== serverIp);
|
|
||||||
|
|
||||||
// Save the filtered IPs to 'users_ip'
|
|
||||||
await applicationInfo.writeValue('users_ip', filteredIPs);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error during user IP lookup:', err);
|
|
||||||
} finally {
|
|
||||||
ipLookupBusy = false; // Mark as not busy
|
|
||||||
}
|
|
||||||
}, interval);
|
|
||||||
|
|
||||||
intervalIds.push(intervalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendLoginRequest(databaseResetPage: string, interval: number = 5000): void {
|
|
||||||
// Read user_info from userConfig for email and password
|
|
||||||
const intervalId = setInterval(async () => {
|
|
||||||
if (sendLoginBusy && !appStarted) return;
|
|
||||||
sendLoginBusy = true;
|
|
||||||
|
|
||||||
const userInfo = await userConfig.readValue('user_info');
|
|
||||||
if (!userInfo || !userInfo.email || !userInfo.password) {
|
|
||||||
console.error("Email or password not found in user config.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const app_type = await userConfig.readValue('app_type');
|
|
||||||
|
|
||||||
const email = userInfo.email;
|
|
||||||
const password = userInfo.password;
|
|
||||||
|
|
||||||
// Initialize the TCP communicator with the server IP from applicationInfo
|
|
||||||
const serverIp = await applicationInfo.readValue('serverIp');
|
|
||||||
if (!serverIp) {
|
|
||||||
console.error("Server IP not found in application info.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tcpCommunicator = new TcpCommunicator(serverIp, tcpPort);
|
|
||||||
|
|
||||||
if (!await tcpCommunicator.connect()) {
|
|
||||||
console.error("Failed to connect to the server.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the login request data
|
|
||||||
const metaInfo = {email, password, app_type};
|
|
||||||
if (!await tcpCommunicator.sendMessage(operationCodes.LOGIN, metaInfo)) {
|
|
||||||
console.error("Failed to send login request.");
|
|
||||||
await tcpCommunicator.disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Await and process the response
|
|
||||||
const response = await waitForResponse(tcpCommunicator);
|
|
||||||
if (response?.operationCode !== operationCodes.OK) {
|
|
||||||
await userConfig.resetFile();
|
|
||||||
await userConfig.writeValue('app_type', app_type);
|
|
||||||
parentPort?.postMessage({type: 'changeContent', page: databaseResetPage});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}, interval)
|
|
||||||
intervalIds.push(intervalId); // Store the interval ID for later clearing if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to wait for a response
|
|
||||||
async function waitForResponse(tcpCommunicator: TcpCommunicator): Promise<ParsedMessage | null> {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const checkInterval = setInterval(() => {
|
|
||||||
if (tcpCommunicator.hasResponseArrived()) {
|
|
||||||
clearInterval(checkInterval);
|
|
||||||
resolve(tcpCommunicator.getLastResult());
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the UC Check and User IP Lookup tasks
|
|
||||||
startUCCheck(udpPort, okPage, errorPage);
|
|
||||||
startUserIPLookup(udpPort);
|
|
||||||
sendLoginRequest(databaseResetPage);
|
|
||||||
|
|||||||
@@ -8,45 +8,15 @@ import {DepartmentSharer} from "../helpers/department_sharer";
|
|||||||
const { usersConfigPath, applicationInfoPath, memoryManagerPath, queueManagerPath, tcpPort } = workerData;
|
const { usersConfigPath, applicationInfoPath, memoryManagerPath, queueManagerPath, tcpPort } = workerData;
|
||||||
|
|
||||||
const usersInfoFetcher = new UsersInfoFetcher(applicationInfoPath, memoryManagerPath, tcpPort);
|
const usersInfoFetcher = new UsersInfoFetcher(applicationInfoPath, memoryManagerPath, tcpPort);
|
||||||
usersInfoFetcher.start()
|
usersInfoFetcher.start();
|
||||||
.then(() => {
|
|
||||||
console.log('Users Info Fetcher started successfully');
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error('Error starting Users Info Fetcher:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
const backupManager = new BackupRetrievalWorker(
|
const backupManager = new BackupRetrievalWorker(usersConfigPath, applicationInfoPath, memoryManagerPath, tcpPort);
|
||||||
usersConfigPath,
|
backupManager.start();
|
||||||
applicationInfoPath,
|
|
||||||
memoryManagerPath,
|
|
||||||
tcpPort
|
|
||||||
);
|
|
||||||
|
|
||||||
backupManager.start()
|
|
||||||
.then(() => {
|
|
||||||
console.log('Backup Manager started successfully');
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error('Error starting Backup Manager:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
const fileSharer = new FileSharer(queueManagerPath, tcpPort);
|
const fileSharer = new FileSharer(queueManagerPath, tcpPort);
|
||||||
fileSharer.start()
|
fileSharer.start();
|
||||||
.then(() => {
|
|
||||||
console.log('File Sharer started successfully');
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error('Error starting File Sharer:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const departmentSharer = new DepartmentSharer(usersConfigPath, applicationInfoPath, memoryManagerPath, tcpPort);
|
const departmentSharer = new DepartmentSharer(usersConfigPath, applicationInfoPath, memoryManagerPath, tcpPort);
|
||||||
departmentSharer.start()
|
departmentSharer.start();
|
||||||
.then(() => {
|
|
||||||
console.log('Department Sharer started successfully');
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error('Error starting Department Sharer:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user