import { eq } from 'drizzle-orm'; import { db } from '../config.js'; import { users } from '../db/schema/index.js'; /** * Hash a password using Bun's built-in password hashing * @param {string} password - Plain text password * @returns {Promise} Hashed password */ async function hashPassword(password) { return Bun.password.hash(password); } /** * Verify a password against a hash * @param {string} password - Plain text password * @param {string} hash - Hashed password * @returns {Promise} True if password matches */ async function verifyPassword(password, hash) { return Bun.password.verify(password, hash); } /** * Register a new user * @param {Object} userData - User registration data * @param {string} userData.email - User email * @param {string} userData.password - Plain text password * @param {string} userData.callsign - Ham radio callsign * @returns {Promise} Created user object (without password) or null if email exists */ export async function registerUser({ email, password, callsign }) { // Check if user already exists const [existingUser] = await db .select() .from(users) .where(eq(users.email, email)) .limit(1); if (existingUser) { return null; } // Hash password const passwordHash = await hashPassword(password); // Create user const newUser = await db .insert(users) .values({ email, passwordHash, callsign: callsign.toUpperCase(), }) .returning(); // Return user without password hash const { passwordHash: _, ...userWithoutPassword } = newUser[0]; return userWithoutPassword; } /** * Authenticate user with email and password * @param {string} email - User email * @param {string} password - Plain text password * @returns {Promise} User object (without password) if authenticated * @throws {Error} If credentials are invalid */ export async function authenticateUser(email, password) { // Find user by email const [user] = await db .select() .from(users) .where(eq(users.email, email)) .limit(1); if (!user) { return null; } // Verify password const isValid = await verifyPassword(password, user.passwordHash); if (!isValid) { return null; } // Return user without password hash const { passwordHash: _, ...userWithoutPassword } = user; return userWithoutPassword; } /** * Get user by ID * @param {number} userId - User ID * @returns {Promise} User object (without password) or null */ export async function getUserById(userId) { const [user] = await db .select() .from(users) .where(eq(users.id, userId)) .limit(1); if (!user) return null; const { passwordHash: _, ...userWithoutPassword } = user; return userWithoutPassword; } /** * Update user's LoTW credentials (encrypted) * @param {number} userId - User ID * @param {string} lotwUsername - LoTW username * @param {string} lotwPassword - LoTW password (will be encrypted) * @returns {Promise} */ export async function updateLoTWCredentials(userId, lotwUsername, lotwPassword) { // Simple encryption for storage (in production, use a proper encryption library) // For now, we'll store as-is but marked for encryption await db .update(users) .set({ lotwUsername, lotwPassword, // TODO: Encrypt before storing updatedAt: new Date(), }) .where(eq(users.id, userId)); } /** * Update user's DCL API key * @param {number} userId - User ID * @param {string} dclApiKey - DCL API key * @returns {Promise} */ export async function updateDCLCredentials(userId, dclApiKey) { await db .update(users) .set({ dclApiKey, updatedAt: new Date(), }) .where(eq(users.id, userId)); }