import { cookies } from 'next/headers'; import { SignJWT, jwtVerify } from 'jose'; import bcrypt from 'bcryptjs'; import { db } from './db'; function getSecret() { const s = process.env.JWT_SECRET; if (!s) throw new Error('JWT_SECRET is not configured'); return new TextEncoder().encode(s); } export async function hashPassword(password: string) { return bcrypt.hashSync(password, 10); } export async function verifyPassword(password: string, hash: string) { return bcrypt.compareSync(password, hash); } export async function createUser( username: string, password: string, role: 'admin' | 'user', profile?: { first_name?: string; last_name?: string; email?: string; phone?: string; country?: string; preferred_language?: string } ) { const hash = await hashPassword(password); const { rows } = await db.query( `INSERT INTO users (username, password_hash, role, first_name, last_name, email, phone, country, preferred_language) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (username) DO UPDATE SET password_hash = EXCLUDED.password_hash, role = EXCLUDED.role RETURNING id, username, role, first_name, last_name, email, phone, country, preferred_language`, [username, hash, role, profile?.first_name || null, profile?.last_name || null, profile?.email || null, profile?.phone || null, profile?.country || null, profile?.preferred_language || 'es'] ); return rows[0]; } export async function authenticateUser(username: string, password: string) { const { rows } = await db.query('SELECT id, username, password_hash, role, preferred_language, terms_accepted_at, first_name, last_name FROM users WHERE username = $1', [username]); if (rows.length === 0) return null; const user = rows[0]; const valid = await verifyPassword(password, user.password_hash); if (!valid) return null; return { id: user.id, username: user.username, role: user.role, preferred_language: user.preferred_language || 'es', terms_accepted_at: user.terms_accepted_at, first_name: user.first_name, last_name: user.last_name }; } export async function createSession(user: { id: number; username: string; role: string }) { const token = await new SignJWT({ id: user.id, username: user.username, role: user.role }) .setProtectedHeader({ alg: 'HS256' }) .setExpirationTime('4h') // 4 hours for security (reduced from 7 days) .sign(getSecret()); cookies().set('session', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: 60 * 60 * 4 }); // 4 hours return token; } export async function getSession() { const token = cookies().get('session')?.value; if (!token) return null; try { const { payload } = await jwtVerify(token, getSecret()); return payload as { id: number; username: string; role: string }; } catch { return null; } } export async function clearSession() { cookies().delete('session'); } export async function requireRole(role: 'admin' | 'user') { const session = await getSession(); if (!session || session.role !== role) { throw new Error('Unauthorized'); } return session; } export async function requireAuth() { const session = await getSession(); if (!session) { throw new Error('Unauthorized'); } return session; } export async function canComment(userId: number) { const { rows } = await db.query('SELECT comments_disabled FROM users WHERE id = $1', [userId]); return rows.length === 0 || !rows[0].comments_disabled; }