Add curator roles/permissions with Users admin, and fix lineage branch joins.

Staff accounts use admin/curator roles and fine-grained flags; transitions connect source-to-target with color gradients and stream cutout masks so overlaps stay seamless.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-07-27 18:04:17 +03:00
co-authored by Cursor
parent bfa21989c9
commit 0466b77328
31 changed files with 1492 additions and 269 deletions
+24 -19
View File
@@ -1,6 +1,7 @@
const express = require('express');
const bcrypt = require('bcryptjs');
const pool = require('../db');
const { loadStaffUser, staffAuthPayload } = require('../middleware/auth');
const router = express.Router();
@@ -11,19 +12,13 @@ router.get('/me', async (req, res) => {
}
try {
const { rows } = await pool.query(
`SELECT id, username FROM users WHERE id = $1`,
[userId]
);
if (rows.length === 0) {
const user = await loadStaffUser(userId);
if (!user || !user.is_active) {
req.session.destroy(() => {});
return res.json({ role: 'user' });
}
res.json({
role: 'curator',
username: rows[0].username,
});
res.json(staffAuthPayload(user));
} catch (err) {
console.error('Auth me error:', err.message);
res.status(500).json({ error: 'Failed to read session' });
@@ -38,23 +33,36 @@ router.post('/login', async (req, res) => {
try {
const { rows } = await pool.query(
`SELECT id, username, password_hash FROM users WHERE LOWER(username) = LOWER($1)`,
`SELECT id, username, password_hash, role, permissions, is_active
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);
const row = rows[0];
if (row.is_active === false) {
return res.status(401).json({ error: 'Account is disabled' });
}
const valid = await bcrypt.compare(password, row.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]);
await pool.query(`UPDATE users SET last_login_at = NOW() WHERE id = $1`, [row.id]);
req.session.userId = user.id;
req.session.username = user.username;
req.session.userId = row.id;
req.session.username = row.username;
const user = {
id: row.id,
username: row.username,
role: row.role,
permissions: row.permissions || [],
is_active: true,
};
// Ensure the store writes before the response finishes (proxy / HTTPS).
req.session.save((err) => {
@@ -62,10 +70,7 @@ router.post('/login', async (req, res) => {
console.error('Auth session save error:', err.message);
return res.status(500).json({ error: 'Login failed' });
}
res.json({
role: 'curator',
username: user.username,
});
res.json(staffAuthPayload(user));
});
} catch (err) {
console.error('Auth login error:', err.message);