import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/options"; import { NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; import { logger } from "@/lib/logger"; async function getAdminToken() { try { const tokenResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'client_credentials', client_id: process.env.KEYCLOAK_CLIENT_ID!, client_secret: process.env.KEYCLOAK_CLIENT_SECRET!, }), } ); const data = await tokenResponse.json(); if (!tokenResponse.ok || !data.access_token) { logger.error('Token Error', { error: data }); return null; } return data.access_token; } catch (error) { logger.error('Token Error', { error: error instanceof Error ? error.message : String(error) }); return null; } } /** * PATCH /api/groups/[groupId]/calendar * Updates the color of a group's calendar */ export async function PATCH( req: Request, props: { params: Promise<{ groupId: string }> } ) { const params = await props.params; try { const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); } const { color } = await req.json(); if (!color) { return NextResponse.json( { error: "La couleur est requise" }, { status: 400 } ); } // Validate color format (hex color) const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; if (!hexColorRegex.test(color)) { return NextResponse.json( { error: "Format de couleur invalide. Utilisez un code hexadécimal (ex: #FF5733)" }, { status: 400 } ); } const token = await getAdminToken(); if (!token) { return NextResponse.json( { error: "Erreur d'authentification" }, { status: 500 } ); } // Get group details from Keycloak const groupResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/groups/${params.groupId}`, { headers: { Authorization: `Bearer ${token}`, }, } ); if (!groupResponse.ok) { logger.error('Group not found', { groupId: params.groupId }); return NextResponse.json( { error: "Groupe non trouvé" }, { status: 404 } ); } const group = await groupResponse.json(); // Check if user is a member of the group const membersResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/groups/${params.groupId}/members`, { headers: { Authorization: `Bearer ${token}`, }, } ); if (membersResponse.ok) { const members = await membersResponse.json(); const isMember = members.some((member: any) => member.id === session.user.id); if (!isMember) { logger.warn('User not a member of group', { userId: session.user.id, groupId: params.groupId }); return NextResponse.json( { error: "Vous devez être membre du groupe pour modifier sa couleur" }, { status: 403 } ); } } // Find the group's calendar const calendar = await prisma.calendar.findFirst({ where: { name: `Groupe: ${group.name}`, }, }); if (!calendar) { logger.error('Calendar not found for group', { groupId: params.groupId, groupName: group.name }); return NextResponse.json( { error: "Calendrier du groupe non trouvé" }, { status: 404 } ); } // Update calendar color const updatedCalendar = await prisma.calendar.update({ where: { id: calendar.id, }, data: { color: color, }, }); logger.debug('Group calendar color updated', { groupId: params.groupId, calendarId: calendar.id, newColor: color, userId: session.user.id }); return NextResponse.json({ success: true, calendar: updatedCalendar, }); } catch (error) { logger.error('Error updating group calendar color', { groupId: params.groupId, error: error instanceof Error ? error.message : String(error) }); return NextResponse.json( { error: "Erreur lors de la mise à jour de la couleur" }, { status: 500 } ); } } /** * GET /api/groups/[groupId]/calendar * Gets the calendar associated with a group */ export async function GET( req: Request, props: { params: Promise<{ groupId: string }> } ) { const params = await props.params; try { const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); } const token = await getAdminToken(); if (!token) { return NextResponse.json( { error: "Erreur d'authentification" }, { status: 500 } ); } // Get group details from Keycloak const groupResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/groups/${params.groupId}`, { headers: { Authorization: `Bearer ${token}`, }, } ); if (!groupResponse.ok) { return NextResponse.json( { error: "Groupe non trouvé" }, { status: 404 } ); } const group = await groupResponse.json(); // Find the group's calendar const calendar = await prisma.calendar.findFirst({ where: { name: `Groupe: ${group.name}`, }, }); if (!calendar) { return NextResponse.json( { error: "Calendrier du groupe non trouvé" }, { status: 404 } ); } return NextResponse.json(calendar); } catch (error) { logger.error('Error getting group calendar', { groupId: params.groupId, error: error instanceof Error ? error.message : String(error) }); return NextResponse.json( { error: "Erreur lors de la récupération du calendrier" }, { status: 500 } ); } }