247 lines
6.3 KiB
TypeScript
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 }
|
|
);
|
|
}
|
|
}
|