Initial commit
Dieser Commit ist enthalten in:
121
backend/src/routes/auth.ts
Normale Datei
121
backend/src/routes/auth.ts
Normale Datei
@ -0,0 +1,121 @@
|
||||
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
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren