Files
pulse-signage/src/web/routes/admin-account.js
T
lzstealth 2ea8d389fa This PR breaks the large web and player bootstrap files into smaller modules with clearer ownership.
Web changes:

Split shared helpers, bootstrap logic, route groups, and upload-sync behavior out of web.js.
Kept web.js focused on wiring and server startup.
Fixed screen playlist reassignment so changing a screen’s playlist now triggers a refresh.
Fixed single-slide playlist refresh behavior so updates do not get stuck behind the current slide.
Player changes:

Split websocket/runtime handling into runtime.js.
Split playlist assembly and revision hashing into playlist.js.
Split onboarding and player HTTP routes into dedicated modules.
Split render utilities and template loading into render-helpers.js.
Kept player.js mostly as startup/orchestration.
Validation:

Rebuilt both services with Docker Compose.
Smoke-checked web and player routes after the refactor.
Verified get_errors was clean on the touched modules.
2026-07-20 23:58:27 +01:00

69 lines
2.8 KiB
JavaScript

module.exports = function registerAdminAccountRoutes(app, deps) {
const pool = deps.pool;
const pages = deps.pages;
const formatDashboardDate = deps.formatDashboardDate;
const getAuditUserId = deps.getAuditUserId;
const verifyPassword = deps.verifyPassword;
const hashPassword = deps.hashPassword;
const createUserSession = deps.createUserSession;
const setSessionCookie = deps.setSessionCookie;
app.get('/admin/account', function (req, res) {
res.send(pages.renderAccountPage(req.currentUser, req.query.message ? String(req.query.message) : '', req.query.return_url ? String(req.query.return_url) : ''));
});
app.post('/admin/account/name', async function (req, res, next) {
try {
const name = String(req.body.name || '').trim();
if (!name) {
return res.status(400).send('Name is required.');
}
const actorId = getAuditUserId(req);
const [result] = await pool.query('UPDATE users SET name = ?, modified_by = ? WHERE id = ?', [name, actorId, req.currentUser.id]);
if (!result.affectedRows) {
return res.status(404).send('User not found.');
}
res.redirect('/admin/account?message=' + encodeURIComponent('Name updated.'));
} catch (error) {
next(error);
}
});
app.post('/admin/account/password', async function (req, res, next) {
try {
const currentPassword = String(req.body.current_password || '');
const newPassword = String(req.body.new_password || '');
const confirmPassword = String(req.body.confirm_password || '');
const [rows] = await pool.query('SELECT id, name, username, password_hash, password_salt, password_iterations FROM users WHERE id = ? LIMIT 1', [req.currentUser.id]);
const user = rows[0] || null;
if (!user) {
return res.status(404).send('User not found.');
}
if (!verifyPassword(currentPassword, user)) {
return res.status(400).send('Current password is incorrect.');
}
if (!newPassword || newPassword.length < 8) {
return res.status(400).send('New password must be at least 8 characters.');
}
if (newPassword !== confirmPassword) {
return res.status(400).send('New passwords do not match.');
}
const passwordRecord = hashPassword(newPassword);
await pool.query(
'UPDATE users SET password_hash = ?, password_salt = ?, password_iterations = ?, modified_by = ? WHERE id = ?',
[passwordRecord.hash, passwordRecord.salt, passwordRecord.iterations, getAuditUserId(req), user.id]
);
await pool.query('DELETE FROM auth_sessions WHERE user_id = ?', [user.id]);
const token = await createUserSession(pool, user.id);
setSessionCookie(res, token);
res.redirect('/admin/account?message=' + encodeURIComponent('Password updated.'));
} catch (error) {
next(error);
}
});
};