import bcryptjs from 'bcryptjs'; const { hash, compare } = bcryptjs; import jwt from 'jsonwebtoken'; const { sign, verify } = jwt; import { getPool } from './db.js'; import type { RowDataPacket } from 'mysql2'; const JWT_SECRET = process.env.JWT_SECRET ?? process.env.CREDENTIAL_ENCRYPTION_KEY ?? 'dev-secret-change-me'; const SALT_ROUNDS = 12; export interface JWTPayload { sub: string; // customer id email: string; plan: string; role?: string; } export async function hashPassword(password: string): Promise { return hash(password, SALT_ROUNDS); } export async function verifyPassword(password: string, hash: string): Promise { return compare(password, hash); } export function signJWT(payload: JWTPayload): string { return sign(payload, JWT_SECRET, { expiresIn: '7d' }); } export function verifyJWT(token: string): JWTPayload { return verify(token, JWT_SECRET) as JWTPayload; } interface CustomerRow extends RowDataPacket { id: string; email: string; plan: string; active: boolean; api_key: string; password_hash: string | null; role: string; } export async function findCustomerByEmail(email: string): Promise { const [rows] = await getPool().query( 'SELECT id, email, plan, active, api_key, password_hash, role FROM customers WHERE email = ?', [email] ); return rows[0] ?? null; } export async function findCustomerById(id: string): Promise { const [rows] = await getPool().query( 'SELECT id, email, plan, active, api_key, password_hash, role FROM customers WHERE id = ?', [id] ); return rows[0] ?? null; } export async function createCustomer( id: string, email: string, passwordHash: string, apiKey: string ): Promise { await getPool().query( 'INSERT INTO customers (id, email, password_hash, api_key, plan, active) VALUES (?, ?, ?, ?, ?, ?)', [id, email, passwordHash, apiKey, 'free', true] ); } export async function setResetToken(email: string, token: string): Promise { const [result] = await getPool().query( 'UPDATE customers SET reset_token = ?, reset_expires_at = DATE_ADD(NOW(), INTERVAL 1 HOUR) WHERE email = ?', [token, email] ); return result.affectedRows > 0; } export async function findCustomerByResetToken(token: string) { const [rows] = await getPool().query( 'SELECT id, email, plan, active, api_key, password_hash, role FROM customers WHERE reset_token = ? AND reset_expires_at > NOW()', [token] ); return rows[0] ?? null; } export async function clearResetToken(customerId: string): Promise { await getPool().query( 'UPDATE customers SET reset_token = NULL, reset_expires_at = NULL WHERE id = ?', [customerId] ); } export async function updatePassword(customerId: string, passwordHash: string): Promise { await getPool().query( 'UPDATE customers SET password_hash = ? WHERE id = ?', [passwordHash, customerId] ); }