echos-ocr/src/app/api/org/members/[id]/route.ts

130 lines
3.6 KiB
TypeScript
Raw Normal View History

import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/auth";
import { prisma } from "@/lib/db";
const VALID_ROLES = ["viewer", "reviewer", "editor", "admin"];
async function requireAdmin(orgId: string, userId: string) {
const membership = await prisma.orgMember.findFirst({
where: { userId, organizationId: orgId },
});
if (!membership || !["owner", "admin"].includes(membership.role)) {
return null;
}
return membership;
}
export async function PATCH(
req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth();
if (!session?.user?.orgId || !session.user.id) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const callerMembership = await requireAdmin(session.user.orgId, session.user.id);
if (!callerMembership) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
const { id } = await params;
const { role } = await req.json();
if (!role || !VALID_ROLES.includes(role)) {
return NextResponse.json(
{ error: `Role must be one of: ${VALID_ROLES.join(", ")}` },
{ status: 400 }
);
}
const target = await prisma.orgMember.findFirst({
where: { id, organizationId: session.user.orgId },
});
if (!target) {
return NextResponse.json({ error: "Member not found" }, { status: 404 });
}
if (target.role === "owner") {
return NextResponse.json(
{ error: "Cannot change the owner's role" },
{ status: 403 }
);
}
if (target.userId === session.user.id) {
return NextResponse.json(
{ error: "You cannot change your own role" },
{ status: 403 }
);
}
const updated = await prisma.orgMember.update({
where: { id },
data: { role },
include: { user: { select: { id: true, email: true, displayName: true } } },
});
return NextResponse.json({ member: updated });
} catch (error) {
console.error("[org/members/[id]] PATCH error:", error);
return NextResponse.json({ error: "Failed to update member" }, { status: 500 });
}
}
export async function DELETE(
_req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth();
if (!session?.user?.orgId || !session.user.id) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const callerMembership = await requireAdmin(session.user.orgId, session.user.id);
if (!callerMembership) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
const { id } = await params;
const target = await prisma.orgMember.findFirst({
where: { id, organizationId: session.user.orgId },
});
if (!target) {
return NextResponse.json({ error: "Member not found" }, { status: 404 });
}
if (target.role === "owner") {
return NextResponse.json(
{ error: "Cannot remove the organization owner" },
{ status: 403 }
);
}
if (target.userId === session.user.id) {
return NextResponse.json(
{ error: "You cannot remove yourself" },
{ status: 403 }
);
}
await prisma.$transaction([
prisma.orgMember.delete({ where: { id } }),
prisma.user.updateMany({
where: { id: target.userId, activeOrgId: session.user.orgId },
data: { activeOrgId: null },
}),
]);
return NextResponse.json({ success: true });
} catch (error) {
console.error("[org/members/[id]] DELETE error:", error);
return NextResponse.json({ error: "Failed to remove member" }, { status: 500 });
}
}