From 2b894e62bb41a3a001ca1920abb663d53679ca17 Mon Sep 17 00:00:00 2001 From: Nabil Ould Hamou Date: Wed, 18 Dec 2024 11:23:52 +0100 Subject: [PATCH] =?UTF-8?q?atelier=20pr=C3=AAt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 + .gitignore | 1 - Dockerfile | 13 -- docker-compose.yml | 18 --- docker_entrypoint.sh | 13 -- src/routes/api/auth/login/+server.ts | 24 ++-- src/routes/api/auth/register/+server.ts | 57 ++++---- src/routes/api/channels/+server.ts | 98 ++++++++------ src/routes/api/channels/[id]/+server.ts | 47 ++++--- .../api/channels/[id]/messages/+server.ts | 124 ++++++++---------- src/routes/api/users/+server.ts | 49 +++++-- 11 files changed, 232 insertions(+), 214 deletions(-) create mode 100644 .env delete mode 100644 Dockerfile delete mode 100644 docker_entrypoint.sh diff --git a/.env b/.env new file mode 100644 index 0000000..37a2ddd --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +DATABASE_URL="mongodb://temp-root-username:temp-password@localhost/chat_projetweb?authSource=admin" +JWT_SECRET="ba63466f102443f4bb6f3670891358bc4488d0c717f6ebcd3ee3c5144e55fe2d" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 65babd0..86686d4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ node_modules Thumbs.db # Env -.env .env.* !.env.example !.env.test diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5584653..0000000 --- a/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# Build stage -FROM node:18.18-alpine - -WORKDIR /app - -COPY . . - -RUN npm i -g pnpm - -EXPOSE 3000 - -RUN chmod +x /app/docker_entrypoint.sh -ENTRYPOINT ["/app/docker_entrypoint.sh"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b2ed544..55ae5f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,22 +1,4 @@ services: - - app: - build: . - hostname: app - depends_on: - - mongodb - - redis - environment: - - DATABASE_URL=mongodb://temp-root-username:temp-password@mongodb/chat_projetweb?authSource=admin - - JWT_SECRET=ba63466f102443f4bb6f3670891358bc4488d0c717f6ebcd3ee3c5144e55fe2d - ports: - - "3000:3000" - networks: - - app_network - volumes: - - .:/usr/src/app - - /usr/src/app/node_modules - mongodb: build: ./mongodb_rs hostname: mongodb diff --git a/docker_entrypoint.sh b/docker_entrypoint.sh deleted file mode 100644 index 5d6f3b7..0000000 --- a/docker_entrypoint.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -xe - -pnpm install -pnpm prisma generate - -pnpm run build - -pnpx prisma db push - -ls - -node build \ No newline at end of file diff --git a/src/routes/api/auth/login/+server.ts b/src/routes/api/auth/login/+server.ts index 69324d1..85519f6 100644 --- a/src/routes/api/auth/login/+server.ts +++ b/src/routes/api/auth/login/+server.ts @@ -4,7 +4,8 @@ import * as argon2 from 'argon2'; import jwt from 'jsonwebtoken'; import logger from '$lib/logger'; -export async function POST({request}) { +export async function POST({ request }) { + // Étape 1 : Récupérer les données du formulaire de la requête const formData = await request.formData(); // @ts-ignore @@ -12,35 +13,40 @@ export async function POST({request}) { // @ts-ignore const password: string = formData.get('password').toString(); + // Étape 2 : Vérifier si l'utilisateur existe dans la base de données const user = await prismaClient.user.findFirst({ where: { email: email, } }); + // Si l'utilisateur n'est pas trouvé, retourner une erreur if (user == null) { logger.debug(`Could not find user with email (${email}) in database`); - return error(400, {message: "Email ou mot de passe invalide."}); + return error(400, { message: "Email ou mot de passe invalide." }); } + // Étape 3 : Vérifier si le mot de passe est correct logger.debug(`Found user with email (${email}) in database`); try { if (await argon2.verify(user.password, password)) { logger.debug(`Password for user ${user.email} is correct.`); + + // Étape 4 : Générer un token JWT pour l'utilisateur // @ts-ignore const token = jwt.sign(user, process.env.JWT_SECRET, { expiresIn: "1h" }); - logger.debug(`Generated a JWT token for user ${user.email}.`) - return json({token: token, userId: user.id}); + logger.debug(`Generated a JWT token for user ${user.email}.`); + + // Étape 5 : Retourner le token et l'ID de l'utilisateur + return json({ token: token, userId: user.id }); } else { - return error(400, {message: "Email ou mot de passe invalide."}); + return error(400, { message: "Email ou mot de passe invalide." }); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { + // Étape 6 : Gestion des erreurs logger.error(e); - return error(500, {message: e.body.message}); + return error(500, { message: e.body.message }); } - - } \ No newline at end of file diff --git a/src/routes/api/auth/register/+server.ts b/src/routes/api/auth/register/+server.ts index dfbb955..dece7fb 100644 --- a/src/routes/api/auth/register/+server.ts +++ b/src/routes/api/auth/register/+server.ts @@ -4,52 +4,47 @@ import * as argon2 from 'argon2'; import jwt from 'jsonwebtoken'; import logger from '$lib/logger'; -export async function POST({request}) { +// POST: Créer un utilisateur et générer un token JWT +export async function POST({ request }) { + // Étape 1 : Récupérer les données du formulaire de la requête const formData = await request.formData(); - // @ts-ignore - const username: string = formData.get('username').toString().toLowerCase(); - // @ts-ignore - const email: string = formData.get('email').toString().toLowerCase(); - // @ts-ignore - const password: string = formData.get('password').toString(); + const username: string = formData.get('username').toString().toLowerCase(); // Nom d'utilisateur en minuscules + const email: string = formData.get('email').toString().toLowerCase(); // Email en minuscules + const password: string = formData.get('password').toString(); // Mot de passe brut - const user = await prismaClient.user.findFirst({ - where: { - OR: [ - { username: username }, - { email: email }, - ] - } - }); + // Étape 2 : Vérifier si l'utilisateur existe déjà dans la base de données + // Question 12 - A implémenter : Recuperer l'utilisateur avec le nom d'utilisateur ou l'email fourni. + // - Utilisez la méthode await prismaClient.user.findFirst pour récupérer l'utilisateur par son nom d'utilisateur ou son email. + // - Utilisez une clause OR pour rechercher l'utilisateur par nom d'utilisateur ou email. + // - Stockez l'utilisateur dans une variable 'user' constante (const). + // Si l'utilisateur existe déjà, retourner une erreur if (user != null) { logger.debug(`A user with email (${email}) already exists in database`); - return error(400, {message: "Un compte avec cette adresse email ou nom d'utilisateur existe déjà."}); + return error(400, { message: "Un compte avec cette adresse email ou nom d'utilisateur existe déjà." }); } try { + // Étape 3 : Hash du mot de passe const hash = await argon2.hash(password); - const newUser = await prismaClient.user.create({ - data: { - username: username, - email: email, - password: hash, - surname: "", - name: "" - } - }); + // Étape 4 : Créer un nouvel utilisateur dans la base de données + // Question 13 - A implémenter : Utilisez `prismaClient.user.create` pour créer un utilisateur avec les données récupérées et le mot de passe hashé. + // - Utilisez await prismaClient.user.create pour créer un utilisateur avec les données fournies. + // - Incluez le nom d'utilisateur, l'email, le mot de passe hashé, le nom (par défaut vide) et le nom de famille (par défaut vide). + // - Stockez le nouvel utilisateur dans une variable `newUser` (const). - // @ts-ignore + // Étape 5 : Générer un token JWT pour l'utilisateur const token = jwt.sign(newUser, process.env.JWT_SECRET, { expiresIn: "1h" }); - logger.debug(`Generated a JWT token for user ${newUser.email}.`) - return json({token: token, userId: newUser.id}); + logger.debug(`Generated a JWT token for user ${newUser.email}.`); + + // Étape 6 : Retourner le token et l'ID de l'utilisateur créé + return json({ token: token, userId: newUser.id }); } catch (e) { + // Étape 7 : Gestion des erreurs logger.error(e); - return error(500, {message: "Erreur interne."}); + return error(500, { message: "Erreur interne." }); } - - } \ No newline at end of file diff --git a/src/routes/api/channels/+server.ts b/src/routes/api/channels/+server.ts index f5bf13c..ae074c5 100644 --- a/src/routes/api/channels/+server.ts +++ b/src/routes/api/channels/+server.ts @@ -6,9 +6,11 @@ import { sortChannels } from '$lib/utils/sort.ts'; // GET: Liste tous les canaux avec leur premier message export async function GET({ url }) { - if(url.searchParams.get("name") != null && url.searchParams.get("name") != ""){ + // Vérifier si un paramètre "name" est passé dans l'URL (pour filtrer par nom) + if (url.searchParams.get("name") != null && url.searchParams.get("name") != "") { const name = url.searchParams.get("name"); try { + // Étape 1 : Récupérer les canaux depuis la base de données (Prisma) let canaux = await prisma.channel.findMany({ where: { name: { @@ -18,118 +20,139 @@ export async function GET({ url }) { }, include: { messages: { - take: 1, // Récupère le dernier message - orderBy: { createdAt: 'desc' },// Trie par date décroissante - // as lastMessage not list last message + take: 1, + orderBy: { createdAt: 'desc' }, }, }, }); + // Étape 2 : Transformation des résultats canaux = canaux.map((canaux) => { return { ...canaux, lastMessage: canaux.messages.length > 0 ? canaux.messages[0] : null, - messages: undefined + messages: undefined, }; }); - canaux = sortChannels(canaux); + // Étape 3 : Trier les canaux par date du dernier message + canaux = sortChannels(canaux); // Fonction pour trier les canaux return json(canaux); } catch (err) { + // Gérer les erreurs et renvoyer une réponse d'erreur en cas de problème logger.error(err); return json({ error: 'Erreur serveur' }, { status: 500 }); } - }else{ + } else { try { - + // Étape 4 : Vérifier si les canaux sont dans le cache Redis let channels = []; - - const cachedChannels = await redisClient.get('channels'); + // Question 8 - A implémenter : Récupérer les canaux dans le cache Redis. + // - A l'aide de la clé 'channels', récupérez les canaux depuis le cache Redis. + // - Utilisez await redisClient.get pour récupérer les canaux depuis le cache Redis. + // - Stockez les canaux dans une variable 'cachedChannels' constante (const). if (cachedChannels != null) { logger.debug('Cache entry found, fetching channels from cache'); - channels = JSON.parse(cachedChannels); - }else{ + channels = JSON.parse(cachedChannels); // Charger les canaux depuis le cache + } else { logger.debug('No cache entry was found, fetching channels from database'); } - if(channels.length < 10){ - logger.debug('Fetching channels from database to fill cache'); - let canaux = await prisma.channel.findMany({ - include: { - messages: { - take: 1, // Récupère le dernier message - orderBy: { createdAt: 'desc' }, // Trie par date décroissante - }, - }, - }); + // Étape 5 : Si le cache est insuffisant, récupérer les canaux depuis la base de données + if (channels.length < 10) { + logger.debug('Fetching channels from database to fill cache'); + // Question 9 - A implémenter : Récupérer les canaux dans la base de donnée mongoDB. + // - Utilisez Prisma pour récupérer les canaux depuis MongoDB. + // - Utilisez la méthode await prisma.channel.findMany pour récupérer les canaux. + // - Incluez le dernier message des canaux. + // - Stockez les canaux dans une variable 'canaux' (let). + + // Transformation des canaux pour ne garder que le dernier message canaux = canaux.map((canaux) => { return { ...canaux, lastMessage: canaux.messages.length > 0 ? canaux.messages[0] : null, - messages: undefined + messages: undefined, // Ne pas retourner la liste complète des messages }; }); + // Fusionner les nouveaux canaux récupérés avec les canaux existants dans le cache channels = channels.concat(canaux); + // Supprimer les doublons en vérifiant par l'ID du canal channels = channels.filter((channel, index, self) => - index === self.findIndex((t) => ( - t.id === channel.id - )) + index === self.findIndex((t) => ( + t.id === channel.id + )) ); + // Trier les canaux par la date du dernier message channels = sortChannels(channels); + // Limiter à 10 canaux maximum channels = channels.slice(0, 10); - await redisClient.set('channels', JSON.stringify(channels), { EX: 3600 }); + // Mettre à jour le cache Redis avec les canaux + await redisClient.set('channels', JSON.stringify(channels), { EX: 3600 }); // Cache pendant 1 heure } - return json(channels); + return json(channels); // Retourner les canaux sous forme JSON } catch (err) { - logger.error(err) + // Gérer les erreurs et renvoyer une réponse d'erreur en cas de problème + logger.error(err); return json({ error: 'Erreur serveur' }, { status: 500 }); } } - } + +// POST: Créer un nouveau canal et mettre à jour le cache Redis export async function POST({ request }) { + // Étape 1 : Récupérer les données de la requête (nom du canal) const { name } = await request.json(); try { - let canal = await prisma.channel.create({ - data: { - name - }, - }); + // Étape 2 : Créer un nouveau canal dans la base de données + // Question 10 - A implémenter : Utilisez `prisma.channel.create` pour créer un canal avec le nom fourni. + // - Dans une variable `canal` (let) , stockez le résultat de la création du canal. logger.debug('Creating a new channel in database with id ' + canal.id); + // Étape 3 : Mettre à jour le cache Redis des canaux const cachedChanels = await redisClient.get('channels'); let channels = cachedChanels != null ? JSON.parse(cachedChanels) : []; + // Étape 4 : Structurer les données du canal à ajouter au cache canal = { ...canal, lastMessage: null, lastUpdate: canal.createdAt, - messages: undefined + messages: undefined, } + // Étape 5 : Ajouter le canal au tableau des canaux et trier channels.push(canal); + // Trier les canaux par la dernière mise à jour channels = sortChannels(channels); + // Étape 6 : Mettre à jour le cache Redis avec la nouvelle liste de canaux logger.debug(`Added channel (${canal.id}) to channels cache.`); - await redisClient.set('channels', JSON.stringify(channels), { EX: 600 }); + // Question 11 - A implémenter : Mettez à jour le cache Redis avec la liste des canaux, avec une expiration de 10 minutes. + // - Utilisez await redisClient.set pour mettre à jour le cache des canaux. + // - Utilisez une expiration de 10 minutes (600 secondes). + // - Stockez les canaux dans le cache Redis en utilisant la clé 'channels'. + // - Utilisez JSON.stringify pour convertir les canaux en chaîne JSON. + // Étape 7 : Retourner la réponse avec le canal créé return json(canal, { status: 201 }); } catch (err) { + // Étape 8 : Gérer les erreurs en cas de problème console.log(err); logger.error(err); return json({ error: 'Erreur lors de la création du canal' }, { status: 500 }); @@ -137,3 +160,4 @@ export async function POST({ request }) { } + diff --git a/src/routes/api/channels/[id]/+server.ts b/src/routes/api/channels/[id]/+server.ts index e7fa8c8..178f4e9 100644 --- a/src/routes/api/channels/[id]/+server.ts +++ b/src/routes/api/channels/[id]/+server.ts @@ -3,46 +3,56 @@ import prisma from '$lib/prismaClient'; import redisClient from '$lib/redisClient'; import logger from '$lib/logger'; -// Récupérer les informations du canal et le dernier message (avec cache Redis) +// GET: Récupérer les informations d'un canal export async function GET({ params }) { const channelId = params.id; + // Définir la clé de cache Redis pour le canal const channelCacheKey = `channel:${channelId}:info`; try { + // Étape 1 : Vérifier si les informations du canal sont en cache Redis const cachedChannel = await redisClient.get(channelCacheKey); if (cachedChannel) { + // Si le canal est dans le cache, renvoyez-le logger.debug(`Cache entry found, fetching channel (${channelId}) from cache`); return json(JSON.parse(cachedChannel)); } + // Étape 2 : Si le canal n'est pas dans le cache, le récupérer depuis la base de données MongoDB logger.debug(`No cache entry was found, fetching channel (${channelId}) from database`); - const canal = await prisma.channel.findUnique({ - where: { id: channelId }, - }); + // Question 6 - A implémenter : Récupérer les informations du canal depuis la base de données. + // Récupérer les informations du canal depuis la base de données + // Utilisez la méthode await prisma.channel.findUnique pour récupérer le canal par son ID. + // Stockez les informations du canal dans une variable 'canal' constante (const). + + // Vérifier si le canal existe dans la base de données if (!canal) { - logger.debug(`No channel for id ${channelId} was found in database`) + logger.debug(`No channel for id ${channelId} was found in database`); return json({ error: 'Canal non trouvé' }, { status: 404 }); } - const lastMessage = await prisma.message.findFirst({ - where: { id: channelId }, - orderBy: { createdAt: 'desc' }, - }); + // Étape 3 : Récupérer le dernier message du canal + // Question 7 - A implémenter : Récupérer le dernier message du canal depuis la base de données. + // Utilisez la méthode await prisma.message.findFirst pour récupérer le dernier message du canal. + // Filtrez les messages par `channelId` et triez-les par date de création décroissante. + // Stockez le dernier message dans une variable 'lastMessage' constante (const). - // Créer un objet combiné pour le canal et le dernier message + // Créer un objet combiné pour le canal et son dernier message const canalData = { canal, - lastMessage, // Inclure uniquement le dernier message + lastMessage, }; + // Étape 4 : Mettre à jour le cache global des canaux dans Redis + const cachedChannels = await redisClient.get('channels'); + let channels = cachedChannels != null ? JSON.parse(cachedChannels) : []; - const cachedChanels = await redisClient.get('channels'); - let channels = cachedChanels != null ? JSON.parse(cachedChanels) : []; - + // Ajouter le canal actuel dans la liste des canaux channels.push(canal); + // Trier les canaux par la date du dernier message channels = channels.sort( ( a: { messages: { createdAt: Date }[]; createdAt: Date }, @@ -54,16 +64,19 @@ export async function GET({ params }) { } ); + // Enregistrer la liste mise à jour des canaux dans Redis logger.debug(`Added channel (${canal.id}) to channels cache.`); await redisClient.set('channels', JSON.stringify(channels), { EX: 600 }); + // Étape 5 : Ajouter les informations du canal et du dernier message dans le cache Redis logger.debug(`Creating a new cache entry with key channel:${channelId}:info`); - await redisClient.set(channelCacheKey, JSON.stringify(canalData), {EX: 600, NX: true}); // Cache pendant 5 minutes - + await redisClient.set(channelCacheKey, JSON.stringify(canalData), { EX: 600, NX: true }); + // Étape 6 : Retourner les données du canal et du dernier message return json(canalData); - } catch (err) { + } catch (err) { + // Si une erreur survient lors de la récupération des informations du canal ou du dernier message, retournez une erreur logger.error(err); return json({ error: 'Erreur lors de la récupération du canal ou du dernier message' }, { status: 500 }); } diff --git a/src/routes/api/channels/[id]/messages/+server.ts b/src/routes/api/channels/[id]/messages/+server.ts index 82d3aec..ef44918 100644 --- a/src/routes/api/channels/[id]/messages/+server.ts +++ b/src/routes/api/channels/[id]/messages/+server.ts @@ -4,6 +4,7 @@ import redisClient from '$lib/redisClient'; import logger from '$lib/logger'; import { sortChannels } from '$lib/utils/sort.ts'; +// GET: Liste tous les messages d'un canal avec pagination export async function GET({ params, url }) { const channelId = params.id; logger.debug(`GET /api/channels/${channelId}/messages`); @@ -14,16 +15,17 @@ export async function GET({ params, url }) { try { logger.debug(`Tentative de récupération des messages du cache pour le channel : ${channelId}`); - let redisMessageKeys = await redisClient.zRangeWithScores( - `channel:${channelId}:messages`, - offset, - offset + limit - 1, - { REV: true } - ); + // Étape 1 : Récupérer les clés Redis des messages + // Question 1 - A implémenter : Récupérer les messages depuis Redis avec la pagination. + // - Utilisez await redisClient.zRangeWithScores pour récupérer les clés des messages dans une variable redisMessageKeys (let). + // - La clé Redis pour l'ensemble trié des messages est sous la forme : `channel::messages`. + // - Vous devez récupérer les messages selon l'offset et le limit. + + + //Suppression des messages dans le cache si message: n'existe plus const redisPipelineRemove = redisClient.multi(); - for (const messageKey of redisMessageKeys) { // Vérifie si la clé existe dans Redis const messageKeyValue = messageKey.value; @@ -36,6 +38,7 @@ export async function GET({ params, url }) { } await redisPipelineRemove.exec(); + // Étape 2 : Si des messages sont trouvés dans Redis if (redisMessageKeys.length > 0) { const messages = await Promise.all( redisMessageKeys.map(async (key) => { @@ -44,11 +47,12 @@ export async function GET({ params, url }) { }) ); + // Met à jour le TTL pour les messages et l'ensemble trié const redisPipeline = redisClient.multi(); for (const key of redisMessageKeys) { const message = await redisClient.get(key.value); - const msg = JSON.parse(message) - redisPipeline.set(key.value, JSON.stringify(msg), {EX: 1800}); + const msg = JSON.parse(message); + redisPipeline.set(key.value, JSON.stringify(msg), { EX: 1800 }); // TTL 30 minutes redisPipeline.zAdd(`channel:${channelId}:messages`, { score: key.score, value: key.value, @@ -59,35 +63,28 @@ export async function GET({ params, url }) { return json({ limit, page, messages: messages.reverse() }); } + // Étape 3 : Aucun message trouvé dans Redis, récupération depuis MongoDB logger.debug(`Aucun message trouvé dans le cache, récupération depuis MongoDB pour le channel : ${channelId}`); - const messagesFromDB = await prisma.message.findMany({ - where: { channelId }, - select: { - id: true, - createdAt: true, - text: true, - user: { - select: { - id: true, - }, - }, - }, - orderBy: { createdAt: 'desc' }, - skip: offset, - take: limit, - }); + // Question 2 - A implémenter : Si aucun message n'est trouvé dans Redis, récupérez-les depuis MongoDB. + // - Utiliser Prisma pour récupérer les messages depuis MongoDB. + // - Utilisez la méthode await prisma.message.findMany de Prisma pour récupérer les messages. + // - Filtrez les messages par `channelId` et recuperer l'id, le createdAt, le text, le user ( avec son id ). + // - Appliquez la pagination avec `skip` et `take` pour gérer le `offset` et le `limit`. + // - Triez les messages par date de création décroissante. + // - Stockez les messages dans une variable `messagesFromDB` constante (const). + + // Étape 4 : Si des messages sont récupérés depuis MongoDB, les stocker dans Redis if (messagesFromDB.length > 0) { const redisPipeline = redisClient.multi(); for (const message of messagesFromDB) { const messageKey = `message:${message.id}`; - redisPipeline.set(messageKey, JSON.stringify(message), {EX: 1800}); + redisPipeline.set(messageKey, JSON.stringify(message), { EX: 1800 }); // TTL 30 minutes redisPipeline.zAdd(`channel:${channelId}:messages`, { score: new Date(message.createdAt).getTime(), value: messageKey, }); } - await redisPipeline.exec(); } @@ -98,48 +95,36 @@ export async function GET({ params, url }) { } } + +// Fonction POST permettant de créer un nouveau message export async function POST({ params, request }) { const channelId = params.id; const { userId, text } = await request.json(); try { - // Créer un nouveau message dans MongoDB - let newMessage = await prisma.message.create({ - data: { - userId, - channelId, - text, - }, - select: { - id: true, - createdAt: true, - text: true, - user: { - select: { - id: true, - }, - }, - channel: { - select: { - id: true, - name: true, - }, - } - }, - }); + // Étape 1 : Créer un nouveau message dans MongoDB + // Question 3 - A implémenter : Utilisez Prisma pour créer un nouveau message dans MongoDB. + // - Utilisez await prisma.message.create pour créer un message avec les données fournies. + // - Incluez l'id, la date de création, le text, l'utilisateur (avec son id), et le canal (avec son id et son name). + // - Stockez le message dans une variable `newMessage` (let). + // - Utiliser userId, channelId et text pour créer le message. - // Ajouter le message dans Redis - await redisClient.set(`message:${newMessage.id}`, JSON.stringify(newMessage), {EX: 1800}); - await redisClient.zAdd(`channel:${channelId}:messages`, { - score: new Date(newMessage.createdAt).getTime(), - value: `message:${newMessage.id}`, - }); + // Étape 2 : Ajouter le message dans Redis + // Question 4 - A implémenter : Stocker le message dans Redis. + // - Utiliser await redisClient.set pour ajouter le message dans Redis. + // - Avec un Time To Live de 30 Minutes (1800 secondes) + // - Assurez-vous de structurer la clé comme suit : `message:`. - //update the channels cache with the new message + // Question 5 - A implémenter : Ajouter la clé du message dans la liste ordonnée des messages du canal. + // - Utiliser await redisClient.zAdd pour insérer le message dans un ensemble trié basé sur la date de création. + // Assurez-vous de structurer la clé comme suit : `channel::messages` + + // Étape 3 : Mettre à jour le cache des channels avec le nouveau message const cachedChannels = await redisClient.get('channels'); let channels = cachedChannels ? JSON.parse(cachedChannels) : []; let channel = channels.find((c) => c.id === channelId); - if(channel){ + + if (channel) { channel.lastMessage = { id: newMessage.id, text: newMessage.text, @@ -149,13 +134,18 @@ export async function POST({ params, request }) { channel.lastUpdate = newMessage.createdAt; channel.messages = undefined; - }else{ - channel = {...newMessage.channel, lastMessage: { - id: newMessage.id, - text: newMessage.text, - user: newMessage.user, - createdAt: newMessage.createdAt, - }, lastUpdate: newMessage.createdAt, messages: undefined}; + } else { + channel = { + ...newMessage.channel, + lastMessage: { + id: newMessage.id, + text: newMessage.text, + user: newMessage.user, + createdAt: newMessage.createdAt, + }, + lastUpdate: newMessage.createdAt, + messages: undefined + }; channels = [channel, ...channels]; } await redisClient.set('channels', JSON.stringify(channels), { EX: 600 }); @@ -170,12 +160,14 @@ export async function POST({ params, request }) { logger.debug(`Nouveau message ajouté pour le channel : ${channelId}`); return json(newMessage, { status: 201 }); + } catch (err) { logger.error(`Erreur lors de la création du message : ${err.message}`); return json({ error: 'Erreur lors de la création du message' }, { status: 500 }); } } + export async function DELETE({ params, request }) { const channelId = params.id; const { messageId } = await request.json(); diff --git a/src/routes/api/users/+server.ts b/src/routes/api/users/+server.ts index c5951b5..7abaccd 100644 --- a/src/routes/api/users/+server.ts +++ b/src/routes/api/users/+server.ts @@ -4,34 +4,52 @@ import redisClient from '$lib/redisClient'; import prisma from '$lib/prismaClient'; import logger from '$lib/logger'; +// GET: Récupérer tous les utilisateurs avec cache Redis export async function GET() { try { - // Vérifier si les utilisateurs sont dans le cache Redis + // Étape 1 : Vérifier si les utilisateurs sont déjà présents dans le cache Redis const cachedUsers = await redisClient.get('users'); + + // Si les utilisateurs sont trouvés dans le cache, renvoyez-les directement if (cachedUsers) { + // Si le cache contient les utilisateurs, on les récupère et on les renvoie logger.debug('Cache entry found, fetching users from cache'); return json(JSON.parse(cachedUsers)); } + // Étape 2 : Si les utilisateurs ne sont pas dans le cache, les récupérer depuis la base de données logger.debug('No cache entry was found, fetching users from database'); - // Sinon, récupérer les utilisateurs depuis MongoDB + + // Question 14 - A implémenter : Utilisez Prisma pour récupérer la liste des utilisateurs dans la base de données. + // - Utilisez la méthode await prisma.user.findMany pour récupérer la liste des utilisateurs. + // - Stockez les utilisateurs dans une variable 'users' constante (const). const users = await prisma.user.findMany(); - // Mettre les utilisateurs en cache + // Étape 3 : Mettre en cache les utilisateurs récupérés depuis la base de données logger.debug('Caching users with EX of 600 secs'); - await redisClient.set('users', JSON.stringify(users), { EX: 600 }); + // Question 15 - A implémenter : Mettre dans le cache Redis avec une expiration de 600 secondes. + // - Utilisez await redisClient.set pour mettre en cache les utilisateurs. + // - Utilisez la méthode JSON.stringify pour convertir les utilisateurs en format JSON. + // - Utilisez l'option { EX: 600 } pour définir l'expiration + + // Étape 4 : Retourner la liste des utilisateurs récupérés return json(users); + } catch (err) { + // Étape 5 : Gestion des erreurs logger.error(err); return json({ error: 'Erreur serveur' }, { status: 500 }); } } +// POST: Créer un utilisateur et mettre à jour le cache export async function POST({ request }) { + // Étape 1 : Récupérer les données envoyées dans la requête (username, surname, name, email, password) const { username, surname, name, email, password } = await request.json(); try { + // Étape 2 : Créer un nouvel utilisateur dans la base de données const user = await prisma.user.create({ data: { username: username.toLowerCase(), @@ -41,22 +59,35 @@ export async function POST({ request }) { password, }, }); + + // Étape 3 : Loguer la création de l'utilisateur avec son ID logger.debug('Creating a new user in database with id ' + user.id); - // Mettre le nouvel utilisateur dans le cache + // Étape 4 : Mettre à jour le cache global des utilisateurs logger.debug(`Caching user (${user.id})`); const cachedUsers = await redisClient.get('users'); const usersArray = cachedUsers != null ? JSON.parse(cachedUsers) : []; usersArray.push(user); + // Étape 5 : Enregistrer la liste mise à jour des utilisateurs dans le cache Redis logger.debug(`Added user (${user.id}) to users cache.`); - await redisClient.set('users', JSON.stringify(usersArray), { EX: 600 }) - logger.debug(`Creating a new cache entry with key user:${user.id}, with EX of 3600 secs`); - await redisClient.set(`user:${user.id}`, JSON.stringify(user), { EX: 3600 }); + await redisClient.set('users', JSON.stringify(usersArray), { EX: 600 }); + // Étape 6 : Créer un cache individuel pour cet utilisateur spécifique + logger.debug(`Creating a new cache entry with key user:${user.id}, with EX of 3600 secs`); + // Question 16 - A implémenter : Mettre en cache l'utilisateur créé avec une expiration de 1 heure (3600 secondes). + // - Utilisez await redisClient.set pour mettre en cache l'utilisateur. + // - Utilisez JSON.stringify pour convertir l'utilisateur en format JSON. + // - Utilisez l'option { EX: 3600 } pour définir l'expiration. + // - Utilisez la clé 'user:${user.id}' pour stocker l'utilisateur dans le cache. + + // Étape 7 : Retourner l'utilisateur créé avec un statut 201 return json(user, { status: 201 }); + } catch (err) { - logger.error(err) + // Étape 8 : Gestion des erreurs + logger.error(err); return json({ error: 'Erreur lors de la création de l’utilisateur' }, { status: 500 }); } } +