async plugins fixed

This commit is contained in:
andrei-mihnea-cerbu
2024-11-12 18:05:50 +02:00
parent 4fb58b9727
commit 44df437425
22 changed files with 1690 additions and 2262 deletions
+9 -6
View File
@@ -87,11 +87,10 @@ export class DepartmentSharer {
const userIp = user.ip;
this.tcpCommunicator = new TcpCommunicator(userIp, this.clientPort);
if (await this.tcpCommunicator.connect()) continue;
if (!await this.tcpCommunicator.connect()) continue;
// First clear the department directory
const clearSuccess = await this.clearDepartmentDirectory();
if (clearSuccess) {
// First clear the department directory;
if (await this.clearDepartmentDirectory(userName)) {
await this.sendFilesToUser(departmentFiles.structure, userName);
}
@@ -108,12 +107,14 @@ export class DepartmentSharer {
}
// Clear the department directory for a user
private async clearDepartmentDirectory(): Promise<boolean> {
private async clearDepartmentDirectory(userName: string): Promise<boolean> {
if(!this.tcpCommunicator) return false;
if (!await this.tcpCommunicator.sendMessage(operationCodes.CLEAR_DEPARTMENT)) return false;
if (!await this.tcpCommunicator.sendMessage(operationCodes.CLEAR_DEPARTMENT, {userName: userName})) return false;
const response = await this.waitForResponse();
this.log(`${response?.operationCode}: ${response?.metaInfo?.message}`);
if (!response || response.operationCode !== operationCodes.OK){
this.log('Failed to clear the department directory.', 'error');
return false;
@@ -158,6 +159,8 @@ export class DepartmentSharer {
return;
}
this.log(`File sent successfully: ${fileName} to ${userName}`);
unsentFiles.splice(unsentFiles.indexOf(fileName), 1);
await this.tcpCommunicator.disconnect();
}
+32 -18
View File
@@ -10,6 +10,17 @@ export class WindowManager {
constructor(mainWindow: BrowserWindow, pathToPagesDir: string) {
this.pathToPagesDir = pathToPagesDir;
this.mainWindow = mainWindow;
this.log('WindowManager initialized.');
}
// Logging helper function
private log(message: string, level: 'log' | 'error' = 'log'): void {
const prefix = '[WindowManager]';
if (level === 'error') {
console.error(`${prefix} ${message}`);
} else {
console.log(`${prefix} ${message}`);
}
}
// Show an alert dialog
@@ -21,8 +32,9 @@ export class WindowManager {
message: message,
buttons: ['OK'],
});
this.log(`Alert displayed with message: "${message}"`);
} else {
console.error('Main window is not available.');
this.log('Main window is not available.', 'error');
}
}
@@ -31,17 +43,17 @@ export class WindowManager {
if (this.mainWindow) {
try {
const destinationPath = path.join(this.pathToPagesDir, `${destination}.html`);
console.log(`Navigating to: ${destinationPath}`);
this.log(`Navigating to: ${destinationPath}`);
// Load the destination HTML file into the main window
await this.mainWindow.loadFile(destinationPath);
console.log(`Navigated to ${destination}`);
this.log(`Navigated to ${destination}`);
} catch (error) {
console.error('Error changing content:', error);
throw error; // Pass the error back to the render process
this.log(`Error changing content: ${error}`, 'error');
throw error; // Pass the error back to the render process
}
} else {
console.error('Main window is not available.');
this.log('Main window is not available.', 'error');
}
}
@@ -51,26 +63,26 @@ export class WindowManager {
properties: ['openDirectory'], // Only allow selecting directories
});
// If the user cancels, result.filePaths will be an empty array
if (result.filePaths && result.filePaths.length > 0) {
this.log(`Directory selected: ${result.filePaths[0]}`);
return result.filePaths[0]; // Return the selected directory path
} else {
console.log('No directory selected.');
this.log('No directory selected.');
return undefined; // Return undefined if no directory was selected
}
}
// Show a file in the explorer
async showFileInExplorer(filePath: string): Promise<void> {
if (filePath && fs.existsSync(filePath)) {
try {
// Use Electron's shell module to show the file in the explorer
shell.showItemInFolder(filePath);
console.log(`Opened file explorer for: ${filePath}`);
this.log(`Opened file explorer for: ${filePath}`);
} catch (error: any) {
console.error(`Error showing file in explorer: ${error.message}`);
this.log(`Error showing file in explorer: ${error.message}`, 'error');
}
} else {
console.error('File path is undefined or does not exist.');
this.log('File path is undefined or does not exist.', 'error');
}
}
@@ -84,9 +96,10 @@ export class WindowManager {
});
if (result.filePaths && result.filePaths.length > 0) {
this.log(`File selected: ${result.filePaths[0]}`);
return result.filePaths[0]; // Return the selected file path
} else {
console.log('No file selected.');
this.log('No file selected.');
return undefined; // Return undefined if no file was selected
}
}
@@ -94,15 +107,14 @@ export class WindowManager {
// 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();
this.log('Announcement window focused.');
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,
@@ -116,20 +128,22 @@ export class WindowManager {
});
this.announcementWindow.removeMenu();
// Load the announcement page
const announcementPath = path.join(this.pathToPagesDir, 'announcement.html');
await this.announcementWindow.loadFile(announcementPath);
this.log(`Announcement window opened at: ${announcementPath}`);
// Handle window close
this.announcementWindow.on('closed', () => {
this.announcementWindow = null; // Clean up the reference
this.announcementWindow = null;
this.log('Announcement window closed.');
});
}
async closeAnnouncementWindow(): Promise<void> {
if (this.announcementWindow) {
this.announcementWindow.close();
this.log('Announcement window closed by user.');
}
}
}
@@ -1,40 +0,0 @@
import { ParsedMessage } from '../message_handler';
import { OperationHandler } from './operation_handler';
import { OperationPlugin } from './operation_plugin';
export abstract class OperationBase implements OperationPlugin {
// Shared operation codes
public static readonly operationCodes = {
OK: 'OK',
ERR: 'ERR',
END: 'END',
};
// Default handler for OK operation
public static handleOk(parsedMessage: ParsedMessage): ParsedMessage {
return parsedMessage; // Typically, you would just acknowledge with OK, returning as-is
}
// Default handler for ERR operation
public static handleErr(parsedMessage: ParsedMessage): ParsedMessage {
return parsedMessage; // Typically, you would log the error and return
}
// Default handler for END operation
public static handleEnd(parsedMessage: ParsedMessage): ParsedMessage {
return {
operationCode: OperationBase.operationCodes.END,
metaInfo: { message: 'Connection ended.' },
};
}
// Register the common OK, ERR, and END handlers
public static registerCommonOperations(operationHandler: OperationHandler): void {
operationHandler.registerHandler(OperationBase.operationCodes.OK, OperationBase.handleOk);
operationHandler.registerHandler(OperationBase.operationCodes.ERR, OperationBase.handleErr);
operationHandler.registerHandler(OperationBase.operationCodes.END, OperationBase.handleEnd);
}
// Abstract register method that will be implemented by subclasses
public abstract register(operationHandler: OperationHandler): void;
}
@@ -2,14 +2,14 @@
import { ParsedMessage, MessageHandler } from '../message_handler';
import { OperationPlugin } from './operation_plugin';
type OperationHandlerFunction = (parsedMessage: ParsedMessage) => ParsedMessage;
// Define handler function type to return Promise<ParsedMessage>
type OperationHandlerFunction = (parsedMessage: ParsedMessage) => Promise<ParsedMessage>;
export class OperationHandler {
private static instance: OperationHandler;
private handlers: { [operationCode: string]: OperationHandlerFunction } = {};
private constructor() {
// Register only the unknown command handler on initialization
this.registerHandler('UNKNOWN_COMMAND', this.handleUnknownCommand);
}
@@ -26,25 +26,24 @@ export class OperationHandler {
this.handlers[operationCode] = handler;
}
// Handle operation request
public handleOperation(rawMessage: string): ParsedMessage {
// Parse and validate the message
// Handle operation request asynchronously
public async handleOperation(rawMessage: string): Promise<ParsedMessage> {
const parsedMessage = MessageHandler.parseMessage(rawMessage);
if (!parsedMessage || !MessageHandler.validateMessage(parsedMessage)) {
return this.handleUnknownCommand(parsedMessage);
}
// Dispatch the handler for the given operation code
// Retrieve the handler for the operation code and invoke it asynchronously
const handler = this.handlers[parsedMessage.operationCode];
if (handler) {
return handler(parsedMessage);
return await handler(parsedMessage);
} else {
return this.handleUnknownCommand(parsedMessage);
}
}
// Default handler for unknown commands
private handleUnknownCommand(parsedMessage: ParsedMessage): ParsedMessage {
private async handleUnknownCommand(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
return {
operationCode: 'UNKNOWN_COMMAND',
metaInfo: { message: 'Unknown command received.' },
@@ -1,25 +1,26 @@
import { ParsedMessage } from '../message_handler';
import { OperationBase } from '../operations_base/operation_base';
import { OperationHandler } from '../operations_base/operation_handler';
import os from 'node:os';
import {OperationPlugin} from "../operations_base/operation_plugin";
export class GeneralOperations extends OperationBase {
export class GeneralOperations implements OperationPlugin {
public static readonly operationCodes = {
...OperationBase.operationCodes, // Inherit common operation codes (OK, ERR, END)
OK: 'OK',
ERR: 'ERR',
END: 'END',
HEARTBEAT: 'HEARTBEAT',
ALIVE: 'ALIVE',
SET_PUBLIC_KEY: 'SET_PUBLIC_KEY',
SET_AES_KEY: 'SET_AES_KEY',
};
// Handle heartbeat operation
public static handleHeartbeat(): ParsedMessage {
// Handle heartbeat operation asynchronously
public static async handleHeartbeat(): Promise<ParsedMessage> {
const networkInterfaces = os.networkInterfaces();
let ipAddress = 'Unknown';
for (const iface of Object.values(networkInterfaces)) {
// @ts-ignore
for (const address of iface) {
for (const address of iface!) {
if (address.family === 'IPv4' && !address.internal) {
ipAddress = address.address;
break;
@@ -34,8 +35,8 @@ export class GeneralOperations extends OperationBase {
};
}
// Handle public key exchange
public static handlePublicKey(parsedMessage: ParsedMessage): ParsedMessage {
// Handle public key exchange asynchronously
public static async handlePublicKey(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const clientPublicKey = parsedMessage.metaInfo?.publicKey;
if (clientPublicKey) {
return {
@@ -50,14 +51,14 @@ export class GeneralOperations extends OperationBase {
}
}
// Handle AES key exchange
public static handleAESKey(parsedMessage: ParsedMessage): ParsedMessage {
// Handle AES key exchange asynchronously
public static async handleAESKey(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const aesKey = parsedMessage.metaInfo?.aesKey;
const aesIv = parsedMessage.metaInfo?.aesIv;
if (aesKey && aesIv) {
return {
operationCode: GeneralOperations.operationCodes.SET_AES_KEY,
metaInfo: { aesKey: aesKey, aesIv: aesIv },
metaInfo: { aesKey, aesIv },
};
} else {
return {
@@ -67,14 +68,22 @@ export class GeneralOperations extends OperationBase {
}
}
// Default async handler for OK operation
public static async handleOk(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
return parsedMessage; // Acknowledge with OK, returning as-is
}
// Default async handler for ERR operation
public static async handleErr(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
return parsedMessage; // Log the error and return
}
// Register general operations with the OperationHandler
public register(operationHandler: OperationHandler): void {
// Register specific handlers for the general operations
operationHandler.registerHandler(GeneralOperations.operationCodes.HEARTBEAT, GeneralOperations.handleHeartbeat);
operationHandler.registerHandler(GeneralOperations.operationCodes.SET_PUBLIC_KEY, GeneralOperations.handlePublicKey);
operationHandler.registerHandler(GeneralOperations.operationCodes.SET_AES_KEY, GeneralOperations.handleAESKey); // Register AES key handler
// Register common operations inherited from the base class
OperationBase.registerCommonOperations(operationHandler);
operationHandler.registerHandler(GeneralOperations.operationCodes.SET_AES_KEY, GeneralOperations.handleAESKey);
operationHandler.registerHandler(GeneralOperations.operationCodes.OK, GeneralOperations.handleOk);
operationHandler.registerHandler(GeneralOperations.operationCodes.ERR, GeneralOperations.handleErr);
}
}
@@ -1,18 +1,16 @@
import { ParsedMessage } from '../message_handler';
import { OperationBase } from '../operations_base/operation_base';
import { OperationHandler } from '../operations_base/operation_handler';
import {operationCodes} from "../operation_codes";
import path from 'path';
import { execSync } from 'child_process';
import fs from 'fs';
import fs from 'fs/promises';
import checkDiskSpace from "check-disk-space";
import { JsonManager } from '../../helpers/json_manager';
import {OperationPlugin} from "../operations_base/operation_plugin";
const LOCK_FILE_EXTENSION = '.lock';
export class UserToUserOperations extends OperationBase {
export class UserToUserOperations implements OperationPlugin {
public static readonly operationCodes = {
...OperationBase.operationCodes, // Inherit common operation codes (OK, ERR, END)
SEND_ANNOUNCEMENT: 'SEND_ANNOUNCEMENT',
GET_USER_INFORMATION: 'GET_USER_INFORMATION',
RESET_DATABASE: 'RESET_DATABASE',
BACKUP_FILE: 'BACKUP_FILE',
CLEAR_BACKUP: 'CLEAR_BACKUP',
SHARE_FILE: 'SHARE_FILE',
@@ -24,429 +22,149 @@ export class UserToUserOperations extends OperationBase {
DECRYPT_AND_SAVE_OLD_BACKUP_FILE: 'DECRYPT_AND_SAVE_OLD_BACKUP_FILE',
};
// Utility function to pause execution (sleep)
static sleep(ms: number): void {
const start = Date.now();
while (Date.now() - start < ms) {
// Busy wait loop (not optimal but fine for this short duration)
}
}
// Read JSON file with a lock mechanism
static readJsonSync(filePath: string): any {
const lockFilePath = `${filePath}.${LOCK_FILE_EXTENSION}`;
const absolutePath = path.resolve(filePath);
private static async hasEnoughDiskSpace(directory: string, requiredPercentage: number = 25): Promise<boolean> {
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);
}
// Create lock file to signal this process is working on the file
fs.writeFileSync(lockFilePath, ''); // Create the lock file
// Read and parse the JSON file
const fileContents = fs.readFileSync(absolutePath, 'utf-8');
const parsedJson = JSON.parse(fileContents);
// Once processing is done, delete the lock file
fs.unlinkSync(lockFilePath); // Remove the lock file
return parsedJson; // Return the parsed JSON data
} catch (error) {
console.error(`Error reading or parsing JSON from ${filePath}:`, error);
// Ensure the lock file is removed even in case of an error
if (fs.existsSync(lockFilePath)) {
fs.unlinkSync(lockFilePath);
}
return null; // Return null or throw error based on preference
}
}
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 diskInfo = await checkDiskSpace(directory);
const availableSpace = diskInfo.free;
const totalSpace = diskInfo.size;
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
return false;
}
}
public static handleResetDatabase(parsedMessage: ParsedMessage): ParsedMessage {
// Path to the application.json file
const pathToApplicationJson = path.join(__dirname, '..', '..', 'json_files', 'application.json');
public static async handleSendAnnouncement(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
if (!parsedMessage.metaInfo?.message) {
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Missing announcement message.' }};
}
const jsonManager = new JsonManager(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");
}
await jsonManager.writeValue('announcement', parsedMessage.metaInfo.message);
console.log(`Announcement saved: ${parsedMessage.metaInfo.message}`);
return { operationCode: operationCodes.OK, metaInfo: { message: 'Announcement saved.' }};
} catch (error: any) {
console.error(`Error resetting application preferences: ${error.message}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Error resetting application preferences: ${error.message}` },
};
console.error(`Error saving announcement: ${error.message}`);
return { operationCode: operationCodes.ERR, metaInfo: { message: `Error: ${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');
public static async handleGetUserInformation(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
const jsonManager = new JsonManager(path.join(__dirname, '..', '..', 'json_files', 'userConfig.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.' },
};
const userInfo = await jsonManager.readValue('user_info');
return { operationCode: operationCodes.OK, metaInfo: userInfo };
} catch (error: any) {
console.error(`Error saving announcement message: ${error.message}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Error saving announcement message: ${error.message}` },
};
console.error(`Error fetching user info: ${error.message}`);
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Error fetching user info' }};
}
}
// Handle user information retrieval
public static handleGetUserInformation(parsedMessage: ParsedMessage): ParsedMessage {
const pathToUserJson = path.join(__dirname, '..', '..', 'json_files', 'userConfig.json');
const userInfo = UserToUserOperations.readJsonSync(pathToUserJson);
if (userInfo) {
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: userInfo.user_info,
};
} else {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Error fetching user information.' },
};
}
}
// Handle file reception and saving
public static handleBackupFile(parsedMessage: ParsedMessage): ParsedMessage {
// Ensure metaInfo and fileContent are available
if (!parsedMessage.metaInfo || !parsedMessage.fileContent) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing file content or meta information.' },
};
public static async handleBackupFile(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
if (!parsedMessage.metaInfo?.userName || !parsedMessage.metaInfo?.relativeFilePath || !parsedMessage.fileContent) {
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Missing file or user information.' }};
}
const { userName, relativeFilePath } = parsedMessage.metaInfo;
if (!userName || !relativeFilePath) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing user name or file path information.' },
};
}
// Base directory where backups will be stored
const baseBackupDir = path.join(__dirname, '..', '..', 'backups');
// Full path where the file will be stored (under the user's directory)
const fullFilePath = path.join(baseBackupDir, userName, relativeFilePath);
const fullFilePath = path.join(__dirname, '..', '..', 'backups', userName, relativeFilePath);
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.' },
};
if (!await UserToUserOperations.hasEnoughDiskSpace(path.dirname(fullFilePath), 25)) {
return { operationCode: operationCodes.ERR, metaInfo:
{ message: 'Insufficient disk space.' }};
}
// Ensure the directory structure exists (create directories if they don't exist)
const dirPath = path.dirname(fullFilePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
await fs.mkdir(path.dirname(fullFilePath), { recursive: true });
await fs.writeFile(fullFilePath, parsedMessage.fileContent as Buffer);
console.log(`File saved: ${fullFilePath}`);
// Write the file content to the correct path
fs.writeFileSync(fullFilePath, Buffer.from(parsedMessage.fileContent as Buffer), 'base64');
console.log(`File saved successfully: ${fullFilePath}`);
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: { message: `File saved successfully: ${relativeFilePath}` },
};
return { operationCode: operationCodes.OK, metaInfo: { message: `File saved: ${relativeFilePath}` }};
} catch (error: any) {
console.error(`Error saving file: ${error.message}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Error saving file: ${error.message}` },
};
return { operationCode: operationCodes.ERR, metaInfo: { message: `Error saving file: ${error.message}` }};
}
}
// Handle clearing all backups for a user
public static handleClearBackup(parsedMessage: ParsedMessage): ParsedMessage {
// Ensure the userName is available in metaInfo
if (!parsedMessage.metaInfo || !parsedMessage.metaInfo.userName) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing user name in meta information.' },
};
public static async handleClearBackup(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
if (!parsedMessage.metaInfo?.userName) {
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Missing user name.' }};
}
const { userName } = parsedMessage.metaInfo;
// Base directory where backups are stored
const baseBackupDir = path.join(__dirname, '..', '..', 'backups');
const userBackupDir = path.join(baseBackupDir, userName);
const userBackupDir = path.join(__dirname, '..', '..', 'backups', parsedMessage.metaInfo.userName);
try {
// Check if the user's backup directory exists
if (fs.existsSync(userBackupDir)) {
// Recursively delete the user's backup directory
fs.rmSync(userBackupDir, { recursive: true, force: true });
console.log(`Backup cleared successfully for user: ${userName}`);
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: { message: `Backup cleared successfully for user: ${userName}` },
};
} else {
console.warn(`Backup directory not found for user: ${userName}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Backup directory not found for user: ${userName}` },
};
}
await fs.rm(userBackupDir, { recursive: true, force: true });
console.log(`Backup cleared: ${userBackupDir}`);
return { operationCode: operationCodes.OK, metaInfo: { message: `Backup cleared for ${parsedMessage.metaInfo.userName}` }};
} catch (error: any) {
console.error(`Error clearing backup for user: ${userName} - ${error.message}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Error clearing backup for user: ${userName}` },
};
console.error(`Error clearing backup: ${error.message}`);
return { operationCode: operationCodes.ERR, metaInfo: { message: `Error clearing backup: ${error.message}` }};
}
}
// Handle sharing file operation
public static handleShareFile(parsedMessage: ParsedMessage): ParsedMessage {
// Ensure metaInfo and fileContent are available
if (!parsedMessage.metaInfo || !parsedMessage.fileContent) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing file content or meta information.' },
};
public static async handleShareFile(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
if (!parsedMessage.metaInfo?.userName || !parsedMessage.metaInfo?.relativeFilePath || !parsedMessage.fileContent) {
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Missing share info.' }};
}
const jsonManager = new JsonManager(path.join(__dirname, '..', '..', 'json_files', 'application.json'));
const { userName, relativeFilePath } = parsedMessage.metaInfo;
if (!userName || !relativeFilePath) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing user name or file path information.' },
};
}
// Path to the application.json to read the shareDirectory field
const pathToApplicationJson = path.join(__dirname, '..', '..', 'json_files', 'application.json');
const appInfo = UserToUserOperations.readJsonSync(pathToApplicationJson);
// Corrected the condition
if (!appInfo || !appInfo.shareDirectory) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Error retrieving share directory from application.json.' },
};
}
// Get the share directory path
const shareDirectory = appInfo.shareDirectory.path;
// Full path where the file will be stored (under the user's directory in the shared folder)
const fullFilePath = path.join(shareDirectory, userName, relativeFilePath);
try {
// Ensure the directory structure exists (create directories if they don't exist)
const dirPath = path.dirname(fullFilePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
const appInfo = await jsonManager.readValue('shareDirectory');
const shareDirectory = appInfo?.path || '';
if (!shareDirectory) {
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Share directory missing.' }};
}
// Write the file content to the correct path
fs.writeFileSync(fullFilePath, Buffer.from(parsedMessage.fileContent as Buffer), 'base64');
const fullFilePath = path.join(shareDirectory, userName, relativeFilePath);
await fs.mkdir(path.dirname(fullFilePath), { recursive: true });
await fs.writeFile(fullFilePath, parsedMessage.fileContent as Buffer);
console.log(`File shared: ${fullFilePath}`);
console.log(`File shared successfully: ${fullFilePath}`);
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: { message: `File shared successfully: ${relativeFilePath}` },
};
return { operationCode: operationCodes.OK, metaInfo: { message: `File shared: ${relativeFilePath}` }};
} catch (error: any) {
console.error(`Error sharing file: ${error.message}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Error sharing file: ${error.message}` },
};
return { operationCode: operationCodes.ERR, metaInfo: { message: `Error sharing file: ${error.message}` }};
}
}
public static handleClearDepartment(parsedMessage: ParsedMessage): ParsedMessage {
// Ensure the userName is available in metaInfo
if (!parsedMessage.metaInfo || !parsedMessage.metaInfo.userName) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing user name in meta information.' },
};
public static async handleClearDepartment(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
if (!parsedMessage.metaInfo?.userName) {
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Missing user name.' }};
}
const jsonManager = new JsonManager(path.join(__dirname, '..', '..', 'json_files', 'application.json'));
const { userName } = parsedMessage.metaInfo;
const pathToApplicationJson = path.join(__dirname, '..', '..', 'json_files', 'application.json');
const appInfo = UserToUserOperations.readJsonSync(pathToApplicationJson);
// Corrected the condition
if (!appInfo || !appInfo.departmentDirectory) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Error retrieving share directory from application.json.' },
};
}
// Get the share directory path
const baseDepartmentDir = appInfo.departmentDirectory.path;
const userDepartmentDir = path.join(baseDepartmentDir, '..', 'DEPARTMENT_FILES', userName);
try {
// Check if the user's backup directory exists
if (fs.existsSync(userDepartmentDir)) {
// Recursively delete the user's backup directory
fs.rmSync(userDepartmentDir, { recursive: true, force: true });
console.log(`Backup cleared successfully for user: ${userName}`);
const appInfo = await jsonManager.readValue('departmentDirectory');
const departmentDir = appInfo?.path || '';
const userDepartmentDir = path.join(departmentDir, '..', 'DEPARTMENT_SHARE', userName);
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: { message: `Backup cleared successfully for user: ${userName}`},
};
} else {
console.warn(`Backup directory not found for user: ${userName}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Backup directory not found for user: ${userName}`},
};
try {
await fs.access(userDepartmentDir)
}catch(ex: any){
return { operationCode: operationCodes.OK};
}
await fs.rm(userDepartmentDir, { recursive: true, force: true });
console.log(`Department backup cleared: ${userDepartmentDir}`);
return { operationCode: operationCodes.OK, metaInfo: { message: `Department backup cleared for ${userName}` }};
} catch (error: any) {
console.error(`Error clearing backup for user: ${userName} - ${error.message}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Error clearing backup for user: ${userName}`},
};
console.error(`Error clearing department: ${error.message}`);
return { operationCode: operationCodes.ERR, metaInfo: { message: `Error clearing department: ${error.message}` }};
}
}
public static handleDepartmentFile(parsedMessage: ParsedMessage): ParsedMessage {
public static async handleDepartmentFile(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
// Ensure metaInfo and fileContent are available
if (!parsedMessage.metaInfo || !parsedMessage.fileContent) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
operationCode: operationCodes.ERR,
metaInfo: { message: 'Missing file content or meta information.' },
};
}
@@ -455,130 +173,99 @@ export class UserToUserOperations extends OperationBase {
if (!userName || !relativeFilePath) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
operationCode: operationCodes.ERR,
metaInfo: { message: 'Missing user name or file path information.' },
};
}
// Path to the application.json to read the shareDirectory field
const pathToApplicationJson = path.join(__dirname, '..', '..', 'json_files', 'application.json');
const appInfo = UserToUserOperations.readJsonSync(pathToApplicationJson);
const jsonManager = new JsonManager(pathToApplicationJson);
// Corrected the condition
if (!appInfo || !appInfo.departmentDirectory) {
// Read application configuration asynchronously
let appInfo;
try {
appInfo = await jsonManager.readValue('departmentDirectory');
} catch (error: any) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Error retrieving share directory from application.json.' },
operationCode: operationCodes.ERR,
metaInfo: { message: `Error reading application config: ${error.message}` },
};
}
if (!appInfo || !appInfo.path) {
return {
operationCode: operationCodes.ERR,
metaInfo: { message: 'Error retrieving department directory from application.json.' },
};
}
// Get the share directory path
const departmentDirectory = appInfo.departmentDirectory.path;
// Full path where the file will be stored (under the user's directory in the shared folder)
const departmentDirectory = appInfo.path;
const fullFilePath = path.join(departmentDirectory, '..', 'DEPARTMENT_FILES', userName, relativeFilePath);
try {
// Ensure the directory structure exists (create directories if they don't exist)
const dirPath = path.dirname(fullFilePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
await fs.mkdir(dirPath, { recursive: true });
// Write the file content to the correct path
fs.writeFileSync(fullFilePath, Buffer.from(parsedMessage.fileContent as Buffer), 'base64');
console.log(`File shared successfully: ${fullFilePath}`);
// Write the file content to the specified path
await fs.writeFile(fullFilePath, Buffer.from(parsedMessage.fileContent as Buffer), 'base64');
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: { message: `File shared successfully: ${relativeFilePath}`},
};
operationCode: operationCodes.OK,
metaInfo: { message: `File shared successfully: ${relativeFilePath}` },
};
} catch (error: any) {
console.error(`Error sharing file: ${error.message}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: {message: `Error sharing file: ${error.message}`},
operationCode: operationCodes.ERR,
metaInfo: { message: `Error sharing file: ${error.message}` },
};
}
}
// Check if a backup has been created for a user
public static handleIsBackupCreated(parsedMessage: ParsedMessage): ParsedMessage {
if(!parsedMessage.metaInfo) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing metaInfo.' },
};
public static async handleIsBackupCreated(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
if (!parsedMessage.metaInfo?.name) {
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Missing name in meta information.' }};
}
const {name} = parsedMessage.metaInfo;
if (!name) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing name in meta information.' },
};
const userBackupDir = path.join(__dirname, '..', '..', 'backups', parsedMessage.metaInfo.name);
try {
const exists = await fs.access(userBackupDir).then(() => true).catch(() => false);
return { operationCode: operationCodes.OK, metaInfo: { backupExists: exists }};
} catch (error: any) {
console.error(`Error checking backup: ${error.message}`);
return { operationCode: operationCodes.ERR, metaInfo: { message: `Error checking backup: ${error.message}` }};
}
const baseBackupDir = path.join(__dirname, '..', '..', 'backups');
const userBackupDir = path.join(baseBackupDir, name);
console.log(userBackupDir);
const exists = fs.existsSync(userBackupDir);
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: { backupExists: exists },
};
}
// Get the structure of the backup directory for a user
public static handleGetBackupStructure(parsedMessage: ParsedMessage): ParsedMessage {
if(!parsedMessage.metaInfo) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing metaInfo.' },
};
public static async handleGetBackupStructure(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
if (!parsedMessage.metaInfo?.name) {
return { operationCode: operationCodes.ERR, metaInfo: { message: 'Missing name in meta information.' }};
}
const {name} = parsedMessage.metaInfo;
if (!name) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing name in meta information.' },
};
const userBackupDir = path.join(__dirname, '..', '..', 'backups', parsedMessage.metaInfo.name);
try {
const structure = await UserToUserOperations.buildDirectoryStructure(userBackupDir);
return { operationCode: operationCodes.OK, metaInfo: { structure }};
} catch (error: any) {
console.error(`Error building backup structure: ${error.message}`);
return { operationCode: operationCodes.ERR, metaInfo: { message: `Error: ${error.message}` }};
}
const baseBackupDir = path.join(__dirname, '..', '..', 'backups');
const userBackupDir = path.join(baseBackupDir, name);
if (!fs.existsSync(userBackupDir)) {
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: { structure: {} }, // Return empty structure if directory doesn't exist
};
}
const fileStructure = UserToUserOperations.buildDirectoryStructure(userBackupDir);
return {
operationCode: UserToUserOperations.operationCodes.OK,
metaInfo: { structure: fileStructure },
};
}
// Build the directory structure recursively
private static buildDirectoryStructure(directoryPath: string): any {
private static async buildDirectoryStructure(directoryPath: string): Promise<any> {
const structure: any = {};
const files = fs.readdirSync(directoryPath);
const files = await fs.readdir(directoryPath);
for (const file of files) {
const filePath = path.join(directoryPath, file);
const stats = fs.statSync(filePath);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
structure[file] = UserToUserOperations.buildDirectoryStructure(filePath); // Recursive for subdirectories
structure[file] = await UserToUserOperations.buildDirectoryStructure(filePath);
} else {
structure[file] = path.relative(directoryPath, filePath);
}
@@ -587,67 +274,43 @@ export class UserToUserOperations extends OperationBase {
return structure;
}
// Handle file request from backup directory
public static handleReqFileFromBackup(parsedMessage: ParsedMessage): ParsedMessage {
if(!parsedMessage.metaInfo) {
public static async handleReqFileFromBackup(parsedMessage: ParsedMessage): Promise<ParsedMessage> {
if (!parsedMessage.metaInfo?.name || !parsedMessage.metaInfo?.relativeFilePath) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing metaInfo.' },
operationCode: operationCodes.ERR,
metaInfo: { message: 'Missing user name or file path in meta information.' },
};
}
const { name, relativeFilePath } = parsedMessage.metaInfo;
if (!name || !relativeFilePath) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'Missing name or file path in meta information.' },
};
}
const baseBackupDir = path.join(__dirname, '..', '..', 'backups');
const userBackupDir = path.join(baseBackupDir, name);
const fullFilePath = path.join(userBackupDir, relativeFilePath);
if (!fs.existsSync(fullFilePath)) {
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: 'File not found in backup.' },
};
}
const fullFilePath = path.join(__dirname, '..', '..', 'backups', name, relativeFilePath);
try {
const fileContent = fs.readFileSync(fullFilePath, 'base64');
const fileContent = await fs.readFile(fullFilePath);
return {
operationCode: UserToUserOperations.operationCodes.OK,
operationCode: operationCodes.OK,
metaInfo: { relativeFilePath },
fileContent: Buffer.from(fileContent, 'base64')
fileContent,
};
} catch (error: any) {
console.error(`Error reading file: ${error.message}`);
console.error(`Error reading file from backup: ${error.message}`);
return {
operationCode: UserToUserOperations.operationCodes.ERR,
metaInfo: { message: `Error reading file: ${error.message}`},
operationCode: operationCodes.ERR,
metaInfo: { message: `Error reading file: ${error.message}` },
};
}
}
// Register user-to-user operations with the OperationHandler
public register(operationHandler: OperationHandler): void {
// 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.BACKUP_FILE, UserToUserOperations.handleBackupFile);
operationHandler.registerHandler(UserToUserOperations.operationCodes.CLEAR_BACKUP, UserToUserOperations.handleClearBackup);
operationHandler.registerHandler(UserToUserOperations.operationCodes.SHARE_FILE, UserToUserOperations.handleShareFile);
operationHandler.registerHandler(UserToUserOperations.operationCodes.DEPARTMENT_FILE, UserToUserOperations.handleDepartmentFile);
operationHandler.registerHandler(UserToUserOperations.operationCodes.CLEAR_DEPARTMENT, UserToUserOperations.handleClearDepartment);
operationHandler.registerHandler(UserToUserOperations.operationCodes.DEPARTMENT_FILE, UserToUserOperations.handleDepartmentFile);
operationHandler.registerHandler(UserToUserOperations.operationCodes.IS_BACKUP_CREATED, UserToUserOperations.handleIsBackupCreated);
operationHandler.registerHandler(UserToUserOperations.operationCodes.GET_BACKUP_STRUCTURE, UserToUserOperations.handleGetBackupStructure);
operationHandler.registerHandler(UserToUserOperations.operationCodes.REQ_FILE_FROM_BACKUP, UserToUserOperations.handleReqFileFromBackup);
// Register common operations inherited from the base class
OperationBase.registerCommonOperations(operationHandler);
}
}
@@ -122,7 +122,7 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
for (let i = 0; i < messages.length - 1; i++) {
const completeMessage = messages[i];
if (completeMessage) {
this.processCompleteMessage(completeMessage);
await this.processCompleteMessage(completeMessage);
}
}
@@ -130,7 +130,7 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
}
}
private processCompleteMessage(completeMessage: string): void {
private async processCompleteMessage(completeMessage: string): Promise<void> {
const [headerJson, chunkContent] = completeMessage.split('|');
const header = JSON.parse(headerJson);
@@ -142,12 +142,12 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
if (this.chunkBuffers[header.messageId].every((chunk) => chunk !== undefined)) {
const fullMessage = this.chunkBuffers[header.messageId].join('');
this.handleIncomingMessage(fullMessage);
await this.handleIncomingMessage(fullMessage);
delete this.chunkBuffers[header.messageId];
}
}
handleIncomingMessage(incomingMessage: string): void {
async handleIncomingMessage(incomingMessage: string): Promise<void> {
let messageToProcess = incomingMessage;
if (this.aesKey && this.aesIv) {
messageToProcess = this.decryptWithAes(incomingMessage);
@@ -155,7 +155,7 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
messageToProcess = this.decryptWithRsa(incomingMessage);
}
const result = this.operationHandler.handleOperation(messageToProcess);
const result = await this.operationHandler.handleOperation(messageToProcess);
if (result.operationCode === operationCodes.SET_AES_KEY) {
this.isAesKeySetFlag = true;
@@ -176,6 +176,8 @@ export class TcpClientCommunicator extends SocketCommunicatorBase {
}
getHandlerResult(): ParsedMessage | null {
return this.handlerResult;
const message = this.handlerResult;
this.handlerResult = null;
return message;
}
}
@@ -106,7 +106,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
for (let i = 0; i < messages.length - 1; i++) {
const completeMessage = messages[i];
if (completeMessage) {
this.processCompleteMessage(completeMessage);
await this.processCompleteMessage(completeMessage);
}
}
@@ -114,7 +114,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
}
}
private processCompleteMessage(completeMessage: string): void {
private async processCompleteMessage(completeMessage: string): Promise<void> {
const [headerJson, chunkContent] = completeMessage.split('|');
const header = JSON.parse(headerJson);
@@ -126,7 +126,7 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
if (this.chunkBuffers[header.messageId].every((chunk) => chunk !== undefined)) {
const fullMessage = this.chunkBuffers[header.messageId].join('');
this.handleIncomingMessage(fullMessage);
await this.handleIncomingMessage(fullMessage);
delete this.chunkBuffers[header.messageId];
}
}
@@ -169,14 +169,14 @@ export class TcpServerCommunicator extends SocketCommunicatorBase {
}
// Handle incoming message (decrypt with AES if available)
handleIncomingMessage(incomingMessage: string): void {
async handleIncomingMessage(incomingMessage: string): Promise<void> {
let messageToProcess = incomingMessage;
if (this.aesKey && this.aesIv) {
messageToProcess = this.decryptWithAes(incomingMessage);
}
this.handlerResult = this.operationHandler.handleOperation(messageToProcess);
this.handlerResult = await this.operationHandler.handleOperation(messageToProcess);
}
// Write message to socket
@@ -12,8 +12,8 @@ export class UdpSocketCommunicator extends SocketCommunicatorBase {
}
// Handle incoming message (no decryption needed for UDP)
handleIncomingMessage(incomingMessage: string): void {
this.handlerResult = this.operationHandler.handleOperation(incomingMessage);
async handleIncomingMessage(incomingMessage: string): Promise<void> {
this.handlerResult = await this.operationHandler.handleOperation(incomingMessage);
}
// Send plain message over UDP (metaInfo as JSON and fileContent as Buffer)
+23 -24
View File
@@ -14,6 +14,7 @@ export class TcpServer {
private readonly operationHandler: OperationHandler;
private readonly port: number;
private readonly host: string;
private clientQueues: Map<string, Promise<void>> = new Map();
constructor(host: string, port: number) {
this.connectionManager = new ConnectionManager();
@@ -25,21 +26,14 @@ export class TcpServer {
this.operationHandler.loadPlugin(new UserToUserOperations());
}
// Unified logging function
private log(message: string, level: 'log' | 'error' = 'log'): void {
const prefix = '[TcpServer]';
if (level === 'error') {
console.error(`${prefix} ${message}`);
} else {
console.log(`${prefix} ${message}`);
}
console[level === 'error' ? 'error' : 'log'](`${prefix} ${message}`);
}
// Start the TCP server
public start(): void {
const tcpServer = net.createServer();
// Handle incoming connections
tcpServer.on('connection', (socket: Socket) => {
const ip = socket.remoteAddress || 'unknown';
const port = socket.remotePort || 0;
@@ -49,61 +43,66 @@ export class TcpServer {
const tcpCommunicator = new TcpServerCommunicator(socket, ip, port, this.operationHandler);
this.connectionManager.addConnection(ip, port, tcpCommunicator);
// Generate RSA key pair and start key exchange
tcpCommunicator.generateKeyPair();
tcpCommunicator.sendPublicKey()
.then(() => tcpCommunicator.sendAesKey())
.catch(err => {
this.log(`Error during key exchange with client ${clientId}: ${err}`, 'error');
socket.end(); // Close the connection in case of any error
socket.end();
});
// Handle incoming data in chunks
socket.on('data', async (data: Buffer) => {
this.log(`Data received from client ${clientId}`);
await this.handleData(data, ip, port);
this.clientQueues.set(clientId, Promise.resolve());
socket.on('data', (data: Buffer) => {
this.queueClientDataProcessing(data, ip, port);
});
// Handle client disconnect
socket.on('end', () => {
this.log(`Client disconnected: ${clientId}`);
this.connectionManager.removeCommunicator(ip, port);
this.clientQueues.delete(clientId);
});
// Handle socket errors
socket.on('error', (err: Error) => {
this.log(`Error from client ${clientId}: ${err.message}`, 'error');
this.connectionManager.removeCommunicator(ip, port);
this.clientQueues.delete(clientId);
});
});
// Handle server errors
tcpServer.on('error', (err: Error) => {
this.log(`TCP server error: ${err.message}`, 'error');
});
// Start listening for connections
tcpServer.listen(this.port, this.host, () => {
this.log(`TCP server listening on ${this.host}:${this.port}`);
});
}
// Handle incoming data from a client
private queueClientDataProcessing(data: Buffer, ip: string, port: number): void {
const clientId = `${ip}:${port}`;
const clientQueue = this.clientQueues.get(clientId) || Promise.resolve();
this.clientQueues.set(
clientId,
clientQueue.then(() => this.handleData(data, ip, port)).catch(error => {
this.log(`Error handling data for ${clientId}: ${error}`, 'error');
})
);
}
private async handleData(data: Buffer, ip: string, port: number): Promise<void> {
const clientId = `${ip}:${port}`;
// Retrieve the communicator associated with this connection
const communicator = this.connectionManager.getCommunicator(ip, port) as TcpServerCommunicator;
if (!communicator) {
this.log(`No communicator found for ${clientId}`, 'error');
return;
}
// Handle incoming chunked message via communicator
await communicator.handleIncomingChunk(data);
// Fetch and process result if available
// Check if message is complete before fetching result
const handlerResult = communicator.getHandlerResult();
if (handlerResult) {
try {
+1 -1
View File
@@ -50,7 +50,7 @@ export class UdpServer {
const communicator = new UdpSocketCommunicator(this.udpServer, ip, port, this.operationHandler);
// Process the incoming message using the communicator
communicator.handleIncomingMessage(msg.toString());
await communicator.handleIncomingMessage(msg.toString());
const communicatorResult = communicator.getHandlerResult();
if (communicatorResult) {