const { Pool } = require('pg'); const jwt = require('jsonwebtoken'); const url = require('url'); const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); // Middleware to verify admin access function verifyAdmin(req) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { throw new Error('No token provided'); } const token = authHeader.substring(7); const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production'; try { const decoded = jwt.verify(token, jwtSecret); // Check if user has admin role if (!decoded.roles || !decoded.roles.includes('admin')) { throw new Error('Admin access required'); } return decoded; } catch (error) { throw new Error('Invalid token or insufficient permissions'); } } export default async function handler(req, res) { const client = await pool.connect(); try { // Verify admin access verifyAdmin(req); if (req.method === 'GET') { // Parse query parameters const parsedUrl = url.parse(req.url, true); const query = parsedUrl.query; const page = parseInt(query.page || '1'); const limit = parseInt(query.limit || '20'); const search = query.search || ''; const offset = (page - 1) * limit; let whereClause = ''; let queryParams = [limit, offset]; if (search) { whereClause = 'WHERE u.username ILIKE $3 OR u.email ILIKE $3 OR u.first_name ILIKE $3 OR u.last_name ILIKE $3'; queryParams.push(`%${search}%`); } // Get users with their roles const usersQuery = ` SELECT u.id, u.username, u.email, u.first_name, u.last_name, u.is_active, u.created_at, u.last_login, ARRAY_AGG(DISTINCT r.name) FILTER (WHERE r.name IS NOT NULL) as roles FROM users u LEFT JOIN user_roles ur ON u.id = ur.user_id LEFT JOIN roles r ON ur.role_id = r.id ${whereClause} GROUP BY u.id, u.username, u.email, u.first_name, u.last_name, u.is_active, u.created_at, u.last_login ORDER BY u.created_at DESC LIMIT $1 OFFSET $2 `; const usersResult = await client.query(usersQuery, queryParams); // Get total count let countQuery = 'SELECT COUNT(*) FROM users u'; let countParams = []; if (search) { countQuery += ' WHERE u.username ILIKE $1 OR u.email ILIKE $1 OR u.first_name ILIKE $1 OR u.last_name ILIKE $1'; countParams.push(`%${search}%`); } const countResult = await client.query(countQuery, countParams); const total = parseInt(countResult.rows[0].count); res.status(200).json({ success: true, users: usersResult.rows.map(user => ({ id: user.id, username: user.username, email: user.email, firstName: user.first_name, lastName: user.last_name, isActive: user.is_active, roles: user.roles || [], createdAt: user.created_at, lastLogin: user.last_login })), pagination: { page, limit, total, pages: Math.ceil(total / limit) } }); } else if (req.method === 'PUT') { // Update user (activate/deactivate, change roles) const parsedUrl = url.parse(req.url, true); const userId = parsedUrl.query.id; if (!userId) { return res.status(400).json({ error: 'User ID required' }); } // Parse request body const { isActive, roles } = req.body; // Update user status if (typeof isActive === 'boolean') { await client.query( 'UPDATE users SET is_active = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2', [isActive, userId] ); } // Update user roles if (roles && Array.isArray(roles)) { // Remove existing roles await client.query('DELETE FROM user_roles WHERE user_id = $1', [userId]); // Add new roles for (const roleName of roles) { const roleResult = await client.query('SELECT id FROM roles WHERE name = $1', [roleName]); if (roleResult.rows.length > 0) { await client.query( 'INSERT INTO user_roles (user_id, role_id) VALUES ($1, $2)', [userId, roleResult.rows[0].id] ); } } } // Get updated user data const updatedUserQuery = ` SELECT u.id, u.username, u.email, u.first_name, u.last_name, u.is_active, u.created_at, u.last_login, ARRAY_AGG(DISTINCT r.name) FILTER (WHERE r.name IS NOT NULL) as roles FROM users u LEFT JOIN user_roles ur ON u.id = ur.user_id LEFT JOIN roles r ON ur.role_id = r.id WHERE u.id = $1 GROUP BY u.id, u.username, u.email, u.first_name, u.last_name, u.is_active, u.created_at, u.last_login `; const updatedUser = await client.query(updatedUserQuery, [userId]); res.status(200).json({ success: true, message: 'User updated successfully', user: { id: updatedUser.rows[0].id, username: updatedUser.rows[0].username, email: updatedUser.rows[0].email, firstName: updatedUser.rows[0].first_name, lastName: updatedUser.rows[0].last_name, isActive: updatedUser.rows[0].is_active, roles: updatedUser.rows[0].roles || [], createdAt: updatedUser.rows[0].created_at, lastLogin: updatedUser.rows[0].last_login } }); } else if (req.method === 'DELETE') { // Delete user (soft delete - deactivate) const parsedUrl = url.parse(req.url, true); const userId = parsedUrl.query.id; if (!userId) { return res.status(400).json({ error: 'User ID required' }); } await client.query( 'UPDATE users SET is_active = false, updated_at = CURRENT_TIMESTAMP WHERE id = $1', [userId] ); res.status(200).json({ success: true, message: 'User deactivated successfully' }); } else { res.status(405).json({ error: 'Method not allowed' }); } } catch (error) { console.error('Admin users API error:', error); if (error.message === 'No token provided' || error.message === 'Invalid token or insufficient permissions' || error.message === 'Admin access required') { res.status(401).json({ error: error.message }); } else { res.status(500).json({ error: 'Internal server error', details: error.message }); } } finally { client.release(); } }