Spaces:
Running
Running
| 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}`)); |