import { L33tADSPLCImpl, L33tAgent } from "l33t-lib"; import { L33tADSPLC }from "l33t-lib"; import { L33tTask } from "l33t-lib"; import { Readable } from 'stream'; import { createWriteStream } from 'fs'; import { pipeline } from 'stream/promises'; import { promises as fs } from 'fs'; import { CONFIG_FILE } from "~/server/server"; import { L33tMemDB } from "l33t-lib"; export class MemoryDB implements L33tMemDB { agents: Record = {}; tasks: { [id: string]: L33tTask } = {}; plcs: Record = {}; initialData!: L33tMemDB; async load(filename: string = CONFIG_FILE) { try { // Read the file content and keepit const data = await fs.readFile(filename, 'utf8'); // Parse the JSON data this.initialData = JSON.parse(data); // Validate the data structure if (typeof this.initialData !== 'object') { throw new Error('Invalid data format: root must be an object'); } // Load plcs if (this.initialData.plcs && typeof this.initialData.plcs === 'object') { this.plcs = Object.entries(this.initialData.plcs).reduce((acc, [key, value]) => { acc[key] = Object.assign(new L33tADSPLCImpl(), value); return acc; }, {} as Record); } // Load agents if (this.initialData.agents && typeof this.initialData.agents === 'object') { this.agents = Object.entries(this.initialData.agents).reduce((acc, [key, value]) => { acc[key] = Object.assign(new L33tAgent(), value); return acc; }, {} as Record); } // Load tasks if (this.initialData.tasks && typeof this.initialData.tasks === 'object') { this.tasks = Object.entries(this.initialData.tasks).reduce((acc, [key, value]) => { acc[key] = Object.assign(new L33tTask(), value); return acc; }, {} as { [id: string]: L33tTask }); } console.log(`Data loaded successfully from ${filename}`); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { console.error(`File not found: ${filename}`); } else { console.error('Error loading data:', error); } throw error; // Re-throw the error for the caller to handle } } _persist = async(filename:string = CONFIG_FILE) => { const tempFile = `${filename}.temp`; try { // Create a readable stream from the JSON data const jsonStream = Readable.from(JSON.stringify({ plcs: this.plcs, agents: this.agents, tasks: this.initialData.tasks })); // this.initialData.plcs = this.plcs // Create a write stream to the temporary file const writeStream = createWriteStream(tempFile); // Use pipeline for proper error handling and cleanup await pipeline(jsonStream, writeStream); // Rename the temp file to the final filename (atomic operation) await fs.rename(tempFile, filename); console.log(`Snapshot saved successfully to ${filename}`); } catch (error) { console.error('Error saving snapshot:', error); // Clean up the temp file if it exists try { await fs.unlink(tempFile); } catch (unlinkError) { // Ignore error if temp file doesn't exist } throw error; // Re-throw the original error } } }