import { Router, Request, Response, NextFunction } from 'express' import bcrypt from 'bcryptjs' import jwt from 'jsonwebtoken' import { body, validationResult } from 'express-validator' import { db } from '../config/secureDatabase' import { User, LoginRequest, LoginResponse } from '@skillmate/shared' import { FieldEncryption } from '../services/encryption' import { logger } from '../utils/logger' const router = Router() const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production' router.post('/login', [ body('username').optional().notEmpty().trim(), body('email').optional().isEmail().normalizeEmail(), body('password').notEmpty() ], async (req: Request, res: Response, next: NextFunction) => { try { const errors = validationResult(req) if (!errors.isEmpty()) { return res.status(400).json({ success: false, error: { message: 'Invalid input', details: errors.array() } }) } const { username, email, password } = req.body // Determine login identifier (email takes precedence) const loginIdentifier = email || username if (!loginIdentifier) { return res.status(400).json({ success: false, error: { message: 'Either username or email is required' } }) } let userRow: any // Try to find by email first (if looks like email), then by username if (loginIdentifier.includes('@')) { // Login with email const emailHash = FieldEncryption.hash(loginIdentifier) userRow = db.prepare(` SELECT id, username, email, password, role, employee_id, last_login, is_active, created_at, updated_at FROM users WHERE email_hash = ? AND is_active = 1 `).get(emailHash) as any } else { // Login with username userRow = db.prepare(` SELECT id, username, email, password, role, employee_id, last_login, is_active, created_at, updated_at FROM users WHERE username = ? AND is_active = 1 `).get(loginIdentifier) as any } if (!userRow) { return res.status(401).json({ success: false, error: { message: 'Invalid credentials' } }) } // Check password const isValidPassword = await bcrypt.compare(password, userRow.password) if (!isValidPassword) { return res.status(401).json({ success: false, error: { message: 'Invalid credentials' } }) } // Update last login const now = new Date().toISOString() db.prepare('UPDATE users SET last_login = ? WHERE id = ?').run(now, userRow.id) // Create user object without password (decrypt email) const user: User = { id: userRow.id, username: userRow.username, email: FieldEncryption.decrypt(userRow.email) || '', role: userRow.role, employeeId: userRow.employee_id, lastLogin: new Date(now), isActive: Boolean(userRow.is_active), createdAt: new Date(userRow.created_at), updatedAt: new Date(userRow.updated_at) } // Generate token const token = jwt.sign( { user }, JWT_SECRET, { expiresIn: '24h' } ) const response: LoginResponse = { user, token: { accessToken: token, expiresIn: 86400, tokenType: 'Bearer' } } logger.info(`User ${loginIdentifier} logged in successfully`) res.json({ success: true, data: response }) } catch (error) { next(error) } } ) router.post('/logout', (req, res) => { res.json({ success: true, message: 'Logged out successfully' }) }) export default router