import { usersPool } from "./pools.js"; // ── Schema ──────────────────────────────────────────────────────────────────── export async function initUsersSchema() { await usersPool.query(` CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, team TEXT NOT NULL CHECK (team IN ('blue', 'red')), role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('user', 'admin')), email_verified BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); `); // Add email_verified to existing deployments that lack it await usersPool.query(` ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verified BOOLEAN NOT NULL DEFAULT false; `); await usersPool.query(` CREATE TABLE IF NOT EXISTS email_tokens ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, token TEXT NOT NULL UNIQUE, type TEXT NOT NULL CHECK (type IN ('confirm', 'reset')), expires_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); `); } // ── Queries ─────────────────────────────────────────────────────────────────── export async function createUser(username, email, passwordHash, team) { const { rows } = await usersPool.query( `INSERT INTO users (username, email, password_hash, team) VALUES ($1, $2, $3, $4) RETURNING id, username, email, team, role, email_verified`, [username, email, passwordHash, team] ); return rows[0]; } export async function getUserByUsername(username) { const { rows } = await usersPool.query( `SELECT id, username, email, team, role, password_hash, email_verified FROM users WHERE username = $1`, [username] ); return rows[0] ?? null; } export async function getUserByEmail(email) { const { rows } = await usersPool.query( `SELECT id, username, email, team, role, email_verified FROM users WHERE email = $1`, [email] ); return rows[0] ?? null; } export async function getUserById(id) { const { rows } = await usersPool.query( `SELECT id, username, email, team, role, email_verified FROM users WHERE id = $1`, [id] ); return rows[0] ?? null; } export async function createEmailToken(userId, token, type, expiresAt) { // Delete any existing token of the same type for this user await usersPool.query( `DELETE FROM email_tokens WHERE user_id = $1 AND type = $2`, [userId, type] ); await usersPool.query( `INSERT INTO email_tokens (user_id, token, type, expires_at) VALUES ($1, $2, $3, $4)`, [userId, token, type, expiresAt] ); } export async function getEmailToken(token, type) { const { rows } = await usersPool.query( `SELECT et.*, u.email FROM email_tokens et JOIN users u ON u.id = et.user_id WHERE et.token = $1 AND et.type = $2`, [token, type] ); return rows[0] ?? null; } export async function deleteEmailToken(token) { await usersPool.query(`DELETE FROM email_tokens WHERE token = $1`, [token]); } export async function markEmailVerified(userId) { await usersPool.query( `UPDATE users SET email_verified = true WHERE id = $1`, [userId] ); } export async function updatePassword(userId, passwordHash) { await usersPool.query( `UPDATE users SET password_hash = $1 WHERE id = $2`, [passwordHash, userId] ); }