import { randomUUID } from 'node:crypto';
import bcrypt from 'bcryptjs';
import type Database from 'better-sqlite3';
import { logger } from '../utils/logger.js';
export interface DashboardUser {
id: string;
username: string;
password_hash: string;
created_at: number;
last_login: number | null;
}
export class UserManager {
constructor(private db: Database.Database) {}
async seedDefaultUser(username: string, password: string): Promise<void> {
if (!password) {
logger.warn('DASHBOARD_PASSWORD not set — dashboard login disabled');
return;
}
const existing = this.db
.prepare('SELECT id FROM dashboard_users WHERE username = ?')
.get(username) as { id: string } | undefined;
if (existing) {
logger.info({ username }, 'Dashboard user already exists');
return;
}
const hash = await bcrypt.hash(password, 12);
this.db
.prepare('INSERT INTO dashboard_users (id, username, password_hash) VALUES (?, ?, ?)')
.run(randomUUID(), username, hash);
logger.info({ username }, 'Default dashboard user created');
}
async verifyCredentials(username: string, password: string): Promise<DashboardUser | null> {
const user = this.db
.prepare('SELECT * FROM dashboard_users WHERE username = ?')
.get(username) as DashboardUser | undefined;
if (!user) return null;
const valid = await bcrypt.compare(password, user.password_hash);
if (!valid) return null;
this.db
.prepare('UPDATE dashboard_users SET last_login = unixepoch() WHERE id = ?')
.run(user.id);
return user;
}
}