import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; import db from "./db.js"; import { JWT_SECRET, JWT_EXPIRES_IN } from "./config.js"; function serializeUser(row) { if (!row) return null; return { id: row.id, username: row.username, name: row.name, email: row.email, isAdmin: Boolean(row.is_admin), createdAt: row.created_at ? new Date(row.created_at).toISOString() : null }; } function signToken(userId) { return jwt.sign({ sub: userId }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN }); } export async function registerUser({ username, name, email, password }) { const passwordHash = await bcrypt.hash(password, 10); const [[{ count }]] = await db.query("SELECT COUNT(*) AS count FROM users"); const isAdmin = count === 0 ? 1 : 0; try { const [result] = await db.query( `INSERT INTO users (username, name, email, password_hash, is_admin) VALUES (?, ?, ?, ?, ?)`, [username, name, email, passwordHash, isAdmin] ); const user = await getUserById(result.insertId); const token = signToken(user.id); return { user, token }; } catch (error) { if (error?.code === "ER_DUP_ENTRY") { const message = error.sqlMessage || "Duplicate"; if (message.includes("username")) { error.meta = "USERNAME"; } else if (message.includes("email")) { error.meta = "EMAIL"; } } throw error; } } export async function authenticateUser(identifier, password) { const [rows] = await db.query( `SELECT * FROM users WHERE username = ? OR email = ? LIMIT 1`, [identifier, identifier] ); if (rows.length === 0) { const err = new Error("Onjuiste inloggegevens."); err.meta = "INVALID_CREDENTIALS"; throw err; } const row = rows[0]; const ok = await bcrypt.compare(password, row.password_hash); if (!ok) { const err = new Error("Onjuiste inloggegevens."); err.meta = "INVALID_CREDENTIALS"; throw err; } const user = serializeUser(row); const token = signToken(user.id); return { user, token }; } export async function getUserById(id) { const [rows] = await db.query( `SELECT id, username, name, email, is_admin, created_at FROM users WHERE id = ? LIMIT 1`, [id] ); return serializeUser(rows[0]); } export async function listUsers() { const [rows] = await db.query( `SELECT id, username, name, email, is_admin, created_at FROM users ORDER BY created_at ASC` ); return rows.map(serializeUser); } export async function adminCreateUser({ username, name, email, password, isAdmin = false }) { const passwordHash = await bcrypt.hash(password, 10); try { const [result] = await db.query( `INSERT INTO users (username, name, email, password_hash, is_admin) VALUES (?, ?, ?, ?, ?)`, [username, name, email, passwordHash, isAdmin ? 1 : 0] ); return await getUserById(result.insertId); } catch (error) { if (error?.code === "ER_DUP_ENTRY") { const message = error.sqlMessage || "Duplicate"; if (message.includes("username")) { error.meta = "USERNAME"; } else if (message.includes("email")) { error.meta = "EMAIL"; } } throw error; } }