NeahStable/app/api/groups/[groupId]/calendar/route.ts
2026-01-20 15:17:22 +01:00

247 lines
6.3 KiB
TypeScript

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 }
);
}
}