async plugins fixed
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user