210 lines
5.9 KiB
TypeScript
210 lines
5.9 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { auth } from "@/lib/auth";
|
|
import { userDb } from "@/lib/db";
|
|
import bcrypt from "bcryptjs";
|
|
|
|
// GET /api/users/[id] - Get single user (admin only)
|
|
export async function GET(
|
|
request: Request,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
try {
|
|
const session = await auth();
|
|
|
|
if (!session?.user) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
// Only admins can view users
|
|
if ((session.user as any).role !== 'ADMIN') {
|
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
|
}
|
|
|
|
const { id } = await params;
|
|
const user = userDb.findById(id);
|
|
|
|
if (!user) {
|
|
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
|
}
|
|
|
|
const currentUsername = session.user.name || '';
|
|
const currentUserId = (session.user as any).id || '';
|
|
|
|
// Only the "admin" user can view any user details
|
|
// ADMIN users can only view their own created viewers
|
|
if (currentUsername !== 'admin') {
|
|
// Check if this user is a child of the current user
|
|
if (user.parent_user_id !== currentUserId) {
|
|
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
|
}
|
|
}
|
|
|
|
// Remove password hash from response
|
|
const { passwordHash, ...safeUser } = user;
|
|
|
|
return NextResponse.json({ user: safeUser });
|
|
} catch (error) {
|
|
console.error("Error fetching user:", error);
|
|
return NextResponse.json(
|
|
{ error: "Failed to fetch user" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// PATCH /api/users/[id] - Update user (admin only)
|
|
export async function PATCH(
|
|
request: Request,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
try {
|
|
const session = await auth();
|
|
|
|
if (!session?.user) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
// Only admins can update users
|
|
if ((session.user as any).role !== 'ADMIN') {
|
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
|
}
|
|
|
|
const { id } = await params;
|
|
const user = userDb.findById(id);
|
|
if (!user) {
|
|
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
|
}
|
|
|
|
const currentUsername = session.user.name || '';
|
|
const currentUserId = (session.user as any).id || '';
|
|
|
|
// Only the "admin" user can modify any user
|
|
// ADMIN users can only modify their own created viewers
|
|
if (currentUsername !== 'admin') {
|
|
// Check if this user is a child of the current user
|
|
if (user.parent_user_id !== currentUserId) {
|
|
return NextResponse.json(
|
|
{ error: "Forbidden: Cannot modify this user" },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
}
|
|
|
|
const body = await request.json();
|
|
const { username, email, password, role } = body;
|
|
|
|
// Validation
|
|
if (role && !['ADMIN', 'VIEWER'].includes(role)) {
|
|
return NextResponse.json(
|
|
{ error: "Invalid role. Must be ADMIN or VIEWER" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Check if username is taken by another user
|
|
if (username && username !== user.username) {
|
|
const existing = userDb.findByUsername(username);
|
|
if (existing && existing.id !== id) {
|
|
return NextResponse.json(
|
|
{ error: "Username already exists" },
|
|
{ status: 409 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// Prepare update data
|
|
const updateData: {
|
|
username?: string;
|
|
email?: string | null;
|
|
passwordHash?: string;
|
|
role?: string;
|
|
} = {};
|
|
|
|
if (username !== undefined) updateData.username = username;
|
|
if (email !== undefined) updateData.email = email;
|
|
if (role !== undefined) updateData.role = role;
|
|
if (password) {
|
|
updateData.passwordHash = await bcrypt.hash(password, 10);
|
|
}
|
|
|
|
const updated = userDb.update(id, updateData);
|
|
|
|
if (!updated) {
|
|
return NextResponse.json({ error: "Failed to update user" }, { status: 500 });
|
|
}
|
|
|
|
// Remove password hash from response
|
|
const { passwordHash, ...safeUser } = updated;
|
|
|
|
return NextResponse.json({ user: safeUser });
|
|
} catch (error) {
|
|
console.error("Error updating user:", error);
|
|
return NextResponse.json(
|
|
{ error: "Failed to update user" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// DELETE /api/users/[id] - Delete user (admin only)
|
|
export async function DELETE(
|
|
request: Request,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
try {
|
|
const session = await auth();
|
|
|
|
if (!session?.user) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
// Only admins can delete users
|
|
if ((session.user as any).role !== 'ADMIN') {
|
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
|
}
|
|
|
|
const { id } = await params;
|
|
const user = userDb.findById(id);
|
|
if (!user) {
|
|
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
|
}
|
|
|
|
const currentUsername = session.user.name || '';
|
|
const currentUserId = (session.user as any).id || '';
|
|
|
|
// Only the "admin" user can delete any user
|
|
// ADMIN users can only delete their own created viewers
|
|
if (currentUsername !== 'admin') {
|
|
// Check if this user is a child of the current user
|
|
if (user.parent_user_id !== currentUserId) {
|
|
return NextResponse.json(
|
|
{ error: "Forbidden: Cannot delete this user" },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// Prevent deleting yourself
|
|
if ((session.user as any).id === id) {
|
|
return NextResponse.json(
|
|
{ error: "Cannot delete your own account" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const success = userDb.delete(id);
|
|
|
|
if (!success) {
|
|
return NextResponse.json({ error: "Failed to delete user" }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json({ message: "User deleted successfully" });
|
|
} catch (error) {
|
|
console.error("Error deleting user:", error);
|
|
return NextResponse.json(
|
|
{ error: "Failed to delete user" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|