export default defineEventHandler(async (event) => { console.log('[api/members/[id]/keycloak-groups.put] ========================='); console.log('[api/members/[id]/keycloak-groups.put] PUT /api/members/:id/keycloak-groups - Update member Keycloak groups'); try { // Validate session and require admin privileges const sessionManager = createSessionManager(); const cookieHeader = getCookie(event, 'monacousa-session') ? getHeader(event, 'cookie') : undefined; const session = sessionManager.getSession(cookieHeader); if (!session?.user) { throw createError({ statusCode: 401, statusMessage: 'Authentication required' }); } // Require admin privileges for group management if (session.user.tier !== 'admin') { throw createError({ statusCode: 403, statusMessage: 'Admin privileges required' }); } console.log('[api/members/[id]/keycloak-groups.put] Authorized admin user:', session.user.email); // Get member ID from route parameter const memberId = getRouterParam(event, 'id'); if (!memberId) { throw createError({ statusCode: 400, statusMessage: 'Member ID is required' }); } // Parse request body const body = await readBody(event); const { newGroup } = body; console.log('[api/members/[id]/keycloak-groups.put] Processing member ID:', memberId, 'new group:', newGroup); // Validate new group if (!['user', 'board', 'admin'].includes(newGroup)) { throw createError({ statusCode: 400, statusMessage: 'Invalid group. Must be one of: user, board, admin' }); } // 1. Get member data const { getMemberById, updateMember } = await import('~/server/utils/nocodb'); const member = await getMemberById(memberId); if (!member) { throw createError({ statusCode: 404, statusMessage: 'Member not found' }); } // 2. Check if member has portal account if (!member.keycloak_id) { console.log('[api/members/[id]/keycloak-groups.put] Member has no portal account'); throw createError({ statusCode: 404, statusMessage: 'Member has no portal account' }); } console.log('[api/members/[id]/keycloak-groups.put] Found member with Keycloak ID:', member.keycloak_id); // 3. Get current group status const { createKeycloakAdminClient } = await import('~/server/utils/keycloak-admin'); const keycloakAdmin = createKeycloakAdminClient(); console.log('[api/members/[id]/keycloak-groups.put] Getting current groups...'); const currentGroups = await keycloakAdmin.getUserGroups(member.keycloak_id); const currentPrimaryGroups = currentGroups.filter(g => ['user', 'board', 'admin'].includes(g.name || '')); const currentPrimaryGroup = currentPrimaryGroups.length > 0 ? currentPrimaryGroups[0].name : 'user'; console.log('[api/members/[id]/keycloak-groups.put] Current primary group:', currentPrimaryGroup); // 4. Check if change is needed if (currentPrimaryGroup === newGroup) { console.log('[api/members/[id]/keycloak-groups.put] User already in target group, syncing database...'); // Still sync database to ensure consistency await updateMember(memberId, { portal_group: newGroup }); return { success: true, message: `User was already in ${newGroup} group. Database synced.`, data: { member_id: memberId, keycloak_id: member.keycloak_id, old_group: currentPrimaryGroup, new_group: newGroup, changed: false } }; } // 5. Change user's primary group in Keycloak console.log('[api/members/[id]/keycloak-groups.put] Changing user primary group in Keycloak...'); await keycloakAdmin.changeUserPrimaryGroup(member.keycloak_id, newGroup); // 6. Update database portal_group field console.log('[api/members/[id]/keycloak-groups.put] Updating database portal_group field...'); await updateMember(memberId, { portal_group: newGroup }); // 7. Verify the change console.log('[api/members/[id]/keycloak-groups.put] Verifying group change...'); const updatedGroups = await keycloakAdmin.getUserGroups(member.keycloak_id); const newPrimaryGroups = updatedGroups.filter(g => ['user', 'board', 'admin'].includes(g.name || '')); const verifiedNewGroup = newPrimaryGroups.length > 0 ? newPrimaryGroups[0].name : 'user'; const changeSuccessful = verifiedNewGroup === newGroup; if (changeSuccessful) { console.log('[api/members/[id]/keycloak-groups.put] ✅ Group change successful and verified'); } else { console.error('[api/members/[id]/keycloak-groups.put] ❌ Group change verification failed'); } return { success: changeSuccessful, message: changeSuccessful ? `Successfully changed user from ${currentPrimaryGroup} to ${newGroup} group` : `Group change attempted but verification failed. Current group: ${verifiedNewGroup}`, data: { member_id: memberId, keycloak_id: member.keycloak_id, old_group: currentPrimaryGroup, new_group: newGroup, verified_group: verifiedNewGroup, changed: changeSuccessful, database_updated: true } }; } catch (error: any) { console.error('[api/members/[id]/keycloak-groups.put] ❌ Failed to update member groups:', error); // If it's already an HTTP error, re-throw it if (error.statusCode) { throw error; } // Handle specific Keycloak errors if (error.message?.includes('Failed to get user groups')) { throw createError({ statusCode: 500, statusMessage: 'Failed to retrieve user groups from Keycloak. Check service account permissions.' }); } if (error.message?.includes('Invalid group name')) { throw createError({ statusCode: 400, statusMessage: error.message }); } if (error.message?.includes('Group not found')) { throw createError({ statusCode: 500, statusMessage: `Keycloak group not found. Ensure the groups 'user', 'board', and 'admin' exist in Keycloak.` }); } // Otherwise, wrap it in a generic error throw createError({ statusCode: 500, statusMessage: error.message || 'Failed to update member Keycloak groups' }); } });