Files
SkillMate/backend/src/routes/auth.ts
Claude Project Manager 6b9b6d4f20 Initial commit
2025-09-20 21:31:04 +02:00

121 Zeilen
3.7 KiB
TypeScript

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