const express = require('express'); const jwt = require('jsonwebtoken'); const cookieParser = require('cookie-parser'); const { Pool } = require('pg'); const path = require('path'); const app = express(); app.use(express.json()); app.use(cookieParser()); const PORT = 7860; const SECRET_KEY = process.env.JWT_SECRET || 'fallback_secret'; // Connect to PostgreSQL (Vela) const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: { rejectUnauthorized: false } }); const authenticateToken = (req, res, next) => { const token = req.cookies.token; if (!token) return res.status(401).json({ error: "Unauthorized" }); jwt.verify(token, SECRET_KEY, (err, user) => { if (err) return res.status(403).json({ error: "Forbidden" }); req.user = user; next(); }); }; // --- AUTH ROUTES --- app.post('/api/login', async (req, res) => { const { username, password } = req.body; try { const result = await pool.query('SELECT * FROM portal_users WHERE username = $1 AND password = $2', [username, password]); if (result.rows.length === 0) return res.status(401).json({ error: "Invalid credentials" }); const user = result.rows[0]; // Token expires in 8 hours const token = jwt.sign({ id: user.id, username: user.username, role: user.role }, SECRET_KEY, { expiresIn: '8h' }); // Cookie explicitly expires in 8 hours res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'Strict', maxAge: 8 * 60 * 60 * 1000 }); res.json({ role: user.role, id: user.id }); } catch (err) { res.status(500).json({ error: "Database error" }); } }); app.post('/api/logout', (req, res) => { res.clearCookie('token'); res.json({ success: true }); }); // --- ADMIN ROUTES --- app.get('/api/admin/users', authenticateToken, async (req, res) => { if (req.user.role !== 'admin') return res.status(403).json({ error: "Admin only" }); try { const result = await pool.query("SELECT id, username, password, pages FROM portal_users WHERE role = 'user' ORDER BY id ASC"); res.json(result.rows); } catch (err) { res.status(500).json({ error: "Database error" }); } }); app.post('/api/admin/users', authenticateToken, async (req, res) => { if (req.user.role !== 'admin') return res.status(403).json({ error: "Admin only" }); try { const pagesJson = JSON.stringify(req.body.pages || []); const result = await pool.query( 'INSERT INTO portal_users (username, password, role, pages) VALUES ($1, $2, $3, $4) RETURNING id, username, password, pages', [req.body.username, req.body.password, 'user', pagesJson] ); res.json(result.rows[0]); } catch (err) { res.status(500).json({ error: "Database error. Username might already exist." }); } }); app.delete('/api/admin/users/:id', authenticateToken, async (req, res) => { if (req.user.role !== 'admin') return res.status(403).json({ error: "Admin only" }); try { await pool.query('DELETE FROM portal_users WHERE id = $1', [req.params.id]); res.json({ success: true }); } catch (err) { res.status(500).json({ error: "Database error" }); } }); app.put('/api/admin/users/:id/pages', authenticateToken, async (req, res) => { if (req.user.role !== 'admin') return res.status(403).json({ error: "Admin only" }); try { const pagesJson = JSON.stringify(req.body.pages || []); await pool.query('UPDATE portal_users SET pages = $1 WHERE id = $2', [pagesJson, req.params.id]); res.json({ success: true }); } catch (err) { res.status(500).json({ error: "Database error" }); } }); app.put('/api/admin/users/:id/password', authenticateToken, async (req, res) => { if (req.user.role !== 'admin') return res.status(403).json({ error: "Admin only" }); try { await pool.query('UPDATE portal_users SET password = $1 WHERE id = $2', [req.body.password, req.params.id]); res.json({ success: true }); } catch (err) { res.status(500).json({ error: "Database error" }); } }); // --- USER ROUTES --- app.get('/api/user/pages', authenticateToken, async (req, res) => { try { const result = await pool.query('SELECT pages FROM portal_users WHERE id = $1', [req.user.id]); if (result.rows.length === 0) return res.status(404).json({ error: "User not found" }); const userPages = result.rows[0].pages || []; res.json(userPages); } catch (err) { res.status(500).json({ error: "Database error" }); } }); // Serve React App app.use(express.static(path.join(__dirname, 'public'))); app.get('*', (req, res) => res.sendFile(path.join(__dirname, 'public', 'index.html'))); app.listen(PORT, () => console.log(`Server running on port ${PORT}`));