Spaces:
Sleeping
Sleeping
| import { NextRequest, NextResponse } from 'next/server' | |
| import { getUserFromRequest, getAllUsers, createUser, updateUser, deleteUser, getUserById, requireRole } from '@/lib/auth' | |
| import { logAuditEvent } from '@/lib/db' | |
| import { validateBody, createUserSchema } from '@/lib/validation' | |
| import { mutationLimiter } from '@/lib/rate-limit' | |
| import { logger } from '@/lib/logger' | |
| /** | |
| * GET /api/auth/users - List all users (admin only) | |
| */ | |
| export async function GET(request: NextRequest) { | |
| const auth = requireRole(request, 'viewer') | |
| if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status }) | |
| const user = getUserFromRequest(request) | |
| if (!user || user.role !== 'admin') { | |
| return NextResponse.json({ error: 'Admin access required' }, { status: 403 }) | |
| } | |
| const users = getAllUsers() | |
| const workspaceId = user.workspace_id ?? 1 | |
| return NextResponse.json({ users: users.filter((u) => (u.workspace_id ?? 1) === workspaceId) }) | |
| } | |
| /** | |
| * POST /api/auth/users - Create a new user (admin only) | |
| */ | |
| export async function POST(request: NextRequest) { | |
| const currentUser = getUserFromRequest(request) | |
| if (!currentUser || currentUser.role !== 'admin') { | |
| return NextResponse.json({ error: 'Admin access required' }, { status: 403 }) | |
| } | |
| const rateCheck = mutationLimiter(request) | |
| if (rateCheck) return rateCheck | |
| try { | |
| const result = await validateBody(request, createUserSchema) | |
| if ('error' in result) return result.error | |
| const { username, password, display_name, role, provider, email } = result.data | |
| const workspaceId = currentUser.workspace_id ?? 1 | |
| const newUser = createUser(username, password, display_name || username, role, { | |
| provider, | |
| email: email || null, | |
| workspace_id: workspaceId, | |
| }) | |
| const ipAddress = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || 'unknown' | |
| logAuditEvent({ | |
| action: 'user_create', actor: currentUser.username, actor_id: currentUser.id, | |
| target_type: 'user', target_id: newUser.id, | |
| detail: { username, role, provider, email }, ip_address: ipAddress, | |
| }) | |
| return NextResponse.json({ | |
| user: { | |
| id: newUser.id, | |
| username: newUser.username, | |
| display_name: newUser.display_name, | |
| role: newUser.role, | |
| provider: newUser.provider || 'local', | |
| email: newUser.email || null, | |
| avatar_url: newUser.avatar_url || null, | |
| is_approved: newUser.is_approved ?? 1, | |
| workspace_id: newUser.workspace_id ?? 1, | |
| tenant_id: newUser.tenant_id ?? 1, | |
| } | |
| }, { status: 201 }) | |
| } catch (error: any) { | |
| if (error.message?.includes('UNIQUE constraint failed')) { | |
| return NextResponse.json({ error: 'Username already exists' }, { status: 409 }) | |
| } | |
| logger.error({ err: error }, 'POST /api/auth/users error') | |
| return NextResponse.json({ error: 'Failed to create user' }, { status: 500 }) | |
| } | |
| } | |
| /** | |
| * PUT /api/auth/users - Update a user (admin only) | |
| */ | |
| export async function PUT(request: NextRequest) { | |
| const currentUser = getUserFromRequest(request) | |
| if (!currentUser || currentUser.role !== 'admin') { | |
| return NextResponse.json({ error: 'Admin access required' }, { status: 403 }) | |
| } | |
| try { | |
| const { id, display_name, role, password, is_approved, email, avatar_url } = await request.json() | |
| const userId = parseInt(String(id)) | |
| if (!id || Number.isNaN(userId)) { | |
| return NextResponse.json({ error: 'User ID is required' }, { status: 400 }) | |
| } | |
| if (role && !['admin', 'operator', 'viewer'].includes(role)) { | |
| return NextResponse.json({ error: 'Invalid role' }, { status: 400 }) | |
| } | |
| // Prevent demoting yourself | |
| if (userId === currentUser.id && role && role !== currentUser.role) { | |
| return NextResponse.json({ error: 'Cannot change your own role' }, { status: 400 }) | |
| } | |
| const workspaceId = currentUser.workspace_id ?? 1 | |
| const existing = getUserById(userId) | |
| if (!existing || (existing.workspace_id ?? 1) !== workspaceId) { | |
| return NextResponse.json({ error: 'User not found' }, { status: 404 }) | |
| } | |
| const updated = updateUser(userId, { display_name, role, password: password || undefined, is_approved, email, avatar_url }) | |
| if (!updated) { | |
| return NextResponse.json({ error: 'User not found' }, { status: 404 }) | |
| } | |
| const ipAddress = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || 'unknown' | |
| logAuditEvent({ | |
| action: 'user_update', actor: currentUser.username, actor_id: currentUser.id, | |
| target_type: 'user', target_id: userId, | |
| detail: { display_name, role, password_changed: !!password, is_approved }, ip_address: ipAddress, | |
| }) | |
| return NextResponse.json({ | |
| user: { | |
| id: updated.id, | |
| username: updated.username, | |
| display_name: updated.display_name, | |
| role: updated.role, | |
| provider: updated.provider || 'local', | |
| email: updated.email || null, | |
| avatar_url: updated.avatar_url || null, | |
| is_approved: updated.is_approved ?? 1, | |
| workspace_id: updated.workspace_id ?? 1, | |
| tenant_id: updated.tenant_id ?? 1, | |
| } | |
| }) | |
| } catch (error) { | |
| logger.error({ err: error }, 'PUT /api/auth/users error') | |
| return NextResponse.json({ error: 'Failed to update user' }, { status: 500 }) | |
| } | |
| } | |
| /** | |
| * DELETE /api/auth/users - Delete a user (admin only) | |
| */ | |
| export async function DELETE(request: NextRequest) { | |
| const currentUser = getUserFromRequest(request) | |
| if (!currentUser || currentUser.role !== 'admin') { | |
| return NextResponse.json({ error: 'Admin access required' }, { status: 403 }) | |
| } | |
| let body: any | |
| try { body = await request.json() } catch { return NextResponse.json({ error: 'Request body required' }, { status: 400 }) } | |
| const id = body.id | |
| if (!id) { | |
| return NextResponse.json({ error: 'User ID is required' }, { status: 400 }) | |
| } | |
| const userId = parseInt(id) | |
| // Prevent deleting yourself | |
| if (userId === currentUser.id) { | |
| return NextResponse.json({ error: 'Cannot delete your own account' }, { status: 400 }) | |
| } | |
| const workspaceId = currentUser.workspace_id ?? 1 | |
| const existing = getUserById(userId) | |
| if (!existing || (existing.workspace_id ?? 1) !== workspaceId) { | |
| return NextResponse.json({ error: 'User not found' }, { status: 404 }) | |
| } | |
| const deleted = deleteUser(userId) | |
| if (!deleted) { | |
| return NextResponse.json({ error: 'User not found' }, { status: 404 }) | |
| } | |
| const ipAddress = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || 'unknown' | |
| logAuditEvent({ | |
| action: 'user_delete', actor: currentUser.username, actor_id: currentUser.id, | |
| target_type: 'user', target_id: userId, | |
| ip_address: ipAddress, | |
| }) | |
| return NextResponse.json({ success: true }) | |
| } | |