84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
import { getUserByUsername, checkRateLimit, resetRateLimit, createSession } from '~/server/utils/database'
|
|
import { setAuthCookie, generateSessionToken } from '~/server/utils/auth'
|
|
import bcrypt from 'bcrypt'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
// Get real client IP from proxy headers (prioritize x-real-ip for NPM)
|
|
const xRealIp = getHeader(event, 'x-real-ip')
|
|
const xForwardedFor = getHeader(event, 'x-forwarded-for')
|
|
const cfConnectingIp = getHeader(event, 'cf-connecting-ip')
|
|
|
|
// Use x-real-ip first (set by NPM), then x-forwarded-for, then cf-connecting-ip, then fallback
|
|
const clientIp = xRealIp ||
|
|
(xForwardedFor ? xForwardedFor.split(',')[0].trim() : null) ||
|
|
cfConnectingIp ||
|
|
getRequestIP(event) ||
|
|
'unknown'
|
|
|
|
// Log IP for verification
|
|
console.log(`[LOGIN ATTEMPT] IP: ${clientIp}, Headers:`, {
|
|
'x-forwarded-for': xForwardedFor,
|
|
'x-real-ip': xRealIp,
|
|
'cf-connecting-ip': cfConnectingIp,
|
|
'getRequestIP': getRequestIP(event)
|
|
})
|
|
|
|
// Check rate limit: 5 attempts per 15 minutes
|
|
if (!checkRateLimit(clientIp, 'login', 5, 15)) {
|
|
throw createError({
|
|
statusCode: 429,
|
|
message: 'Too many login attempts. Please try again in 15 minutes.'
|
|
})
|
|
}
|
|
|
|
const body = await readBody(event)
|
|
const { username, password } = body
|
|
|
|
if (!username || !password) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: 'Username and password are required'
|
|
})
|
|
}
|
|
|
|
const user = getUserByUsername(username.toLowerCase())
|
|
|
|
if (!user) {
|
|
console.log(`[LOGIN FAILED] Username not found: ${username.toLowerCase()}, IP: ${clientIp}`)
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Invalid credentials'
|
|
})
|
|
}
|
|
|
|
// Compare the provided password with the hashed password in the database
|
|
const passwordMatch = await bcrypt.compare(password, user.password)
|
|
|
|
if (!passwordMatch) {
|
|
console.log(`[LOGIN FAILED] Invalid password for user: ${username.toLowerCase()}, IP: ${clientIp}`)
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Invalid credentials'
|
|
})
|
|
}
|
|
|
|
// Generate session token and create session
|
|
const sessionToken = generateSessionToken()
|
|
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() // 24 hours
|
|
createSession(sessionToken, user.username, expiresAt)
|
|
|
|
// Set session cookie
|
|
setAuthCookie(event, sessionToken)
|
|
|
|
// Reset rate limit on successful login
|
|
resetRateLimit(clientIp, 'login')
|
|
|
|
// Log successful login
|
|
console.log(`[LOGIN SUCCESS] User: ${user.username}, IP: ${clientIp}`)
|
|
|
|
return {
|
|
success: true,
|
|
username: user.username
|
|
}
|
|
})
|