File size: 4,985 Bytes
b5757d0
 
 
114a9f6
b5757d0
 
 
 
 
 
 
 
 
e3a4602
114a9f6
 
e3a4602
114a9f6
b5757d0
001abd1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61b5f02
001abd1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0c7a171
 
 
 
 
 
 
 
 
 
47dcec6
114a9f6
 
 
 
 
 
e3a4602
114a9f6
 
 
b5757d0
 
 
 
47dcec6
b5757d0
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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}`));