Paintings get editable curator notes with brass plates in the 3D hall, and visit order now uses the far/end wall between left and right. Co-authored-by: Cursor <cursoragent@cursor.com>
88 lines
2.4 KiB
JavaScript
88 lines
2.4 KiB
JavaScript
const express = require('express');
|
|
const bcrypt = require('bcryptjs');
|
|
const pool = require('../db');
|
|
|
|
const router = express.Router();
|
|
|
|
router.get('/me', async (req, res) => {
|
|
const userId = req.session?.userId;
|
|
if (!userId) {
|
|
return res.json({ role: 'user' });
|
|
}
|
|
|
|
try {
|
|
const { rows } = await pool.query(
|
|
`SELECT id, username FROM users WHERE id = $1`,
|
|
[userId]
|
|
);
|
|
if (rows.length === 0) {
|
|
req.session.destroy(() => {});
|
|
return res.json({ role: 'user' });
|
|
}
|
|
|
|
res.json({
|
|
role: 'curator',
|
|
username: rows[0].username,
|
|
});
|
|
} catch (err) {
|
|
console.error('Auth me error:', err.message);
|
|
res.status(500).json({ error: 'Failed to read session' });
|
|
}
|
|
});
|
|
|
|
router.post('/login', async (req, res) => {
|
|
const { username, password } = req.body ?? {};
|
|
if (!username || typeof username !== 'string' || !password || typeof password !== 'string') {
|
|
return res.status(400).json({ error: 'Username and password required' });
|
|
}
|
|
|
|
try {
|
|
const { rows } = await pool.query(
|
|
`SELECT id, username, password_hash FROM users WHERE LOWER(username) = LOWER($1)`,
|
|
[username.trim()]
|
|
);
|
|
if (rows.length === 0) {
|
|
return res.status(401).json({ error: 'Invalid username or password' });
|
|
}
|
|
|
|
const user = rows[0];
|
|
const valid = await bcrypt.compare(password, user.password_hash);
|
|
if (!valid) {
|
|
return res.status(401).json({ error: 'Invalid username or password' });
|
|
}
|
|
|
|
await pool.query(`UPDATE users SET last_login_at = NOW() WHERE id = $1`, [user.id]);
|
|
|
|
req.session.userId = user.id;
|
|
req.session.username = user.username;
|
|
|
|
// Ensure the store writes before the response finishes (proxy / HTTPS).
|
|
req.session.save((err) => {
|
|
if (err) {
|
|
console.error('Auth session save error:', err.message);
|
|
return res.status(500).json({ error: 'Login failed' });
|
|
}
|
|
res.json({
|
|
role: 'curator',
|
|
username: user.username,
|
|
});
|
|
});
|
|
} catch (err) {
|
|
console.error('Auth login error:', err.message);
|
|
res.status(500).json({ error: 'Login failed' });
|
|
}
|
|
});
|
|
|
|
router.post('/logout', (req, res) => {
|
|
req.session.destroy((err) => {
|
|
if (err) {
|
|
console.error('Auth logout error:', err.message);
|
|
return res.status(500).json({ error: 'Logout failed' });
|
|
}
|
|
res.clearCookie('gallery.sid');
|
|
res.json({ ok: true });
|
|
});
|
|
});
|
|
|
|
module.exports = router;
|