import { getDb } from '../db/client.js' const sql = getDb() export const getCcUserById = async (id) => { const [user] = await sql` SELECT u.id, u.email, u.display_name, u.created_at, u.password_hash, u.failed_login_count, u.lockout_until, r.id as role_id, r.name as role_name, r.permissions FROM control_center_users u JOIN roles r ON r.id = u.role_id WHERE u.id = ${id} ` if (!user) return null return { id: user.id, email: user.email, display_name: user.display_name, created_at: user.created_at, password_hash: user.password_hash, failed_login_count: user.failed_login_count, lockout_until: user.lockout_until, role: { id: user.role_id, name: user.role_name, permissions: user.permissions }, } } export const getCcUserByEmail = async (email) => { const [user] = await sql` SELECT u.id, u.email, u.display_name, u.created_at, u.password_hash, u.failed_login_count, u.lockout_until, r.id as role_id, r.name as role_name, r.permissions FROM control_center_users u JOIN roles r ON r.id = u.role_id WHERE u.email = ${email} ` if (!user) return null return { id: user.id, email: user.email, display_name: user.display_name, created_at: user.created_at, password_hash: user.password_hash, failed_login_count: user.failed_login_count, lockout_until: user.lockout_until, role: { id: user.role_id, name: user.role_name, permissions: user.permissions }, } } export const createCcUserWithPassword = async ({ email, display_name, role_id, password_hash }) => { const [user] = await sql` INSERT INTO control_center_users (email, display_name, role_id, password_hash) VALUES (${email}, ${display_name}, ${role_id}, ${password_hash}) RETURNING id, email, display_name, role_id, created_at ` const [role] = await sql`SELECT id, name FROM roles WHERE id = ${role_id}` return { ...user, role } } export const updateCcUserPasswordHash = async (id, password_hash) => { await sql` UPDATE control_center_users SET password_hash = ${password_hash} WHERE id = ${id} ` } export const incrementCcUserFailedLogin = async (id, lockoutMinutes, maxAttempts) => { // Atomic: increment counter; if reaches maxAttempts set lockout_until const [row] = await sql` UPDATE control_center_users SET failed_login_count = failed_login_count + 1, lockout_until = CASE WHEN failed_login_count + 1 >= ${maxAttempts} THEN NOW() + (${lockoutMinutes} || ' minutes')::interval ELSE lockout_until END WHERE id = ${id} RETURNING failed_login_count, lockout_until ` return row } export const resetCcUserFailedLogin = async (id) => { await sql` UPDATE control_center_users SET failed_login_count = 0, lockout_until = NULL WHERE id = ${id} ` } export const listCcUsers = async ({ page = 1, limit = 20 }) => { const offset = (page - 1) * limit const items = await sql` SELECT u.id, u.email, u.display_name, u.created_at, r.id as role_id, r.name as role_name FROM control_center_users u JOIN roles r ON r.id = u.role_id ORDER BY u.created_at DESC LIMIT ${limit} OFFSET ${offset} ` const [{ count }] = await sql`SELECT COUNT(*) FROM control_center_users` return { items: items.map((u) => ({ id: u.id, email: u.email, display_name: u.display_name, created_at: u.created_at, role: { id: u.role_id, name: u.role_name }, })), total: Number(count), page, limit, } }