| import crypto from 'crypto'; |
| import { getDb } from '../db/index.js'; |
| import { hashPassword, verifyPassword } from '../lib/password.js'; |
| |
| |
| |
| const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000; |
| function sha256(s) { |
| return crypto.createHash('sha256').update(s).digest('hex'); |
| } |
| function normalizeEmail(email) { |
| return email.trim().toLowerCase(); |
| } |
| export async function userCount() { |
| const row = await getDb().get('SELECT COUNT(*) AS c FROM users'); |
| return row?.c ?? 0; |
| } |
| |
| export async function createUser(email, password) { |
| const db = getDb(); |
| const normalized = normalizeEmail(email); |
| const existing = await db.get('SELECT id FROM users WHERE email = ?', [normalized]); |
| if (existing) { |
| const err = new Error('An account with that email already exists'); |
| err.code = 'email_taken'; |
| throw err; |
| } |
| const result = await db.run('INSERT INTO users (email, password_hash) VALUES (?, ?)', [normalized, hashPassword(password)]); |
| return { userId: Number(result.lastInsertRowid), email: normalized }; |
| } |
| |
| export async function verifyCredentials(email, password) { |
| const db = getDb(); |
| const row = await db.get('SELECT id, email, password_hash FROM users WHERE email = ?', [normalizeEmail(email)]); |
| if (!row) |
| return null; |
| if (!verifyPassword(password, row.password_hash)) |
| return null; |
| return { userId: row.id, email: row.email }; |
| } |
| |
| export async function createSession(userId) { |
| const token = crypto.randomBytes(32).toString('hex'); |
| await getDb().run('INSERT INTO sessions (token_hash, user_id, expires_at_ms) VALUES (?, ?, ?)', [sha256(token), userId, Date.now() + SESSION_TTL_MS]); |
| return token; |
| } |
| |
| export async function validateSession(token) { |
| if (!token) |
| return null; |
| const db = getDb(); |
| const row = await db.get(` |
| SELECT s.user_id, s.expires_at_ms, u.email |
| FROM sessions s JOIN users u ON u.id = s.user_id |
| WHERE s.token_hash = ? |
| `, [sha256(token)]); |
| if (!row) |
| return null; |
| if (row.expires_at_ms < Date.now()) { |
| await db.run('DELETE FROM sessions WHERE token_hash = ?', [sha256(token)]); |
| return null; |
| } |
| return { userId: row.user_id, email: row.email }; |
| } |
| export async function deleteSession(token) { |
| if (!token) |
| return; |
| await getDb().run('DELETE FROM sessions WHERE token_hash = ?', [sha256(token)]); |
| } |
| |