issue-ai/src/app/api/auth/login/route.ts

94 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextRequest, NextResponse } from 'next/server'
import { createToken } from '@/lib/auth'
import { initDatabase } from '@/lib/db-schema'
import { signSharedJwt, sharedCookieConfig } from '@/lib/jwt-shared'
import { ldapAuth } from '@/lib/ldap'
import { getDb } from '@/lib/db'
import bcrypt from 'bcryptjs'
export async function POST(request: NextRequest) {
try {
initDatabase()
const { username, password } = await request.json()
if (!username || !password) return NextResponse.json({ error: '请输入用户名和密码' }, { status: 400 })
let userId: number
let role: string
let displayName: string
const db = getDb()
// 1. localadmin纯本地 BCrypt不依赖 LLDAP
if (username === 'localadmin') {
const localUser = db.prepare(
'SELECT * FROM users WHERE username = ? AND is_active = 1'
).get(username) as { id: number; username: string; display_name: string; role: string; password_hash: string } | undefined
if (!localUser || !bcrypt.compareSync(password, localUser.password_hash)) {
return NextResponse.json({ error: '用户名或密码错误' }, { status: 401 })
}
userId = localUser.id
role = localUser.role
displayName = localUser.display_name || username
} else {
// 2. 其他用户LLDAP 优先
const ldapResult = await ldapAuth(username, password)
if (ldapResult.success) {
// LDAP 认证成功 → 更新本地密码缓存 + 自动创建用户
displayName = ldapResult.displayName || username
const pwHash = bcrypt.hashSync(password, 10)
const existing = db.prepare(
'SELECT id, role FROM users WHERE username = ? AND is_active = 1'
).get(username) as { id: number; role: string } | undefined
if (existing) {
db.prepare('UPDATE users SET password_hash = ?, display_name = ? WHERE id = ?')
.run(pwHash, displayName, existing.id)
userId = existing.id
role = existing.role
} else {
db.prepare(
"INSERT INTO users (username, password_hash, display_name, role, is_active, created_at, updated_at) VALUES (?, ?, ?, 'viewer', 1, datetime('now', '+8 hours'), datetime('now', '+8 hours'))"
).run(username, pwHash, displayName)
const created = db.prepare(
'SELECT id, role FROM users WHERE username = ?'
).get(username) as { id: number; role: string } | undefined
if (!created) return NextResponse.json({ error: '用户创建失败' }, { status: 500 })
userId = created.id
role = created.role
}
} else if (ldapResult.unreachable) {
// LLDAP 不可达 → 回退本地密码缓存
const localUser = db.prepare(
'SELECT * FROM users WHERE username = ? AND is_active = 1'
).get(username) as { id: number; username: string; display_name: string; role: string; password_hash: string } | undefined
if (!localUser || !bcrypt.compareSync(password, localUser.password_hash)) {
return NextResponse.json({ error: '认证服务不可用,且本地密码不匹配' }, { status: 401 })
}
userId = localUser.id
role = localUser.role
displayName = localUser.display_name || username
} else {
return NextResponse.json({ error: '用户名或密码错误' }, { status: 401 })
}
}
// 3. 更新最后登录时间和活跃时间
db.prepare("UPDATE users SET last_login_at = datetime('now', '+8 hours'), last_active_at = datetime('now', '+8 hours') WHERE id = ?").run(userId)
// 4. 签发两个 cookie
const localToken = await createToken({ id: userId, username, display_name: displayName, role })
const sharedToken = signSharedJwt({ username, displayName })
const sharedCfg = sharedCookieConfig()
const response = NextResponse.json({
user: { id: userId, username, display_name: displayName, role },
})
response.cookies.set('session_issue', localToken, {
httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60, path: '/',
})
response.cookies.set(sharedCfg.name, sharedToken, sharedCfg)
return response
} catch (e) { console.error('Login error:', e); return NextResponse.json({ error: '登录失败' }, { status: 500 }) }
}