index.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. const express = require('express');
  2. const fs = require('fs');
  3. const path = require('path');
  4. const {parser} = require('stream-json');
  5. const {streamObject} = require('stream-json/streamers/StreamObject');
  6. const app = express();
  7. const PORT = 8080;
  8. const SNAPSHOT_DIR = path.join(__dirname, 'snapshots');
  9. // Ensure snapshot folder exists
  10. if(!fs.existsSync(SNAPSHOT_DIR)) {
  11. fs.mkdirSync(SNAPSHOT_DIR);
  12. }
  13. // app.use(bodyParser.json({limit: '100mb'})); // Accept large JSON payloads
  14. app.use(express.static(path.join(__dirname, 'public')));
  15. app.use(express.text({type: 'text/plain', limit: '100mb'}));
  16. // List all snapshots
  17. app.get('/api/snapshots', async(req, res) => {
  18. const jsonFiles = fs.readdirSync(SNAPSHOT_DIR)
  19. .filter(f => f.endsWith('.json'));
  20. const meta = await Promise.all(jsonFiles.map(async f => ({
  21. name: f,
  22. comment: await getComment(f),
  23. timestamp: fs.statSync(path.join(SNAPSHOT_DIR, f)).mtimeMs
  24. })));
  25. const sorted = meta
  26. .sort((a, b) => b.timestamp - a.timestamp);
  27. res.json(sorted);
  28. });
  29. // Save a new snapshot
  30. app.post('/api/snapshots', (req, res) => {
  31. const data = req.body;
  32. const filename = `snapshot-${getFormattedDate()}.json`;
  33. const filepath = path.join(SNAPSHOT_DIR, filename);
  34. fs.writeFile(filepath, data, (err) => {
  35. if(err) {
  36. res.status = 500;
  37. res.json({message: 'something went wrong'});
  38. } else {
  39. res.json({success: true, filename});
  40. }
  41. });
  42. });
  43. // Load a snapshot by filename
  44. app.get('/api/snapshots/:filename', (req, res) => {
  45. const {filename} = req.params;
  46. const filepath = path.join(SNAPSHOT_DIR, filename);
  47. if(!fs.existsSync(filepath)) {
  48. return res.status(404).json({error: 'Snapshot not found'});
  49. }
  50. const data = fs.readFileSync(filepath, 'utf-8');
  51. res.json(JSON.parse(data));
  52. });
  53. // Delete a snapshot by filename
  54. app.delete('/api/snapshots/:filename', (req, res) => {
  55. const {filename} = req.params;
  56. const filepath = path.join(SNAPSHOT_DIR, filename);
  57. if(!fs.existsSync(filepath)) {
  58. return res.status(404).json({error: 'Snapshot not found'});
  59. }
  60. fs.unlinkSync(filepath);
  61. res.json({success: true});
  62. });
  63. // Start server with optional port argument
  64. const portArg = process.argv.find(arg => arg.startsWith('--port='));
  65. const portToUse = portArg ? parseInt(portArg.split('=')[1], 10) : PORT;
  66. app.listen(portToUse, () => {
  67. console.log(`🟢 Server running at http://localhost:${portToUse}`);
  68. });
  69. function getFormattedDate() {
  70. const d = new Date();
  71. const pad = n => String(n).padStart(2, '0');
  72. const date = [d.getFullYear(), pad(d.getMonth() + 1), pad(d.getDate())].join('-');
  73. const time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join('-');
  74. return `${date}_${time}`;
  75. }
  76. const getComment = (f) => new Promise((_resolve) => {
  77. const timeout = setTimeout(() => {resolve('')}, 500); // Don't let it stall
  78. const resolve = (value) => {
  79. _resolve(value);
  80. clearTimeout(timeout);
  81. pipeline.destroy(); // Stop once we get the value
  82. };
  83. const pipeline = fs.createReadStream(path.join(SNAPSHOT_DIR, f))
  84. .pipe(parser())
  85. .pipe(streamObject())
  86. // Assuming comment is positioned first in the json
  87. pipeline.on('data', ({key, value}) => {
  88. if(key === 'comment') resolve(value);
  89. else resolve('');
  90. });
  91. pipeline.on('close', () => {
  92. resolve('');
  93. });
  94. pipeline.on('error', () => {
  95. resolve('')
  96. });
  97. });