import { NextRequest, NextResponse } from 'next/server'; import { Pool } from 'pg'; import bcrypt from 'bcryptjs'; import jwt from 'jsonwebtoken'; const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); interface LoginRequest { username: string; password: string; } export default async function handler(req: NextRequest) { if (req.method !== 'POST') { return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), { status: 405, headers: { 'Content-Type': 'application/json' }, }); } const client = await pool.connect(); try { const body: LoginRequest = await req.json(); const { username, password } = body; // Validate input if (!username || !password) { return new NextResponse(JSON.stringify({ error: 'Username and password are required' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } // Get user by username or email const userResult = await client.query(` SELECT id, username, email, password_hash, first_name, last_name, is_active, last_login FROM users WHERE (username = $1 OR email = $1) AND is_active = true `, [username]); if (userResult.rows.length === 0) { return new NextResponse(JSON.stringify({ error: 'Invalid credentials' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }); } const user = userResult.rows[0]; // Verify password const isValidPassword = await bcrypt.compare(password, user.password_hash); if (!isValidPassword) { return new NextResponse(JSON.stringify({ error: 'Invalid credentials' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }); } // Get user roles and permissions const userRoles = await client.query(` SELECT r.name, r.description, array_agg(p.name) as permissions FROM roles r JOIN user_roles ur ON r.id = ur.role_id LEFT JOIN role_permissions rp ON r.id = rp.role_id LEFT JOIN permissions p ON rp.permission_id = p.id WHERE ur.user_id = $1 GROUP BY r.id, r.name, r.description `, [user.id]); const roles = userRoles.rows.map(r => r.name); const permissions = [...new Set(userRoles.rows.flatMap(r => r.permissions || []))]; // Generate JWT token const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production'; const token = jwt.sign( { userId: user.id, username: user.username, email: user.email, roles, permissions }, jwtSecret, { expiresIn: '7d' } ); // Store session const tokenHash = await bcrypt.hash(token, 10); const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days await client.query(` INSERT INTO user_sessions (user_id, token_hash, expires_at, user_agent, ip_address) VALUES ($1, $2, $3, $4, $5) `, [ user.id, tokenHash, expiresAt, req.headers.get('user-agent') || null, req.headers.get('x-forwarded-for') || req.headers.get('x-real-ip') || null ]); // Update last login await client.query( 'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = $1', [user.id] ); return new NextResponse(JSON.stringify({ success: true, message: 'Login successful', user: { id: user.id, username: user.username, email: user.email, firstName: user.first_name, lastName: user.last_name, roles, permissions, lastLogin: user.last_login }, token }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } catch (error) { console.error('Login error:', error); return new NextResponse(JSON.stringify({ error: 'Login failed', details: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } finally { client.release(); } }