11import { type NextRequest , NextResponse } from "next/server" ;
22import { z } from "zod" ;
3- import { permissionProtected , type RouteContext } from "~/auth/routes-wrapper" ;
3+ import {
4+ type DashboardUser ,
5+ permissionProtected ,
6+ type RouteContext ,
7+ } from "~/auth/routes-wrapper" ;
48import {
59 deleteTeamMember ,
610 findTeamMemberById ,
@@ -11,6 +15,11 @@ import {
1115 deleteCloudinaryAsset ,
1216 extractCloudinaryPublicId ,
1317} from "~/lib/cloudinary/server" ;
18+ import {
19+ getAllowedCommittees ,
20+ type TeamCommittee ,
21+ } from "~/lib/constants/team-committees" ;
22+ import { isAdmin } from "~/lib/auth/permissions" ;
1423import { updateTeamMemberSchema } from "~/lib/validation/team-member" ;
1524
1625type TeamMemberParams = {
@@ -21,13 +30,35 @@ const updateTeamMemberRequestSchema = updateTeamMemberSchema.extend({
2130 socialLinks : updateTeamMemberSchema . shape . socialLinks . optional ( ) ,
2231} ) ;
2332
33+ function getUserPermissionKeys ( user : NonNullable < DashboardUser > ) : string [ ] {
34+ return user . roles . flatMap ( ( r ) => r . permissions ) . map ( ( p ) => p . key ) ;
35+ }
36+ function canManageCommittee (
37+ user : NonNullable < DashboardUser > ,
38+ committee : string ,
39+ ) : boolean {
40+ const allowed = getAllowedCommittees (
41+ getUserPermissionKeys ( user ) ,
42+ isAdmin ( user ) ,
43+ ) ;
44+ return allowed . includes ( committee as TeamCommittee ) ;
45+ }
46+
2447export const PUT = permissionProtected < TeamMemberParams > (
25- [ "team:view_all " ] ,
26- async ( request : NextRequest , { params } : RouteContext < TeamMemberParams > ) => {
48+ [ "core:manage " ] ,
49+ async ( request : NextRequest , { params } : RouteContext < TeamMemberParams > , user ) => {
2750 try {
2851 const { id } = await params ;
2952 const existing = await findTeamMemberById ( id ) ;
3053
54+ // Verify the user can manage this member's committee
55+ if ( ! canManageCommittee ( user , existing . committee ) ) {
56+ return NextResponse . json (
57+ { message : "You do not have permission to manage this committee" } ,
58+ { status : 403 } ,
59+ ) ;
60+ }
61+
3162 const body = await request . json ( ) ;
3263 const parsed = updateTeamMemberRequestSchema . safeParse ( body ) ;
3364
@@ -38,6 +69,17 @@ export const PUT = permissionProtected<TeamMemberParams>(
3869 ) ;
3970 }
4071
72+ if (
73+ parsed . data . committee &&
74+ parsed . data . committee !== existing . committee &&
75+ ! canManageCommittee ( user , parsed . data . committee )
76+ ) {
77+ return NextResponse . json (
78+ { message : "You do not have permission to move members to that committee" } ,
79+ { status : 403 } ,
80+ ) ;
81+ }
82+
4183 const nextPhoto = parsed . data . photo ;
4284 const hasPhotoReplacement = ! ! nextPhoto && nextPhoto !== existing . photo ;
4385
@@ -71,10 +113,21 @@ export const PUT = permissionProtected<TeamMemberParams>(
71113) ;
72114
73115export const DELETE = permissionProtected < TeamMemberParams > (
74- [ "team:view_all " ] ,
75- async ( _request : NextRequest , { params } : RouteContext < TeamMemberParams > ) => {
116+ [ "core:manage " ] ,
117+ async ( _request : NextRequest , { params } : RouteContext < TeamMemberParams > , user ) => {
76118 try {
77119 const { id } = await params ;
120+
121+ // Fetch member first to check committee
122+ const existing = await findTeamMemberById ( id ) ;
123+
124+ if ( ! canManageCommittee ( user , existing . committee ) ) {
125+ return NextResponse . json (
126+ { message : "You do not have permission to manage this committee" } ,
127+ { status : 403 } ,
128+ ) ;
129+ }
130+
78131 const member = await deleteTeamMember ( id ) ;
79132
80133 const cloudinaryId =
0 commit comments