Domain Admin

This commit is contained in:
2026-04-27 16:57:08 -05:00
parent 31b3fd8c9f
commit 85b608e3d4
10 changed files with 903 additions and 61 deletions

View File

@@ -4,6 +4,7 @@ import { z } from 'zod';
import { pool } from '../db.js';
import { config } from '../config.js';
import { requireAuth, signUser } from '../middleware/auth.js';
import { audit } from '../services/audit.js';
export const authRouter = Router();
@@ -38,3 +39,30 @@ authRouter.post('/logout', (_req, res) => {
authRouter.get('/me', requireAuth, (req, res) => {
res.json(req.user);
});
// Self-service password change. Requires the current password to prevent
// session hijacking from changing the password silently.
const changePwSchema = z.object({
current_password: z.string().min(1),
new_password: z.string().min(8),
});
authRouter.post('/change-password', requireAuth, async (req, res) => {
const body = changePwSchema.parse(req.body);
const result = await pool.query(
`SELECT id, password_hash FROM admin_users WHERE email=$1 AND active=true`,
[req.user!.email.toLowerCase()],
);
const row = result.rows[0];
if (!row || !(await bcrypt.compare(body.current_password, row.password_hash))) {
res.status(401).json({ error: 'Current password is incorrect' });
return;
}
const newHash = await bcrypt.hash(body.new_password, 12);
await pool.query(
`UPDATE admin_users SET password_hash=$1, updated_at=now() WHERE id=$2`,
[newHash, row.id],
);
await audit(req.user!.email, 'admin.self_password_change', 'admin', req.user!.email, {}, req.ip);
res.json({ ok: true });
});