const express = require('express'); const fs = require('fs'); const path = require('path'); const {parser} = require('stream-json'); const {streamObject} = require('stream-json/streamers/StreamObject'); const app = express(); const PORT = 8080; const SNAPSHOT_DIR = path.join(__dirname, 'snapshots'); // Ensure snapshot folder exists if(!fs.existsSync(SNAPSHOT_DIR)) { fs.mkdirSync(SNAPSHOT_DIR); } // app.use(bodyParser.json({limit: '100mb'})); // Accept large JSON payloads app.use(express.static(path.join(__dirname, 'public'))); app.use(express.text({type: 'text/plain', limit: '100mb'})); // List all snapshots app.get('/api/snapshots', async(req, res) => { const jsonFiles = fs.readdirSync(SNAPSHOT_DIR) .filter(f => f.endsWith('.json')); const meta = await Promise.all(jsonFiles.map(async f => ({ name: f, comment: await getComment(f), timestamp: fs.statSync(path.join(SNAPSHOT_DIR, f)).mtimeMs }))); const sorted = meta .sort((a, b) => b.timestamp - a.timestamp); res.json(sorted); }); // Save a new snapshot app.post('/api/snapshots', (req, res) => { const data = req.body; const filename = `snapshot-${getFormattedDate()}.json`; const filepath = path.join(SNAPSHOT_DIR, filename); fs.writeFile(filepath, data, (err) => { if(err) { res.status = 500; res.json({message: 'something went wrong'}); } else { res.json({success: true, filename}); } }); }); // Load a snapshot by filename app.get('/api/snapshots/:filename', (req, res) => { const {filename} = req.params; const filepath = path.join(SNAPSHOT_DIR, filename); if(!fs.existsSync(filepath)) { return res.status(404).json({error: 'Snapshot not found'}); } const data = fs.readFileSync(filepath, 'utf-8'); res.json(JSON.parse(data)); }); // Delete a snapshot by filename app.delete('/api/snapshots/:filename', (req, res) => { const {filename} = req.params; const filepath = path.join(SNAPSHOT_DIR, filename); if(!fs.existsSync(filepath)) { return res.status(404).json({error: 'Snapshot not found'}); } fs.unlinkSync(filepath); res.json({success: true}); }); // Start server with optional port argument const portArg = process.argv.find(arg => arg.startsWith('--port=')); const portToUse = portArg ? parseInt(portArg.split('=')[1], 10) : PORT; app.listen(portToUse, () => { console.log(`🟢 Server running at http://localhost:${portToUse}`); }); function getFormattedDate() { const d = new Date(); const pad = n => String(n).padStart(2, '0'); const date = [d.getFullYear(), pad(d.getMonth() + 1), pad(d.getDate())].join('-'); const time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join('-'); return `${date}_${time}`; } const getComment = (f) => new Promise((_resolve) => { const timeout = setTimeout(() => {resolve('')}, 500); // Don't let it stall const resolve = (value) => { _resolve(value); clearTimeout(timeout); pipeline.destroy(); // Stop once we get the value }; const pipeline = fs.createReadStream(path.join(SNAPSHOT_DIR, f)) .pipe(parser()) .pipe(streamObject()) // Assuming comment is positioned first in the json pipeline.on('data', ({key, value}) => { if(key === 'comment') resolve(value); else resolve(''); }); pipeline.on('close', () => { resolve(''); }); pipeline.on('error', () => { resolve('') }); });