// Load environment variables from .env file for development if (process.env.NODE_ENV !== 'production') { require('dotenv').config(); } const express = require('express'); const bodyParser = require('body-parser'); const fs = require('fs'); const path = require('path'); const cors = require('cors'); const app = express(); const port = 3050; // Admin password (shared secret). Defaults to "balloons" for development if not provided. const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'balloons'; // --- Production Security Check --- if (process.env.NODE_ENV === 'production' && (!ADMIN_PASSWORD || ADMIN_PASSWORD === "balloons")) { console.warn(` **************************************************************** ** WARNING: ADMIN_PASSWORD is not set or is insecure in production. ** ** If the admin UI is already behind auth, this may be acceptable. ** ** Otherwise, set a strong ADMIN_PASSWORD environment variable. ** **************************************************************** `); } // --- Middleware Setup --- // More explicit CORS configuration to allow all origins app.use(cors({ origin: '*' })); app.use(bodyParser.json()); // --- API Routes --- const apiRouter = express.Router(); apiRouter.post('/update-status', (req, res) => { console.log(`[${new Date().toISOString()}] Received request for /api/update-status`); const { data } = req.body; if (!data) { return res.status(400).json({ success: false, message: 'Bad Request: No data provided.' }); } const jsonString = JSON.stringify(data, null, 4); const filePath = path.join(__dirname, 'update.json'); fs.writeFile(filePath, jsonString, (err) => { if (err) { console.error(`[${new Date().toISOString()}] Error writing to update.json:`, err); return res.status(500).json({ success: false, message: 'Internal Server Error: Could not write to file.' }); } console.log(`[${new Date().toISOString()}] update.json was successfully updated.`); res.json({ success: true, message: 'Status updated successfully.' }); }); }); // Mount the API router under the /api path app.use('/api', apiRouter); // --- Static Files --- const staticCacheOptions = { maxAge: process.env.NODE_ENV === 'production' ? '30d' : 0, setHeaders: (res, filePath) => { if (filePath.endsWith('.html')) { res.setHeader('Cache-Control', 'no-store'); } else if (/\.(js|css|svg|ico|png|jpg|jpeg|webp|avif|woff2?)$/i.test(filePath)) { res.setHeader('Cache-Control', 'public, max-age=2592000, immutable'); } } }; // Serve bundled assets under /build with long cache app.use('/build', express.static(path.join(__dirname, 'public/build'), staticCacheOptions)); // Serve static files from the root directory (handles all other GET requests) app.use(express.static(path.join(__dirname), staticCacheOptions)); app.listen(port, () => { console.log(`Server listening at http://localhost:${port}`); if (process.env.NODE_ENV !== 'production') { console.log(`Admin panel available at http://localhost:${port}/admin.html`); } });