const express = require('express'); const bodyParser = require('body-parser'); const rateLimit = require('express-rate-limit'); const fs = require('fs'); const path = require('path'); const app = express(); const PORT = process.env.PORT || 3000; // User preferred default const DATA_FILE = path.join(__dirname, '../data/ressentis.json'); const APP_PASSWORD = process.env.APP_PASSWORD || 'parox'; // Default password app.use(bodyParser.json()); app.use(express.static(path.join(__dirname, 'public'))); // Middleware to check authentication const checkAuth = (req, res, next) => { // For simplicity, we'll check a custom header 'x-auth-token' or query param const token = req.headers['x-auth-token']; if (token === APP_PASSWORD) { next(); } else { res.status(401).json({ error: 'Unauthorized' }); } }; // GCS Setup const { Storage } = require('@google-cloud/storage'); const GCS_BUCKET_NAME = process.env.GCS_BUCKET_NAME; // HF Spaces / General Secret support: Write key from ENV to file if available if (process.env.GCS_KEY_JSON && !fs.existsSync('/app/gcs-key.json')) { try { console.log("> Writing GCS Key from ENV (GCS_KEY_JSON) to file..."); fs.writeFileSync('/app/gcs-key.json', process.env.GCS_KEY_JSON); process.env.GOOGLE_APPLICATION_CREDENTIALS = '/app/gcs-key.json'; } catch (e) { console.error("! Failed to write GCS Key from ENV:", e); } } let gcsBucket = null; if (GCS_BUCKET_NAME) { try { const storage = new Storage(); gcsBucket = storage.bucket(GCS_BUCKET_NAME); console.log(`> GCS Enabled: Using bucket '${GCS_BUCKET_NAME}'`); } catch (e) { console.error("! GCS config found but failed to init:", e); } } else { console.log("> GCS Disabled: Using local filesystem"); // Ensure data file exists locally if (!fs.existsSync(DATA_FILE)) { try { fs.mkdirSync(path.dirname(DATA_FILE), { recursive: true }); fs.writeFileSync(DATA_FILE, JSON.stringify([], null, 2)); } catch (e) { console.error("FS Init Error:", e); } } } // Data Handling Functions async function readData() { if (gcsBucket) { try { const [contents] = await gcsBucket.file('ressentis.json').download(); return JSON.parse(contents.toString()); } catch (e) { if (e.code === 404) return []; // Auto-create on first write throw e; } } else { if (fs.existsSync(DATA_FILE)) { return JSON.parse(fs.readFileSync(DATA_FILE, 'utf8')); } return []; } } async function writeData(data) { const jsonStr = JSON.stringify(data, null, 2); if (gcsBucket) { await gcsBucket.file('ressentis.json').save(jsonStr, { contentType: 'application/json', resumable: false }); } else { fs.writeFileSync(DATA_FILE, jsonStr); } } // API Routes // Login check const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // Limit each IP to 5 requests per windowMs messsage: { error: 'Too many login attempts, please try again after 15 minutes' }, standardHeaders: true, legacyHeaders: false, }); app.post('/api/login', loginLimiter, (req, res) => { const { password } = req.body; if (password === APP_PASSWORD) { res.json({ success: true, token: password }); } else { res.status(401).json({ success: false, error: 'Invalid Password' }); } }); // Get Data app.get('/api/data', checkAuth, async (req, res) => { try { const data = await readData(); res.json(data); } catch (e) { console.error("Read Error:", e); res.status(500).json({ error: 'Failed to read data' }); } }); // Save Data app.post('/api/data', checkAuth, async (req, res) => { const newData = req.body; try { await writeData(newData); res.json({ success: true }); } catch (e) { console.error("Save Error:", e); res.status(500).json({ error: 'Failed to save data' }); } }); app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });