diff --git a/package.json b/package.json index fb08de5..b8e0281 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,9 @@ "vite": "^5.0.3" }, "dependencies": { + "@prisma/client": "^5.22.0", + "prisma": "^5.22.0", + "redis": "^4.7.0", "svelte-radix": "^2.0.1" } } diff --git a/src/lib/prismaClient.ts b/src/lib/prismaClient.ts new file mode 100644 index 0000000..031acc8 --- /dev/null +++ b/src/lib/prismaClient.ts @@ -0,0 +1,4 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); +export default prisma; \ No newline at end of file diff --git a/src/lib/redisClient.ts b/src/lib/redisClient.ts new file mode 100644 index 0000000..739c27b --- /dev/null +++ b/src/lib/redisClient.ts @@ -0,0 +1,11 @@ +import redis from 'redis'; + +const client = redis.createClient({ + url: process.env.REDIS_URL || 'redis://localhost:6379', +}); + +client.on('error', (err) => console.error('Redis Error:', err)); + +await client.connect(); + +export default client; diff --git a/src/routes/api/canal/[id]/+server.js.ts b/src/routes/api/canal/[id]/+server.js.ts new file mode 100644 index 0000000..cfdb6f3 --- /dev/null +++ b/src/routes/api/canal/[id]/+server.js.ts @@ -0,0 +1,123 @@ +import { json } from '@sveltejs/kit'; +import prisma from '$lib/prismaClient'; +import redisClient from '$lib/redisClient'; // Assurez-vous d'importer le client Redis + +// Récupérer les informations du canal et le dernier message (avec cache Redis) +export async function GET({ params }) { + const canalId = parseInt(params.id); + + // Clé cache pour les informations du canal et le dernier message + const canalCacheKey = `canal:${canalId}:info`; + + try { + // Vérifier si les informations du canal et le dernier message sont dans le cache Redis + const cachedCanalData = await redisClient.get(canalCacheKey); + if (cachedCanalData) { + console.log('✅ Cache hit pour les informations du canal et le dernier message'); + return json(JSON.parse(cachedCanalData)); + } + + console.log('❌ Cache miss'); + // Si non, récupérer les informations du canal et le dernier message depuis Prisma + const canal = await prisma.canal.findUnique({ + where: { id: canalId }, + include: { + users: true, // Inclut les utilisateurs associés au canal + }, + }); + + if (!canal) { + return json({ error: 'Canal non trouvé' }, { status: 404 }); + } + + // Récupérer le dernier message + const lastMessage = await prisma.message.findFirst({ + where: { canalId }, + include: { + user: { select: { id: true, pseudo: true } }, + }, + orderBy: { createdAt: 'desc' }, // Trie par date décroissante, donc le dernier message est récupéré en premier + }); + + // Créer un objet combiné pour le canal et le dernier message + const canalData = { + canal, + lastMessage, // Inclure uniquement le dernier message + }; + + // Mettre en cache les informations du canal et le dernier message pendant 5 minutes + await redisClient.set(canalCacheKey, JSON.stringify(canalData), 'EX', 300); // Cache pendant 5 minutes + + console.log('❌ Cache miss - Mise en cache des résultats'); + return json(canalData); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la récupération du canal ou du dernier message' }, { status: 500 }); + } +} + +// Supprimer un canal et invalider le cache associé +export async function DELETE({ params }) { + const canalId = parseInt(params.id); + + try { + // Supprimer le canal de la base de données + await prisma.canal.delete({ + where: { id: canalId }, + }); + + // Invalider le cache + await redisClient.del(`canal:${canalId}:info`); + + return json({ message: 'Canal supprimé avec succès' }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la suppression du canal' }, { status: 500 }); + } +} + +// Modifier un canal +export async function PUT({ params, request }) { + const canalId = parseInt(params.id); + const { nom, domaine } = await request.json(); // On suppose que ce sont les champs à mettre à jour + + // Clé cache pour les informations du canal et le dernier message + const canalCacheKey = `canal:${canalId}:info`; + + try { + // Mettre à jour les informations du canal dans la base de données + const updatedCanal = await prisma.canal.update({ + where: { id: canalId }, + data: { + nom, // Nom du canal + domaine, // Domaine du canal + }, + include: { + users: true, // Inclut les utilisateurs associés au canal + }, + }); + + // Récupérer le dernier message associé au canal après mise à jour + const lastMessage = await prisma.message.findFirst({ + where: { canalId }, + include: { + user: { select: { id: true, pseudo: true } }, + }, + orderBy: { createdAt: 'desc' }, + }); + + // Créer un objet combiné pour les nouvelles informations du canal et le dernier message + const canalData = { + canal: updatedCanal, + lastMessage, // Inclure uniquement le dernier message + }; + + // Mettre en cache les nouvelles informations pendant 5 minutes + await redisClient.set(canalCacheKey, JSON.stringify(canalData), 'EX', 60 * 5); // Cache pendant 5 minutes + + return json(canalData); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la mise à jour du canal' }, { status: 500 }); + } +} diff --git a/src/routes/api/canal/[id]/messages/+server.ts b/src/routes/api/canal/[id]/messages/+server.ts new file mode 100644 index 0000000..9fd337b --- /dev/null +++ b/src/routes/api/canal/[id]/messages/+server.ts @@ -0,0 +1,149 @@ +import { json } from '@sveltejs/kit'; +import prisma from '$lib/prismaClient'; +import redisClient from '$lib/redisClient'; // Assure-toi d'importer ton client Redis + +export async function GET({ params, url }) { + const canalId = parseInt(params.id); + + // Gestion de la pagination avec des paramètres optionnels `page` et `limit` + const page = parseInt(url.searchParams.get('page')) || 1; + const limit = parseInt(url.searchParams.get('limit')) || 10; + const offset = (page - 1) * limit; + + // Générer une clé cache Redis unique en fonction du canal et des paramètres de pagination + const cacheKey = `canal:${canalId}:messages:page:${page}:limit:${limit}`; + + try { + // 1. Vérifier si les messages sont déjà dans le cache Redis + const cachedMessages = await redisClient.get(cacheKey); + if (cachedMessages) { + console.log('✅ Cache hit'); + return json(JSON.parse(cachedMessages)); // Si les données sont en cache, les retourner + } + + // 2. Si les messages ne sont pas en cache, récupérer depuis la base de données + const messages = await prisma.message.findMany({ + where: { canalId }, + include: { + user: { + select: { id: true, pseudo: true }, // Inclut uniquement l’ID et le pseudo de l’utilisateur + }, + }, + orderBy: { + createdAt: 'asc', // Trie par date croissante + }, + skip: offset, + take: limit, + }); + + // 3. Compter le nombre total de messages pour la pagination + const totalMessages = await prisma.message.count({ + where: { canalId }, + }); + + const response = { + messages, + pagination: { + page, + limit, + totalMessages, + totalPages: Math.ceil(totalMessages / limit), + }, + }; + + // 4. Mettre en cache les messages avec une expiration (par exemple 5 minutes) + await redisClient.set(cacheKey, JSON.stringify(response), 'EX', 60 * 5); // Cache pendant 5 minutes + + console.log('❌ Cache miss - Mise en cache des résultats'); + return json(response); // Retourner les données récupérées + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la récupération des messages' }, { status: 500 }); + } +} + +export async function POST({ params, request }) { + const canalId = parseInt(params.id); + const { userId, text } = await request.json(); + + try { + // Créer un nouveau message dans la base de données + const newMessage = await prisma.message.create({ + data: { + userId, + canalId, + text, + }, + include: { user: { select: { id: true, pseudo: true } } }, + }); + + updateCaches(); // Mettre à jour les caches après la création d’un nouveau message + + return json(newMessage, { status: 201 }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la création du message' }, { status: 500 }); + } +} + +export async function DELETE({ params }) { + const messageId = parseInt(params.id); + + try { + // Supprimer le message de la base de données + await prisma.message.delete({ + where: { id: messageId }, + }); + + updateCaches(); // Mettre à jour les caches après la suppression d’un message + + return json({ message: 'Message supprimé avec succès' }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la suppression du message' }, { status: 500 }); + } +} + +// Fonction pour mettre à jour tous les caches des messages +function updateCaches(canalId) { + // Mettre à jour tous les caches + // Mettre à jour toutes les pages dans le cache + let page : number = 1; + let limit : number = 10; + let offset : number = (page - 1) * limit; + while (true) { + const cacheKey = `canal:${canalId}:messages:page:${page}:limit:${limit}`; + const cachedMessages = await redisClient.get(cacheKey); + if (!cachedMessages) { + break; + } + const totalMessages = await prisma.message.count({ + where: { canalId }, + }); + const messages = await prisma.message.findMany({ + where: { canalId }, + include: { + user: { + select: { id: true, pseudo: true }, + }, + }, + orderBy: { + createdAt: 'asc', + }, + skip: offset, + take: limit, + }); + const response = { + messages, + pagination: { + page, + limit, + totalMessages, + totalPages: Math.ceil(totalMessages / limit), + }, + }; + await redisClient.set(cacheKey, JSON.stringify(response), 'EX', 60 * 5); + page++; + offset = (page - 1) * limit; + } +} diff --git a/src/routes/api/canals/+server.ts b/src/routes/api/canals/+server.ts new file mode 100644 index 0000000..b154366 --- /dev/null +++ b/src/routes/api/canals/+server.ts @@ -0,0 +1,47 @@ +import { json } from '@sveltejs/kit'; +import prisma from '$lib/prismaClient'; +import redisClient from '$lib/redisClient'; + +// GET: Liste tous les canaux +export async function GET() { + try { + const cachedCanaux = await redisClient.get('canaux'); + if (cachedCanaux) { + console.log('✅ Cache hit'); + return json(JSON.parse(cachedCanaux)); + } + + console.log('❌ Cache miss'); + const canaux = await prisma.canal.findMany({ + include: { users: true, messages: true }, // Inclut les relations + }); + + await redisClient.set('canaux', JSON.stringify(canaux), { EX: 600 }); // Met en cache + return json(canaux); + } catch (err) { + console.error(err); + return json({ error: 'Erreur serveur' }, { status: 500 }); + } +} + +export async function POST({ request }) { + const { nom, domaine, userIds } = await request.json(); + + try { + const canal = await prisma.canal.create({ + data: { + nom, + domaine, + users: { + connect: userIds.map((id) => ({ id })), // Associe des utilisateurs au canal + }, + }, + }); + + return json(canal, { status: 201 }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la création du canal' }, { status: 500 }); + } +} + diff --git a/src/routes/api/user/[id]/+server.ts b/src/routes/api/user/[id]/+server.ts new file mode 100644 index 0000000..fed07c3 --- /dev/null +++ b/src/routes/api/user/[id]/+server.ts @@ -0,0 +1,108 @@ +import { json } from '@sveltejs/kit'; +import redisClient from '$lib/redisClient'; +import prisma from '$lib/prismaClient'; + +export async function GET({ params }) { + const userId = params.id; + + try { + // Vérifier si l'utilisateur est dans le cache Redis + const cachedUser = await redisClient.get(`user:${userId}`); + if (cachedUser) { + console.log('✅ Cache hit'); + return json(JSON.parse(cachedUser)); + } + + console.log('❌ Cache miss'); + // Si non, récupérer depuis MongoDB via Prisma + const user = await prisma.user.findUnique({ + where: { id: parseInt(userId) }, + }); + + if (!user) { + return json({ error: 'Utilisateur non trouvé' }, { status: 404 }); + } + + // Mettre l'utilisateur en cache + await redisClient.set(`user:${userId}`, JSON.stringify(user), { EX: 3600 }); + + return json(user); + } catch (err) { + console.error(err); + return json({ error: 'Erreur serveur' }, { status: 500 }); + } +} + +export async function POST({ request }) { + const { pseudo, nom, prenom, email, password } = await request.json(); + + try { + const user = await prisma.user.create({ + data: { + pseudo, + nom, + prenom, + email, + password, + }, + }); + + // Mettre le nouvel utilisateur dans le cache + await redisClient.set(`user:${user.id}`, JSON.stringify(user), { EX: 3600 }); + + return json(user, { status: 201 }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la création de l’utilisateur' }, { status: 500 }); + } +} + +// Mettre à jour un utilisateur avec PUT +export async function PUT({ params, request }) { + const userId = parseInt(params.id); + const { pseudo, nom, prenom, email, password } = await request.json(); // Assurez-vous d'envoyer tous les champs nécessaires dans le body + + try { + // Mettre à jour l'utilisateur dans la base de données + const updatedUser = await prisma.user.update({ + where: { id: userId }, + data: { + pseudo, + nom, + prenom, + email, + password, // Attention à ne pas oublier de sécuriser le mot de passe avec bcrypt ou une autre méthode + }, + }); + + // Mettre à jour l'utilisateur dans le cache Redis + await redisClient.set(`user:${userId}`, JSON.stringify(updatedUser), 'EX', 3600); // Cache pendant 1 heure (3600 secondes) + + return json(updatedUser); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la mise à jour de l’utilisateur' }, { status: 500 }); + } +} + + +export async function DELETE({ params }) { + const userId = parseInt(params.id); + + try { + await prisma.user.delete({ + where: { id: userId }, + }); + + // Supprimer l'utilisateur du cache Redis + await redisClient.del(`user:${userId}`); + + return json({ message: 'Utilisateur supprimé avec succès' }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la suppression de l’utilisateur' }, { status: 500 }); + } +} + + + diff --git a/src/routes/api/users/+server.ts b/src/routes/api/users/+server.ts new file mode 100644 index 0000000..0d7b259 --- /dev/null +++ b/src/routes/api/users/+server.ts @@ -0,0 +1,27 @@ +// src/routes/api/users/+server.js +import { json } from '@sveltejs/kit'; +import redisClient from '$lib/redisClient'; +import prisma from '$lib/prismaClient'; + +export async function GET() { + try { + // Vérifier si les utilisateurs sont dans le cache Redis + const cachedUsers = await redisClient.get('users'); + if (cachedUsers) { + console.log('✅ Cache hit'); + return json(JSON.parse(cachedUsers)); + } + + console.log('❌ Cache miss'); + // Sinon, récupérer les utilisateurs depuis MongoDB + const users = await prisma.user.findMany(); + + // Mettre les utilisateurs en cache + await redisClient.set('users', JSON.stringify(users), { EX: 600 }); + + return json(users); + } catch (err) { + console.error(err); + return json({ error: 'Erreur serveur' }, { status: 500 }); + } +}