// Import necessary modules const express = require('express'); const path = require('path'); // --- Configuration --- const PORT = process.env.PORT || 3000; // Use environment variable or default to 3000 const PASTE_TTL_HOURS = 2; const PASTE_TTL_MS = PASTE_TTL_HOURS * 60 * 60 * 1000; // Time-to-live in milliseconds // --- In-Memory Storage --- // Using a Map for efficient key-based storage and deletion const pastes = new Map(); // Stores { id: content } // --- Express App Setup --- const app = express(); // Middleware to parse plain text request bodies (like Hastebin) // Limit set to 1MB, adjust as needed app.use(express.text({ limit: '1mb', type: 'text/plain' })); app.use(express.json()); // For JSON API responses // Serve static files from the 'public' directory app.use(express.static('public')); // Function to generate a unique ID without crypto function generateUniqueId() { const timestamp = Date.now().toString(36); const randomPart = Math.random().toString(36).substring(2, 10); return `${timestamp}-${randomPart}`; } // --- API Routes --- /** * POST /api/pastes * Creates a new paste. * Expects the raw text content in the request body. * Responds with the generated ID for the paste. */ app.post('/api/pastes', (req, res) => { const content = req.body; // Basic validation: Ensure content is present and is a string if (!content || typeof content !== 'string' || content.trim() === '') { return res.status(400).json({ error: 'Paste content cannot be empty.' }); } // Generate a unique ID without using crypto const id = generateUniqueId(); // Store the paste content in the map pastes.set(id, content); console.log(`[${new Date().toISOString()}] Paste created with ID: ${id}`); // Schedule the paste for deletion after the TTL setTimeout(() => { if (pastes.has(id)) { pastes.delete(id); console.log(`[${new Date().toISOString()}] Paste expired and deleted: ${id}`); } }, PASTE_TTL_MS); // Respond with the ID res.status(201).json({ id: id }); }); /** * GET /api/pastes/:id * Retrieves the content of a specific paste by its ID. * Responds with the raw text content or 404 if not found. */ app.get('/api/pastes/:id', (req, res) => { const id = req.params.id; if (pastes.has(id)) { const content = pastes.get(id); // Send raw text content res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.status(200).send(content); } else { res.status(404).json({ error: 'Paste not found or has expired.' }); } }); // Serve the main HTML page for all other routes app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); // --- Start Server --- app.listen(PORT, () => { console.log(`Paste server listening on port ${PORT}`); console.log(`Pastes will be stored for ${PASTE_TTL_HOURS} hours.`); console.log(`Open http://localhost:${PORT} in your browser to use the application`); }); // --- Graceful Shutdown (Optional but Recommended) --- // Handles CTRL+C in the terminal process.on('SIGINT', () => { console.log('\nShutting down server...'); // Perform any cleanup here if needed (though in-memory storage is lost anyway) process.exit(0); });