Spaces:
Runtime error
Runtime error
| import { NextResponse } from 'next/server' | |
| import { authenticateUser, createSession } from '@/lib/auth' | |
| import { logAuditEvent, needsFirstTimeSetup } from '@/lib/db' | |
| import { getMcSessionCookieName, getMcSessionCookieOptions, isRequestSecure } from '@/lib/session-cookie' | |
| import { loginLimiter } from '@/lib/rate-limit' | |
| import { logger } from '@/lib/logger' | |
| export async function POST(request: Request) { | |
| try { | |
| const rateCheck = loginLimiter(request) | |
| if (rateCheck) return rateCheck | |
| const { username, password } = await request.json() | |
| if (!username || !password) { | |
| return NextResponse.json({ error: 'Username and password are required' }, { status: 400 }) | |
| } | |
| const ipAddress = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || 'unknown' | |
| const userAgent = request.headers.get('user-agent') || undefined | |
| const user = authenticateUser(username, password) | |
| if (!user) { | |
| logAuditEvent({ action: 'login_failed', actor: username, ip_address: ipAddress, user_agent: userAgent }) | |
| // When no users exist at all, give actionable feedback instead of "Invalid credentials" | |
| if (needsFirstTimeSetup()) { | |
| return NextResponse.json( | |
| { | |
| error: 'No admin account has been created yet', | |
| code: 'NO_USERS', | |
| hint: 'Visit /setup to create your admin account', | |
| }, | |
| { status: 401 } | |
| ) | |
| } | |
| return NextResponse.json({ error: 'Invalid credentials' }, { status: 401 }) | |
| } | |
| const { token, expiresAt } = createSession(user.id, ipAddress, userAgent, user.workspace_id) | |
| logAuditEvent({ action: 'login', actor: user.username, actor_id: user.id, ip_address: ipAddress, user_agent: userAgent }) | |
| const response = NextResponse.json({ | |
| user: { | |
| id: user.id, | |
| username: user.username, | |
| display_name: user.display_name, | |
| role: user.role, | |
| provider: user.provider || 'local', | |
| email: user.email || null, | |
| avatar_url: user.avatar_url || null, | |
| workspace_id: user.workspace_id ?? 1, | |
| tenant_id: user.tenant_id ?? 1, | |
| }, | |
| }) | |
| const isSecureRequest = isRequestSecure(request) | |
| const cookieName = getMcSessionCookieName(isSecureRequest) | |
| response.cookies.set(cookieName, token, { | |
| ...getMcSessionCookieOptions({ maxAgeSeconds: expiresAt - Math.floor(Date.now() / 1000), isSecureRequest }), | |
| }) | |
| return response | |
| } catch (error) { | |
| logger.error({ err: error }, 'Login error') | |
| return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) | |
| } | |
| } | |