Refactor event handling and user management: Replace direct database calls with service layer methods for events, user profiles, and preferences, enhancing code organization and maintainability. Update API routes to utilize new services for event registration, feedback, and user statistics, ensuring a consistent approach across the application.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { sitePreferencesService } from "@/services/preferences/site-preferences.service";
|
||||
import { Role } from "@/prisma/generated/prisma/client";
|
||||
import NavigationWrapper from "@/components/NavigationWrapper";
|
||||
import AdminPanel from "@/components/AdminPanel";
|
||||
@@ -18,22 +18,9 @@ export default async function AdminPage() {
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
// Récupérer les préférences globales du site
|
||||
let sitePreferences = await prisma.sitePreferences.findUnique({
|
||||
where: { id: "global" },
|
||||
});
|
||||
|
||||
// Si elles n'existent pas, créer une entrée par défaut
|
||||
if (!sitePreferences) {
|
||||
sitePreferences = await prisma.sitePreferences.create({
|
||||
data: {
|
||||
id: "global",
|
||||
homeBackground: null,
|
||||
eventsBackground: null,
|
||||
leaderboardBackground: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
// Récupérer les préférences globales du site (ou créer si elles n'existent pas)
|
||||
const sitePreferences =
|
||||
await sitePreferencesService.getOrCreateSitePreferences();
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-black relative">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { Role, EventType } from "@/prisma/generated/prisma/client";
|
||||
import { eventService } from "@/services/events/event.service";
|
||||
import { Role } from "@/prisma/generated/prisma/client";
|
||||
import { ValidationError, NotFoundError } from "@/services/errors";
|
||||
|
||||
export async function PUT(
|
||||
request: Request,
|
||||
@@ -19,63 +20,27 @@ export async function PUT(
|
||||
const { date, name, description, type, room, time, maxPlaces } = body;
|
||||
// Le statut est ignoré s'il est fourni, il sera calculé automatiquement
|
||||
|
||||
// Vérifier que l'événement existe
|
||||
const existingEvent = await prisma.event.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!existingEvent) {
|
||||
return NextResponse.json(
|
||||
{ error: "Événement non trouvé" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
const updateData: {
|
||||
date?: Date;
|
||||
name?: string;
|
||||
description?: string;
|
||||
type?: EventType;
|
||||
room?: string | null;
|
||||
time?: string | null;
|
||||
maxPlaces?: number | null;
|
||||
} = {};
|
||||
|
||||
if (date !== undefined) {
|
||||
const eventDate = new Date(date);
|
||||
if (isNaN(eventDate.getTime())) {
|
||||
return NextResponse.json(
|
||||
{ error: "Format de date invalide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
updateData.date = eventDate;
|
||||
}
|
||||
if (name !== undefined) updateData.name = name;
|
||||
if (description !== undefined) updateData.description = description;
|
||||
if (type !== undefined) {
|
||||
if (!Object.values(EventType).includes(type)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Type d'événement invalide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
updateData.type = type as EventType;
|
||||
}
|
||||
// Le statut est toujours calculé automatiquement, on ignore s'il est fourni
|
||||
if (room !== undefined) updateData.room = room || null;
|
||||
if (time !== undefined) updateData.time = time || null;
|
||||
if (maxPlaces !== undefined)
|
||||
updateData.maxPlaces = maxPlaces ? parseInt(maxPlaces) : null;
|
||||
|
||||
const event = await prisma.event.update({
|
||||
where: { id },
|
||||
data: updateData,
|
||||
const event = await eventService.validateAndUpdateEvent(id, {
|
||||
date,
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
room,
|
||||
time,
|
||||
maxPlaces,
|
||||
});
|
||||
|
||||
return NextResponse.json(event);
|
||||
} catch (error) {
|
||||
console.error("Error updating event:", error);
|
||||
|
||||
if (error instanceof ValidationError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Erreur lors de la mise à jour de l'événement" },
|
||||
{ status: 500 }
|
||||
@@ -97,9 +62,7 @@ export async function DELETE(
|
||||
const { id } = await params;
|
||||
|
||||
// Vérifier que l'événement existe
|
||||
const existingEvent = await prisma.event.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
const existingEvent = await eventService.getEventById(id);
|
||||
|
||||
if (!existingEvent) {
|
||||
return NextResponse.json(
|
||||
@@ -108,9 +71,7 @@ export async function DELETE(
|
||||
);
|
||||
}
|
||||
|
||||
await prisma.event.delete({
|
||||
where: { id },
|
||||
});
|
||||
await eventService.deleteEvent(id);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { Role, EventType } from "@/prisma/generated/prisma/client";
|
||||
import { calculateEventStatus } from "@/lib/eventStatus";
|
||||
import { eventService } from "@/services/events/event.service";
|
||||
import { Role } from "@/prisma/generated/prisma/client";
|
||||
import { ValidationError } from "@/services/errors";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
@@ -12,34 +12,22 @@ export async function GET() {
|
||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
||||
}
|
||||
|
||||
const events = await prisma.event.findMany({
|
||||
orderBy: {
|
||||
date: "desc",
|
||||
},
|
||||
include: {
|
||||
_count: {
|
||||
select: {
|
||||
registrations: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const events = await eventService.getEventsWithStatus();
|
||||
|
||||
// Transformer les données pour inclure le nombre d'inscriptions
|
||||
// Le statut est calculé automatiquement en fonction de la date
|
||||
// Transformer les données pour la sérialisation
|
||||
const eventsWithCount = events.map((event) => ({
|
||||
id: event.id,
|
||||
date: event.date.toISOString(),
|
||||
name: event.name,
|
||||
description: event.description,
|
||||
type: event.type,
|
||||
status: calculateEventStatus(event.date),
|
||||
status: event.status,
|
||||
room: event.room,
|
||||
time: event.time,
|
||||
maxPlaces: event.maxPlaces,
|
||||
createdAt: event.createdAt.toISOString(),
|
||||
updatedAt: event.updatedAt.toISOString(),
|
||||
registrationsCount: event._count.registrations,
|
||||
registrationsCount: event.registrationsCount,
|
||||
}));
|
||||
|
||||
return NextResponse.json(eventsWithCount);
|
||||
@@ -63,45 +51,24 @@ export async function POST(request: Request) {
|
||||
const body = await request.json();
|
||||
const { date, name, description, type, room, time, maxPlaces } = body;
|
||||
|
||||
if (!date || !name || !description || !type) {
|
||||
return NextResponse.json(
|
||||
{ error: "Tous les champs sont requis" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Convertir la date string en Date object
|
||||
const eventDate = new Date(date);
|
||||
if (isNaN(eventDate.getTime())) {
|
||||
return NextResponse.json(
|
||||
{ error: "Format de date invalide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Valider les enums
|
||||
if (!Object.values(EventType).includes(type)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Type d'événement invalide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const event = await prisma.event.create({
|
||||
data: {
|
||||
date: eventDate,
|
||||
name,
|
||||
description,
|
||||
type: type as EventType,
|
||||
room: room || null,
|
||||
time: time || null,
|
||||
maxPlaces: maxPlaces ? parseInt(maxPlaces) : null,
|
||||
},
|
||||
const event = await eventService.validateAndCreateEvent({
|
||||
date,
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
room,
|
||||
time,
|
||||
maxPlaces,
|
||||
});
|
||||
|
||||
return NextResponse.json(event);
|
||||
} catch (error) {
|
||||
console.error("Error creating event:", error);
|
||||
|
||||
if (error instanceof ValidationError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Erreur lors de la création de l'événement" },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { eventFeedbackService } from "@/services/events/event-feedback.service";
|
||||
import { Role } from "@/prisma/generated/prisma/client";
|
||||
|
||||
export async function GET() {
|
||||
@@ -15,72 +15,14 @@ export async function GET() {
|
||||
}
|
||||
|
||||
// Récupérer tous les feedbacks avec les détails de l'événement et de l'utilisateur
|
||||
const feedbacks = await prisma.eventFeedback.findMany({
|
||||
include: {
|
||||
event: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
date: true,
|
||||
type: true,
|
||||
},
|
||||
},
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
});
|
||||
const feedbacks = await eventFeedbackService.getAllFeedbacks();
|
||||
|
||||
// Calculer les statistiques par événement
|
||||
const eventStats = await prisma.eventFeedback.groupBy({
|
||||
by: ["eventId"],
|
||||
_avg: {
|
||||
rating: true,
|
||||
},
|
||||
_count: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Récupérer les détails des événements pour les stats
|
||||
const eventIds = eventStats.map((stat) => stat.eventId);
|
||||
const events = await prisma.event.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: eventIds,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
date: true,
|
||||
type: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Combiner les stats avec les détails des événements
|
||||
const statsWithDetails = eventStats.map((stat) => {
|
||||
const event = events.find((e) => e.id === stat.eventId);
|
||||
return {
|
||||
eventId: stat.eventId,
|
||||
eventName: event?.name || "Événement supprimé",
|
||||
eventDate: event?.date || null,
|
||||
eventType: event?.type || null,
|
||||
averageRating: stat._avg.rating || 0,
|
||||
feedbackCount: stat._count.id,
|
||||
};
|
||||
});
|
||||
const statistics = await eventFeedbackService.getFeedbackStatistics();
|
||||
|
||||
return NextResponse.json({
|
||||
feedbacks,
|
||||
statistics: statsWithDetails,
|
||||
statistics,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching feedbacks:", error);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { sitePreferencesService } from "@/services/preferences/site-preferences.service";
|
||||
import { Role } from "@/prisma/generated/prisma/client";
|
||||
|
||||
export async function GET() {
|
||||
@@ -11,22 +11,8 @@ export async function GET() {
|
||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
||||
}
|
||||
|
||||
// Récupérer les préférences globales du site
|
||||
let sitePreferences = await prisma.sitePreferences.findUnique({
|
||||
where: { id: "global" },
|
||||
});
|
||||
|
||||
// Si elles n'existent pas, créer une entrée par défaut
|
||||
if (!sitePreferences) {
|
||||
sitePreferences = await prisma.sitePreferences.create({
|
||||
data: {
|
||||
id: "global",
|
||||
homeBackground: null,
|
||||
eventsBackground: null,
|
||||
leaderboardBackground: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
// Récupérer les préférences globales du site (ou créer si elles n'existent pas)
|
||||
const sitePreferences = await sitePreferencesService.getOrCreateSitePreferences();
|
||||
|
||||
return NextResponse.json(sitePreferences);
|
||||
} catch (error) {
|
||||
@@ -49,26 +35,10 @@ export async function PUT(request: Request) {
|
||||
const body = await request.json();
|
||||
const { homeBackground, eventsBackground, leaderboardBackground } = body;
|
||||
|
||||
const preferences = await prisma.sitePreferences.upsert({
|
||||
where: { id: "global" },
|
||||
update: {
|
||||
homeBackground:
|
||||
homeBackground === "" ? null : (homeBackground ?? undefined),
|
||||
eventsBackground:
|
||||
eventsBackground === "" ? null : (eventsBackground ?? undefined),
|
||||
leaderboardBackground:
|
||||
leaderboardBackground === ""
|
||||
? null
|
||||
: (leaderboardBackground ?? undefined),
|
||||
},
|
||||
create: {
|
||||
id: "global",
|
||||
homeBackground: homeBackground === "" ? null : (homeBackground ?? null),
|
||||
eventsBackground:
|
||||
eventsBackground === "" ? null : (eventsBackground ?? null),
|
||||
leaderboardBackground:
|
||||
leaderboardBackground === "" ? null : (leaderboardBackground ?? null),
|
||||
},
|
||||
const preferences = await sitePreferencesService.updateSitePreferences({
|
||||
homeBackground,
|
||||
eventsBackground,
|
||||
leaderboardBackground,
|
||||
});
|
||||
|
||||
return NextResponse.json(preferences);
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { userService } from "@/services/users/user.service";
|
||||
import { userStatsService } from "@/services/users/user-stats.service";
|
||||
import { Role } from "@/prisma/generated/prisma/client";
|
||||
import {
|
||||
ValidationError,
|
||||
NotFoundError,
|
||||
ConflictError,
|
||||
} from "@/services/errors";
|
||||
|
||||
export async function PUT(
|
||||
request: Request,
|
||||
@@ -18,157 +24,26 @@ export async function PUT(
|
||||
const body = await request.json();
|
||||
const { username, avatar, hpDelta, xpDelta, score, level, role } = body;
|
||||
|
||||
// Récupérer l'utilisateur actuel
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: "Utilisateur non trouvé" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Calculer les nouvelles valeurs
|
||||
let newHp = user.hp;
|
||||
let newXp = user.xp;
|
||||
let newLevel = user.level;
|
||||
let newMaxXp = user.maxXp;
|
||||
|
||||
// Appliquer les changements de HP
|
||||
if (hpDelta !== undefined) {
|
||||
newHp = Math.max(0, Math.min(user.maxHp, user.hp + hpDelta));
|
||||
}
|
||||
|
||||
// Appliquer les changements de XP
|
||||
if (xpDelta !== undefined) {
|
||||
newXp = user.xp + xpDelta;
|
||||
newLevel = user.level;
|
||||
newMaxXp = user.maxXp;
|
||||
|
||||
// Gérer le niveau up si nécessaire (quand on ajoute de l'XP)
|
||||
if (newXp >= newMaxXp && newXp > 0) {
|
||||
while (newXp >= newMaxXp) {
|
||||
newXp -= newMaxXp;
|
||||
newLevel += 1;
|
||||
// Augmenter le maxXp pour le prochain niveau (formule simple)
|
||||
newMaxXp = Math.floor(newMaxXp * 1.2);
|
||||
}
|
||||
}
|
||||
|
||||
// Gérer le niveau down si nécessaire (quand on enlève de l'XP)
|
||||
if (newXp < 0 && newLevel > 1) {
|
||||
while (newXp < 0 && newLevel > 1) {
|
||||
newLevel -= 1;
|
||||
// Calculer le maxXp du niveau précédent
|
||||
newMaxXp = Math.floor(newMaxXp / 1.2);
|
||||
newXp += newMaxXp;
|
||||
}
|
||||
// S'assurer que l'XP ne peut pas être négative
|
||||
newXp = Math.max(0, newXp);
|
||||
}
|
||||
|
||||
// S'assurer que le niveau minimum est 1
|
||||
if (newLevel < 1) {
|
||||
newLevel = 1;
|
||||
newXp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Appliquer les changements directs (username, avatar, score, level, role)
|
||||
const updateData: {
|
||||
hp: number;
|
||||
xp: number;
|
||||
level: number;
|
||||
maxXp: number;
|
||||
username?: string;
|
||||
avatar?: string | null;
|
||||
score?: number;
|
||||
role?: Role;
|
||||
} = {
|
||||
hp: newHp,
|
||||
xp: newXp,
|
||||
level: newLevel,
|
||||
maxXp: newMaxXp,
|
||||
};
|
||||
|
||||
// Validation et mise à jour du username
|
||||
// Valider username si fourni
|
||||
if (username !== undefined) {
|
||||
if (typeof username !== "string" || username.trim().length === 0) {
|
||||
return NextResponse.json(
|
||||
{ error: "Le nom d'utilisateur ne peut pas être vide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (username.length < 3 || username.length > 20) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error:
|
||||
"Le nom d'utilisateur doit contenir entre 3 et 20 caractères",
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier si le username est déjà pris par un autre utilisateur
|
||||
const existingUser = await prisma.user.findFirst({
|
||||
where: {
|
||||
username: username.trim(),
|
||||
NOT: { id },
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
{ error: "Ce nom d'utilisateur est déjà pris" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
updateData.username = username.trim();
|
||||
}
|
||||
|
||||
// Mise à jour de l'avatar
|
||||
if (avatar !== undefined) {
|
||||
updateData.avatar = avatar || null;
|
||||
}
|
||||
|
||||
if (score !== undefined) {
|
||||
updateData.score = Math.max(0, score);
|
||||
}
|
||||
|
||||
if (level !== undefined) {
|
||||
// Si le niveau est modifié directement, utiliser cette valeur
|
||||
const targetLevel = Math.max(1, level);
|
||||
updateData.level = targetLevel;
|
||||
|
||||
// Recalculer le maxXp pour le nouveau niveau
|
||||
// Formule: maxXp = 5000 * (1.2 ^ (level - 1))
|
||||
let calculatedMaxXp = 5000;
|
||||
for (let i = 1; i < targetLevel; i++) {
|
||||
calculatedMaxXp = Math.floor(calculatedMaxXp * 1.2);
|
||||
}
|
||||
updateData.maxXp = calculatedMaxXp;
|
||||
|
||||
// Réinitialiser l'XP si le niveau change directement (sauf si on modifie aussi l'XP)
|
||||
if (targetLevel !== user.level && xpDelta === undefined) {
|
||||
updateData.xp = 0;
|
||||
try {
|
||||
await userService.validateAndUpdateUserProfile(id, { username });
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof ValidationError ||
|
||||
error instanceof ConflictError
|
||||
) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (role !== undefined) {
|
||||
if (role === "ADMIN" || role === "USER") {
|
||||
updateData.role = role as Role;
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour l'utilisateur
|
||||
const updatedUser = await prisma.user.update({
|
||||
where: { id },
|
||||
data: updateData,
|
||||
select: {
|
||||
// Mettre à jour stats et profil
|
||||
const updatedUser = await userStatsService.updateUserStatsAndProfile(
|
||||
id,
|
||||
{ username, avatar, hpDelta, xpDelta, score, level, role },
|
||||
{
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
@@ -180,8 +55,8 @@ export async function PUT(
|
||||
xp: true,
|
||||
maxXp: true,
|
||||
avatar: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return NextResponse.json(updatedUser);
|
||||
} catch (error) {
|
||||
@@ -206,34 +81,19 @@ export async function DELETE(
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
// Vérifier que l'utilisateur existe
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: "Utilisateur non trouvé" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Empêcher la suppression de soi-même
|
||||
if (user.id === session.user.id) {
|
||||
return NextResponse.json(
|
||||
{ error: "Vous ne pouvez pas supprimer votre propre compte" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Supprimer l'utilisateur (les relations seront supprimées en cascade)
|
||||
await prisma.user.delete({
|
||||
where: { id },
|
||||
});
|
||||
await userService.validateAndDeleteUser(id, session.user.id);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("Error deleting user:", error);
|
||||
|
||||
if (error instanceof ValidationError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Erreur lors de la suppression de l'utilisateur" },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { userService } from "@/services/users/user.service";
|
||||
import { Role } from "@/prisma/generated/prisma/client";
|
||||
|
||||
export async function GET() {
|
||||
@@ -12,7 +12,10 @@ export async function GET() {
|
||||
}
|
||||
|
||||
// Récupérer tous les utilisateurs avec leurs stats
|
||||
const users = await prisma.user.findMany({
|
||||
const users = await userService.getAllUsers({
|
||||
orderBy: {
|
||||
score: "desc",
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
@@ -27,9 +30,6 @@ export async function GET() {
|
||||
avatar: true,
|
||||
createdAt: true,
|
||||
},
|
||||
orderBy: {
|
||||
score: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(users);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { calculateEventStatus } from "@/lib/eventStatus";
|
||||
import { eventRegistrationService } from "@/services/events/event-registration.service";
|
||||
import {
|
||||
ValidationError,
|
||||
NotFoundError,
|
||||
ConflictError,
|
||||
} from "@/services/errors";
|
||||
|
||||
export async function POST(
|
||||
request: Request,
|
||||
@@ -19,50 +23,10 @@ export async function POST(
|
||||
|
||||
const { id: eventId } = await params;
|
||||
|
||||
// Vérifier si l'événement existe
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { id: eventId },
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
return NextResponse.json(
|
||||
{ error: "Événement introuvable" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
const eventStatus = calculateEventStatus(event.date);
|
||||
if (eventStatus !== "UPCOMING") {
|
||||
return NextResponse.json(
|
||||
{ error: "Vous ne pouvez vous inscrire qu'aux événements à venir" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier si l'utilisateur est déjà inscrit
|
||||
const existingRegistration = await prisma.eventRegistration.findUnique({
|
||||
where: {
|
||||
userId_eventId: {
|
||||
userId: session.user.id,
|
||||
eventId: eventId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (existingRegistration) {
|
||||
return NextResponse.json(
|
||||
{ error: "Vous êtes déjà inscrit à cet événement" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Créer l'inscription
|
||||
const registration = await prisma.eventRegistration.create({
|
||||
data: {
|
||||
userId: session.user.id,
|
||||
eventId: eventId,
|
||||
},
|
||||
});
|
||||
const registration = await eventRegistrationService.validateAndRegisterUser(
|
||||
session.user.id,
|
||||
eventId
|
||||
);
|
||||
|
||||
return NextResponse.json(
|
||||
{ message: "Inscription réussie", registration },
|
||||
@@ -70,6 +34,17 @@ export async function POST(
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Registration error:", error);
|
||||
|
||||
if (
|
||||
error instanceof ValidationError ||
|
||||
error instanceof ConflictError
|
||||
) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Une erreur est survenue lors de l'inscription" },
|
||||
{ status: 500 }
|
||||
@@ -94,12 +69,10 @@ export async function DELETE(
|
||||
const { id: eventId } = await params;
|
||||
|
||||
// Supprimer l'inscription
|
||||
await prisma.eventRegistration.deleteMany({
|
||||
where: {
|
||||
userId: session.user.id,
|
||||
eventId: eventId,
|
||||
},
|
||||
});
|
||||
await eventRegistrationService.unregisterUserFromEvent(
|
||||
session.user.id,
|
||||
eventId
|
||||
);
|
||||
|
||||
return NextResponse.json({ message: "Inscription annulée" });
|
||||
} catch (error) {
|
||||
@@ -124,16 +97,12 @@ export async function GET(
|
||||
|
||||
const { id: eventId } = await params;
|
||||
|
||||
const registration = await prisma.eventRegistration.findUnique({
|
||||
where: {
|
||||
userId_eventId: {
|
||||
userId: session.user.id,
|
||||
eventId: eventId,
|
||||
},
|
||||
},
|
||||
});
|
||||
const isRegistered = await eventRegistrationService.checkUserRegistration(
|
||||
session.user.id,
|
||||
eventId
|
||||
);
|
||||
|
||||
return NextResponse.json({ registered: !!registration });
|
||||
return NextResponse.json({ registered: isRegistered });
|
||||
} catch (error) {
|
||||
console.error("Check registration error:", error);
|
||||
return NextResponse.json({ registered: false });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { eventService } from "@/services/events/event.service";
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
@@ -8,9 +8,7 @@ export async function GET(
|
||||
try {
|
||||
const { id } = await params;
|
||||
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
const event = await eventService.getEventById(id);
|
||||
|
||||
if (!event) {
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { eventService } from "@/services/events/event.service";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const events = await prisma.event.findMany({
|
||||
orderBy: {
|
||||
date: "asc",
|
||||
},
|
||||
const events = await eventService.getAllEvents({
|
||||
orderBy: { date: "asc" },
|
||||
});
|
||||
|
||||
return NextResponse.json(events);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { eventFeedbackService } from "@/services/events/event-feedback.service";
|
||||
import {
|
||||
ValidationError,
|
||||
NotFoundError,
|
||||
} from "@/services/errors";
|
||||
|
||||
export async function POST(
|
||||
request: Request,
|
||||
@@ -16,49 +20,23 @@ export async function POST(
|
||||
const body = await request.json();
|
||||
const { rating, comment } = body;
|
||||
|
||||
// Valider la note (1-5)
|
||||
if (!rating || rating < 1 || rating > 5) {
|
||||
return NextResponse.json(
|
||||
{ error: "La note doit être entre 1 et 5" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier que l'événement existe
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { id: eventId },
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
return NextResponse.json(
|
||||
{ error: "Événement introuvable" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Créer ou mettre à jour le feedback (unique par utilisateur/événement)
|
||||
const feedback = await prisma.eventFeedback.upsert({
|
||||
where: {
|
||||
userId_eventId: {
|
||||
userId: session.user.id,
|
||||
eventId,
|
||||
},
|
||||
},
|
||||
update: {
|
||||
rating,
|
||||
comment: comment || null,
|
||||
},
|
||||
create: {
|
||||
userId: session.user.id,
|
||||
eventId,
|
||||
rating,
|
||||
comment: comment || null,
|
||||
},
|
||||
});
|
||||
const feedback = await eventFeedbackService.validateAndCreateFeedback(
|
||||
session.user.id,
|
||||
eventId,
|
||||
{ rating, comment }
|
||||
);
|
||||
|
||||
return NextResponse.json({ success: true, feedback });
|
||||
} catch (error) {
|
||||
console.error("Error saving feedback:", error);
|
||||
|
||||
if (error instanceof ValidationError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Erreur lors de l'enregistrement du feedback" },
|
||||
{ status: 500 }
|
||||
@@ -79,23 +57,10 @@ export async function GET(
|
||||
const { eventId } = await params;
|
||||
|
||||
// Récupérer le feedback de l'utilisateur pour cet événement
|
||||
const feedback = await prisma.eventFeedback.findUnique({
|
||||
where: {
|
||||
userId_eventId: {
|
||||
userId: session.user.id,
|
||||
eventId,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
event: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
date: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const feedback = await eventFeedbackService.getUserFeedback(
|
||||
session.user.id,
|
||||
eventId
|
||||
);
|
||||
|
||||
return NextResponse.json({ feedback });
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,49 +1,9 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { userStatsService } from "@/services/users/user-stats.service";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const users = await prisma.user.findMany({
|
||||
orderBy: {
|
||||
score: "desc",
|
||||
},
|
||||
take: 10,
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
score: true,
|
||||
level: true,
|
||||
avatar: true,
|
||||
bio: true,
|
||||
characterClass: true,
|
||||
},
|
||||
});
|
||||
|
||||
const leaderboard = users.map(
|
||||
(
|
||||
user: {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
score: number;
|
||||
level: number;
|
||||
avatar: string | null;
|
||||
bio: string | null;
|
||||
characterClass: string | null;
|
||||
},
|
||||
index: number
|
||||
) => ({
|
||||
rank: index + 1,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
score: user.score,
|
||||
level: user.level,
|
||||
avatar: user.avatar,
|
||||
bio: user.bio,
|
||||
characterClass: user.characterClass,
|
||||
})
|
||||
);
|
||||
const leaderboard = await userStatsService.getLeaderboard(10);
|
||||
|
||||
return NextResponse.json(leaderboard);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { sitePreferencesService } from "@/services/preferences/site-preferences.service";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Récupérer les préférences globales du site (pas besoin d'authentification)
|
||||
let sitePreferences = await prisma.sitePreferences.findUnique({
|
||||
where: { id: "global" },
|
||||
});
|
||||
const sitePreferences = await sitePreferencesService.getSitePreferences();
|
||||
|
||||
// Si elles n'existent pas, retourner des valeurs par défaut
|
||||
if (!sitePreferences) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { userService } from "@/services/users/user.service";
|
||||
import { ValidationError, NotFoundError } from "@/services/errors";
|
||||
|
||||
export async function PUT(request: Request) {
|
||||
try {
|
||||
@@ -14,68 +14,24 @@ export async function PUT(request: Request) {
|
||||
const body = await request.json();
|
||||
const { currentPassword, newPassword, confirmPassword } = body;
|
||||
|
||||
// Validation
|
||||
if (!currentPassword || !newPassword || !confirmPassword) {
|
||||
return NextResponse.json(
|
||||
{ error: "Tous les champs sont requis" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (newPassword.length < 6) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Le nouveau mot de passe doit contenir au moins 6 caractères",
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
return NextResponse.json(
|
||||
{ error: "Les mots de passe ne correspondent pas" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Récupérer l'utilisateur avec le mot de passe
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
select: { password: true },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: "Utilisateur non trouvé" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier l'ancien mot de passe
|
||||
const isPasswordValid = await bcrypt.compare(
|
||||
await userService.validateAndUpdatePassword(
|
||||
session.user.id,
|
||||
currentPassword,
|
||||
user.password
|
||||
newPassword,
|
||||
confirmPassword
|
||||
);
|
||||
|
||||
if (!isPasswordValid) {
|
||||
return NextResponse.json(
|
||||
{ error: "Mot de passe actuel incorrect" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Hasher le nouveau mot de passe
|
||||
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
||||
|
||||
// Mettre à jour le mot de passe
|
||||
await prisma.user.update({
|
||||
where: { id: session.user.id },
|
||||
data: { password: hashedPassword },
|
||||
});
|
||||
|
||||
return NextResponse.json({ message: "Mot de passe modifié avec succès" });
|
||||
} catch (error) {
|
||||
console.error("Error updating password:", error);
|
||||
|
||||
if (error instanceof ValidationError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Erreur lors de la modification du mot de passe" },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { CharacterClass } from "@/prisma/generated/prisma/enums";
|
||||
import { userService } from "@/services/users/user.service";
|
||||
import {
|
||||
ValidationError,
|
||||
ConflictError,
|
||||
NotFoundError,
|
||||
} from "@/services/errors";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
@@ -11,23 +15,20 @@ export async function GET() {
|
||||
return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
avatar: true,
|
||||
bio: true,
|
||||
characterClass: true,
|
||||
hp: true,
|
||||
maxHp: true,
|
||||
xp: true,
|
||||
maxXp: true,
|
||||
level: true,
|
||||
score: true,
|
||||
createdAt: true,
|
||||
},
|
||||
const user = await userService.getUserById(session.user.id, {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
avatar: true,
|
||||
bio: true,
|
||||
characterClass: true,
|
||||
hp: true,
|
||||
maxHp: true,
|
||||
xp: true,
|
||||
maxXp: true,
|
||||
level: true,
|
||||
score: true,
|
||||
createdAt: true,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@@ -58,103 +59,10 @@ export async function PUT(request: Request) {
|
||||
const body = await request.json();
|
||||
const { username, avatar, bio, characterClass } = body;
|
||||
|
||||
// Validation
|
||||
if (username !== undefined) {
|
||||
if (typeof username !== "string" || username.trim().length === 0) {
|
||||
return NextResponse.json(
|
||||
{ error: "Le nom d'utilisateur ne peut pas être vide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (username.length < 3 || username.length > 20) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error:
|
||||
"Le nom d'utilisateur doit contenir entre 3 et 20 caractères",
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier si le username est déjà pris par un autre utilisateur
|
||||
const existingUser = await prisma.user.findFirst({
|
||||
where: {
|
||||
username: username.trim(),
|
||||
NOT: { id: session.user.id },
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
{ error: "Ce nom d'utilisateur est déjà pris" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validation bio
|
||||
if (bio !== undefined && bio !== null) {
|
||||
if (typeof bio !== "string") {
|
||||
return NextResponse.json(
|
||||
{ error: "La bio doit être une chaîne de caractères" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
if (bio.length > 500) {
|
||||
return NextResponse.json(
|
||||
{ error: "La bio ne peut pas dépasser 500 caractères" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validation characterClass
|
||||
const validClasses = [
|
||||
"WARRIOR",
|
||||
"MAGE",
|
||||
"ROGUE",
|
||||
"RANGER",
|
||||
"PALADIN",
|
||||
"ENGINEER",
|
||||
"MERCHANT",
|
||||
"SCHOLAR",
|
||||
"BERSERKER",
|
||||
"NECROMANCER",
|
||||
];
|
||||
if (characterClass !== undefined && characterClass !== null) {
|
||||
if (!validClasses.includes(characterClass)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Classe de personnage invalide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour l'utilisateur
|
||||
const updateData: {
|
||||
username?: string;
|
||||
avatar?: string | null;
|
||||
bio?: string | null;
|
||||
characterClass?: CharacterClass | null;
|
||||
} = {};
|
||||
if (username !== undefined) {
|
||||
updateData.username = username.trim();
|
||||
}
|
||||
if (avatar !== undefined) {
|
||||
updateData.avatar = avatar || null;
|
||||
}
|
||||
if (bio !== undefined) {
|
||||
updateData.bio = bio === null ? null : bio.trim() || null;
|
||||
}
|
||||
if (characterClass !== undefined) {
|
||||
updateData.characterClass = (characterClass as CharacterClass) || null;
|
||||
}
|
||||
|
||||
const updatedUser = await prisma.user.update({
|
||||
where: { id: session.user.id },
|
||||
data: updateData,
|
||||
select: {
|
||||
const updatedUser = await userService.validateAndUpdateUserProfile(
|
||||
session.user.id,
|
||||
{ username, avatar, bio, characterClass },
|
||||
{
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
@@ -167,12 +75,17 @@ export async function PUT(request: Request) {
|
||||
maxXp: true,
|
||||
level: true,
|
||||
score: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return NextResponse.json(updatedUser);
|
||||
} catch (error) {
|
||||
console.error("Error updating profile:", error);
|
||||
|
||||
if (error instanceof ValidationError || error instanceof ConflictError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Erreur lors de la mise à jour du profil" },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { CharacterClass } from "@/prisma/generated/prisma/enums";
|
||||
import { userService } from "@/services/users/user.service";
|
||||
import {
|
||||
ValidationError,
|
||||
NotFoundError,
|
||||
ConflictError,
|
||||
} from "@/services/errors";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
@@ -14,139 +18,10 @@ export async function POST(request: Request) {
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier que l'utilisateur existe et a été créé récemment (dans les 10 dernières minutes)
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: "Utilisateur non trouvé" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier que le compte a été créé récemment (dans les 10 dernières minutes)
|
||||
const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000);
|
||||
if (user.createdAt < tenMinutesAgo) {
|
||||
return NextResponse.json(
|
||||
{ error: "Temps écoulé pour finaliser l'inscription" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validation username
|
||||
if (username !== undefined) {
|
||||
if (typeof username !== "string" || username.trim().length === 0) {
|
||||
return NextResponse.json(
|
||||
{ error: "Le nom d'utilisateur ne peut pas être vide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (username.length < 3 || username.length > 20) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error:
|
||||
"Le nom d'utilisateur doit contenir entre 3 et 20 caractères",
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier si le username est déjà pris par un autre utilisateur
|
||||
const existingUser = await prisma.user.findFirst({
|
||||
where: {
|
||||
username: username.trim(),
|
||||
NOT: { id: userId },
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
{ error: "Ce nom d'utilisateur est déjà pris" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validation bio
|
||||
if (bio !== undefined && bio !== null) {
|
||||
if (typeof bio !== "string") {
|
||||
return NextResponse.json(
|
||||
{ error: "La bio doit être une chaîne de caractères" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
if (bio.length > 500) {
|
||||
return NextResponse.json(
|
||||
{ error: "La bio ne peut pas dépasser 500 caractères" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validation characterClass
|
||||
const validClasses = [
|
||||
"WARRIOR",
|
||||
"MAGE",
|
||||
"ROGUE",
|
||||
"RANGER",
|
||||
"PALADIN",
|
||||
"ENGINEER",
|
||||
"MERCHANT",
|
||||
"SCHOLAR",
|
||||
"BERSERKER",
|
||||
"NECROMANCER",
|
||||
];
|
||||
if (characterClass !== undefined && characterClass !== null) {
|
||||
if (!validClasses.includes(characterClass)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Classe de personnage invalide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour l'utilisateur
|
||||
const updateData: {
|
||||
username?: string;
|
||||
avatar?: string | null;
|
||||
bio?: string | null;
|
||||
characterClass?: CharacterClass | null;
|
||||
} = {};
|
||||
|
||||
if (username !== undefined) {
|
||||
updateData.username = username.trim();
|
||||
}
|
||||
if (avatar !== undefined) {
|
||||
updateData.avatar = avatar || null;
|
||||
}
|
||||
if (bio !== undefined) {
|
||||
if (bio === null || bio === "") {
|
||||
updateData.bio = null;
|
||||
} else if (typeof bio === "string") {
|
||||
updateData.bio = bio.trim() || null;
|
||||
} else {
|
||||
updateData.bio = null;
|
||||
}
|
||||
}
|
||||
if (characterClass !== undefined) {
|
||||
updateData.characterClass = (characterClass as CharacterClass) || null;
|
||||
}
|
||||
|
||||
// Si aucun champ à mettre à jour, retourner succès quand même
|
||||
if (Object.keys(updateData).length === 0) {
|
||||
return NextResponse.json({
|
||||
message: "Profil finalisé avec succès",
|
||||
userId: user.id,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedUser = await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: updateData,
|
||||
});
|
||||
const updatedUser = await userService.validateAndCompleteRegistration(
|
||||
userId,
|
||||
{ username, avatar, bio, characterClass }
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
message: "Profil finalisé avec succès",
|
||||
@@ -154,11 +29,20 @@ export async function POST(request: Request) {
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error completing registration:", error);
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Erreur inconnue";
|
||||
|
||||
if (
|
||||
error instanceof ValidationError ||
|
||||
error instanceof ConflictError
|
||||
) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: `Erreur lors de la finalisation de l'inscription: ${errorMessage}`,
|
||||
error: `Erreur lors de la finalisation de l'inscription: ${error instanceof Error ? error.message : "Erreur inconnue"}`,
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
|
||||
@@ -1,73 +1,19 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { userService } from "@/services/users/user.service";
|
||||
import { ValidationError, ConflictError } from "@/services/errors";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { email, username, password, bio, characterClass, avatar } = body;
|
||||
|
||||
if (!email || !username || !password) {
|
||||
return NextResponse.json(
|
||||
{ error: "Email, nom d'utilisateur et mot de passe sont requis" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
return NextResponse.json(
|
||||
{ error: "Le mot de passe doit contenir au moins 6 caractères" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Valider characterClass si fourni
|
||||
const validCharacterClasses = [
|
||||
"WARRIOR",
|
||||
"MAGE",
|
||||
"ROGUE",
|
||||
"RANGER",
|
||||
"PALADIN",
|
||||
"ENGINEER",
|
||||
"MERCHANT",
|
||||
"SCHOLAR",
|
||||
"BERSERKER",
|
||||
"NECROMANCER",
|
||||
];
|
||||
if (characterClass && !validCharacterClasses.includes(characterClass)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Classe de personnage invalide" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier si l'email existe déjà
|
||||
const existingUser = await prisma.user.findFirst({
|
||||
where: {
|
||||
OR: [{ email }, { username }],
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
{ error: "Cet email ou nom d'utilisateur est déjà utilisé" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Hasher le mot de passe
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// Créer l'utilisateur
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email,
|
||||
username,
|
||||
password: hashedPassword,
|
||||
bio: bio || null,
|
||||
characterClass: characterClass || null,
|
||||
avatar: avatar || null,
|
||||
},
|
||||
const user = await userService.validateAndCreateUser({
|
||||
email,
|
||||
username,
|
||||
password,
|
||||
bio,
|
||||
characterClass,
|
||||
avatar,
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
@@ -76,6 +22,11 @@ export async function POST(request: Request) {
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Registration error:", error);
|
||||
|
||||
if (error instanceof ValidationError || error instanceof ConflictError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: "Une erreur est survenue lors de l'inscription" },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { userService } from "@/services/users/user.service";
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
@@ -19,19 +19,16 @@ export async function GET(
|
||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
avatar: true,
|
||||
hp: true,
|
||||
maxHp: true,
|
||||
xp: true,
|
||||
maxXp: true,
|
||||
level: true,
|
||||
score: true,
|
||||
},
|
||||
const user = await userService.getUserById(id, {
|
||||
id: true,
|
||||
username: true,
|
||||
avatar: true,
|
||||
hp: true,
|
||||
maxHp: true,
|
||||
xp: true,
|
||||
maxXp: true,
|
||||
level: true,
|
||||
score: true,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import NavigationWrapper from "@/components/NavigationWrapper";
|
||||
import EventsPageSection from "@/components/EventsPageSection";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { eventService } from "@/services/events/event.service";
|
||||
import { eventRegistrationService } from "@/services/events/event-registration.service";
|
||||
import { getBackgroundImage } from "@/lib/preferences";
|
||||
import { auth } from "@/lib/auth";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function EventsPage() {
|
||||
const events = await prisma.event.findMany({
|
||||
orderBy: {
|
||||
date: "desc",
|
||||
},
|
||||
const events = await eventService.getAllEvents({
|
||||
orderBy: { date: "desc" },
|
||||
});
|
||||
|
||||
// Sérialiser les dates pour le client
|
||||
@@ -29,14 +28,8 @@ export default async function EventsPage() {
|
||||
|
||||
if (session?.user?.id) {
|
||||
// Récupérer toutes les inscriptions (passées et à venir) pour permettre le feedback
|
||||
const allRegistrations = await prisma.eventRegistration.findMany({
|
||||
where: {
|
||||
userId: session.user.id,
|
||||
},
|
||||
select: {
|
||||
eventId: true,
|
||||
},
|
||||
});
|
||||
const allRegistrations =
|
||||
await eventRegistrationService.getUserRegistrations(session.user.id);
|
||||
|
||||
allRegistrations.forEach((reg) => {
|
||||
initialRegistrations[reg.eventId] = true;
|
||||
|
||||
@@ -1,49 +1,12 @@
|
||||
import NavigationWrapper from "@/components/NavigationWrapper";
|
||||
import LeaderboardSection from "@/components/LeaderboardSection";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { userStatsService } from "@/services/users/user-stats.service";
|
||||
import { getBackgroundImage } from "@/lib/preferences";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
interface LeaderboardEntry {
|
||||
rank: number;
|
||||
username: string;
|
||||
email: string;
|
||||
score: number;
|
||||
level: number;
|
||||
avatar: string | null;
|
||||
bio: string | null;
|
||||
characterClass: string | null;
|
||||
}
|
||||
|
||||
export default async function LeaderboardPage() {
|
||||
const users = await prisma.user.findMany({
|
||||
orderBy: {
|
||||
score: "desc",
|
||||
},
|
||||
take: 10,
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
score: true,
|
||||
level: true,
|
||||
avatar: true,
|
||||
bio: true,
|
||||
characterClass: true,
|
||||
},
|
||||
});
|
||||
|
||||
const leaderboard: LeaderboardEntry[] = users.map((user, index) => ({
|
||||
rank: index + 1,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
score: user.score,
|
||||
level: user.level,
|
||||
avatar: user.avatar,
|
||||
bio: user.bio,
|
||||
characterClass: user.characterClass,
|
||||
}));
|
||||
const leaderboard = await userStatsService.getLeaderboard(10);
|
||||
|
||||
const backgroundImage = await getBackgroundImage(
|
||||
"leaderboard",
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import NavigationWrapper from "@/components/NavigationWrapper";
|
||||
import HeroSection from "@/components/HeroSection";
|
||||
import EventsSection from "@/components/EventsSection";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { eventService } from "@/services/events/event.service";
|
||||
import { getBackgroundImage } from "@/lib/preferences";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function Home() {
|
||||
const events = await prisma.event.findMany({
|
||||
orderBy: {
|
||||
date: "asc",
|
||||
},
|
||||
take: 3,
|
||||
});
|
||||
const events = await eventService.getUpcomingEvents(3);
|
||||
|
||||
// Convert Date objects to strings for serialization
|
||||
const serializedEvents = events.map((event) => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { userService } from "@/services/users/user.service";
|
||||
import { getBackgroundImage } from "@/lib/preferences";
|
||||
import NavigationWrapper from "@/components/NavigationWrapper";
|
||||
import ProfileForm from "@/components/ProfileForm";
|
||||
@@ -12,23 +12,20 @@ export default async function ProfilePage() {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
avatar: true,
|
||||
bio: true,
|
||||
characterClass: true,
|
||||
hp: true,
|
||||
maxHp: true,
|
||||
xp: true,
|
||||
maxXp: true,
|
||||
level: true,
|
||||
score: true,
|
||||
createdAt: true,
|
||||
},
|
||||
const user = await userService.getUserById(session.user.id, {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
avatar: true,
|
||||
bio: true,
|
||||
characterClass: true,
|
||||
hp: true,
|
||||
maxHp: true,
|
||||
xp: true,
|
||||
maxXp: true,
|
||||
level: true,
|
||||
score: true,
|
||||
createdAt: true,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
|
||||
Reference in New Issue
Block a user