From b45ae21c47df616d5391fc4a7402a82dc8540efb Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Thu, 23 Apr 2026 00:52:37 +0200 Subject: [PATCH 1/7] chore: fix lint issues --- backend/server.ts | 63 +- backend/src/controllers/auth.controller.ts | 329 ++++--- backend/src/controllers/bus.controller.ts | 87 +- .../src/controllers/challenge.controller.ts | 146 ++-- backend/src/controllers/discord.controller.ts | 26 +- backend/src/controllers/email.controller.ts | 218 ++--- backend/src/controllers/event.controller.ts | 13 +- backend/src/controllers/faction.controller.ts | 18 +- .../src/controllers/im_export.controller.ts | 285 +++---- backend/src/controllers/news.controller.ts | 233 +++-- .../src/controllers/permanence.controller.ts | 556 ++++++------ backend/src/controllers/role.controller.ts | 266 +++--- backend/src/controllers/team.controller.ts | 31 +- backend/src/controllers/tent.controller.ts | 177 ++-- backend/src/controllers/user.controller.ts | 164 ++-- backend/src/database/db.ts | 79 +- backend/src/database/initdb/initChallenge.ts | 18 +- backend/src/database/initdb/initUser.ts | 32 +- backend/src/database/initdb/initevent.ts | 23 +- backend/src/database/initdb/initrole.ts | 100 +-- backend/src/middlewares/auth.middleware.ts | 4 +- backend/src/middlewares/multer.middleware.ts | 140 ++- .../src/middlewares/respoperm.middleware.ts | 43 +- backend/src/middlewares/user.middleware.ts | 64 +- backend/src/routes/bus.routes.ts | 5 +- backend/src/routes/challenge.routes.ts | 7 +- backend/src/routes/default.routes.ts | 5 +- backend/src/routes/discord.routes.ts | 5 +- backend/src/routes/email.routes.ts | 9 +- backend/src/routes/event.routes.ts | 4 +- backend/src/routes/faction.routes.ts | 14 +- backend/src/routes/im_export.routes.ts | 14 +- backend/src/routes/news.routes.ts | 14 +- backend/src/routes/permanences.routes.ts | 38 +- backend/src/routes/role.routes.ts | 15 +- backend/src/routes/team.routes.ts | 25 +- backend/src/routes/tent.routes.ts | 15 +- backend/src/routes/user.routes.ts | 15 +- backend/src/schemas/Basic/challenge.schema.ts | 6 +- backend/src/schemas/Basic/event.schema.ts | 4 +- backend/src/schemas/Basic/faction.schema.ts | 7 +- backend/src/schemas/Basic/news.schema.ts | 20 +- .../src/schemas/Basic/permanence.schema.ts | 30 +- backend/src/schemas/Basic/role.schema.ts | 7 +- backend/src/schemas/Basic/team.schema.ts | 9 +- backend/src/schemas/Basic/user.schema.ts | 26 +- .../Relational/busattribution.schema.ts | 8 +- .../Relational/challengevalidation.schema.ts | 24 +- .../schemas/Relational/registration.schema.ts | 12 +- .../schemas/Relational/rolepoints.schema.ts | 10 +- .../schemas/Relational/teamfaction.schema.ts | 13 +- .../schemas/Relational/teamshotgun.schema.ts | 2 +- .../Relational/userpermanences.schema.ts | 32 +- .../schemas/Relational/userroles.schema.ts | 15 +- .../schemas/Relational/userteams.schema.ts | 13 +- .../src/schemas/Relational/usertent.schema.ts | 15 +- backend/src/services/auth.service.ts | 251 +++--- backend/src/services/bus.service.ts | 108 ++- backend/src/services/challenge.service.ts | 150 ++-- backend/src/services/discord.service.ts | 13 +- backend/src/services/email.service.ts | 71 +- backend/src/services/event.service.ts | 100 +-- backend/src/services/faction.service.ts | 44 +- backend/src/services/im_export.service.ts | 117 ++- backend/src/services/news.service.ts | 75 +- backend/src/services/permanence.service.ts | 800 +++++++++--------- backend/src/services/registration.service.ts | 13 +- backend/src/services/role.service.ts | 266 +++--- backend/src/services/team.service.ts | 420 +++++---- backend/src/services/tent.service.ts | 207 +++-- backend/src/services/user.service.ts | 406 +++++---- backend/src/utils/emailtemplates.ts | 6 - backend/src/utils/no_sync_list.ts | 2 +- backend/src/utils/responses.ts | 109 ++- backend/src/utils/siep.ts | 106 +-- backend/src/utils/token.ts | 4 +- backend/types/express.d.ts | 8 +- frontend/eslint.config.js | 40 +- frontend/src/App.tsx | 205 +++-- .../AdminChallenge/adminChalengeList.tsx | 444 +++++----- .../adminChallengeAddPointsForm.tsx | 177 ++-- .../AdminChallenge/adminChallengeEditor.tsx | 190 ++--- .../adminChallengeValidatedList.tsx | 236 +++--- .../Admin/AdminPerm/adminPermAction.tsx | 149 ++-- .../Admin/AdminPerm/adminPermForm.tsx | 420 ++++----- .../Admin/AdminPerm/adminPermImport.tsx | 158 ++-- .../Admin/AdminPerm/adminPermList.tsx | 260 +++--- .../Admin/AdminPerm/adminPermMembers.tsx | 347 ++++---- frontend/src/components/Admin/adminBus.tsx | 288 +++---- frontend/src/components/Admin/adminEmail.tsx | 300 +++---- frontend/src/components/Admin/adminEvent.tsx | 346 ++++---- .../components/Admin/adminExportImport.tsx | 690 +++++++-------- .../src/components/Admin/adminFaction.tsx | 196 ++--- frontend/src/components/Admin/adminGames.tsx | 284 +++---- frontend/src/components/Admin/adminLayout.tsx | 96 +-- frontend/src/components/Admin/adminNews.tsx | 574 ++++++------- frontend/src/components/Admin/adminRole.tsx | 496 ++++++----- .../src/components/Admin/adminShotgun.tsx | 6 +- frontend/src/components/Admin/adminTeam.tsx | 682 ++++++++------- frontend/src/components/Admin/adminTent.tsx | 374 ++++---- frontend/src/components/Admin/adminUser.tsx | 668 +++++++-------- .../src/components/Games/roleLeaderboard.tsx | 156 ++-- .../components/Parrainnage/parrainageForm.tsx | 68 +- .../components/Plannings/planningSection.tsx | 188 ++-- .../components/WEI_SDI_Food/foodSection.tsx | 210 ++--- .../components/WEI_SDI_Food/sdiSection.tsx | 96 +-- .../components/WEI_SDI_Food/weiSection.tsx | 96 +-- frontend/src/components/auth/authForm.tsx | 6 +- .../src/components/auth/registrationForm.tsx | 106 +-- .../src/components/auth/resetPasswordForm.tsx | 5 +- .../components/challenge/challengeList.tsx | 344 ++++---- frontend/src/components/footer.tsx | 2 +- frontend/src/components/home/infosSection.tsx | 276 +++--- .../src/components/home/socialSection.tsx | 93 +- .../src/components/legals/legalsSection.tsx | 172 ++-- frontend/src/components/navbar.tsx | 518 ++++++------ frontend/src/components/news/newsSection.tsx | 147 ++-- .../src/components/permanence/appealPerm.tsx | 352 ++++---- .../src/components/permanence/permList.tsx | 138 +-- .../src/components/permanence/permUser.tsx | 146 ++-- .../src/components/privacy/privacySection.tsx | 232 +++-- .../src/components/profil/discordPage.tsx | 36 +- frontend/src/components/profil/profilForm.tsx | 226 ++--- frontend/src/components/profil/roleForm.tsx | 156 ++-- .../src/components/roadbook/roadbookCard.tsx | 71 +- .../shotgun/preregisterCESection.tsx | 84 +- .../shotgun/preregisterTeamSection.tsx | 292 +++---- .../src/components/shotgun/shotgunSection.tsx | 182 ++-- frontend/src/components/tent/tentSection.tsx | 410 ++++----- frontend/src/components/ui/accordion.tsx | 87 +- frontend/src/components/ui/button.tsx | 91 +- frontend/src/components/ui/card.tsx | 125 ++- frontend/src/components/ui/checkbox.tsx | 41 +- frontend/src/components/ui/input.tsx | 27 +- frontend/src/components/ui/label.tsx | 27 +- frontend/src/components/ui/popover.tsx | 53 +- frontend/src/components/ui/revealSection.tsx | 3 +- frontend/src/components/ui/select.tsx | 271 +++--- frontend/src/components/ui/textarea.tsx | 21 +- frontend/src/components/utils/adminroute.tsx | 33 +- .../src/components/utils/datetime_utils.ts | 53 +- .../src/components/utils/privateroute.tsx | 58 +- .../src/components/utils/protectedroute.tsx | 23 +- .../src/interfaces/challenge.interface.ts | 29 +- frontend/src/interfaces/faction.interface.ts | 4 +- frontend/src/interfaces/news.interface.ts | 18 +- .../src/interfaces/permanence.interface.ts | 21 +- frontend/src/interfaces/role.interface.ts | 7 +- frontend/src/interfaces/team.interface.ts | 2 +- frontend/src/interfaces/tent.interface.ts | 3 +- frontend/src/interfaces/token.interfaces.ts | 4 +- frontend/src/interfaces/user.interface.ts | 7 +- frontend/src/lib/utils.ts | 2 +- frontend/src/pages/admin.tsx | 590 ++++++------- frontend/src/pages/auth.tsx | 9 +- frontend/src/pages/challenge.tsx | 8 +- frontend/src/pages/discord.tsx | 11 +- frontend/src/pages/food.tsx | 26 +- frontend/src/pages/games.tsx | 8 +- frontend/src/pages/home.tsx | 22 +- frontend/src/pages/legals.tsx | 20 +- frontend/src/pages/news.tsx | 7 +- frontend/src/pages/notFound.tsx | 8 +- frontend/src/pages/parrainage.tsx | 13 +- frontend/src/pages/perm.tsx | 344 ++++---- frontend/src/pages/plannings.tsx | 9 +- frontend/src/pages/privacy.tsx | 20 +- frontend/src/pages/profil.tsx | 46 +- frontend/src/pages/register.tsx | 11 +- frontend/src/pages/resetPassword.tsx | 11 +- frontend/src/pages/roadbook.tsx | 26 +- frontend/src/pages/sdi.tsx | 23 +- frontend/src/pages/shotgun.tsx | 7 +- frontend/src/pages/wei.tsx | 47 +- frontend/src/services/api.ts | 58 +- .../src/services/requests/auth.service.ts | 56 +- frontend/src/services/requests/bus.service.ts | 24 +- .../services/requests/challenge.service.ts | 105 ++- .../src/services/requests/email.service.ts | 16 +- .../src/services/requests/event.service.ts | 93 +- .../src/services/requests/faction.service.ts | 26 +- .../services/requests/im_export.service.ts | 24 +- .../src/services/requests/news.service.ts | 52 +- .../services/requests/permanence.service.ts | 171 ++-- .../src/services/requests/role.service.ts | 34 +- .../src/services/requests/team.service.ts | 43 +- .../src/services/requests/tent.service.ts | 34 +- .../src/services/requests/user.service.ts | 89 +- frontend/src/utils/utils.ts | 34 +- frontend/vite.config.ts | 26 +- 190 files changed, 10980 insertions(+), 11444 deletions(-) diff --git a/backend/server.ts b/backend/server.ts index e198b12..b4630dd 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -1,31 +1,31 @@ -import express from 'express'; -import cors from 'cors'; import bodyParser from 'body-parser'; +import cors from 'cors'; +import express from 'express'; import dotenv from 'dotenv'; import path from "path"; -import defaultRoute from './src/routes/default.routes'; +import { initChallenge } from './src/database/initdb/initChallenge'; +import { initUser } from './src/database/initdb/initUser'; +import { initEvent } from './src/database/initdb/initevent'; +import { initRoles } from './src/database/initdb/initrole'; +import { authenticateUser } from './src/middlewares/auth.middleware'; import authRoutes from './src/routes/auth.routes'; -import roleRoutes from './src/routes/role.routes'; -import userRoutes from './src/routes/user.routes'; -import teamRoutes from './src/routes/team.routes'; +import busRoutes from './src/routes/bus.routes'; +import challengeRoutes from './src/routes/challenge.routes'; +import defaultRoute from './src/routes/default.routes'; +import discordRoutes from './src/routes/discord.routes'; +import emailRoutes from './src/routes/email.routes'; import eventRoutes from './src/routes/event.routes'; import factionRoutes from './src/routes/faction.routes'; import imexportRouter from './src/routes/im_export.routes'; -import permanenceRoutes from './src/routes/permanences.routes'; -import challengeRoutes from './src/routes/challenge.routes'; -import emailRoutes from './src/routes/email.routes'; import newsRoutes from './src/routes/news.routes'; -import discordRoutes from './src/routes/discord.routes'; +import permanenceRoutes from './src/routes/permanences.routes'; +import roleRoutes from './src/routes/role.routes'; +import teamRoutes from './src/routes/team.routes'; import tentRoutes from './src/routes/tent.routes'; -import busRoutes from './src/routes/bus.routes'; +import userRoutes from './src/routes/user.routes'; import { server_port } from './src/utils/secret'; -import { initUser } from './src/database/initdb/initUser' -import { initRoles } from './src/database/initdb/initrole' -import {initEvent} from './src/database/initdb/initevent' -import {initChallenge} from './src/database/initdb/initChallenge' -import { authenticateUser } from './src/middlewares/auth.middleware'; dotenv.config(); @@ -36,7 +36,6 @@ async function startServer() { app.use(cors({ origin: "*" })); app.use(bodyParser.json()); app.use(express.urlencoded({ extended: true })); - try { // Initialisation de la base de données @@ -45,24 +44,24 @@ async function startServer() { await initEvent(); await initChallenge(); console.log('Base de données initialisée avec succès'); - + // Utilisation des routes d'authentification app.use('/api', defaultRoute) app.use('/api/auth', authRoutes); - app.use('/api/authadmin',authenticateUser, authRoutes); - app.use('/api/role',authenticateUser, roleRoutes); - app.use('/api/user',authenticateUser, userRoutes); - app.use('/api/team',authenticateUser, teamRoutes); - app.use('/api/event',authenticateUser, eventRoutes); - app.use('/api/faction',authenticateUser, factionRoutes); + app.use('/api/authadmin', authenticateUser, authRoutes); + app.use('/api/role', authenticateUser, roleRoutes); + app.use('/api/user', authenticateUser, userRoutes); + app.use('/api/team', authenticateUser, teamRoutes); + app.use('/api/event', authenticateUser, eventRoutes); + app.use('/api/faction', authenticateUser, factionRoutes); app.use('/api/imexport', authenticateUser, imexportRouter) - app.use('/api/permanence',authenticateUser, permanenceRoutes); - app.use('/api/challenge',authenticateUser, challengeRoutes); - app.use('/api/email',authenticateUser, emailRoutes); - app.use('/api/news',authenticateUser, newsRoutes); - app.use('/api/discord',authenticateUser, discordRoutes); - app.use('/api/tent',authenticateUser, tentRoutes); - app.use('/api/bus',authenticateUser, busRoutes); + app.use('/api/permanence', authenticateUser, permanenceRoutes); + app.use('/api/challenge', authenticateUser, challengeRoutes); + app.use('/api/email', authenticateUser, emailRoutes); + app.use('/api/news', authenticateUser, newsRoutes); + app.use('/api/discord', authenticateUser, discordRoutes); + app.use('/api/tent', authenticateUser, tentRoutes); + app.use('/api/bus', authenticateUser, busRoutes); app.use("/api/uploads/imgnews", express.static(path.join(__dirname, "/uploads/imgnews"))); app.use("/api/uploads/foodmenu", express.static(path.join(__dirname, "/uploads/foodmenu"))); app.use("/api/uploads/plannings", express.static(path.join(__dirname, "/uploads/plannings"))); @@ -78,4 +77,4 @@ async function startServer() { } } -startServer(); \ No newline at end of file +startServer(); diff --git a/backend/src/controllers/auth.controller.ts b/backend/src/controllers/auth.controller.ts index baf8af4..9cccdca 100644 --- a/backend/src/controllers/auth.controller.ts +++ b/backend/src/controllers/auth.controller.ts @@ -1,43 +1,43 @@ +import bcrypt from 'bcryptjs'; +import bigInt from 'big-integer'; import { Request, Response } from 'express'; +import { sign, verify } from 'jsonwebtoken'; import * as auth_service from '../services/auth.service'; -import * as user_service from '../services/user.service'; import * as email_service from '../services/email.service'; -import * as role_service from '../services/role.service'; import * as registration_service from '../services/registration.service'; -import bigInt from 'big-integer'; +import * as role_service from '../services/role.service'; +import * as user_service from '../services/user.service'; +import * as template from '../utils/emailtemplates'; import { Error, Ok, Unauthorized } from '../utils/responses'; -import { decodeToken } from '../utils/token'; -import { sign, verify } from 'jsonwebtoken'; import { jwtSecret, service_url } from '../utils/secret'; +import { decodeToken } from '../utils/token'; import { EmailOptions } from './email.controller'; -import * as template from '../utils/emailtemplates'; -import bcrypt from 'bcryptjs'; // Fonction de connexion export const login = async (req: Request, res: Response) => { - const { email, password } = req.body; + const { email, password } = req.body; - try { - const token = await auth_service.loginUser(email, password); - Ok(res, { data:{token}, msg: 'Connexion réussie' }); - } catch (err) { + try { + const token = await auth_service.loginUser(email, password); + Ok(res, { data: { token }, msg: 'Connexion réussie' }); + } catch (err) { Error(res, { msg: err.message }); - } + } }; // Fonction d'inscription export const register = async (req: Request, res: Response) => { - const { firstName, lastName, email, password } = req.body; + const { firstName, lastName, email, password } = req.body; - try { - const newUser = await auth_service.registerUser(firstName, lastName, email, password); + try { + const newUser = await auth_service.registerUser(firstName, lastName, email, password); Ok(res, { - msg: 'Utilisateur inscrit avec succès', - data: newUser, - }); - } catch (err) { - Error(res, { msg: err.message }); - } + msg: 'Utilisateur inscrit avec succès', + data: newUser, + }); + } catch (err) { + Error(res, { msg: err.message }); + } }; @@ -51,33 +51,33 @@ export const handlecasticket = async (req: Request, res: Response) => { if (CASuser && CASuser.email && CASuser.givenName && CASuser.sn) { // Assurez-vous que user.email est un string let user = await user_service.getUserByEmail(CASuser.email.toLowerCase()); - if(!user){ - const password = bigInt.randBetween(bigInt(2).pow(255),bigInt(2).pow(256).minus(1)).toString() - await user_service.createUser(CASuser.givenName, CASuser.sn, CASuser.email, true, "Student", " " , password) + if (!user) { + const password = bigInt.randBetween(bigInt(2).pow(255), bigInt(2).pow(256).minus(1)).toString() + await user_service.createUser(CASuser.givenName, CASuser.sn, CASuser.email, true, "Student", " ", password) user = await user_service.getUserByEmail(CASuser.email.toLowerCase()) } const id = user?.id - if (!id){ Error(res,{ msg: "Pas d'id" }); return;} - - await user_service.updateUserStudent( CASuser.givenName, CASuser.sn, CASuser.email); + if (!id) { Error(res, { msg: "Pas d'id" }); return; } + + await user_service.updateUserStudent(CASuser.givenName, CASuser.sn, CASuser.email); // Récupérer les rôles de l'utilisateur - const userRoles = await role_service.getUserRoles(user.id); // [{ roleId, roleName }] - - // Ajouter les rôles à l'objet utilisateur - const enrichedUser = { + const userRoles = await role_service.getUserRoles(user.id); // [{ roleId, roleName }] + + // Ajouter les rôles à l'objet utilisateur + const enrichedUser = { ...user, roles: userRoles, - }; - + }; + const token = auth_service.generateToken(enrichedUser); - + Ok(res, { data: { token } }) - - + + } else { Unauthorized(res, { msg: 'Unauthorized: Invalid user email' }); } @@ -85,163 +85,162 @@ export const handlecasticket = async (req: Request, res: Response) => { Unauthorized(res, { msg: 'Unauthorized: No ticket provided' }); } } catch (error) { - Unauthorized(res, { msg: 'Unauthorized: Invalid token' }); + Unauthorized(res, { msg: 'Unauthorized: Invalid token' }); } } export const isTokenValid = async (req: Request, res: Response) => { - try { - const authHeader = req.headers["authorization"]; - if (!authHeader || !authHeader.startsWith("Bearer ")) { - Unauthorized(res, { - msg: "Unauthorized: Missing or malformed token", - data: false, - }); - return; - } - - const token = authHeader.split(" ")[1]; - - // Décoder et valider le token - const decodedToken = decodeToken(token); - if (!decodedToken) { - Unauthorized(res, { - msg: "Unauthorized: Token has expired or is invalid", - data: false, - }); - return - } + try { + const authHeader = req.headers["authorization"]; + if (!authHeader || !authHeader.startsWith("Bearer ")) { + Unauthorized(res, { + msg: "Unauthorized: Missing or malformed token", + data: false, + }); + return; + } + const token = authHeader.split(" ")[1]; - // Vérifier que l'email est bien présent dans le token - if (!decodedToken.userEmail) { - Unauthorized(res, { - msg: "Unauthorized: Invalid token content", - data: false, - }); - return - } + // Décoder et valider le token + const decodedToken = decodeToken(token); + if (!decodedToken) { + Unauthorized(res, { + msg: "Unauthorized: Token has expired or is invalid", + data: false, + }); + return + } - // Répondre une seule fois - Ok(res, {data: true }); - return - } catch (error) { - Error(res, { msg: "Unauthorized: Token validation failed" }); - return - } + + // Vérifier que l'email est bien présent dans le token + if (!decodedToken.userEmail) { + Unauthorized(res, { + msg: "Unauthorized: Invalid token content", + data: false, + }); + return + } + + // Répondre une seule fois + Ok(res, { data: true }); + return + } catch (error) { + Error(res, { msg: "Unauthorized: Token validation failed" }); + return + } }; -export const completeRegistration = async(req : Request, res : Response) => { +export const completeRegistration = async (req: Request, res: Response) => { + + const { token, password } = req.body; - const { token, password } = req.body; + try { - try{ + await auth_service.completeRegistration(token, password) + Ok(res, { msg: "Inscription complétée avec succès.", data: true }) - await auth_service.completeRegistration(token, password) - Ok(res, {msg: "Inscription complétée avec succès.", data: true}) + } catch (error) { + Error(res, { msg: error.message || "Une erreur est survenue." }); + } - }catch(error){ - Error(res, { msg: error.message || "Une erreur est survenue." }); - } - } export const requestPasswordUser = async (req: Request, res: Response) => { - const {user_email} = req.body - const user = await user_service.getUserByEmail(user_email); - - if (!user) { - Error(res, { msg: 'User not found' }); - return - } - - // Générer un token JWT - const token = sign({ userId: user.id }, jwtSecret, { expiresIn: '1h' }); - - // Créer le lien de réinitialisation - const resetLink = `${service_url}ResetPassword?token=${token}`; - - - // Générer le contenu HTML du mail - const htmlEmail = template.compileTemplate({ resetLink: resetLink}, template.templateResetPassword); - - if (!htmlEmail) { - Error(res, { msg: "Nom de template invalide" }); - return; - } - - // Préparer les options d'email - const emailOptions: EmailOptions = { - from: "integration@utt.fr", - to: [user_email], - cc: [], - bcc: [], - subject : '[INTEGRATION UTT] - Réinitialisation de votre mot de passe', - html: htmlEmail, - }; - - try{ - // Envoyer l'e-mail - await email_service.sendEmail(emailOptions); - Ok(res, {msg:'Email for password reste sent !'}) - return - }catch(error){ - Error(res, { msg: 'Error when reseting password' }); - return - } + const { user_email } = req.body + const user = await user_service.getUserByEmail(user_email); + + if (!user) { + Error(res, { msg: 'User not found' }); + return + } + + // Générer un token JWT + const token = sign({ userId: user.id }, jwtSecret, { expiresIn: '1h' }); + + // Créer le lien de réinitialisation + const resetLink = `${service_url}ResetPassword?token=${token}`; + + + // Générer le contenu HTML du mail + const htmlEmail = template.compileTemplate({ resetLink: resetLink }, template.templateResetPassword); + + if (!htmlEmail) { + Error(res, { msg: "Nom de template invalide" }); + return; + } + + // Préparer les options d'email + const emailOptions: EmailOptions = { + from: "integration@utt.fr", + to: [user_email], + cc: [], + bcc: [], + subject: '[INTEGRATION UTT] - Réinitialisation de votre mot de passe', + html: htmlEmail, + }; + + try { + // Envoyer l'e-mail + await email_service.sendEmail(emailOptions); + Ok(res, { msg: 'Email for password reste sent !' }) + return + } catch (error) { + Error(res, { msg: 'Error when reseting password' }); + return + } } export const resetPasswordUser = async (req: Request, res: Response) => { - const {token, password} = req.body; - - - try { - // Vérifiez et décodez le token - const decoded: any = verify(token, jwtSecret); - - // Trouvez l'utilisateur par ID - const user = await user_service.getUserById(decoded.userId); - if (!user) { - Error(res, {msg :'Utilisateur non trouvé'}); - return - } - - // Hash du nouveau mot de passe - const hashedPassword = await bcrypt.hash(password, 10); - - // Mettez à jour le mot de passe de l'utilisateur - await user_service.updateUserPassword(Number(user.userId), hashedPassword); - Ok(res, {msg: 'Mot de passe réinitialisé avec succès'}); - return - } catch (error) { + const { token, password } = req.body; + + + try { + // Vérifiez et décodez le token + const decoded: any = verify(token, jwtSecret); + + // Trouvez l'utilisateur par ID + const user = await user_service.getUserById(decoded.userId); + if (!user) { + Error(res, { msg: 'Utilisateur non trouvé' }); + return + } + + // Hash du nouveau mot de passe + const hashedPassword = await bcrypt.hash(password, 10); + + // Mettez à jour le mot de passe de l'utilisateur + await user_service.updateUserPassword(Number(user.userId), hashedPassword); + Ok(res, { msg: 'Mot de passe réinitialisé avec succès' }); + return + } catch (error) { console.log(error); - Error(res, { msg: 'Token invalid or expire' }); - return - } + Error(res, { msg: 'Token invalid or expire' }); + return + } } export const renewToken = async (req: Request, res: Response) => { - const { userId } = req.body; + const { userId } = req.body; - try { + try { - const userToken = await registration_service.getRegistrationByUserId(userId); + const userToken = await registration_service.getRegistrationByUserId(userId); - if(userToken){ - await auth_service.deleteUserRegistrationToken(userId); - } + if (userToken) { + await auth_service.deleteUserRegistrationToken(userId); + } - const newToken = await auth_service.createRegistrationToken(userId) - - Ok(res, { - msg: 'Token renouvelé, vous pouvez renvoyer un email de bienvenu avec ce lien : https://integration.utt.fr/Register?token=' + newToken, - }); - } catch (err) { - Error(res, { msg: err.message }); - } -}; + const newToken = await auth_service.createRegistrationToken(userId) + Ok(res, { + msg: 'Token renouvelé, vous pouvez renvoyer un email de bienvenu avec ce lien : https://integration.utt.fr/Register?token=' + newToken, + }); + } catch (err) { + Error(res, { msg: err.message }); + } +}; diff --git a/backend/src/controllers/bus.controller.ts b/backend/src/controllers/bus.controller.ts index 0c530a0..2925a06 100644 --- a/backend/src/controllers/bus.controller.ts +++ b/backend/src/controllers/bus.controller.ts @@ -1,59 +1,58 @@ -// src/controllers/bus.controller.ts import { Request, Response } from "express"; import * as bus_service from "../services/bus.service"; import { sendEmail } from "../services/email.service"; import { Error, Ok } from "../utils/responses"; -import { generateEmailHtml } from "./email.controller"; +import { generateEmailHtml } from "./email.controller"; interface MulterRequest extends Request { - file?: Express.Multer.File; + file?: Express.Multer.File; } export const sendBusAttributionEmails = async (req: Request, res: Response) => { - try { - const attributions = await bus_service.getAllBusAttributions(); - - if (!attributions.length) { - Error(res, { msg: "Aucune attribution de bus trouvée." }); - return; - } - - for (const attr of attributions) { - const htmlEmail = generateEmailHtml("templateAttributionBus", { - bus: attr.bus, time: attr.departure_time - }); - - const emailOptions = { - from: "integration@utt.fr", - to: [attr.email], - cc: [], - bcc: [], - subject: `Attribution Bus - ${attr.firstName ?? ""} ${attr.lastName ?? ""}`, - text: `Votre bus attribué est le numéro ${attr.bus}`, - html: htmlEmail || "", - }; - - await sendEmail(emailOptions); + try { + const attributions = await bus_service.getAllBusAttributions(); + + if (!attributions.length) { + Error(res, { msg: "Aucune attribution de bus trouvée." }); + return; + } + + for (const attr of attributions) { + const htmlEmail = generateEmailHtml("templateAttributionBus", { + bus: attr.bus, time: attr.departure_time + }); + + const emailOptions = { + from: "integration@utt.fr", + to: [attr.email], + cc: [], + bcc: [], + subject: `Attribution Bus - ${attr.firstName ?? ""} ${attr.lastName ?? ""}`, + text: `Votre bus attribué est le numéro ${attr.bus}`, + html: htmlEmail || "", + }; + + await sendEmail(emailOptions); + } + + Ok(res, { msg: "Emails d'attribution bus envoyés avec succès !" }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de l'envoi des emails d'attribution bus." }); } - - Ok(res, { msg: "Emails d'attribution bus envoyés avec succès !" }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de l'envoi des emails d'attribution bus." }); - } }; export const uploadbusCSV = async (req: MulterRequest, res: Response) => { - try { - const file = req.file; - if (!file) { - Error(res, { msg: "Fichier CSV manquant." }); + try { + const file = req.file; + if (!file) { + Error(res, { msg: "Fichier CSV manquant." }); + } + + await bus_service.importBusFromCSV(file.path); + Ok(res, { msg: "Importation réalisée avec succès." }); + } catch (error) { + console.error("Erreur import CSV :", error); + Error(res, { msg: "Échec de l'importation." }); } - - await bus_service.importBusFromCSV(file.path); - Ok(res,{ msg: "Importation réalisée avec succès." }); - } catch (error) { - console.error("Erreur import CSV :", error); - Error(res, { msg: "Échec de l'importation." }); - } }; diff --git a/backend/src/controllers/challenge.controller.ts b/backend/src/controllers/challenge.controller.ts index c880067..0e606ca 100644 --- a/backend/src/controllers/challenge.controller.ts +++ b/backend/src/controllers/challenge.controller.ts @@ -1,46 +1,44 @@ import { Request, Response } from "express"; -import { Ok, Error, Unauthorized, Created } from "../utils/responses"; import * as challenge_service from "../services/challenge.service"; - -// === ADMIN PANEL === +import { Created, Error, Ok, Unauthorized } from "../utils/responses"; export const createChallenge = async (req: Request, res: Response) => { - const { title, description, category, points } = req.body; - const adminId = req.user.userId; - - try { - const challenge = await challenge_service.createChallenge(title, description, category, points, adminId ); - Created(res, { msg: "Challenge créé avec succès", data: challenge }); - } catch (err) { - Error(res, { msg: "Erreur lors de la création du challenge : " + err }); - } + const { title, description, category, points } = req.body; + const adminId = req.user.userId; + + try { + const challenge = await challenge_service.createChallenge(title, description, category, points, adminId); + Created(res, { msg: "Challenge créé avec succès", data: challenge }); + } catch (err) { + Error(res, { msg: "Erreur lors de la création du challenge : " + err }); + } }; export const deleteChallenge = async (req: Request, res: Response) => { const { challengeId } = req.query; - + try { - await challenge_service.deleteChallenge(Number(challengeId)); - Ok(res, { msg: "Challenge supprimée avec succès"}); + await challenge_service.deleteChallenge(Number(challengeId)); + Ok(res, { msg: "Challenge supprimée avec succès" }); } catch (err) { - Error(res, { msg: "Erreur lors de la suppression du challenge : " + err }); + Error(res, { msg: "Erreur lors de la suppression du challenge : " + err }); } - }; +}; export const validateChallenge = async (req: Request, res: Response) => { - const adminId = req.user.userId; - const { challengeId, type, targetId } = req.body; - - if (!["user", "team", "faction"].includes(type)) { - return Unauthorized(res, { msg: "Type de validation invalide." }); - } - - try { - const validation = await challenge_service.validateChallenge({ challengeId, type, targetId, validatedBy: adminId }); - Ok(res, { msg: "Challenge validé avec succès", data: validation }); - } catch (err) { - Error(res, { msg: "Erreur lors de la validation du challenge : " + err }); - } + const adminId = req.user.userId; + const { challengeId, type, targetId } = req.body; + + if (!["user", "team", "faction"].includes(type)) { + return Unauthorized(res, { msg: "Type de validation invalide." }); + } + + try { + const validation = await challenge_service.validateChallenge({ challengeId, type, targetId, validatedBy: adminId }); + Ok(res, { msg: "Challenge validé avec succès", data: validation }); + } catch (err) { + Error(res, { msg: "Erreur lors de la validation du challenge : " + err }); + } }; export const unvalidateChallenge = async (req: Request, res: Response) => { const { challengeId, factionId, teamId, userId } = req.body; @@ -49,85 +47,83 @@ export const unvalidateChallenge = async (req: Request, res: Response) => { userId ? userId : null try { - const unvalidation = await challenge_service.unvalidateChallenge({challengeId, factionId, teamId, userId}); - Ok(res, { msg: "Challenge invalidé avec succès", data: unvalidation }); + const unvalidation = await challenge_service.unvalidateChallenge({ challengeId, factionId, teamId, userId }); + Ok(res, { msg: "Challenge invalidé avec succès", data: unvalidation }); } catch (err) { - Error(res, { msg: "Erreur lors de l'invalidation du challenge : " + err }); + Error(res, { msg: "Erreur lors de l'invalidation du challenge : " + err }); } }; export const addPointsToFaction = async (req: Request, res: Response) => { - const adminId = req.user.userId; - const { title, factionId, points, reason } = req.body; - - try { - const result = await challenge_service.modifyFactionPoints({title, factionId, points, reason, adminId }); - Ok(res, { msg: "Points ajoutés à la faction", data: result }); - } catch (err) { - Error(res, { msg: "Erreur lors de l'ajout de points : " + err }); - } + const adminId = req.user.userId; + const { title, factionId, points, reason } = req.body; + + try { + const result = await challenge_service.modifyFactionPoints({ title, factionId, points, reason, adminId }); + Ok(res, { msg: "Points ajoutés à la faction", data: result }); + } catch (err) { + Error(res, { msg: "Erreur lors de l'ajout de points : " + err }); + } }; export const removePointsFromFaction = async (req: Request, res: Response) => { - const adminId = req.user.userId; - const { title, factionId, points, reason } = req.body; - - try { - const result = await challenge_service.modifyFactionPoints({ title, factionId, points: -Math.abs(points), reason, adminId }); - Ok(res, { msg: "Points retirés à la faction", data: result }); - } catch (err) { - Error(res, { msg: "Erreur lors du retrait de points : " + err }); - } + const adminId = req.user.userId; + const { title, factionId, points, reason } = req.body; + + try { + const result = await challenge_service.modifyFactionPoints({ title, factionId, points: -Math.abs(points), reason, adminId }); + Ok(res, { msg: "Points retirés à la faction", data: result }); + } catch (err) { + Error(res, { msg: "Erreur lors du retrait de points : " + err }); + } }; export const updateChallenge = async (req: Request, res: Response) => { const { id, title, description, category, points } = req.body; try { - const updated = await challenge_service.modifyChallenge({ - challengeId: id, - title, - description, - category, - points, - }); - Ok(res, { msg: "Challenge mis à jour avec succès", data: updated }); + const updated = await challenge_service.modifyChallenge({ + challengeId: id, + title, + description, + category, + points, + }); + Ok(res, { msg: "Challenge mis à jour avec succès", data: updated }); } catch (err) { - Error(res, { msg: "Erreur lors de la mise à jour : " + err }); + Error(res, { msg: "Erreur lors de la mise à jour : " + err }); } }; export const getValidatedChallenges = async (req: Request, res: Response) => { - try{ + try { const challengesValidated = await challenge_service.getValidatedChallenges(); Ok(res, { data: challengesValidated }); - }catch(err){ + } catch (err) { Error(res, { msg: "Erreur lors de la récupération des challenges validés " + err }); } }; - + // === PUBLIC === export const getAllChallenges = async (req: Request, res: Response) => { - try { - const challenges = await challenge_service.getAllChallenges(); - Ok(res, { data: challenges }); - } catch (err) { - Error(res, { msg: "Erreur lors de la récupération des challenges : " + err }); - } + try { + const challenges = await challenge_service.getAllChallenges(); + Ok(res, { data: challenges }); + } catch (err) { + Error(res, { msg: "Erreur lors de la récupération des challenges : " + err }); + } }; export const getTotalFactionPoints = async (req: Request, res: Response) => { - const {factionId} = req.query; + const { factionId } = req.query; try { - const points = await challenge_service.getTotalFactionPoints(Number(factionId)); - Ok(res, { data: points }); + const points = await challenge_service.getTotalFactionPoints(Number(factionId)); + Ok(res, { data: points }); } catch (err) { - Error(res, { msg: "Erreur lors de la récupération des points : " + err }); + Error(res, { msg: "Erreur lors de la récupération des points : " + err }); } }; - - diff --git a/backend/src/controllers/discord.controller.ts b/backend/src/controllers/discord.controller.ts index 8acaa4c..cb977e8 100644 --- a/backend/src/controllers/discord.controller.ts +++ b/backend/src/controllers/discord.controller.ts @@ -1,31 +1,21 @@ import { Request, Response } from "express"; -import { Ok, Error, Unauthorized, Created } from "../utils/responses"; import * as discord_service from "../services/discord.service"; - -// === ADMIN PANEL === +import { Error, Ok } from "../utils/responses"; export const createChallenge = async (req: Request, res: Response) => { - - const {code} = req.body; - const userId = req.user?.userId; + const { code } = req.body; + const userId = req.user?.userId; if (!code) { - - Error(res, {msg :"Code manquant dans l'URL"}) - return; + Error(res, { msg: "Code manquant dans l'URL" }) + return; } try { const discordUser = await discord_service.syncDiscordUserId(String(code), userId); - - // ✨ Ici tu peux lier le compte Discord à l'utilisateur connecté sur ton site - // Ex : await userService.linkDiscord(userId, discordUser.id); - - console.log("Utilisateur Discord reçu :", discordUser); - - Ok(res, {msg:`Ton compte Discord (${discordUser.username}#${discordUser.discriminator}) a bien été lié à ton profil UTT.`}); + Ok(res, { msg: `Ton compte Discord (${discordUser.username}#${discordUser.discriminator}) a bien été lié à ton profil UTT.` }); } catch (err) { console.error("Erreur dans handleDiscordCallback:", err); - Error(res, {msg :"Erreur pendant la liaison avec Discord"}); + Error(res, { msg: "Erreur pendant la liaison avec Discord" }); } -}; \ No newline at end of file +}; diff --git a/backend/src/controllers/email.controller.ts b/backend/src/controllers/email.controller.ts index 7debcd8..5b76e22 100644 --- a/backend/src/controllers/email.controller.ts +++ b/backend/src/controllers/email.controller.ts @@ -1,140 +1,140 @@ import { Request, Response } from 'express'; import sanitizeHtml from 'sanitize-html'; import { sendEmail } from '../services/email.service'; -import * as user_service from '../services/user.service'; import * as registration_service from '../services/registration.service'; -import { Error, Ok } from '../utils/responses'; +import * as user_service from '../services/user.service'; import * as template from '../utils/emailtemplates'; +import { Error, Ok } from '../utils/responses'; export interface EmailOptions { - from: string; - to: string[]; - subject: string; - text?: string; - html: string; - cc: string[]; - bcc: string[]; + from: string; + to: string[]; + subject: string; + text?: string; + html: string; + cc: string[]; + bcc: string[]; } // Fonction pour générer l'HTML à partir du template export const generateEmailHtml = (templateName: string, data: any) => { - switch (templateName) { - case 'templateNotebook': - return template.compileTemplate({ notebook: data.notebook }, template.templateNotebook); - - case 'templateAttributionBus': - return template.compileTemplate({ bus: data.bus, time: data.time }, template.templateAttributionBus); - - case 'templateWelcome': - return template.compileTemplate({ token: data.token }, template.templateWelcome); - - case 'templateNotifyNews': - return template.compileTemplate( - { title: data.title, description: data.description }, - template.templateNotifyNews - ); - - case 'templateNotifyTentConfirmation': - return template.compileTemplate( - { user1: data.user1, user2: data.user2, confirmed: data.confirmed }, - template.templateNotifyTentConfirmation - ); - - default: - return null; - } + switch (templateName) { + case 'templateNotebook': + return template.compileTemplate({ notebook: data.notebook }, template.templateNotebook); + + case 'templateAttributionBus': + return template.compileTemplate({ bus: data.bus, time: data.time }, template.templateAttributionBus); + + case 'templateWelcome': + return template.compileTemplate({ token: data.token }, template.templateWelcome); + + case 'templateNotifyNews': + return template.compileTemplate( + { title: data.title, description: data.description }, + template.templateNotifyNews + ); + + case 'templateNotifyTentConfirmation': + return template.compileTemplate( + { user1: data.user1, user2: data.user2, confirmed: data.confirmed }, + template.templateNotifyTentConfirmation + ); + + default: + return null; + } }; // Fonction utilitaire pour récupérer les destinataires const getRecipients = async (permission: string | undefined, sendTo: string[] | undefined) => { - if (permission) { - const users = await user_service.getUsersbyPermission(permission); - return users.map((user) => user.email); - } else { - return sendTo || []; - } + if (permission) { + const users = await user_service.getUsersbyPermission(permission); + return users.map((user) => user.email); + } else { + return sendTo || []; + } }; export const handleSendEmail = async (req: Request, res: Response) => { - const { subject, templateName, format, permission, sendTo, text, html } = req.body.payload; + const { subject, templateName, format, permission, sendTo, text, html } = req.body.payload; - try { - // Récupérer les destinataires - const recipients = await getRecipients(permission, sendTo); + try { + // Récupérer les destinataires + const recipients = await getRecipients(permission, sendTo); - if (!recipients.length) { - Error(res, { msg: 'Aucun destinataire trouvé.' }); - return; - } + if (!recipients.length) { + Error(res, { msg: 'Aucun destinataire trouvé.' }); + return; + } - for (const recp of recipients) { - let htmlEmail = ''; - - if(templateName !== 'custom'){ - - if(templateName === "templateWelcome" ){ - let token; - let user = await user_service.getUserByEmail(recp); - token = await registration_service.getRegistrationByUserId(user.id); - if(!token) continue; - // Générer le contenu HTML du mail - htmlEmail = generateEmailHtml(templateName, {token : token}); - - } - if(templateName === "templateNotebook"){ - htmlEmail = generateEmailHtml(templateName, {notebook : 'https://drive.google.com/file/d/1Tl8UeILFlAdj9IC2vy3gYXdXCOzD4ugX/view?usp=sharing'}); - } - if(templateName === "templateAttributionBus"){ - htmlEmail = generateEmailHtml(templateName, {bus : 'bus', time: '09h00'}); - } - } - - else { - htmlEmail = sanitizeHtml(html || ''); - } + for (const recp of recipients) { + let htmlEmail = ''; + + if (templateName !== 'custom') { + + if (templateName === "templateWelcome") { + let token; + let user = await user_service.getUserByEmail(recp); + token = await registration_service.getRegistrationByUserId(user.id); + if (!token) continue; + // Générer le contenu HTML du mail + htmlEmail = generateEmailHtml(templateName, { token: token }); - const emailOptions: EmailOptions = { - from: "integration@utt.fr", - to: [recp], - cc: [], - bcc: [], - subject, - text: req.body.payload.text || '', - html: htmlEmail, - }; - - await sendEmail(emailOptions); - } - - - Ok(res, { msg: 'Email envoyé avec succès !' }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: 'Erreur lors de l\'envoi de l\'email.' }); - return; - } + } + if (templateName === "templateNotebook") { + htmlEmail = generateEmailHtml(templateName, { notebook: 'https://drive.google.com/file/d/1Tl8UeILFlAdj9IC2vy3gYXdXCOzD4ugX/view?usp=sharing' }); + } + if (templateName === "templateAttributionBus") { + htmlEmail = generateEmailHtml(templateName, { bus: 'bus', time: '09h00' }); + } + } + + else { + htmlEmail = sanitizeHtml(html || ''); + } + + const emailOptions: EmailOptions = { + from: "integration@utt.fr", + to: [recp], + cc: [], + bcc: [], + subject, + text: req.body.payload.text || '', + html: htmlEmail, + }; + + await sendEmail(emailOptions); + } + + + Ok(res, { msg: 'Email envoyé avec succès !' }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: 'Erreur lors de l\'envoi de l\'email.' }); + return; + } }; export const handlePreviewEmail = async (req: Request, res: Response) => { - const { templateName } = req.body; + const { templateName } = req.body; - try { - // Générer le contenu HTML pour l'aperçu - const htmlEmail = generateEmailHtml(templateName, {}); + try { + // Générer le contenu HTML pour l'aperçu + const htmlEmail = generateEmailHtml(templateName, {}); - if (!htmlEmail) { - Error(res, { msg: "Nom de template invalide" }); - return; - } + if (!htmlEmail) { + Error(res, { msg: "Nom de template invalide" }); + return; + } - Ok(res, { data: htmlEmail }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: 'Erreur lors de la génération de la preview.' }); - return; - } + Ok(res, { data: htmlEmail }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: 'Erreur lors de la génération de la preview.' }); + return; + } }; diff --git a/backend/src/controllers/event.controller.ts b/backend/src/controllers/event.controller.ts index 806cd1f..923a9d2 100644 --- a/backend/src/controllers/event.controller.ts +++ b/backend/src/controllers/event.controller.ts @@ -1,19 +1,15 @@ import { Request, Response } from "express"; -import { Accepted, Conflict, Error, Ok, Teapot, Unauthorized } from "../utils/responses"; import * as event_service from "../services/event.service"; import * as team_service from "../services/team.service"; -import { Event } from "../schemas/Basic/event.schema"; +import { Conflict, Error, Ok, Teapot, Unauthorized } from "../utils/responses"; import { shotgun_password } from "../utils/secret"; type AuthenticatedRequest = Request & { user?: { userId?: number } }; - export const checkShotgunStatus = async (req: Request, res: Response) => { try { const status = await event_service.getEventsStatus(); - Ok(res, ({ data: { status: Boolean(status?.shotgun_open), password: Boolean(status?.shotgun_open) ? shotgun_password : "" } })); - } catch (error) { Error(res, { msg: "Error while catching shotgun status :" + error }) } @@ -23,7 +19,6 @@ export const checkPreRegisterStatus = async (req: Request, res: Response) => { try { const status = await event_service.getEventsStatus(); Ok(res, ({ data: status?.pre_registration_open })); - } catch (error) { Error(res, { msg: "Error while catching pre-registration status :" + error }) } @@ -33,7 +28,6 @@ export const checkSDIStatus = async (req: Request, res: Response) => { try { const status = await event_service.getEventsStatus(); Ok(res, ({ data: status?.sdi_open })); - } catch (error) { Error(res, { msg: "Error while catching SDI status :" + error }) } @@ -43,7 +37,6 @@ export const checkWEIStatus = async (req: Request, res: Response) => { try { const status = await event_service.getEventsStatus(); Ok(res, ({ data: status?.wei_open })); - } catch (error) { Error(res, { msg: "Error while catching WEI status :" + error }) } @@ -53,7 +46,6 @@ export const checkFoodStatus = async (req: Request, res: Response) => { try { const status = await event_service.getEventsStatus(); Ok(res, ({ data: status?.food_open })); - } catch (error) { Error(res, { msg: "Error while catching Food status :" + error }) } @@ -63,7 +55,6 @@ export const checkChallStatus = async (req: Request, res: Response) => { try { const status = await event_service.getEventsStatus(); Ok(res, ({ data: status?.chall_open })); - } catch (error) { Error(res, { msg: "Error while catching Challenge status :" + error }) } @@ -206,4 +197,4 @@ export const toggleChall = async (req: Request, res: Response) => { } catch (error) { Error(res, { msg: "Erreur lors de la mise à jour." }); } -}; \ No newline at end of file +}; diff --git a/backend/src/controllers/faction.controller.ts b/backend/src/controllers/faction.controller.ts index 64b3001..2336872 100644 --- a/backend/src/controllers/faction.controller.ts +++ b/backend/src/controllers/faction.controller.ts @@ -1,57 +1,49 @@ import { Request, Response } from 'express'; import * as faction_service from '../services/faction.service'; -import { Ok, Error } from '../utils/responses'; +import { Error, Ok } from '../utils/responses'; export const getFactions = async (req: Request, res: Response) => { - try { const factions = await faction_service.getFactions(); Ok(res, { data: factions }); return; - } catch (error) { Error(res, { msg: "Erreur lors de la récupération des factions" }); } }; export const getFaction = async (req: Request, res: Response) => { - - const {factionId} = req.query; + const { factionId } = req.query; try { const faction = await faction_service.getFaction(factionId); Ok(res, { data: faction }); return; - } catch (error) { Error(res, { msg: "Erreur lors de la récupération des factions" }); } }; export const createFaction = async (req: Request, res: Response) => { + const { factionName } = req.body - - const {factionName} = req.body try { const faction = await faction_service.createFaction(factionName); Ok(res, { msg: "Faction crée avec succès !" }); return; - } catch (error) { Error(res, { msg: "Erreur lors de la création de la faction" }); } }; export const deleteFaction = async (req: Request, res: Response) => { - - const {factionId} = req.query + const { factionId } = req.query try { await faction_service.deleteFaction(Number(factionId)); Ok(res, { msg: "Faction supprimée avec succès !" }); return; - } catch (error) { Error(res, { msg: "Erreur lors de la suppression de la faction" }); } -}; \ No newline at end of file +}; diff --git a/backend/src/controllers/im_export.controller.ts b/backend/src/controllers/im_export.controller.ts index a220073..1e66068 100644 --- a/backend/src/controllers/im_export.controller.ts +++ b/backend/src/controllers/im_export.controller.ts @@ -1,168 +1,159 @@ import { Request, Response } from "express"; +import fs from "fs"; +import path from "path"; +import * as event_service from "../services/event.service"; import * as export_service from "../services/im_export.service"; -import * as user_service from '../services/user.service'; -import * as team_service from "../services/team.service"; import * as permanence_service from "../services/permanence.service"; -import * as event_service from "../services/event.service"; +import * as team_service from "../services/team.service"; +import * as user_service from '../services/user.service'; +import { Error, Ok } from "../utils/responses"; import { spreadsheet_id } from "../utils/secret"; -import { Ok, Error } from "../utils/responses"; -import path from "path"; -import fs from "fs"; export const exportAllDataToSheets = async (req: Request, res: Response) => { - try { - // 1. Récupération depuis la DB - const userList = await user_service.getUsersAll(); - const teamList = await team_service.getTeamsAll(); - const permanenceList = await permanence_service.getAllPermanencesWithUsers(); - const shotgunList = await event_service.getAllTeamShotguns(); - - - // 2. Mapping -> format pour Google Sheets (array de array) - const usersValues = [ - ["ID", "Prénom", "Nom", "Email", "Branche", "Permission", "Majeur", "Contact","Discord", "Team", "Faction"], - ...userList.map(u => [ - u.id ?? 0, - u.first_name ?? "No first name", - u.last_name ?? "No last name", - u.email ?? "No email", - u.branch ?? "No branch", - u.permission ?? "No permissions", - u.majeur ?? "Pas de données", - u.contact ?? "No contact", - u.discord_id ?? "No discord ID", - u.teamName ?? "No Team", - u.factionName ?? "No faction" - ]) - ]; - - const teamsValues = [ - ["ID", "Nom", "Type", "Faction"], - ...teamList.map(t => [ - t.id, - t.name ?? "No name", - t.type ?? "No type", - t.teamFaction?.name ?? "No faction" - ]) - ]; - -const permanenceValues = [ - [ - "ID", - "Nom", - "Début", - "Fin", - "Lieu", - "Responsables", - "Inscrits (noms)", - "Inscrits (emails)", - "Présents", - "Absents" - ], - ...permanenceList.map((p) => { - const respoNames = p.respo ? p.respo.firstName + " " + p.respo.lastName : "Aucun"; - const userNames = p.users?.map((u) => `${u.first_name} ${u.last_name}`)?.join(" ; ") || "Aucun inscrit"; - const userEmails = p.users?.map((u) => u.email)?.join(" ; ") || "Aucun inscrit"; - - const claimedUsers = p.users?.filter((u) => u.claimed)?.map((u) => `${u.first_name} ${u.last_name}`)?.join(" ; ") || "Aucun"; - const unclaimedUsers = p.users?.filter((u) => !u.claimed)?.map((u) => `${u.first_name} ${u.last_name}`)?.join(" ; ") || "Aucun"; - - return [ - p.id, - p.name ?? "Sans nom", - p.start_at ? new Date(p.start_at).toLocaleString("fr-FR") : "N/A", - p.end_at ? new Date(p.end_at).toLocaleString("fr-FR") : "N/A", - p.location ?? "Sans lieu", - respoNames, - userNames, - userEmails, - claimedUsers, - unclaimedUsers - ]; - }) -]; - - - const shotgunValues = [ - ["ID", "Nom de l'équipe", "Type", "Horodatage"], - ...shotgunList.map(s => [ - s.id, - s.teamName ?? "No name", - s.teamType ?? "No type", - s.timestamp?.toISOString() ?? "No timestamp" - ]) - ]; - // 3. Envoi vers les feuilles - await export_service.writeToGoogleSheet(spreadsheet_id, "USER!A1", usersValues); - await export_service.writeToGoogleSheet(spreadsheet_id, "TEAM!A1", teamsValues); - await export_service.writeToGoogleSheet(spreadsheet_id, "PERMANENCES!A1", permanenceValues); - await export_service.writeToGoogleSheet(spreadsheet_id, "SHOTGUN!A1", shotgunValues); - - - Ok(res, { msg: "Export réalisé avec succès !" }); - - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de l'export vers Google Sheets" }); - } + try { + // 1. Récupération depuis la DB + const userList = await user_service.getUsersAll(); + const teamList = await team_service.getTeamsAll(); + const permanenceList = await permanence_service.getAllPermanencesWithUsers(); + const shotgunList = await event_service.getAllTeamShotguns(); + + // 2. Mapping -> format pour Google Sheets (array de array) + const usersValues = [ + ["ID", "Prénom", "Nom", "Email", "Branche", "Permission", "Majeur", "Contact", "Discord", "Team", "Faction"], + ...userList.map(u => [ + u.id ?? 0, + u.first_name ?? "No first name", + u.last_name ?? "No last name", + u.email ?? "No email", + u.branch ?? "No branch", + u.permission ?? "No permissions", + u.majeur ?? "Pas de données", + u.contact ?? "No contact", + u.discord_id ?? "No discord ID", + u.teamName ?? "No Team", + u.factionName ?? "No faction" + ]) + ]; + + const teamsValues = [ + ["ID", "Nom", "Type", "Faction"], + ...teamList.map(t => [ + t.id, + t.name ?? "No name", + t.type ?? "No type", + t.teamFaction?.name ?? "No faction" + ]) + ]; + + const permanenceValues = [ + [ + "ID", + "Nom", + "Début", + "Fin", + "Lieu", + "Responsables", + "Inscrits (noms)", + "Inscrits (emails)", + "Présents", + "Absents" + ], + ...permanenceList.map((p) => { + const respoNames = p.respo ? p.respo.firstName + " " + p.respo.lastName : "Aucun"; + const userNames = p.users?.map((u) => `${u.first_name} ${u.last_name}`)?.join(" ; ") || "Aucun inscrit"; + const userEmails = p.users?.map((u) => u.email)?.join(" ; ") || "Aucun inscrit"; + + const claimedUsers = p.users?.filter((u) => u.claimed)?.map((u) => `${u.first_name} ${u.last_name}`)?.join(" ; ") || "Aucun"; + const unclaimedUsers = p.users?.filter((u) => !u.claimed)?.map((u) => `${u.first_name} ${u.last_name}`)?.join(" ; ") || "Aucun"; + + return [ + p.id, + p.name ?? "Sans nom", + p.start_at ? new Date(p.start_at).toLocaleString("fr-FR") : "N/A", + p.end_at ? new Date(p.end_at).toLocaleString("fr-FR") : "N/A", + p.location ?? "Sans lieu", + respoNames, + userNames, + userEmails, + claimedUsers, + unclaimedUsers + ]; + }) + ]; + + const shotgunValues = [ + ["ID", "Nom de l'équipe", "Type", "Horodatage"], + ...shotgunList.map(s => [ + s.id, + s.teamName ?? "No name", + s.teamType ?? "No type", + s.timestamp?.toISOString() ?? "No timestamp" + ]) + ]; + // 3. Envoi vers les feuilles + await export_service.writeToGoogleSheet(spreadsheet_id, "USER!A1", usersValues); + await export_service.writeToGoogleSheet(spreadsheet_id, "TEAM!A1", teamsValues); + await export_service.writeToGoogleSheet(spreadsheet_id, "PERMANENCES!A1", permanenceValues); + await export_service.writeToGoogleSheet(spreadsheet_id, "SHOTGUN!A1", shotgunValues); + + Ok(res, { msg: "Export réalisé avec succès !" }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de l'export vers Google Sheets" }); + } }; - export const updateFoodMenu = async (req: Request, res: Response) => { - const file = req.file; + const file = req.file; - try { + try { + // Supprimer l'ancien Menu si un nouveau est uploadé + if (file) { + const targetDir = path.join(__dirname, "../../foodmenu"); - // Supprimer l'ancien Menu si un nouveau est uploadé - if (file) { - const targetDir = path.join(__dirname, "../../foodmenu"); + if (fs.existsSync(targetDir)) { + fs.rmSync(targetDir, { recursive: true, force: true }); + fs.mkdirSync(targetDir); + } + } - if (fs.existsSync(targetDir)) { - fs.rmSync(targetDir, { recursive: true, force: true }); - fs.mkdirSync(targetDir); - } + Ok(res, { msg: "Menu mis à jour avec succès" }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la mise à jour du Menu" }); } - - Ok(res, { msg: "Menu mis à jour avec succès" }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la mise à jour du Menu" }); - } }; export const updatePlannings = async (req: Request, res: Response) => { - - const file = req.file; - - try { - - // Supprimer l'ancien Planning si un nouveau est uploadé - if (file) { - const targetDir = path.join(__dirname, "../../plannings"); - - if (fs.existsSync(targetDir)) { - fs.rmSync(targetDir, { recursive: true, force: true }); - fs.mkdirSync(targetDir); - } + const file = req.file; + + try { + // Supprimer l'ancien Planning si un nouveau est uploadé + if (file) { + const targetDir = path.join(__dirname, "../../plannings"); + + if (fs.existsSync(targetDir)) { + fs.rmSync(targetDir, { recursive: true, force: true }); + fs.mkdirSync(targetDir); + } + } + + Ok(res, { msg: "Planning mis à jour avec succès" }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la mise à jour du Planning" }); } - - Ok(res, { msg: "Planning mis à jour avec succès" }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la mise à jour du Planning" }); - } }; - export const exportUsersCSV = async (req: Request, res: Response) => { - try { - const filePath = await export_service.exportUsersToCSV(); - Ok(res, {msg : 'CSV des bus généré'}); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de l'export CSV" }); - } -}; \ No newline at end of file + try { + const filePath = await export_service.exportUsersToCSV(); + Ok(res, { msg: 'CSV des bus généré' }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de l'export CSV" }); + } +}; diff --git a/backend/src/controllers/news.controller.ts b/backend/src/controllers/news.controller.ts index 92994c0..e017031 100644 --- a/backend/src/controllers/news.controller.ts +++ b/backend/src/controllers/news.controller.ts @@ -1,160 +1,149 @@ import { Request, Response } from "express"; +import fs from "fs"; +import path from "path"; +import * as email_service from "../services/email.service"; import * as news_service from "../services/news.service"; -import { Ok, Error } from "../utils/responses"; +import * as user_service from '../services/user.service'; import * as template from "../utils/emailtemplates"; -import * as email_service from "../services/email.service"; -import * as user_service from '../services/user.service' -import path from "path"; -import fs from "fs"; +import { Error, Ok } from "../utils/responses"; export const createNews = async (req: Request, res: Response) => { + const { title, description, type, published, target } = req.body; + const file = req.file; - - const { title, description, type, published, target } = req.body; - const file = req.file; - - try { - - const image_url = file ? `/api/uploads/imgnews/${file.filename}` : undefined; - const news = await news_service.createNews( - title, - description, - type, - published, - target, - image_url); - Ok(res, { msg: "Actu créée avec succès", data: news }); - ; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la création de l'actu" }); - } + try { + const image_url = file ? `/api/uploads/imgnews/${file.filename}` : undefined; + const news = await news_service.createNews( + title, + description, + type, + published, + target, + image_url); + Ok(res, { msg: "Actu créée avec succès", data: news }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la création de l'actu" }); + } }; export const listAllNews = async (_req: Request, res: Response) => { - try { - const news = await news_service.getAllNews(); - Ok(res, { data: news }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la récupération des actus" }); - } + try { + const news = await news_service.getAllNews(); + Ok(res, { data: news }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la récupération des actus" }); + } }; export const listPublishedNews = async (_req: Request, res: Response) => { - try { - const news = await news_service.getPublishedNews(); - Ok(res, { data: news }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la récupération des actus publiées" }); - } + try { + const news = await news_service.getPublishedNews(); + Ok(res, { data: news }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la récupération des actus publiées" }); + } }; export const listPublishedNewsByType = async (req: Request, res: Response) => { + const { type } = req.query; - const { type } = req.query; - - try { - const news = await news_service.getPublishedNewsByType(type as string); - Ok(res, { data: news }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la récupération des actus par type" }); - } + try { + const news = await news_service.getPublishedNewsByType(type as string); + Ok(res, { data: news }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la récupération des actus par type" }); + } }; export const publishNews = async (req: Request, res: Response) => { - const { id, sendEmail } = req.body; - - try { - await news_service.publishNews(id); - - const news = await news_service.getNewsById(Number(id)); - if(sendEmail){ - // Génération du mail HTML - const html = template.compileTemplate({title : news.title}, template.templateNotifyNews); - - const recipients = news.target === "Tous" - ? (await user_service.getUsersAdmin()).map(u => u.email) - : (await user_service.getUsersbyPermission(news.target)).map(u => u.email); - - if(recipients.length === 0){ - Error(res, {msg : "No recipients"}); - - } - - const email = { - from: "integration@utt.fr", - to: [], - subject: `[INTEGRATION UTT] Nouvelle actu : ${news.title}`, - html : html, - cc: [], - bcc: recipients, - }; - - await email_service.sendEmail(email); - } + const { id, sendEmail } = req.body; + + try { + await news_service.publishNews(id); + + const news = await news_service.getNewsById(Number(id)); + if (sendEmail) { + // Génération du mail HTML + const html = template.compileTemplate({ title: news.title }, template.templateNotifyNews); + + const recipients = news.target === "Tous" + ? (await user_service.getUsersAdmin()).map(u => u.email) + : (await user_service.getUsersbyPermission(news.target)).map(u => u.email); + + if (recipients.length === 0) { + Error(res, { msg: "No recipients" }); + } + + const email = { + from: "integration@utt.fr", + to: [], + subject: `[INTEGRATION UTT] Nouvelle actu : ${news.title}`, + html: html, + cc: [], + bcc: recipients, + }; + + await email_service.sendEmail(email); + } - Ok(res, { msg: "Actu publiée" }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la publication ou de la notification" }); - } + Ok(res, { msg: "Actu publiée" }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la publication ou de la notification" }); + } }; export const deleteNews = async (req: Request, res: Response) => { - - const {newsId} = req.query + const { newsId } = req.query try { - - const existing = await news_service.getNewsById(Number(newsId)); - if (existing?.image_url) { - const imagePath = path.join(__dirname, "../../", existing.image_url); - if (fs.existsSync(imagePath)) { - fs.unlinkSync(imagePath); + const existing = await news_service.getNewsById(Number(newsId)); + if (existing?.image_url) { + const imagePath = path.join(__dirname, "../../", existing.image_url); + if (fs.existsSync(imagePath)) { + fs.unlinkSync(imagePath); + } } - } - await news_service.deleteNews(Number(newsId)); - Ok(res, { msg: "Actus supprimée avec succès !" }); - ; + await news_service.deleteNews(Number(newsId)); + Ok(res, { msg: "Actus supprimée avec succès !" }); + ; } catch (error) { Error(res, { msg: "Erreur lors de la suppression de l'actus" }); } }; - export const updateNews = async (req: Request, res: Response) => { - const { id, title, description, type, target } = req.body; - const file = req.file; - const image_url = file ? `/api/uploads/imgnews/${file.filename}` : undefined; - - try { - const existing = await news_service.getNewsById(Number(id)); - if (!existing) { - Error(res, { msg: "Actu introuvable" }); - } + const { id, title, description, type, target } = req.body; + const file = req.file; + const image_url = file ? `/api/uploads/imgnews/${file.filename}` : undefined; - // Supprimer l'ancienne image si une nouvelle est uploadée - if (file && existing.image_url) { - const oldPath = path.join(__dirname, "../../", existing.image_url); - if (fs.existsSync(oldPath)) { - fs.unlinkSync(oldPath); - } - } + try { + const existing = await news_service.getNewsById(Number(id)); + if (!existing) { + Error(res, { msg: "Actu introuvable" }); + } + + // Supprimer l'ancienne image si une nouvelle est uploadée + if (file && existing.image_url) { + const oldPath = path.join(__dirname, "../../", existing.image_url); + if (fs.existsSync(oldPath)) { + fs.unlinkSync(oldPath); + } + } - const updates: any = { title, description, type, target }; - if (image_url) updates.image_url = image_url; + const updates: any = { title, description, type, target }; + if (image_url) updates.image_url = image_url; - const updated = await news_service.updateNews(Number(id), updates); + const updated = await news_service.updateNews(Number(id), updates); - Ok(res, { msg: "Actu mise à jour avec succès", data: updated }); - ; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la mise à jour de l'actu" }); - } + Ok(res, { msg: "Actu mise à jour avec succès", data: updated }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la mise à jour de l'actu" }); + } }; - \ No newline at end of file diff --git a/backend/src/controllers/permanence.controller.ts b/backend/src/controllers/permanence.controller.ts index f873651..006c525 100644 --- a/backend/src/controllers/permanence.controller.ts +++ b/backend/src/controllers/permanence.controller.ts @@ -1,91 +1,91 @@ import { Request, Response } from "express"; import * as permanence_service from "../services/permanence.service"; -import { Ok, Error } from "../utils/responses"; +import { Error, Ok } from "../utils/responses"; interface MulterRequest extends Request { - file?: Express.Multer.File; + file?: Express.Multer.File; } // Validation des données de permanence const validatePermanenceData = (start_at: string, end_at: string) => { - const startDate = new Date(start_at); - const endDate = new Date(end_at); + const startDate = new Date(start_at); + const endDate = new Date(end_at); - if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { - return { valid: false, msg: "Les dates de début et de fin doivent être valides" }; - } + if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { + return { valid: false, msg: "Les dates de début et de fin doivent être valides" }; + } - if (startDate >= endDate) { - return { valid: false, msg: "La date de début doit être avant la date de fin" }; - } + if (startDate >= endDate) { + return { valid: false, msg: "La date de début doit être avant la date de fin" }; + } - return { valid: true }; + return { valid: true }; }; // ➕ Créer une permanence export const createPermanence = async (req: Request, res: Response) => { - const { name, description, location, start_at, end_at, capacity, difficulty, respoId } = req.body; - - if (!name || !location || !start_at || !end_at || !capacity || !difficulty) { - Error(res, { msg: "Tous les champs sont requis" }); - return; - } - - const validation = validatePermanenceData(start_at, end_at); - if (!validation.valid) { - Error(res, { msg: validation.msg }); - return; - } - - try { - await permanence_service.createPermanence( - name, - description, - location, - new Date(start_at), - new Date(end_at), - Number(capacity), - Number(difficulty), - Number(respoId), - ); - Ok(res, { msg: "Permanence créée avec succès" }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la création de la permanence" }); - } + const { name, description, location, start_at, end_at, capacity, difficulty, respoId } = req.body; + + if (!name || !location || !start_at || !end_at || !capacity || !difficulty) { + Error(res, { msg: "Tous les champs sont requis" }); + return; + } + + const validation = validatePermanenceData(start_at, end_at); + if (!validation.valid) { + Error(res, { msg: validation.msg }); + return; + } + + try { + await permanence_service.createPermanence( + name, + description, + location, + new Date(start_at), + new Date(end_at), + Number(capacity), + Number(difficulty), + Number(respoId), + ); + Ok(res, { msg: "Permanence créée avec succès" }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la création de la permanence" }); + } }; export const updatePermanence = async (req: Request, res: Response) => { - const { permId, name, description, location, start_at, end_at, capacity, difficulty, respoId } = req.body; - - const validation = validatePermanenceData(start_at, end_at); - if (!validation.valid) { - Error(res, { msg: validation.msg }); - return; - } - - try { - const perm = await permanence_service.getPermanenceById(permId); - - await permanence_service.updatePermanence( - permId ?? perm.id, - name ?? perm.name, - description ?? perm.description, - location ?? perm.location, - start_at ? new Date(start_at) : perm.start_at, - end_at ? new Date(end_at) : perm.end_at, - capacity !== undefined ? Number(capacity) : perm.capacity, - difficulty !== undefined ? Number(difficulty) : perm.difficulty, - Number(respoId) - ); - - Ok(res, { msg: "Permanence mise à jour avec succès" }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la mise à jour de la permanence" }); - } + const { permId, name, description, location, start_at, end_at, capacity, difficulty, respoId } = req.body; + + const validation = validatePermanenceData(start_at, end_at); + if (!validation.valid) { + Error(res, { msg: validation.msg }); + return; + } + + try { + const perm = await permanence_service.getPermanenceById(permId); + + await permanence_service.updatePermanence( + permId ?? perm.id, + name ?? perm.name, + description ?? perm.description, + location ?? perm.location, + start_at ? new Date(start_at) : perm.start_at, + end_at ? new Date(end_at) : perm.end_at, + capacity !== undefined ? Number(capacity) : perm.capacity, + difficulty !== undefined ? Number(difficulty) : perm.difficulty, + Number(respoId) + ); + + Ok(res, { msg: "Permanence mise à jour avec succès" }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la mise à jour de la permanence" }); + } }; @@ -95,283 +95,279 @@ export const deletePermanence = async (req: Request, res: Response) => { const { permId } = req.query; try { - await permanence_service.deletePermanence(Number(permId)); - Ok(res, { msg: "Permanence supprimée avec succès" }); - return; + await permanence_service.deletePermanence(Number(permId)); + Ok(res, { msg: "Permanence supprimée avec succès" }); + return; } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la suppression de la permanence" }); + console.error(err); + Error(res, { msg: "Erreur lors de la suppression de la permanence" }); } - }; +}; // ➡️ Ouvrir une permanence export const openPermanence = async (req: Request, res: Response) => { - const { permId } = req.body; - - if (!permId) { - Error(res, { msg: "L'ID de la permanence est requis" }); - return; - } - - try { - const permanence = await permanence_service.getPermanenceById(permId); - if (permanence.is_open === true) { - Error(res, { msg: "La permanence est déjà ouverte" }); - return; - } - - await permanence_service.openPermanence(Number(permId)); - Ok(res, { msg: "Permanence ouverte avec succès" }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de l'ouverture de la permanence" }); - } + const { permId } = req.body; + + if (!permId) { + Error(res, { msg: "L'ID de la permanence est requis" }); + return; + } + + try { + const permanence = await permanence_service.getPermanenceById(permId); + if (permanence.is_open === true) { + Error(res, { msg: "La permanence est déjà ouverte" }); + return; + } + + await permanence_service.openPermanence(Number(permId)); + Ok(res, { msg: "Permanence ouverte avec succès" }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de l'ouverture de la permanence" }); + } }; // ➡️ Fermer une permanence export const closePermanence = async (req: Request, res: Response) => { - const { permId } = req.body; - - if (!permId) { - Error(res, { msg: "L'ID de la permanence est requis" }); - return; - } - - try { - const permanence = await permanence_service.getPermanenceById(permId); - if (permanence.is_open === false) { - Error(res, { msg: "La permanence est déjà fermée" }); - return; - } - - await permanence_service.closePermanence(Number(permId)); - Ok(res, { msg: "Permanence fermée avec succès" }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la fermeture de la permanence" }); - return; - } + const { permId } = req.body; + + if (!permId) { + Error(res, { msg: "L'ID de la permanence est requis" }); + return; + } + + try { + const permanence = await permanence_service.getPermanenceById(permId); + if (permanence.is_open === false) { + Error(res, { msg: "La permanence est déjà fermée" }); + return; + } + + await permanence_service.closePermanence(Number(permId)); + Ok(res, { msg: "Permanence fermée avec succès" }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la fermeture de la permanence" }); + return; + } }; // ➕ S'inscrire à une permanence export const applyToPermanence = async (req: Request, res: Response) => { - const { permId } = req.body; - const userId = req.user?.userId; - - - if (!userId || !permId) { - Error(res, { msg: "Requête invalide, permId ou userId manquant" }); - return; - } - - try { - const permanence = await permanence_service.getPermanenceById(permId); - if (permanence.is_open === false) { - Error(res, { msg: "La permanence est fermée, vous ne pouvez pas vous y inscrire" }); - return; - } - - await permanence_service.registerUserToPermanence(Number(userId), Number(permId)); - Ok(res, { msg: "Inscription réussie" }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: err.message || "Erreur pendant l'inscription" }); - return; - } + const { permId } = req.body; + const userId = req.user?.userId; + + + if (!userId || !permId) { + Error(res, { msg: "Requête invalide, permId ou userId manquant" }); + return; + } + + try { + const permanence = await permanence_service.getPermanenceById(permId); + if (permanence.is_open === false) { + Error(res, { msg: "La permanence est fermée, vous ne pouvez pas vous y inscrire" }); + return; + } + + await permanence_service.registerUserToPermanence(Number(userId), Number(permId)); + Ok(res, { msg: "Inscription réussie" }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: err.message || "Erreur pendant l'inscription" }); + return; + } }; // ❌ Se désinscrire d'une permanence export const leavePermanence = async (req: Request, res: Response) => { - const { permId } = req.body; - const userId = req.user?.userId; - - if (!userId || !permId) { - Error(res, { msg: "Requête invalide, permId ou userId manquant" }); - return; - } - - try { - await permanence_service.unregisterUserFromPermanence(Number(userId), Number(permId),); - Ok(res, { msg: "Désinscription réussie" }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: err.message || "Erreur pendant la désinscription" }); - return; - } + const { permId } = req.body; + const userId = req.user?.userId; + + if (!userId || !permId) { + Error(res, { msg: "Requête invalide, permId ou userId manquant" }); + return; + } + + try { + await permanence_service.unregisterUserFromPermanence(Number(userId), Number(permId),); + Ok(res, { msg: "Désinscription réussie" }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: err.message || "Erreur pendant la désinscription" }); + return; + } }; // 👤 Voir ses permanences export const getMyPermanences = async (req: Request, res: Response) => { - const userId = req.user?.userId; - - if (!userId) { - Error(res, { msg: "Utilisateur non identifié" }); - return; - } - - try { - const list = await permanence_service.getMyPermanences(Number(userId)); - Ok(res, { data: list }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur pendant la récupération des permanences" }); - return; - } + const userId = req.user?.userId; + + if (!userId) { + Error(res, { msg: "Utilisateur non identifié" }); + return; + } + + try { + const list = await permanence_service.getMyPermanences(Number(userId)); + Ok(res, { data: list }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur pendant la récupération des permanences" }); + return; + } }; // ✅ Récupérer toutes les permanences export const getAllPermanences = async (req: Request, res: Response) => { - try { - const permanences = await permanence_service.getAllPermanences(); - Ok(res, { data: permanences }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la récupération des permanences" }); - return; - } + try { + const permanences = await permanence_service.getAllPermanences(); + Ok(res, { data: permanences }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la récupération des permanences" }); + return; + } }; // ✅ Récupérer les permanences ouvertes export const getOpenPermanences = async (req: Request, res: Response) => { - try { - const perms = await permanence_service.listOpenPermanences(); - Ok(res, { data: perms }); - return; - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la récupération des permanences ouvertes" }); - return; - } + try { + const perms = await permanence_service.listOpenPermanences(); + Ok(res, { data: perms }); + return; + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la récupération des permanences ouvertes" }); + return; + } }; export const getUsersInPermanence = async (req: Request, res: Response) => { try { - const {permId} = req.query - const users = await permanence_service.getUsersInPermanence(Number(permId)) - Ok(res, { data: users }); - return; + const { permId } = req.query + const users = await permanence_service.getUsersInPermanence(Number(permId)) + Ok(res, { data: users }); + return; } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la récupération des utilisateurs par permanences" }); - return; + console.error(err); + Error(res, { msg: "Erreur lors de la récupération des utilisateurs par permanences" }); + return; } }; export const addUserToPermanence = async (req: Request, res: Response) => { const { permId, userId } = req.body; - + if (!userId || !permId) { - Error(res, { msg: "Requête invalide, permId ou userId manquant" }); - return; + Error(res, { msg: "Requête invalide, permId ou userId manquant" }); + return; } - + try { - await permanence_service.addUserToPermanence(Number(userId), Number(permId)); - Ok(res, { msg: "Inscription réussite" }); - return; + await permanence_service.addUserToPermanence(Number(userId), Number(permId)); + Ok(res, { msg: "Inscription réussite" }); + return; } catch (err) { - console.error(err); - Error(res, { msg: err.message || "Erreur pendant l'inscription" }); - return; + console.error(err); + Error(res, { msg: err.message || "Erreur pendant l'inscription" }); + return; } }; export const removeUserToPermanence = async (req: Request, res: Response) => { const { permId, userId } = req.body; - + if (!userId || !permId) { - Error(res, { msg: "Requête invalide, permId ou userId manquant" }); - return; + Error(res, { msg: "Requête invalide, permId ou userId manquant" }); + return; } - + try { - await permanence_service.removeUserToPermanence(Number(userId), Number(permId)); - Ok(res, { msg: "Désinscription réussite" }); - return; + await permanence_service.removeUserToPermanence(Number(userId), Number(permId)); + Ok(res, { msg: "Désinscription réussite" }); + return; } catch (err) { - console.error(err); - Error(res, { msg: err.message || "Erreur pendant la désinscription" }); - return; + console.error(err); + Error(res, { msg: err.message || "Erreur pendant la désinscription" }); + return; } }; export const uploadPermanencesCSV = async (req: MulterRequest, res: Response) => { - try { - const file = req.file; - if (!file) { - Error(res, { msg: "Fichier CSV manquant." }); - } - - await permanence_service.importPermanencesFromCSV(file.path); - Ok(res,{ msg: "Importation réalisée avec succès." }); - } catch (error) { - console.error("Erreur import CSV :", error); - Error(res, { msg: "Échec de l'importation." }); - } + try { + const file = req.file; + if (!file) { + Error(res, { msg: "Fichier CSV manquant." }); + } + + await permanence_service.importPermanencesFromCSV(file.path); + Ok(res, { msg: "Importation réalisée avec succès." }); + } catch (error) { + console.error("Erreur import CSV :", error); + Error(res, { msg: "Échec de l'importation." }); + } }; export const isUserRespo = async (req: Request, res: Response) => { - const { userId } = req.query; - - if (!userId) { - Error(res, { msg: "userId est requis" }); - return; - } - - try { - const isRespo = await permanence_service.isUserRespoOfPermanence( - Number(userId) - ); - Ok(res, { data: isRespo }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la vérification du responsable" }); - } + const { userId } = req.query; + + if (!userId) { + Error(res, { msg: "userId est requis" }); + return; + } + + try { + const isRespo = await permanence_service.isUserRespoOfPermanence( + Number(userId) + ); + Ok(res, { data: isRespo }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la vérification du responsable" }); + } }; export const getRespoPermanencesWithMembers = async (req: Request, res: Response) => { - const respoId = req.user?.userId; - - if (!respoId) { - Error(res, { msg: "respoId est requis" }); - return; - } - - try { - const data = await permanence_service.getPermanenceDetailsForRespo(Number(respoId)); - Ok(res, { data }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la récupération des permanences du responsable" }); - } -}; + const respoId = req.user?.userId; -export const claimMember = async (req: Request, res: Response) => { - const { userId, permId, claimed } = req.body; - - if (userId === undefined || permId === undefined || claimed === undefined) { - Error(res, { msg: "userId, permId et claimed sont requis" }); - return; - } - - try { - await permanence_service.claimMember(Number(userId), Number(permId), Boolean(claimed)); - Ok(res, { - msg: `Statut mis à jour avec succès (claimed = ${claimed})`, - }); - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la mise à jour du statut du membre" }); - } -}; + if (!respoId) { + Error(res, { msg: "respoId est requis" }); + return; + } + try { + const data = await permanence_service.getPermanenceDetailsForRespo(Number(respoId)); + Ok(res, { data }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la récupération des permanences du responsable" }); + } +}; +export const claimMember = async (req: Request, res: Response) => { + const { userId, permId, claimed } = req.body; + if (userId === undefined || permId === undefined || claimed === undefined) { + Error(res, { msg: "userId, permId et claimed sont requis" }); + return; + } + try { + await permanence_service.claimMember(Number(userId), Number(permId), Boolean(claimed)); + Ok(res, { + msg: `Statut mis à jour avec succès (claimed = ${claimed})`, + }); + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la mise à jour du statut du membre" }); + } +}; diff --git a/backend/src/controllers/role.controller.ts b/backend/src/controllers/role.controller.ts index e653377..dbdd0a9 100644 --- a/backend/src/controllers/role.controller.ts +++ b/backend/src/controllers/role.controller.ts @@ -1,128 +1,128 @@ import { Request, Response } from "express"; -import { Error, Ok } from "../utils/responses"; import * as role_service from "../services/role.service"; +import { Error, Ok } from "../utils/responses"; // 🎯 Préférences utilisateur export const updateUserPreferences = async (req: Request, res: Response) => { - try { - const userId = req.user?.userId; - const { roleIds } = req.body; - - if (!userId || !Array.isArray(roleIds)) { - Error(res, { msg: "Données invalides" }); + try { + const userId = req.user?.userId; + const { roleIds } = req.body; + + if (!userId || !Array.isArray(roleIds)) { + Error(res, { msg: "Données invalides" }); + } + + await role_service.updateUserPreferences(userId, roleIds); + Ok(res, { msg: "Préférences mises à jour avec succès" }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur interne serveur" }); } - - await role_service.updateUserPreferences(userId, roleIds); - Ok(res, { msg: "Préférences mises à jour avec succès" }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur interne serveur" }); - } }; export const getUserPreferences = async (req: Request, res: Response) => { - try { - const userId = req.user?.userId; - - if (!userId) Error(res, { msg: "Utilisateur non authentifié" }); - - const preferences = await role_service.getUserPreferences(userId); - const roleIds = preferences.map((pref) => pref.roleId); - Ok(res, { msg: "Préférences récupérées", data: roleIds }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur interne serveur" }); - } + try { + const userId = req.user?.userId; + + if (!userId) Error(res, { msg: "Utilisateur non authentifié" }); + + const preferences = await role_service.getUserPreferences(userId); + const roleIds = preferences.map((pref) => pref.roleId); + Ok(res, { msg: "Préférences récupérées", data: roleIds }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur interne serveur" }); + } }; // 👥 Utilisateurs par rôle export const getUsersByRoleHandler = async (req: Request, res: Response) => { - try { - const { roleName } = req.params; - if (!roleName) Error(res, { msg: "Nom du rôle requis" }); - - const users = await role_service.getUsersByRoleName(roleName); - Ok(res, { msg: "Utilisateurs récupérés", data: users }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur interne serveur" }); - } + try { + const { roleName } = req.params; + if (!roleName) Error(res, { msg: "Nom du rôle requis" }); + + const users = await role_service.getUsersByRoleName(roleName); + Ok(res, { msg: "Utilisateurs récupérés", data: users }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur interne serveur" }); + } }; // ➕ Ajouter rôle à utilisateur export const addRoleToUser = async (req: Request, res: Response) => { - try { - const { userId, roleIds } = req.body; - - if (!userId || !Array.isArray(roleIds)) { - Error(res, { msg: "userId et roleIds requis" }); - } - - for (const roleId of roleIds) { - const exists = await role_service.checkRoleUser(userId, roleId); - if (exists.length === 0) { - await role_service.insertUserRole(userId, roleId); - } + try { + const { userId, roleIds } = req.body; + + if (!userId || !Array.isArray(roleIds)) { + Error(res, { msg: "userId et roleIds requis" }); + } + + for (const roleId of roleIds) { + const exists = await role_service.checkRoleUser(userId, roleId); + if (exists.length === 0) { + await role_service.insertUserRole(userId, roleId); + } + } + + Ok(res, { msg: "Rôles ajoutés avec succès" }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de l'ajout des rôles" }); } - - Ok(res, { msg: "Rôles ajoutés avec succès" }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de l'ajout des rôles" }); - } }; // ❌ Supprimer rôle d'un utilisateur export const deleteRoleToUser = async (req: Request, res: Response) => { - try { - const { userId, roleId } = req.body; - - if (!userId || !roleId) { - Error(res, { msg: "userId et roleId requis" }); + try { + const { userId, roleId } = req.body; + + if (!userId || !roleId) { + Error(res, { msg: "userId et roleId requis" }); + } + + await role_service.removeRoleFromUser(userId, roleId); + Ok(res, { msg: "Rôle supprimé avec succès" }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de la suppression du rôle" }); } - - await role_service.removeRoleFromUser(userId, roleId); - Ok(res, { msg: "Rôle supprimé avec succès" }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de la suppression du rôle" }); - } }; // 📋 Utilisateurs avec leurs rôles export const getUsersWithRoles = async (req: Request, res: Response) => { - try { - const users = await role_service.getUsersWithRoles(); - Ok(res, { data: users }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de la récupération des utilisateurs" }); - } + try { + const users = await role_service.getUsersWithRoles(); + Ok(res, { data: users }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de la récupération des utilisateurs" }); + } }; // 📦 Tous les rôles export const getRoles = async (req: Request, res: Response) => { - try { - const roles = await role_service.getRoles(); - Ok(res, { data: roles }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de la récupération des rôles" }); - } + try { + const roles = await role_service.getRoles(); + Ok(res, { data: roles }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de la récupération des rôles" }); + } }; // 🔍 Rôles d'un utilisateur export const getUserRoles = async (req: Request, res: Response) => { - try { - const { userId } = req.query; - if (!userId) Error(res, { msg: "userId requis" }); - - const roles = await role_service.getUserRoles(Number(userId)); - Ok(res, { data: roles }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de la récupération des rôles utilisateur" }); - } + try { + const { userId } = req.query; + if (!userId) Error(res, { msg: "userId requis" }); + + const roles = await role_service.getUserRoles(Number(userId)); + Ok(res, { data: roles }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de la récupération des rôles utilisateur" }); + } }; // @@ -131,61 +131,61 @@ export const getUserRoles = async (req: Request, res: Response) => { // ➕ Ajouter des points export const addPointsToRole = async (req: Request, res: Response) => { - try { - const { roleId, points } = req.body; - - if (!roleId || typeof points !== "number") { - Error(res, { msg: "roleId et points requis" }); + try { + const { roleId, points } = req.body; + + if (!roleId || typeof points !== "number") { + Error(res, { msg: "roleId et points requis" }); + } + + await role_service.addPointsToRole(roleId, points); + Ok(res, { msg: "Points ajoutés avec succès" }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de l'ajout des points" }); } - - await role_service.addPointsToRole(roleId, points); - Ok(res, { msg: "Points ajoutés avec succès" }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de l'ajout des points" }); - } }; // ➖ Retirer des points export const removePointsFromRole = async (req: Request, res: Response) => { - try { - const { roleId, points } = req.body; - - if (!roleId || typeof points !== "number") { - Error(res, { msg: "roleId et points requis" }); + try { + const { roleId, points } = req.body; + + if (!roleId || typeof points !== "number") { + Error(res, { msg: "roleId et points requis" }); + } + + await role_service.removePointsFromRole(roleId, points); + Ok(res, { msg: "Points retirés avec succès" }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors du retrait des points" }); } - - await role_service.removePointsFromRole(roleId, points); - Ok(res, { msg: "Points retirés avec succès" }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors du retrait des points" }); - } }; // 📊 Points de tous les rôles export const getAllRolePoints = async (_req: Request, res: Response) => { - try { - const roles = await role_service.getAllRolePoints(); - Ok(res, { data: roles }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de la récupération des points" }); - } + try { + const roles = await role_service.getAllRolePoints(); + Ok(res, { data: roles }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de la récupération des points" }); + } }; // 🔍 Points d'un rôle spécifique export const getRolePoints = async (req: Request, res: Response) => { - try { - const { roleId } = req.params; - if (!roleId) Error(res, { msg: "roleId requis" }); - - const role = await role_service.getRolePoints(Number(roleId)); - if (!role) Error(res, { msg: "Rôle introuvable" }); - - Ok(res, { data: role }); - } catch (error) { - console.error(error); - Error(res, { msg: "Erreur lors de la récupération des points du rôle" }); - } + try { + const { roleId } = req.params; + if (!roleId) Error(res, { msg: "roleId requis" }); + + const role = await role_service.getRolePoints(Number(roleId)); + if (!role) Error(res, { msg: "Rôle introuvable" }); + + Ok(res, { data: role }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de la récupération des points du rôle" }); + } }; diff --git a/backend/src/controllers/team.controller.ts b/backend/src/controllers/team.controller.ts index 06e8064..bc1fa92 100644 --- a/backend/src/controllers/team.controller.ts +++ b/backend/src/controllers/team.controller.ts @@ -1,16 +1,15 @@ import { Request, Response } from "express"; -import { Error, Ok, Unauthorized } from "../utils/responses"; +import { Event } from "../schemas/Basic/event.schema"; +import * as event_service from '../services/event.service'; +import * as faction_service from "../services/faction.service"; import * as team_service from "../services/team.service"; import * as user_service from "../services/user.service"; -import * as faction_service from "../services/faction.service"; -import * as event_service from '../services/event.service' -import { Event } from "../schemas/Basic/event.schema"; +import { Error, Ok } from "../utils/responses"; export const createNewTeam = async (req: Request, res: Response) => { const { teamName, members } = req.body; try { - if (!teamName) { Error(res, { msg: "Il n'y a pas de nom d'équipe" }); return; @@ -42,7 +41,6 @@ export const createNewTeam = async (req: Request, res: Response) => { const newTeam = await team_service.createTeam(teamName, members); Ok(res, { msg: "Équipe créée avec succès !", data: newTeam }); return; - } catch (error) { Error(res, { msg: "Erreur lors de la création de l'équipe." }); } @@ -54,31 +52,26 @@ export const createNewTeamLight = async (req: Request, res: Response) => { try { const newTeamLigth = await team_service.createTeamLight(teamName, factionId); Ok(res, { msg: "Equipe créée !" }); - } catch (error) { Error(res, { msg: "Erreur lors de la création de l'équipe." }); } }; export const getTeams = async (req: Request, res: Response) => { - try { const teams = await team_service.getTeams(); Ok(res, { data: teams }); return; - } catch (error) { Error(res, { msg: "Erreur lors de la récupération des équipes." }); } }; export const getTeamsWithfactions = async (req: Request, res: Response) => { - try { const teams = await team_service.getTeamsAll(); Ok(res, { data: teams }); return; - } catch (error) { Error(res, { msg: "Erreur lors de la récupération des équipes et de leur faction." }); } @@ -94,7 +87,6 @@ export const modifyTeam = async (req: Request, res: Response) => { const updatedTeam = await team_service.modifyTeam(teamID, teamMembers, factionID, teamName, type); Ok(res, { data: updatedTeam }); - } catch (error) { console.error(error); Error(res, { msg: "Erreur lors de la modification de l'équipe." }); @@ -102,8 +94,8 @@ export const modifyTeam = async (req: Request, res: Response) => { }; export const getTeamUsers = async (req: Request, res: Response) => { - const { teamId } = req.query; + try { const teamUsers = await team_service.getTeamUsers(teamId); Ok(res, { data: teamUsers }); @@ -112,13 +104,10 @@ export const getTeamUsers = async (req: Request, res: Response) => { console.error(error); Error(res, { msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); return; - } - } export const getAllTeamsWithUsers = async (req: Request, res: Response) => { - try { const teamUsers = await team_service.getAllTeamsWithUsers(); Ok(res, { data: teamUsers }); @@ -127,14 +116,12 @@ export const getAllTeamsWithUsers = async (req: Request, res: Response) => { console.error(error); Error(res, { msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); return; - } - } export const getTeamFaction = async (req: Request, res: Response) => { - const { teamId } = req.query; + try { const factionId = await team_service.getTeamFaction(teamId); const teamFaction = await faction_service.getFaction(factionId); @@ -144,9 +131,7 @@ export const getTeamFaction = async (req: Request, res: Response) => { console.error(error); Error(res, { msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); return; - } - } export const deleteTeam = async (req: Request, res: Response) => { @@ -159,7 +144,6 @@ export const deleteTeam = async (req: Request, res: Response) => { const deletedTeam = await team_service.deleteTeam(Number(teamID)); Ok(res, { msg: "Équipe supprimée avec succès.", data: deletedTeam }); - } catch (error) { console.error(error); Error(res, { msg: "Erreur lors de la suppression de l'équipe." }); @@ -168,12 +152,10 @@ export const deleteTeam = async (req: Request, res: Response) => { export const teamDistribution = async (req: Request, res: Response) => { try { - const newStudents = await user_service.getUsersbyPermission("Nouveau"); const userswithteams = (await team_service.getUsersWithTeam()).map((entry: any) => entry.userId); const teams = await team_service.getTeams(); - // Filtrer les étudiants qui ne sont pas déjà assignés à une équipe const filteredStudents = newStudents // .filter((student: any) => student.branch !== "RI") // A decommenter pour ignorer les RI dans la répartition automatique @@ -259,4 +241,3 @@ export const teamDistribution = async (req: Request, res: Response) => { return } } - diff --git a/backend/src/controllers/tent.controller.ts b/backend/src/controllers/tent.controller.ts index 9fe0b39..0157634 100644 --- a/backend/src/controllers/tent.controller.ts +++ b/backend/src/controllers/tent.controller.ts @@ -1,113 +1,112 @@ import { Request, Response } from "express"; -import * as tent_service from "../services/tent.service"; -import { Error, Ok } from "../utils/responses"; import { sendEmail } from "../services/email.service"; +import * as tent_service from "../services/tent.service"; import { getUserById } from "../services/user.service"; +import { Error, Ok } from "../utils/responses"; import { generateEmailHtml } from "./email.controller"; export const createTent = async (req: Request, res: Response) => { - const { userId2 } = req.body; - const userId1 = req.user?.userId; // Créateur = utilisateur connecté - - if (!userId1 || !userId2) { - Error(res, { msg: "Identifiants utilisateurs manquants." }); - } - - try { - await tent_service.createTent(userId1, userId2); - Ok(res, { msg: "Tente réservée avec succès." }); - } catch (err: any) { - Error(res, { msg: err.message || "Erreur lors de la création de la tente." }); - } + const { userId2 } = req.body; + const userId1 = req.user?.userId; // Créateur = utilisateur connecté + + if (!userId1 || !userId2) { + Error(res, { msg: "Identifiants utilisateurs manquants." }); + } + + try { + await tent_service.createTent(userId1, userId2); + Ok(res, { msg: "Tente réservée avec succès." }); + } catch (err: any) { + Error(res, { msg: err.message || "Erreur lors de la création de la tente." }); + } }; export const cancelTent = async (req: Request, res: Response) => { + const userId1 = req.user?.userId; - const userId1 = req.user?.userId; - - if (!userId1) { - Error(res, { msg: "Identifiants utilisateurs manquants." }); - } + if (!userId1) { + Error(res, { msg: "Identifiants utilisateurs manquants." }); + } - try { - await tent_service.cancelTent(userId1); - Ok(res, { msg: "Tente annulée." }); - } catch (err) { - Error(res, { msg: "Erreur lors de l'annulation." }); - } + try { + await tent_service.cancelTent(userId1); + Ok(res, { msg: "Tente annulée." }); + } catch (err) { + Error(res, { msg: "Erreur lors de l'annulation." }); + } }; export const getUserTent = async (req: Request, res: Response) => { - const userId = req.user?.userId; - if (!userId) Error(res, { msg: "Utilisateur non authentifié." }); - - try { - const tent = await tent_service.getTentByUser(userId); - Ok(res, { data: tent }); - } catch (err) { - Error(res, { msg: "Erreur lors de la récupération." }); - } + const userId = req.user?.userId; + + if (!userId) Error(res, { msg: "Utilisateur non authentifié." }); + + try { + const tent = await tent_service.getTentByUser(userId); + Ok(res, { data: tent }); + } catch (err) { + Error(res, { msg: "Erreur lors de la récupération." }); + } }; export const getAllTentPairs = async (req: Request, res: Response) => { - try { - const tents = await tent_service.getAllTents(); - Ok(res, { data: tents }); - } catch (err) { - Error(res, { msg: "Erreur lors de la récupération des binômes." }); - } + try { + const tents = await tent_service.getAllTents(); + Ok(res, { data: tents }); + } catch (err) { + Error(res, { msg: "Erreur lors de la récupération des binômes." }); + } }; export const toggleTentConfirmation = async (req: Request, res: Response) => { + const { userId1, userId2, confirmed } = req.body; - const { userId1, userId2, confirmed } = req.body; - - if (!userId1 || !userId2 || typeof confirmed !== "boolean") { - Error(res, { msg: "Paramètres manquants ou invalides." }); - } - - try { - // Mise à jour de la tente - await tent_service.toggleTentConfirmation(userId1, userId2, confirmed); - - // Récupération des infos utilisateurs - const user1 = await getUserById(userId1); - const user2 = await getUserById(userId2); - - if (!user1 || !user2) { - Error(res, { msg: "Impossible de récupérer les utilisateurs." }); + if (!userId1 || !userId2 || typeof confirmed !== "boolean") { + Error(res, { msg: "Paramètres manquants ou invalides." }); } - // Génération du contenu HTML - const htmlEmail = generateEmailHtml("templateNotifyTentConfirmation", { - user1: `${user1.firstName} ${user1.lastName}`, - user2: `${user2.firstName} ${user2.lastName}`, - confirmed, - }); - - // Options d'email - const emailOptions = { - from: "integration@utt.fr", - to: [user1.email, user2.email], - subject: confirmed - ? "🎉 Votre tente a été validée !" - : "⛺ Votre tente a été dévalidée", - text: "", // optionnel - html: htmlEmail, - }; - - // Envoi - await sendEmail(emailOptions); - - Ok(res, { - msg: confirmed - ? "Tente validée et email envoyé." - : "Tente dévalidée et email envoyé.", - }); - } catch (err: any) { - console.error(err); - Error(res, { - msg: err.message || "Erreur lors de la mise à jour ou de l'envoi d'email.", - }); - } + try { + // Mise à jour de la tente + await tent_service.toggleTentConfirmation(userId1, userId2, confirmed); + + // Récupération des infos utilisateurs + const user1 = await getUserById(userId1); + const user2 = await getUserById(userId2); + + if (!user1 || !user2) { + Error(res, { msg: "Impossible de récupérer les utilisateurs." }); + } + + // Génération du contenu HTML + const htmlEmail = generateEmailHtml("templateNotifyTentConfirmation", { + user1: `${user1.firstName} ${user1.lastName}`, + user2: `${user2.firstName} ${user2.lastName}`, + confirmed, + }); + + // Options d'email + const emailOptions = { + from: "integration@utt.fr", + to: [user1.email, user2.email], + subject: confirmed + ? "🎉 Votre tente a été validée !" + : "⛺ Votre tente a été dévalidée", + text: "", // optionnel + html: htmlEmail, + }; + + // Envoi + await sendEmail(emailOptions); + + Ok(res, { + msg: confirmed + ? "Tente validée et email envoyé." + : "Tente dévalidée et email envoyé.", + }); + } catch (err: any) { + console.error(err); + Error(res, { + msg: err.message || "Erreur lors de la mise à jour ou de l'envoi d'email.", + }); + } }; diff --git a/backend/src/controllers/user.controller.ts b/backend/src/controllers/user.controller.ts index 6fc783a..c46fa5f 100644 --- a/backend/src/controllers/user.controller.ts +++ b/backend/src/controllers/user.controller.ts @@ -1,132 +1,124 @@ -import * as user_service from '../services/user.service' -import * as SIEP_Utils from '../utils/siep' -import { Ok, Error } from '../utils/responses'; -import { Request, Response } from "express"; import bcrypt from 'bcryptjs'; +import { Request, Response } from "express"; import * as randomstring from 'randomstring'; -import * as auth_service from "../services/auth.service" +import * as auth_service from "../services/auth.service"; +import * as user_service from '../services/user.service'; import { noSyncEmails } from '../utils/no_sync_list'; +import { Error, Ok } from '../utils/responses'; +import * as SIEP_Utils from '../utils/siep'; export const getUsersAdmin = async (req: Request, res: Response) => { try { const users = await user_service.getUsersAdmin(); - Ok(res,{ data: users }); + Ok(res, { data: users }); return; - } catch (error) { + } catch (error) { console.error(error); - Error(res,{ msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); + Error(res, { msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); return; - } + } }; export const getUsers = async (req: Request, res: Response) => { try { const users = await user_service.getUsers(); - Ok(res,{ data: users }); + Ok(res, { data: users }); return; - } catch (error) { + } catch (error) { console.error(error); - Error(res,{ msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); + Error(res, { msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); return; - } + } }; export const getUsersByPermission = async (req: Request, res: Response) => { + const { permission } = req.params - const {permission} = req.params - try { - const users = await user_service.getUsersbyPermission(permission); - Ok(res,{ data: users }); - return; + try { + const users = await user_service.getUsersbyPermission(permission); + Ok(res, { data: users }); + return; } catch (error) { - console.error(error); - Error(res,{ msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); - return; + console.error(error); + Error(res, { msg: "Erreur interne lors de la récupération des utilisateurs avec leurs rôles." }); + return; } }; export const syncNewstudent = async (req: Request, res: Response) => { + const { date } = req.body; - const {date} = req.body; - - try { - - const token = await SIEP_Utils.getTokenUTTAPI(); - const newStudents = await SIEP_Utils.getNewStudentsFromUTTAPI_NOPAGE(token, date); - const newStudentfiltered = newStudents.filter((student : any) => !noSyncEmails.includes(student.email));//Nouveau à ne pas sync (Démissionnaires, etc) - - for (const element of newStudentfiltered) { - - let userInDb = await user_service.getUserByEmail(element.email.toLowerCase()); - if(userInDb === undefined){ - - let tmpPassword = await bcrypt.hash(randomstring.generate(48), 10); - const newUser = await user_service.createUser( - element.prenom, - element.nom, - element.email.toLowerCase(), - element.Majeur , - "Nouveau", - element.diplome === "MA" ? "Master" : element.specialite, - tmpPassword); - - await auth_service.createRegistrationToken(newUser.id) + try { + const token = await SIEP_Utils.getTokenUTTAPI(); + const newStudents = await SIEP_Utils.getNewStudentsFromUTTAPI_NOPAGE(token, date); + const newStudentfiltered = newStudents.filter((student: any) => !noSyncEmails.includes(student.email));//Nouveau à ne pas sync (Démissionnaires, etc) + + for (const element of newStudentfiltered) { + let userInDb = await user_service.getUserByEmail(element.email.toLowerCase()); + if (userInDb === undefined) { + let tmpPassword = await bcrypt.hash(randomstring.generate(48), 10); + const newUser = await user_service.createUser( + element.prenom, + element.nom, + element.email.toLowerCase(), + element.Majeur, + "Nouveau", + element.diplome === "MA" ? "Master" : element.specialite, + tmpPassword); - } + await auth_service.createRegistrationToken(newUser.id) + } } - - Ok(res, { msg:"All NewStudent created and synced" }) - - - } catch (error) { - Error(res, { error }) - } + Ok(res, { msg: "All NewStudent created and synced" }) + } catch (error) { + Error(res, { error }) + } } export const getCurrentUser = async (req: Request, res: Response) => { - const userId = req.user?.userId; - try { - const user = await user_service.getUserById(userId); - Ok(res, { data : user }); - } catch (err) { - Error(res, { msg: "Erreur lors de la mise à jour du profil." }); - } + const userId = req.user?.userId; + + try { + const user = await user_service.getUserById(userId); + Ok(res, { data: user }); + } catch (err) { + Error(res, { msg: "Erreur lors de la mise à jour du profil." }); + } }; export const updateProfile = async (req: Request, res: Response) => { - const userId = req.user?.userId; - const { branch, contact } = req.body; - - try { - const result = await user_service.updateUserInfoByUserId(userId, branch, contact); - Ok(res, { msg: "Profil mis à jour", data : result }); - } catch (err) { - Error(res, { msg: "Erreur lors de la mise à jour du profil." }); - } + const userId = req.user?.userId; + const { branch, contact } = req.body; + + try { + const result = await user_service.updateUserInfoByUserId(userId, branch, contact); + Ok(res, { msg: "Profil mis à jour", data: result }); + } catch (err) { + Error(res, { msg: "Erreur lors de la mise à jour du profil." }); + } }; export const adminUpdateUser = async (req: Request, res: Response) => { - const { userId } = req.params; - const updates = req.body; - + const { userId } = req.params; + const updates = req.body; - try { - const result = await user_service.updateUserByAdmin(parseInt(userId), updates); - Ok(res, { msg: "Utilisateur mis à jour", data : result }); - } catch (err) { - Error(res, { msg: "Erreur lors de la mise à jour de l'utilisateur." }); - } + try { + const result = await user_service.updateUserByAdmin(parseInt(userId), updates); + Ok(res, { msg: "Utilisateur mis à jour", data: result }); + } catch (err) { + Error(res, { msg: "Erreur lors de la mise à jour de l'utilisateur." }); + } }; export const adminDeleteUser = async (req: Request, res: Response) => { - const { userId } = req.params; - - try { - const result = await user_service.deleteUserById(parseInt(userId)); - Ok(res, { msg: "Utilisateur supprimé", data :result }); - } catch (err) { - Error(res, { msg: "Erreur lors de la suppression de l'utilisateur." }); - } + const { userId } = req.params; + + try { + const result = await user_service.deleteUserById(parseInt(userId)); + Ok(res, { msg: "Utilisateur supprimé", data: result }); + } catch (err) { + Error(res, { msg: "Erreur lors de la suppression de l'utilisateur." }); + } }; diff --git a/backend/src/database/db.ts b/backend/src/database/db.ts index 5d63c0b..75c5005 100644 --- a/backend/src/database/db.ts +++ b/backend/src/database/db.ts @@ -1,53 +1,52 @@ import { drizzle } from "drizzle-orm/node-postgres"; import { Client } from "pg"; -import { postgres_user, postgres_db, postgres_host, postgres_password, postgres_port } from '../utils/secret'; +import { postgres_db, postgres_host, postgres_password, postgres_port, postgres_user } from '../utils/secret'; // ✅ Import de tous tes schémas ici -import * as user from '../schemas/Basic/user.schema'; -import * as team from '../schemas/Basic/team.schema'; -import * as perm from '../schemas/Basic/permanence.schema'; -import * as event from '../schemas/Basic/event.schema'; -import * as faction from '../schemas/Basic/faction.schema'; -import * as role from '../schemas/Basic/role.schema'; -import * as challenge from '../schemas/Basic/challenge.schema'; -import * as permanence from '../schemas/Basic/permanence.schema'; -import * as userTeam from '../schemas/Relational/userteams.schema'; -import * as teamFaction from '../schemas/Relational/teamfaction.schema'; -import * as teamShotgun from '../schemas/Relational/teamshotgun.schema'; -import * as userPermanence from '../schemas/Relational/userpermanences.schema'; -import * as userRole from '../schemas/Relational/userroles.schema'; -import * as challengValidation from '../schemas/Relational/challengevalidation.schema'; -import * as busattribution from "../schemas/Relational/busattribution.schema"; -import * as registration from "../schemas/Relational/registration.schema"; -import * as tent from "../schemas/Relational/usertent.schema"; +import * as challenge from '../schemas/Basic/challenge.schema'; +import * as event from '../schemas/Basic/event.schema'; +import * as faction from '../schemas/Basic/faction.schema'; +import * as perm from '../schemas/Basic/permanence.schema'; +import * as permanence from '../schemas/Basic/permanence.schema'; +import * as role from '../schemas/Basic/role.schema'; +import * as team from '../schemas/Basic/team.schema'; +import * as user from '../schemas/Basic/user.schema'; +import * as busattribution from "../schemas/Relational/busattribution.schema"; +import * as challengValidation from '../schemas/Relational/challengevalidation.schema'; +import * as registration from "../schemas/Relational/registration.schema"; import * as rolepoints from "../schemas/Relational/rolepoints.schema"; +import * as teamFaction from '../schemas/Relational/teamfaction.schema'; +import * as teamShotgun from '../schemas/Relational/teamshotgun.schema'; +import * as userPermanence from '../schemas/Relational/userpermanences.schema'; +import * as userRole from '../schemas/Relational/userroles.schema'; +import * as userTeam from '../schemas/Relational/userteams.schema'; +import * as tent from "../schemas/Relational/usertent.schema"; - -const schema = { - ...user, - ...team, - ...perm, - ...event, - ...faction, - ...role, - ...challenge, - ...permanence, - ...userTeam, - ...teamFaction, - ...teamShotgun, - ...userPermanence, - ...userRole, - ...challengValidation, - ...busattribution, - ...registration, - ...tent, - ...rolepoints +const schema = { + ...user, + ...team, + ...perm, + ...event, + ...faction, + ...role, + ...challenge, + ...permanence, + ...userTeam, + ...teamFaction, + ...teamShotgun, + ...userPermanence, + ...userRole, + ...challengValidation, + ...busattribution, + ...registration, + ...tent, + ...rolepoints }; const client = new Client({ - connectionString: `postgresql://${postgres_user}:${postgres_password}@${postgres_host}:${postgres_port}/${postgres_db}`, + connectionString: `postgresql://${postgres_user}:${postgres_password}@${postgres_host}:${postgres_port}/${postgres_db}`, }); client.connect(); -export const db = drizzle(client, {schema: schema}); +export const db = drizzle(client, { schema: schema }); diff --git a/backend/src/database/initdb/initChallenge.ts b/backend/src/database/initdb/initChallenge.ts index 87d890a..e63ab2d 100644 --- a/backend/src/database/initdb/initChallenge.ts +++ b/backend/src/database/initdb/initChallenge.ts @@ -1,18 +1,18 @@ -import { db } from "../db"; // Assurez-vous que votre instance db est correcte -import {challengeSchema } from "../../schemas/Basic/challenge.schema"; +import { challengeSchema } from "../../schemas/Basic/challenge.schema"; +import { db } from "../db"; // Assurez-vous que votre instance db est correcte export const initChallenge = async () => { const existingChall = await db.select().from(challengeSchema).limit(1); - + // Si il n'y a pas de ligne existante, insérer une nouvelle ligne if (existingChall.length === 0) { const challengeValues = { - title: "Free", - description: "Chalenge de réfrence pour les points gratuits", - category : "Free", - points: 0, - created_by : 1 + title: "Free", + description: "Chalenge de réfrence pour les points gratuits", + category: "Free", + points: 0, + created_by: 1 }; const result = await db.insert(challengeSchema).values(challengeValues).onConflictDoNothing(); } - }; +}; diff --git a/backend/src/database/initdb/initUser.ts b/backend/src/database/initdb/initUser.ts index 351c8cb..1ea653c 100644 --- a/backend/src/database/initdb/initUser.ts +++ b/backend/src/database/initdb/initUser.ts @@ -1,22 +1,22 @@ -import { db } from "../db"; // Assurez-vous que votre instance db est correcte import { userSchema } from "../../schemas/Basic/user.schema"; -import { zimbra_password } from "../../utils/secret"; import { hashPassword } from "../../services/auth.service"; +import { zimbra_password } from "../../utils/secret"; +import { db } from "../db"; // Assurez-vous que votre instance db est correcte export const initUser = async () => { - const existingUser = await db.select().from(userSchema).limit(1); + const existingUser = await db.select().from(userSchema).limit(1); - const hashedPassword = await hashPassword(zimbra_password); + const hashedPassword = await hashPassword(zimbra_password); - // Si il n'y a pas de ligne existante, insérer une nouvelle ligne - if (existingUser.length === 0) { - await db.insert(userSchema).values({ - first_name: "Integration UTT", - last_name: "Integration UTT", - email: "integration@utt.fr", - majeur: true, - password: hashedPassword, - permission: 'Admin', - }); - } -}; \ No newline at end of file + // Si il n'y a pas de ligne existante, insérer une nouvelle ligne + if (existingUser.length === 0) { + await db.insert(userSchema).values({ + first_name: "Integration UTT", + last_name: "Integration UTT", + email: "integration@utt.fr", + majeur: true, + password: hashedPassword, + permission: 'Admin', + }); + } +}; diff --git a/backend/src/database/initdb/initevent.ts b/backend/src/database/initdb/initevent.ts index 46164e8..9ce3452 100644 --- a/backend/src/database/initdb/initevent.ts +++ b/backend/src/database/initdb/initevent.ts @@ -1,17 +1,18 @@ -import { db } from "../db"; // Assurez-vous que votre instance db est correcte -import {eventSchema } from "../../schemas/Basic/event.schema"; +import { eventSchema } from "../../schemas/Basic/event.schema"; +import { db } from "../db"; // Assurez-vous que votre instance db est correcte export const initEvent = async () => { const existingEvent = await db.select().from(eventSchema).limit(1); - + // Si il n'y a pas de ligne existante, insérer une nouvelle ligne if (existingEvent.length === 0) { - await db.insert(eventSchema).values({ - pre_registration_open: false, - shotgun_open: false, - sdi_open: false, - wei_open: false, - food_open : false, - chall_open : false }).onConflictDoNothing(); + await db.insert(eventSchema).values({ + pre_registration_open: false, + shotgun_open: false, + sdi_open: false, + wei_open: false, + food_open: false, + chall_open: false + }).onConflictDoNothing(); } - }; \ No newline at end of file +}; diff --git a/backend/src/database/initdb/initrole.ts b/backend/src/database/initdb/initrole.ts index c387959..72423f4 100644 --- a/backend/src/database/initdb/initrole.ts +++ b/backend/src/database/initdb/initrole.ts @@ -1,62 +1,62 @@ -import { db } from "../db"; // Assurez-vous que votre instance db est correcte -import { Role, roleSchema } from "../../schemas/Basic/role.schema"; -import { rolePoints } from "../../schemas/Relational/rolepoints.schema"; import { eq } from "drizzle-orm"; +import { roleSchema } from "../../schemas/Basic/role.schema"; +import { rolePoints } from "../../schemas/Relational/rolepoints.schema"; +import { db } from "../db"; // Assurez-vous que votre instance db est correcte // Liste des rôles à ajouter // Liste des rôles avec leurs descriptions const roles = [ - { name: "Animation", description: "Animer, divertir et motiver les CE et les nouveaux étudiants tout au long de la pré-inté et de l'inté." }, - { name: "Arbitre", description: "Arbitrer les différents défis pendant le semaine d'intégration." }, - { name: "Argentique", description: "Couvrir les événements de l'intégration, prendre des photos" }, - { name: "Bouffe", description: "Prévoir, organiser et coordonner tous les repas de l'inté. La bouffe c'est sacré !" }, - { name: "Bar", description: "Prévoir, organiser et coordonner toutes les boissons de l'inté !" }, - { name: "Bénévole", description: "Deviens bénévole et participe à différentes activités de l'inté !" }, - { name: "Cahier de vacances", description: "Élaborer le futur cahier de vacances des nouveaux avec des petits exercices et blagues." }, - { name: "Chasse au trésor", description: "Elaborer une chasse au trésor dans toute la capitale Troyenne." }, - { name: "Communication", description: "Préparer et gérer toute la communication de l'intégration" }, - { name: "Graphisme", description: "Créer une charte graphique incroyable pour les rendre l'inté encore plus belle !" }, - { name: "Déco", description: "Être créatif et fabriquer de quoi décorer l'UTT au thème de l'inté." }, - { name: "Défis TC", description: "Préparer un défi où les nouveaux TC doivent faire preuve d'ingéniosité pour fabriquer quelque chose." }, - { name: "Dev / Info", description: "Maintenir le site et l'application de l'inté et développer de nouveaux outils informatiques." }, - { name: "Faux amphi", description: "Créer un faux premier cours compliqué pour les TC avec des professeurs et des faux élèves." }, - { name: "Faux discours de rentrée", description: "Préparer et faire un discours de rentrée pour faire une petite frayeur aux nouveaux." }, - { name: "Logistique", description: "Préparer, organiser et mettre en place tout le matériel nécessaire pour l'intégration." }, - { name: "Lac d'Orient Express", description: "Emmener nos nouveaux voir le magnifique lac de Mesnil" }, - { name: "Média", description: "Couvrir les événements de l'intégration, prendre des photos et monter des films souvenirs." }, - { name: "Mascotte", description: "Etre juste le GOAT de l'intégration !" }, - { name: "Parrainage", description: "Attribuer des parrains/marraines aux nouveaux étudiants de manière personnalisée." }, - { name: "Partenariat", description: "Rechercher et établir des partenariats utiles pour l'intégration et les nouveaux étudiants." }, - { name: "Prévention", description: "Évaluer les risques et mettre en place des mesures préventives avant et pendant l'intégration." }, - { name: "Rallye", description: "Organiser une après-midi de jeux et d'activités sportives pour les nouveaux étudiants." }, - { name: "Respo CE", description: "Gérer le planning des CE et les guider pour qu'ils accueillent au mieux les nouveaux." }, - { name: "Respo Question", description: "Répondre au mieux aux questions des nouveaux !" }, - { name: "Sécu", description: "Gérer la sécurité des événements, notamment durant le WEI et la soirée d'intégration." }, - { name: "Soirée d'intégration", description: "Organiser une soirée sur le campus UTT durant la semaine d'inté." }, - { name: "Son et lumière", description: "Installer et gérer le son et la lumière durant les événements nécessitant une ambiance spéciale." }, - { name: "Soutenabilité", description: "Mettre en place des actions pour réduire l'impact environnemental de l'intégration." }, - { name: "SVE", description: "Mettre en place le Salon de la Vie Etudiante (SVE) lors de la deuxième semaine" }, - { name: "Traduction en anglais", description: "Traduire les contenus de l'intégration pour les étudiants étrangers." }, - { name: "Village Asso", description: "Organiser un événement pour présenter les associations UTTiennes aux nouveaux étudiants." }, - { name: "Visites", description: "Organiser les visites de l'UTT et de la ville pour les nouveaux étudiants." }, - { name: "WEI", description: "Organiser le Week-end d'intégration (transport, animation, logistique, soirée, etc.)." } + { name: "Animation", description: "Animer, divertir et motiver les CE et les nouveaux étudiants tout au long de la pré-inté et de l'inté." }, + { name: "Arbitre", description: "Arbitrer les différents défis pendant le semaine d'intégration." }, + { name: "Argentique", description: "Couvrir les événements de l'intégration, prendre des photos" }, + { name: "Bouffe", description: "Prévoir, organiser et coordonner tous les repas de l'inté. La bouffe c'est sacré !" }, + { name: "Bar", description: "Prévoir, organiser et coordonner toutes les boissons de l'inté !" }, + { name: "Bénévole", description: "Deviens bénévole et participe à différentes activités de l'inté !" }, + { name: "Cahier de vacances", description: "Élaborer le futur cahier de vacances des nouveaux avec des petits exercices et blagues." }, + { name: "Chasse au trésor", description: "Elaborer une chasse au trésor dans toute la capitale Troyenne." }, + { name: "Communication", description: "Préparer et gérer toute la communication de l'intégration" }, + { name: "Graphisme", description: "Créer une charte graphique incroyable pour les rendre l'inté encore plus belle !" }, + { name: "Déco", description: "Être créatif et fabriquer de quoi décorer l'UTT au thème de l'inté." }, + { name: "Défis TC", description: "Préparer un défi où les nouveaux TC doivent faire preuve d'ingéniosité pour fabriquer quelque chose." }, + { name: "Dev / Info", description: "Maintenir le site et l'application de l'inté et développer de nouveaux outils informatiques." }, + { name: "Faux amphi", description: "Créer un faux premier cours compliqué pour les TC avec des professeurs et des faux élèves." }, + { name: "Faux discours de rentrée", description: "Préparer et faire un discours de rentrée pour faire une petite frayeur aux nouveaux." }, + { name: "Logistique", description: "Préparer, organiser et mettre en place tout le matériel nécessaire pour l'intégration." }, + { name: "Lac d'Orient Express", description: "Emmener nos nouveaux voir le magnifique lac de Mesnil" }, + { name: "Média", description: "Couvrir les événements de l'intégration, prendre des photos et monter des films souvenirs." }, + { name: "Mascotte", description: "Etre juste le GOAT de l'intégration !" }, + { name: "Parrainage", description: "Attribuer des parrains/marraines aux nouveaux étudiants de manière personnalisée." }, + { name: "Partenariat", description: "Rechercher et établir des partenariats utiles pour l'intégration et les nouveaux étudiants." }, + { name: "Prévention", description: "Évaluer les risques et mettre en place des mesures préventives avant et pendant l'intégration." }, + { name: "Rallye", description: "Organiser une après-midi de jeux et d'activités sportives pour les nouveaux étudiants." }, + { name: "Respo CE", description: "Gérer le planning des CE et les guider pour qu'ils accueillent au mieux les nouveaux." }, + { name: "Respo Question", description: "Répondre au mieux aux questions des nouveaux !" }, + { name: "Sécu", description: "Gérer la sécurité des événements, notamment durant le WEI et la soirée d'intégration." }, + { name: "Soirée d'intégration", description: "Organiser une soirée sur le campus UTT durant la semaine d'inté." }, + { name: "Son et lumière", description: "Installer et gérer le son et la lumière durant les événements nécessitant une ambiance spéciale." }, + { name: "Soutenabilité", description: "Mettre en place des actions pour réduire l'impact environnemental de l'intégration." }, + { name: "SVE", description: "Mettre en place le Salon de la Vie Etudiante (SVE) lors de la deuxième semaine" }, + { name: "Traduction en anglais", description: "Traduire les contenus de l'intégration pour les étudiants étrangers." }, + { name: "Village Asso", description: "Organiser un événement pour présenter les associations UTTiennes aux nouveaux étudiants." }, + { name: "Visites", description: "Organiser les visites de l'UTT et de la ville pour les nouveaux étudiants." }, + { name: "WEI", description: "Organiser le Week-end d'intégration (transport, animation, logistique, soirée, etc.)." } ]; export const initRoles = async () => { - for (const role of roles) { - await db.insert(roleSchema).values(role).onConflictDoNothing(); // Évite les doublons + for (const role of roles) { + await db.insert(roleSchema).values(role).onConflictDoNothing(); // Évite les doublons - //Initier le table pour le jeux des orga - const [currentRole] = await db - .select() - .from(roleSchema) - .where(eq(roleSchema.name, role.name)) - .limit(1); + //Initier le table pour le jeux des orga + const [currentRole] = await db + .select() + .from(roleSchema) + .where(eq(roleSchema.name, role.name)) + .limit(1); - if (!currentRole) { - throw new Error(`Role not found: ${role.name}`); - } + if (!currentRole) { + throw new Error(`Role not found: ${role.name}`); + } - await db.insert(rolePoints).values({ role_id: currentRole.id, points: 0 }).onConflictDoNothing() - } + await db.insert(rolePoints).values({ role_id: currentRole.id, points: 0 }).onConflictDoNothing() + } }; diff --git a/backend/src/middlewares/auth.middleware.ts b/backend/src/middlewares/auth.middleware.ts index 0e309d6..e7650d2 100644 --- a/backend/src/middlewares/auth.middleware.ts +++ b/backend/src/middlewares/auth.middleware.ts @@ -1,11 +1,9 @@ -import { Request, Response, NextFunction } from "express"; +import { NextFunction, Request, Response } from "express"; import { Unauthorized } from "../utils/responses"; // Assurez-vous que cette fonction est bien définie import { decodeToken } from "../utils/token"; - export const authenticateUser = (req: Request, res: Response, next: NextFunction) => { try { - const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { diff --git a/backend/src/middlewares/multer.middleware.ts b/backend/src/middlewares/multer.middleware.ts index 9299b1b..3cd0de3 100644 --- a/backend/src/middlewares/multer.middleware.ts +++ b/backend/src/middlewares/multer.middleware.ts @@ -1,78 +1,74 @@ +import { NextFunction, Request, Response } from "express"; +import fs from "fs/promises"; import multer from "multer"; import path from "path"; -import fs from "fs/promises"; -import { Request, Response, NextFunction } from "express"; import { Error } from "../utils/responses"; export const createUploadMiddleware = ( - relativeUploadDir: string, - modifiedName: boolean = true - ) => { - const uploadPath = path.resolve(process.cwd(), relativeUploadDir); - - // On stocke d'abord en mémoire pour vérifier le type réel - const storage = multer.memoryStorage(); - - const multerUpload = multer({ - storage, - limits: { - fileSize: 5 * 1024 * 1024, // 5 Mo - }, - }); - - // Middleware custom pour vérifier et sauvegarder - const verifyAndSave = async ( - req: Request, - res: Response, - next: NextFunction - ) => { - try { - if (!req.file) return Error(res, {msg: "Aucun fichier reçu"}); - - const user = (req as Request).user?.userId || "anonymous"; - const { originalname, mimetype, buffer } = req.file; - - console.log( - `[UPLOAD] User: ${user}, File: ${originalname}, Mimetype annoncé: ${mimetype}` - ); - - // Vérif du vrai type - const { fileTypeFromBuffer } = await import("file-type"); - const detected = await fileTypeFromBuffer(buffer); - console.log( - `[UPLOAD] Type détecté: ${detected?.mime || "inconnu"}` - ); - - const isImage = detected?.mime?.startsWith("image/"); - const isPDF = detected?.mime === "application/pdf"; - - if (!isImage && !isPDF) { - Error(res,{ msg:"Seules les images et les PDF sont autorisés"}); - } - - // Création dossier si nécessaire - await fs.mkdir(uploadPath, { recursive: true }); - - const ext = path.extname(originalname); - const baseName = path.basename(originalname, ext); - const timestamp = Date.now(); - const finalName = modifiedName - ? `${baseName}-${timestamp}${ext}` - : originalname; - - const finalPath = path.join(uploadPath, finalName); - - // Sauvegarde du fichier sur disque - await fs.writeFile(finalPath, buffer); - - // On rajoute le chemin pour les middlewares suivants - (req as any).savedFilePath = finalPath; - - next(); - } catch (err) { - next(err); - } - }; - - return { multerUpload, verifyAndSave }; + relativeUploadDir: string, + modifiedName: boolean = true +) => { + const uploadPath = path.resolve(process.cwd(), relativeUploadDir); + + // On stocke d'abord en mémoire pour vérifier le type réel + const storage = multer.memoryStorage(); + + const multerUpload = multer({ + storage, + limits: { + fileSize: 5 * 1024 * 1024, // 5 Mo + }, + }); + + // Middleware custom pour vérifier et sauvegarder + const verifyAndSave = async ( + req: Request, + res: Response, + next: NextFunction + ) => { + try { + if (!req.file) return Error(res, { msg: "Aucun fichier reçu" }); + + const user = (req as Request).user?.userId || "anonymous"; + const { originalname, mimetype, buffer } = req.file; + + // Vérif du vrai type + const { fileTypeFromBuffer } = await import("file-type"); + const detected = await fileTypeFromBuffer(buffer); + console.log( + `[UPLOAD] Type détecté: ${detected?.mime || "inconnu"}` + ); + + const isImage = detected?.mime?.startsWith("image/"); + const isPDF = detected?.mime === "application/pdf"; + + if (!isImage && !isPDF) { + Error(res, { msg: "Seules les images et les PDF sont autorisés" }); + } + + // Création dossier si nécessaire + await fs.mkdir(uploadPath, { recursive: true }); + + const ext = path.extname(originalname); + const baseName = path.basename(originalname, ext); + const timestamp = Date.now(); + const finalName = modifiedName + ? `${baseName}-${timestamp}${ext}` + : originalname; + + const finalPath = path.join(uploadPath, finalName); + + // Sauvegarde du fichier sur disque + await fs.writeFile(finalPath, buffer); + + // On rajoute le chemin pour les middlewares suivants + (req as any).savedFilePath = finalPath; + + next(); + } catch (err) { + next(err); + } + }; + + return { multerUpload, verifyAndSave }; }; diff --git a/backend/src/middlewares/respoperm.middleware.ts b/backend/src/middlewares/respoperm.middleware.ts index c9b3915..19a74d7 100644 --- a/backend/src/middlewares/respoperm.middleware.ts +++ b/backend/src/middlewares/respoperm.middleware.ts @@ -1,29 +1,28 @@ -import { Request, Response, NextFunction } from "express"; -import { Error } from "../utils/responses"; +import { NextFunction, Request, Response } from "express"; import { isUserRespoOfPermanence } from "../services/permanence.service"; +import { Error } from "../utils/responses"; export const isRespoMiddleware = async ( - req: Request, - res: Response, - next: NextFunction + req: Request, + res: Response, + next: NextFunction ) => { - const userId = req.user?.userId; + const userId = req.user?.userId; - if (!userId) { - Error(res, { msg: "Utilisateur ou permanence non spécifié" }); - return; - } - - try { - const isRespo = await isUserRespoOfPermanence(userId); - if (!isRespo) { - Error(res, { msg: "Accès refusé : vous n'êtes pas responsable d'une permanence" }); - return; + if (!userId) { + Error(res, { msg: "Utilisateur ou permanence non spécifié" }); + return; } - next(); // ✅ L'utilisateur est bien respo, on continue - } catch (err) { - console.error(err); - Error(res, { msg: "Erreur lors de la vérification du responsable" }); - } -}; \ No newline at end of file + try { + const isRespo = await isUserRespoOfPermanence(userId); + if (!isRespo) { + Error(res, { msg: "Accès refusé : vous n'êtes pas responsable d'une permanence" }); + return; + } + next(); // ✅ L'utilisateur est bien respo, on continue + } catch (err) { + console.error(err); + Error(res, { msg: "Erreur lors de la vérification du responsable" }); + } +}; diff --git a/backend/src/middlewares/user.middleware.ts b/backend/src/middlewares/user.middleware.ts index a452467..f186ec8 100644 --- a/backend/src/middlewares/user.middleware.ts +++ b/backend/src/middlewares/user.middleware.ts @@ -1,41 +1,41 @@ -import { Request, Response, NextFunction } from "express"; +import { NextFunction, Request, Response } from "express"; import { Unauthorized } from "../utils/responses"; // adapte selon ton projet export const checkRole = ( - requiredPermission?: string, - requiredRoles?: string[] + requiredPermission?: string, + requiredRoles?: string[] ) => { - return (req: Request, res: Response, next: NextFunction) => { - const user = req.user; + return (req: Request, res: Response, next: NextFunction) => { + const user = req.user; - if (!user) { - Unauthorized(res, { msg: "Accès non autorisé" }); - return; - } + if (!user) { + Unauthorized(res, { msg: "Accès non autorisé" }); + return; + } - try { - const isAdmin = user.userPermission === "Admin"; - - const hasPermission = - !requiredPermission || user.userPermission === requiredPermission; + try { + const isAdmin = user.userPermission === "Admin"; - const hasRole = - !requiredRoles || - (Array.isArray(user.userRoles) && - user.userRoles.some((role: { roleName: string }) => - requiredRoles.includes(role.roleName) - )); + const hasPermission = + !requiredPermission || user.userPermission === requiredPermission; - if (!isAdmin && !(hasPermission || hasRole)) { - Unauthorized(res, { - msg: "Accès interdit, rôle ou permission insuffisants", - }); - return; - } + const hasRole = + !requiredRoles || + (Array.isArray(user.userRoles) && + user.userRoles.some((role: { roleName: string }) => + requiredRoles.includes(role.roleName) + )); - next(); - } catch (err) { - Unauthorized(res, { msg: "Token invalide ou expiré" }); - } - }; -}; \ No newline at end of file + if (!isAdmin && !(hasPermission || hasRole)) { + Unauthorized(res, { + msg: "Accès interdit, rôle ou permission insuffisants", + }); + return; + } + + next(); + } catch (err) { + Unauthorized(res, { msg: "Token invalide ou expiré" }); + } + }; +}; diff --git a/backend/src/routes/bus.routes.ts b/backend/src/routes/bus.routes.ts index 916ed55..d35eed5 100644 --- a/backend/src/routes/bus.routes.ts +++ b/backend/src/routes/bus.routes.ts @@ -1,13 +1,12 @@ -// src/routes/bus.routes.ts import { Router } from "express"; +import multer from "multer"; import * as busController from "../controllers/bus.controller"; import { checkRole } from "../middlewares/user.middleware"; -import multer from "multer"; const busRouter = Router(); const upload = multer({ dest: "uploads/buscsv/" }); busRouter.post("/admin/attributionemail", checkRole("Admin", []), busController.sendBusAttributionEmails); -busRouter.post("/admin/importbus",checkRole("Admin",["Respo CE"]), upload.single("file"), busController.uploadbusCSV ); +busRouter.post("/admin/importbus", checkRole("Admin", ["Respo CE"]), upload.single("file"), busController.uploadbusCSV); export default busRouter; diff --git a/backend/src/routes/challenge.routes.ts b/backend/src/routes/challenge.routes.ts index 402deca..8063031 100644 --- a/backend/src/routes/challenge.routes.ts +++ b/backend/src/routes/challenge.routes.ts @@ -7,13 +7,12 @@ const challengeRouter = express.Router(); // Admin routes challengeRouter.post("/admin/challenge", checkRole("Admin", ["Arbitre"]), challengeController.createChallenge); challengeRouter.delete("/admin/delete", checkRole("Admin", ["Arbitre"]), challengeController.deleteChallenge); -challengeRouter.put("/admin/updatechallenge", checkRole("Admin",["Arbitre"]), challengeController.updateChallenge); +challengeRouter.put("/admin/updatechallenge", checkRole("Admin", ["Arbitre"]), challengeController.updateChallenge); challengeRouter.post("/admin/validate", checkRole("Admin", ["Arbitre"]), challengeController.validateChallenge); -challengeRouter.post("/admin/unvalidate",checkRole("Admin", ["Arbitre"]), challengeController.unvalidateChallenge); +challengeRouter.post("/admin/unvalidate", checkRole("Admin", ["Arbitre"]), challengeController.unvalidateChallenge); challengeRouter.get("/admin/challenges", checkRole("Admin", ["Arbitre"]), challengeController.getAllChallenges); challengeRouter.get("/admin/validatedchallenges", checkRole("Admin", ["Arbitre"]), challengeController.getValidatedChallenges); -challengeRouter.post("/admin/assignpoints", checkRole("Admin", ["Arbitre"]), challengeController.addPointsToFaction); - +challengeRouter.post("/admin/assignpoints", checkRole("Admin", ["Arbitre"]), challengeController.addPointsToFaction); // User routes challengeRouter.get("/user/challenges", challengeController.getAllChallenges); diff --git a/backend/src/routes/default.routes.ts b/backend/src/routes/default.routes.ts index feaf724..944d79f 100644 --- a/backend/src/routes/default.routes.ts +++ b/backend/src/routes/default.routes.ts @@ -1,10 +1,9 @@ -import { Request, Response } from 'express'; -import express from 'express'; +import express, { Request, Response } from 'express'; const defaultRouter = express.Router(); defaultRouter.get('/', (req: Request, res: Response) => { - res.json({ status: 'ok' }) + res.json({ status: 'ok' }) }) export default defaultRouter diff --git a/backend/src/routes/discord.routes.ts b/backend/src/routes/discord.routes.ts index 26be613..b6249d9 100644 --- a/backend/src/routes/discord.routes.ts +++ b/backend/src/routes/discord.routes.ts @@ -1,9 +1,8 @@ import express from 'express'; -import * as discordController from '../controllers/discord.controller'; - +import * as discordController from '../controllers/discord.controller'; const discordRouter = express.Router(); discordRouter.post('/user/callback', discordController.createChallenge); -export default discordRouter; \ No newline at end of file +export default discordRouter; diff --git a/backend/src/routes/email.routes.ts b/backend/src/routes/email.routes.ts index 976221b..ea994be 100644 --- a/backend/src/routes/email.routes.ts +++ b/backend/src/routes/email.routes.ts @@ -1,11 +1,10 @@ import express from 'express'; -import * as emailController from '../controllers/email.controller'; +import * as emailController from '../controllers/email.controller'; import { checkRole } from '../middlewares/user.middleware'; - const emailRouter = express.Router(); -emailRouter.post('/admin/sendemail', checkRole("Admin",[]), emailController.handleSendEmail); -emailRouter.post('/admin/previewemail', checkRole("Admin",[]), emailController.handlePreviewEmail); +emailRouter.post('/admin/sendemail', checkRole("Admin", []), emailController.handleSendEmail); +emailRouter.post('/admin/previewemail', checkRole("Admin", []), emailController.handlePreviewEmail); -export default emailRouter; \ No newline at end of file +export default emailRouter; diff --git a/backend/src/routes/event.routes.ts b/backend/src/routes/event.routes.ts index 2c66c8c..206174d 100644 --- a/backend/src/routes/event.routes.ts +++ b/backend/src/routes/event.routes.ts @@ -1,7 +1,6 @@ import express from 'express'; import * as eventController from '../controllers/event.controller'; import { checkRole } from '../middlewares/user.middleware'; -import { authenticateUser } from '../middlewares/auth.middleware'; const eventRouter = express.Router(); @@ -14,7 +13,6 @@ eventRouter.get("/user/foodstatus", eventController.checkFoodStatus); eventRouter.get("/user/challstatus", eventController.checkChallStatus); eventRouter.post("/user/shotgunattempt", checkRole("Student", []), eventController.shotgunAttempt); - // Admin routes eventRouter.post("/admin/shotguntoggle", checkRole("Admin", []), eventController.toggleShotgun); eventRouter.get("/admin/shotgunattempts", checkRole("Admin", ["Respo CE"]), eventController.getShotgunAttempts); @@ -24,4 +22,4 @@ eventRouter.post("/admin/weitoggle", checkRole("Admin", []), eventController.tog eventRouter.post("/admin/foodtoggle", checkRole("Admin", []), eventController.toggleFood); eventRouter.post("/admin/challtoggle", checkRole("Admin", []), eventController.toggleChall); -export default eventRouter; \ No newline at end of file +export default eventRouter; diff --git a/backend/src/routes/faction.routes.ts b/backend/src/routes/faction.routes.ts index 84a4263..af818b9 100644 --- a/backend/src/routes/faction.routes.ts +++ b/backend/src/routes/faction.routes.ts @@ -4,15 +4,13 @@ import { checkRole } from '../middlewares/user.middleware'; const factionRouter = express.Router(); - // Admin routes -factionRouter.get("/admin/factions",checkRole("Admin", ["Respo CE", "Arbitre"]) ,factionController.getFactions); -factionRouter.get("/admin/faction",checkRole("Admin", ["Respo CE"]) ,factionController.getFaction); -factionRouter.post("/admin/createfaction",checkRole("Admin", ["Respo CE"]) ,factionController.createFaction); -factionRouter.delete("/admin/deletefaction",checkRole("Admin", ["Respo CE"]) ,factionController.deleteFaction); +factionRouter.get("/admin/factions", checkRole("Admin", ["Respo CE", "Arbitre"]), factionController.getFactions); +factionRouter.get("/admin/faction", checkRole("Admin", ["Respo CE"]), factionController.getFaction); +factionRouter.post("/admin/createfaction", checkRole("Admin", ["Respo CE"]), factionController.createFaction); +factionRouter.delete("/admin/deletefaction", checkRole("Admin", ["Respo CE"]), factionController.deleteFaction); //Student Routes -factionRouter.get("/user/factions",factionController.getFactions); - +factionRouter.get("/user/factions", factionController.getFactions); -export default factionRouter; \ No newline at end of file +export default factionRouter; diff --git a/backend/src/routes/im_export.routes.ts b/backend/src/routes/im_export.routes.ts index 42cec07..8d73cbe 100644 --- a/backend/src/routes/im_export.routes.ts +++ b/backend/src/routes/im_export.routes.ts @@ -1,19 +1,15 @@ import express from 'express'; import * as imexportController from '../controllers/im_export.controller'; +import { createUploadMiddleware } from "../middlewares/multer.middleware"; import { checkRole } from '../middlewares/user.middleware'; -import {createUploadMiddleware} from "../middlewares/multer.middleware"; const uploadFoodMenu = createUploadMiddleware("uploads/foodmenu/", false); const uploadPlannings = createUploadMiddleware("uploads/plannings/", false); const imexportRouter = express.Router(); - -imexportRouter.post('/admin/foodimport',checkRole("Admin",[]),uploadFoodMenu.multerUpload.single("foodFile"), uploadFoodMenu.verifyAndSave, imexportController.updateFoodMenu); -imexportRouter.post('/admin/plannings',checkRole("Admin",[]),uploadPlannings.multerUpload.single("planningFile"), uploadPlannings.verifyAndSave, imexportController.updatePlannings); -imexportRouter.post('/admin/exportgsheet',checkRole("Admin",[]), imexportController.exportAllDataToSheets); -imexportRouter.get('/admin/exportbus',checkRole("Admin",[]), imexportController.exportUsersCSV); - - - +imexportRouter.post('/admin/foodimport', checkRole("Admin", []), uploadFoodMenu.multerUpload.single("foodFile"), uploadFoodMenu.verifyAndSave, imexportController.updateFoodMenu); +imexportRouter.post('/admin/plannings', checkRole("Admin", []), uploadPlannings.multerUpload.single("planningFile"), uploadPlannings.verifyAndSave, imexportController.updatePlannings); +imexportRouter.post('/admin/exportgsheet', checkRole("Admin", []), imexportController.exportAllDataToSheets); +imexportRouter.get('/admin/exportbus', checkRole("Admin", []), imexportController.exportUsersCSV); export default imexportRouter; diff --git a/backend/src/routes/news.routes.ts b/backend/src/routes/news.routes.ts index a6cb339..cc21fe2 100644 --- a/backend/src/routes/news.routes.ts +++ b/backend/src/routes/news.routes.ts @@ -1,24 +1,20 @@ import express from "express"; import * as newsController from "../controllers/news.controller"; -import { checkRole } from "../middlewares/user.middleware"; import { createUploadMiddleware } from "../middlewares/multer.middleware"; +import { checkRole } from "../middlewares/user.middleware"; const uploadImgNews = createUploadMiddleware("uploads/imgnews/", true); const newsRouter = express.Router(); - //Admin routes -newsRouter.post("/admin/createnews",checkRole("Admin",["Communication"]), uploadImgNews.multerUpload.single("file"), uploadImgNews.verifyAndSave, newsController.createNews); +newsRouter.post("/admin/createnews", checkRole("Admin", ["Communication"]), uploadImgNews.multerUpload.single("file"), uploadImgNews.verifyAndSave, newsController.createNews); newsRouter.post("/admin/updatenews", checkRole("Admin", ["Communication"]), uploadImgNews.multerUpload.single("file"), uploadImgNews.verifyAndSave, newsController.updateNews); -newsRouter.get("/admin/all",checkRole("Admin",["Communication"]), newsController.listAllNews); -newsRouter.post("/admin/publish",checkRole("Admin",["Communication"]) , newsController.publishNews); -newsRouter.delete("/admin/deletenews",checkRole("Admin",["Communication"]) ,newsController.deleteNews); - - +newsRouter.get("/admin/all", checkRole("Admin", ["Communication"]), newsController.listAllNews); +newsRouter.post("/admin/publish", checkRole("Admin", ["Communication"]), newsController.publishNews); +newsRouter.delete("/admin/deletenews", checkRole("Admin", ["Communication"]), newsController.deleteNews); //User routes newsRouter.get("/user/published", newsController.listPublishedNews); newsRouter.get("/user/publishedbytype", newsController.listPublishedNewsByType); - export default newsRouter; diff --git a/backend/src/routes/permanences.routes.ts b/backend/src/routes/permanences.routes.ts index 7b6c307..50fb6e3 100644 --- a/backend/src/routes/permanences.routes.ts +++ b/backend/src/routes/permanences.routes.ts @@ -1,35 +1,33 @@ import express from "express"; import multer from "multer"; import * as permanenceController from "../controllers/permanence.controller"; -import { checkRole } from "../middlewares/user.middleware"; import { isRespoMiddleware } from "../middlewares/respoperm.middleware"; +import { checkRole } from "../middlewares/user.middleware"; const permanenceRouter = express.Router(); const upload = multer({ dest: "uploads/permcsv/" }); // Admin routes -permanenceRouter.post("/admin/permanence", checkRole("Admin",["Respo CE"]), permanenceController.createPermanence); -permanenceRouter.delete("/admin/permanence", checkRole("Admin",["Respo CE"]), permanenceController.deletePermanence); -permanenceRouter.post("/admin/updatepermanence", checkRole("Admin",["Respo CE"]), permanenceController.updatePermanence); -permanenceRouter.post("/admin/open", checkRole("Admin",["Respo CE"]), permanenceController.openPermanence); -permanenceRouter.post("/admin/close", checkRole("Admin",["Respo CE"]), permanenceController.closePermanence); -permanenceRouter.get("/admin/permanences", checkRole("Admin",["Respo CE"]), permanenceController.getAllPermanences); -permanenceRouter.get("/admin/users", checkRole("Admin",["Respo CE"]), permanenceController.getUsersInPermanence); -permanenceRouter.post("/admin/add", checkRole("Admin",["Respo CE"]), permanenceController.addUserToPermanence); -permanenceRouter.post("/admin/remove", checkRole("Admin",["Respo CE"]), permanenceController.removeUserToPermanence); -permanenceRouter.post("/admin/importpermanences",checkRole("Admin",["Respo CE"]), upload.single("file"), permanenceController.uploadPermanencesCSV); -permanenceRouter.post("/admin/claimedmember",checkRole("Admin",["Respo CE"]), permanenceController.claimMember); +permanenceRouter.post("/admin/permanence", checkRole("Admin", ["Respo CE"]), permanenceController.createPermanence); +permanenceRouter.delete("/admin/permanence", checkRole("Admin", ["Respo CE"]), permanenceController.deletePermanence); +permanenceRouter.post("/admin/updatepermanence", checkRole("Admin", ["Respo CE"]), permanenceController.updatePermanence); +permanenceRouter.post("/admin/open", checkRole("Admin", ["Respo CE"]), permanenceController.openPermanence); +permanenceRouter.post("/admin/close", checkRole("Admin", ["Respo CE"]), permanenceController.closePermanence); +permanenceRouter.get("/admin/permanences", checkRole("Admin", ["Respo CE"]), permanenceController.getAllPermanences); +permanenceRouter.get("/admin/users", checkRole("Admin", ["Respo CE"]), permanenceController.getUsersInPermanence); +permanenceRouter.post("/admin/add", checkRole("Admin", ["Respo CE"]), permanenceController.addUserToPermanence); +permanenceRouter.post("/admin/remove", checkRole("Admin", ["Respo CE"]), permanenceController.removeUserToPermanence); +permanenceRouter.post("/admin/importpermanences", checkRole("Admin", ["Respo CE"]), upload.single("file"), permanenceController.uploadPermanencesCSV); +permanenceRouter.post("/admin/claimedmember", checkRole("Admin", ["Respo CE"]), permanenceController.claimMember); //Respo de perm routes - -permanenceRouter.get("/respo/respodetails",isRespoMiddleware, permanenceController.getRespoPermanencesWithMembers); -permanenceRouter.post("/respo/claimedmember",isRespoMiddleware, permanenceController.claimMember); - +permanenceRouter.get("/respo/respodetails", isRespoMiddleware, permanenceController.getRespoPermanencesWithMembers); +permanenceRouter.post("/respo/claimedmember", isRespoMiddleware, permanenceController.claimMember); // Student routes -permanenceRouter.get("/user/permanences", checkRole("Student",[]), permanenceController.getOpenPermanences); -permanenceRouter.post("/user/apply", checkRole("Student",[]), permanenceController.applyToPermanence); -permanenceRouter.post("/user/leave", checkRole("Student",[]), permanenceController.leavePermanence); -permanenceRouter.get("/user/me", checkRole("Student",[]), permanenceController.getMyPermanences ); +permanenceRouter.get("/user/permanences", checkRole("Student", []), permanenceController.getOpenPermanences); +permanenceRouter.post("/user/apply", checkRole("Student", []), permanenceController.applyToPermanence); +permanenceRouter.post("/user/leave", checkRole("Student", []), permanenceController.leavePermanence); +permanenceRouter.get("/user/me", checkRole("Student", []), permanenceController.getMyPermanences); permanenceRouter.get("/user/isrespo", permanenceController.isUserRespo); export default permanenceRouter; diff --git a/backend/src/routes/role.routes.ts b/backend/src/routes/role.routes.ts index e30df90..42d6098 100644 --- a/backend/src/routes/role.routes.ts +++ b/backend/src/routes/role.routes.ts @@ -1,26 +1,23 @@ import express from "express"; import * as roleController from "../controllers/role.controller"; -import { authenticateUser } from "../middlewares/auth.middleware"; // Middleware pour vérifier l'authentification import { checkRole } from "../middlewares/user.middleware"; const roleRouter = express.Router(); //User Routes roleRouter.put("/user/updateuserpreferences", roleController.updateUserPreferences); -roleRouter.get("/user/userpreferences",roleController.getUserPreferences) +roleRouter.get("/user/userpreferences", roleController.getUserPreferences) roleRouter.get("/user/getroles", roleController.getRoles) roleRouter.get("/user/points", roleController.getAllRolePoints); roleRouter.get("/user/points/:roleId", roleController.getRolePoints); - //Admin routes -roleRouter.get("/admin/userbyrolehandler/:roleName", checkRole("Admin",[]), roleController.getUsersByRoleHandler) -roleRouter.get("/admin/userswithroles", checkRole("Admin",[]), roleController.getUsersWithRoles) -roleRouter.get("/admin/getusersroles", checkRole("Admin",[]), roleController.getUserRoles) -roleRouter.post("/admin/addroletouser", checkRole("Admin",[]), roleController.addRoleToUser) -roleRouter.delete("/admin/deleteroletouser", checkRole("Admin",[]), roleController.deleteRoleToUser) +roleRouter.get("/admin/userbyrolehandler/:roleName", checkRole("Admin", []), roleController.getUsersByRoleHandler) +roleRouter.get("/admin/userswithroles", checkRole("Admin", []), roleController.getUsersWithRoles) +roleRouter.get("/admin/getusersroles", checkRole("Admin", []), roleController.getUserRoles) +roleRouter.post("/admin/addroletouser", checkRole("Admin", []), roleController.addRoleToUser) +roleRouter.delete("/admin/deleteroletouser", checkRole("Admin", []), roleController.deleteRoleToUser) roleRouter.post("/admin/points/add", checkRole("Admin", []), roleController.addPointsToRole); roleRouter.post("/admin/points/remove", checkRole("Admin", []), roleController.removePointsFromRole); - export default roleRouter; diff --git a/backend/src/routes/team.routes.ts b/backend/src/routes/team.routes.ts index 827c645..80ec385 100644 --- a/backend/src/routes/team.routes.ts +++ b/backend/src/routes/team.routes.ts @@ -1,24 +1,21 @@ import express from 'express'; import * as teamController from '../controllers/team.controller'; import { checkRole } from '../middlewares/user.middleware'; -import { authenticateUser } from '../middlewares/auth.middleware'; const teamRouter = express.Router(); - //User routes -teamRouter.post("/user/create",checkRole("Student", []) ,teamController.createNewTeam); +teamRouter.post("/user/create", checkRole("Student", []), teamController.createNewTeam); //Admin Routes -teamRouter.post("/admin/createlight",checkRole("Admin", ["Respo CE"]) ,teamController.createNewTeamLight); -teamRouter.get("/admin/teams",checkRole("Admin", ["Respo CE", "Arbitre"]) ,teamController.getTeams); -teamRouter.get("/admin/teamswithfactions",checkRole("Admin", ["Respo CE"]) ,teamController.getTeamsWithfactions); -teamRouter.get("/admin/teamfaction",checkRole("Admin", ["Respo CE"]) ,teamController.getTeamFaction); -teamRouter.get("/admin/teamswithusers",checkRole("Admin", ["Respo CE"]) ,teamController.getAllTeamsWithUsers); -teamRouter.put("/admin/modify",checkRole("Admin", ["Respo CE"]) ,teamController.modifyTeam); -teamRouter.get("/admin/teamusers",checkRole("Admin", ["Respo CE"]) ,teamController.getTeamUsers); -teamRouter.delete("/admin/delete",checkRole("Admin", ["Respo CE"]) ,teamController.deleteTeam); -teamRouter.post('/admin/distributeteam',checkRole("Admin", []), teamController.teamDistribution); - +teamRouter.post("/admin/createlight", checkRole("Admin", ["Respo CE"]), teamController.createNewTeamLight); +teamRouter.get("/admin/teams", checkRole("Admin", ["Respo CE", "Arbitre"]), teamController.getTeams); +teamRouter.get("/admin/teamswithfactions", checkRole("Admin", ["Respo CE"]), teamController.getTeamsWithfactions); +teamRouter.get("/admin/teamfaction", checkRole("Admin", ["Respo CE"]), teamController.getTeamFaction); +teamRouter.get("/admin/teamswithusers", checkRole("Admin", ["Respo CE"]), teamController.getAllTeamsWithUsers); +teamRouter.put("/admin/modify", checkRole("Admin", ["Respo CE"]), teamController.modifyTeam); +teamRouter.get("/admin/teamusers", checkRole("Admin", ["Respo CE"]), teamController.getTeamUsers); +teamRouter.delete("/admin/delete", checkRole("Admin", ["Respo CE"]), teamController.deleteTeam); +teamRouter.post('/admin/distributeteam', checkRole("Admin", []), teamController.teamDistribution); -export default teamRouter; \ No newline at end of file +export default teamRouter; diff --git a/backend/src/routes/tent.routes.ts b/backend/src/routes/tent.routes.ts index de98ac2..fff83c0 100644 --- a/backend/src/routes/tent.routes.ts +++ b/backend/src/routes/tent.routes.ts @@ -5,17 +5,12 @@ import { checkRole } from '../middlewares/user.middleware'; const tentRouter = express.Router(); // Admin routes -tentRouter.get('/admin/tents', checkRole("Admin",[]), tentController.getAllTentPairs); -tentRouter.post('/admin/toggleconfirmation', checkRole("Admin",[]), tentController.toggleTentConfirmation); - +tentRouter.get('/admin/tents', checkRole("Admin", []), tentController.getAllTentPairs); +tentRouter.post('/admin/toggleconfirmation', checkRole("Admin", []), tentController.toggleTentConfirmation); // User routes -tentRouter.post("/user/tent",checkRole("Nouveau",[]), tentController.createTent); -tentRouter.delete("/user/tent",checkRole("Nouveau",[]), tentController.cancelTent); -tentRouter.get("/user/tent",checkRole("Nouveau",[]), tentController.getUserTent); - - - - +tentRouter.post("/user/tent", checkRole("Nouveau", []), tentController.createTent); +tentRouter.delete("/user/tent", checkRole("Nouveau", []), tentController.cancelTent); +tentRouter.get("/user/tent", checkRole("Nouveau", []), tentController.getUserTent); export default tentRouter; diff --git a/backend/src/routes/user.routes.ts b/backend/src/routes/user.routes.ts index e0149c6..ae2f9d2 100644 --- a/backend/src/routes/user.routes.ts +++ b/backend/src/routes/user.routes.ts @@ -5,20 +5,15 @@ import { checkRole } from '../middlewares/user.middleware'; const userRouter = express.Router(); // Admin routes -userRouter.get('/admin/getusersbypermission', checkRole("Admin",[]), userController.getUsersByPermission); -userRouter.patch('/admin/user/:userId', checkRole("Admin",[]), userController.adminUpdateUser); -userRouter.delete('/admin/user/:userId', checkRole("Admin",[]), userController.adminDeleteUser); -userRouter.get('/admin/getusers', checkRole("Admin",["Respo CE"]), userController.getUsersAdmin); -userRouter.post('/admin/syncnewstudent', checkRole("Admin",[]), userController.syncNewstudent); - +userRouter.get('/admin/getusersbypermission', checkRole("Admin", []), userController.getUsersByPermission); +userRouter.patch('/admin/user/:userId', checkRole("Admin", []), userController.adminUpdateUser); +userRouter.delete('/admin/user/:userId', checkRole("Admin", []), userController.adminDeleteUser); +userRouter.get('/admin/getusers', checkRole("Admin", ["Respo CE"]), userController.getUsersAdmin); +userRouter.post('/admin/syncnewstudent', checkRole("Admin", []), userController.syncNewstudent); // User routes userRouter.patch('/user/me', userController.updateProfile); userRouter.get('/user/me', userController.getCurrentUser); userRouter.get('/user/getusers', userController.getUsers); - - - - export default userRouter; diff --git a/backend/src/schemas/Basic/challenge.schema.ts b/backend/src/schemas/Basic/challenge.schema.ts index d47b2b8..ba9840b 100644 --- a/backend/src/schemas/Basic/challenge.schema.ts +++ b/backend/src/schemas/Basic/challenge.schema.ts @@ -8,6 +8,6 @@ export const challengeSchema = pgTable("challenges", { category: text("category").notNull(), points: integer("points").notNull(), created_by: integer("created_by").references(() => userSchema.id), // l'admin qui l'a créé - }); - export type Challenge = typeof challengeSchema.$inferSelect; - \ No newline at end of file +}); + +export type Challenge = typeof challengeSchema.$inferSelect; diff --git a/backend/src/schemas/Basic/event.schema.ts b/backend/src/schemas/Basic/event.schema.ts index a6b4764..0ca2969 100644 --- a/backend/src/schemas/Basic/event.schema.ts +++ b/backend/src/schemas/Basic/event.schema.ts @@ -8,6 +8,6 @@ export const eventSchema = pgTable("events", { wei_open: boolean("wei_open").default(false), food_open: boolean("food_open").default(false), chall_open: boolean("chall_open").default(false), - }); +}); -export type Event = typeof eventSchema.$inferSelect; \ No newline at end of file +export type Event = typeof eventSchema.$inferSelect; diff --git a/backend/src/schemas/Basic/faction.schema.ts b/backend/src/schemas/Basic/faction.schema.ts index 951352a..fa523dc 100644 --- a/backend/src/schemas/Basic/faction.schema.ts +++ b/backend/src/schemas/Basic/faction.schema.ts @@ -1,10 +1,9 @@ import { pgTable, serial, text } from "drizzle-orm/pg-core"; -// Définition de la table `integration_teams` export const factionSchema = pgTable("factions", { - id: serial("id").primaryKey(), - name: text("name").unique(), // Le nom de l'équipe - description: text("description"), // Description de l'équipe + id: serial("id").primaryKey(), + name: text("name").unique(), // Le nom de l'équipe + description: text("description"), // Description de l'équipe }); export type Faction = typeof factionSchema.$inferSelect; diff --git a/backend/src/schemas/Basic/news.schema.ts b/backend/src/schemas/Basic/news.schema.ts index fd6e3f2..434ed13 100644 --- a/backend/src/schemas/Basic/news.schema.ts +++ b/backend/src/schemas/Basic/news.schema.ts @@ -1,14 +1,14 @@ -import { pgTable, serial, text, timestamp, boolean } from "drizzle-orm/pg-core"; +import { boolean, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core"; export const newsSchema = pgTable("news", { - id: serial("id").primaryKey(), - title: text("title"), - description: text("description"), - type: text("type"), - published: boolean("published").default(false), - target: text("target"), // "Tous", "Nouveau", "CE", "Orgas", "Admin", ... - image_url: text("image_url"), - created_at: timestamp("created_at").defaultNow(), + id: serial("id").primaryKey(), + title: text("title"), + description: text("description"), + type: text("type"), + published: boolean("published").default(false), + target: text("target"), // "Tous", "Nouveau", "CE", "Orgas", "Admin", ... + image_url: text("image_url"), + created_at: timestamp("created_at").defaultNow(), }); -export type News = typeof newsSchema.$inferSelect; \ No newline at end of file +export type News = typeof newsSchema.$inferSelect; diff --git a/backend/src/schemas/Basic/permanence.schema.ts b/backend/src/schemas/Basic/permanence.schema.ts index 25cb42a..0f65b13 100644 --- a/backend/src/schemas/Basic/permanence.schema.ts +++ b/backend/src/schemas/Basic/permanence.schema.ts @@ -1,22 +1,22 @@ import { - pgTable, - serial, - text, - timestamp, - integer, - boolean, + boolean, + integer, + pgTable, + serial, + text, + timestamp, } from "drizzle-orm/pg-core"; export const permanenceSchema = pgTable("permanences", { - id: serial("id").primaryKey(), - name: text("name"), - description: text("description"), - location: text("location"), - start_at: timestamp("start_at"), - end_at: timestamp("end_at"), - capacity: integer("capacity"), - is_open: boolean("is_open").default(false), - difficulty: integer("difficulty") + id: serial("id").primaryKey(), + name: text("name"), + description: text("description"), + location: text("location"), + start_at: timestamp("start_at"), + end_at: timestamp("end_at"), + capacity: integer("capacity"), + is_open: boolean("is_open").default(false), + difficulty: integer("difficulty") }); export type Permanence = typeof permanenceSchema.$inferSelect; diff --git a/backend/src/schemas/Basic/role.schema.ts b/backend/src/schemas/Basic/role.schema.ts index 1f916b3..fcb1a63 100644 --- a/backend/src/schemas/Basic/role.schema.ts +++ b/backend/src/schemas/Basic/role.schema.ts @@ -1,10 +1,9 @@ import { pgTable, serial, text } from "drizzle-orm/pg-core"; -// Définition de la table `roles` export const roleSchema = pgTable("roles", { - id: serial("id").primaryKey(), - name: text("name").unique(), // Le nom du rôle (ex: "Animation", "Bouffe") - description: text("description"), // Description du rôle + id: serial("id").primaryKey(), + name: text("name").unique(), // Le nom du rôle (ex: "Animation", "Bouffe") + description: text("description"), // Description du rôle }); export type Role = typeof roleSchema.$inferSelect; diff --git a/backend/src/schemas/Basic/team.schema.ts b/backend/src/schemas/Basic/team.schema.ts index 826aee3..ba63242 100644 --- a/backend/src/schemas/Basic/team.schema.ts +++ b/backend/src/schemas/Basic/team.schema.ts @@ -1,11 +1,10 @@ import { pgTable, serial, text } from "drizzle-orm/pg-core"; -// Définition de la table `integration_teams` export const teamSchema = pgTable("teams", { - id: serial("id").primaryKey(), - name: text("name").unique(), // Le nom de l'équipe - description: text("description"), // Description de l'équipe - type: text('type'), + id: serial("id").primaryKey(), + name: text("name").unique(), // Le nom de l'équipe + description: text("description"), // Description de l'équipe + type: text('type'), }); export type Team = typeof teamSchema.$inferSelect; diff --git a/backend/src/schemas/Basic/user.schema.ts b/backend/src/schemas/Basic/user.schema.ts index 56154bf..8ada041 100644 --- a/backend/src/schemas/Basic/user.schema.ts +++ b/backend/src/schemas/Basic/user.schema.ts @@ -1,19 +1,17 @@ -import { pgTable, serial, text, timestamp, integer, date, boolean } from "drizzle-orm/pg-core"; -import { permission } from "process"; +import { boolean, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core"; -// Définition de la table `users` export const userSchema = pgTable("users", { - id: serial("id").primaryKey(), - first_name: text("first_name"), - last_name: text("last_name"), - email: text("email").unique(), - majeur: boolean("majeur"), - branch: text("branch"), - contact: text("contact"), - password: text("password"), - permission: text("permission").default("Nouveau"), // Par défaut, le rôle sera "Nouveau" - discord_id: text("discord_id"), - created_at: timestamp("created_at").defaultNow(), + id: serial("id").primaryKey(), + first_name: text("first_name"), + last_name: text("last_name"), + email: text("email").unique(), + majeur: boolean("majeur"), + branch: text("branch"), + contact: text("contact"), + password: text("password"), + permission: text("permission").default("Nouveau"), // Par défaut, le rôle sera "Nouveau" + discord_id: text("discord_id"), + created_at: timestamp("created_at").defaultNow(), }); export type User = typeof userSchema.$inferSelect; diff --git a/backend/src/schemas/Relational/busattribution.schema.ts b/backend/src/schemas/Relational/busattribution.schema.ts index c323e9f..d24d477 100644 --- a/backend/src/schemas/Relational/busattribution.schema.ts +++ b/backend/src/schemas/Relational/busattribution.schema.ts @@ -1,10 +1,10 @@ -import { pgTable, integer, text } from "drizzle-orm/pg-core"; +import { integer, pgTable, text } from "drizzle-orm/pg-core"; import { userSchema } from "../Basic/user.schema"; export const busAttributionSchema = pgTable("bus_attribution", { - user_id: integer('user_id').primaryKey().notNull().references(() => userSchema.id), - bus: integer("bus").notNull(), - departure_time : text("departure_time").notNull(), + user_id: integer('user_id').primaryKey().notNull().references(() => userSchema.id), + bus: integer("bus").notNull(), + departure_time: text("departure_time").notNull(), }); export type BusAttributionSchema = typeof busAttributionSchema.$inferInsert; diff --git a/backend/src/schemas/Relational/challengevalidation.schema.ts b/backend/src/schemas/Relational/challengevalidation.schema.ts index a84e2ad..3c11da4 100644 --- a/backend/src/schemas/Relational/challengevalidation.schema.ts +++ b/backend/src/schemas/Relational/challengevalidation.schema.ts @@ -1,19 +1,19 @@ -import { integer, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core"; +import { integer, pgTable, serial, timestamp } from "drizzle-orm/pg-core"; import { challengeSchema } from "../Basic/challenge.schema"; -import { userSchema } from "../Basic/user.schema"; -import { teamSchema } from "../Basic/team.schema"; import { factionSchema } from "../Basic/faction.schema"; +import { teamSchema } from "../Basic/team.schema"; +import { userSchema } from "../Basic/user.schema"; export const challengeValidationSchema = pgTable("challenge_validation", { - id: serial("id").primaryKey(), - challenge_id: integer("challenge_id").references(() => challengeSchema.id).notNull(), - validated_by_admin_id: integer("validated_by_admin_id").references(() => userSchema.id).notNull(), - validated_at: timestamp("validated_at", { withTimezone: true }).defaultNow().notNull(), - target_user_id: integer("target_user_id").references(() => userSchema.id), - target_team_id: integer("target_team_id").references(() => teamSchema.id), - target_faction_id: integer("target_faction_id").references(() => factionSchema.id), - points: integer("points").notNull(), // Nombre de points attribués - added_by_admin_id: integer("added_by_admin_id").references(() => userSchema.id).notNull(), // Qui a validé les points + id: serial("id").primaryKey(), + challenge_id: integer("challenge_id").references(() => challengeSchema.id).notNull(), + validated_by_admin_id: integer("validated_by_admin_id").references(() => userSchema.id).notNull(), + validated_at: timestamp("validated_at", { withTimezone: true }).defaultNow().notNull(), + target_user_id: integer("target_user_id").references(() => userSchema.id), + target_team_id: integer("target_team_id").references(() => teamSchema.id), + target_faction_id: integer("target_faction_id").references(() => factionSchema.id), + points: integer("points").notNull(), // Nombre de points attribués + added_by_admin_id: integer("added_by_admin_id").references(() => userSchema.id).notNull(), // Qui a validé les points }); export type ChallengeValidationPoints = typeof challengeValidationSchema.$inferSelect; diff --git a/backend/src/schemas/Relational/registration.schema.ts b/backend/src/schemas/Relational/registration.schema.ts index 38fdd44..6b6c6e8 100644 --- a/backend/src/schemas/Relational/registration.schema.ts +++ b/backend/src/schemas/Relational/registration.schema.ts @@ -1,12 +1,12 @@ -import { pgTable, serial, text, timestamp, integer } from "drizzle-orm/pg-core"; +import { integer, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core"; import { userSchema } from "../Basic/user.schema"; export const registrationSchema = pgTable("registration_tokens", { - id: serial("id").primaryKey(), - user_id: integer("user_id").references(() => userSchema.id).notNull(), - token: text("token").unique().notNull(), - expires_at: timestamp("expires_at").notNull(), - created_at: timestamp("created_at").defaultNow(), + id: serial("id").primaryKey(), + user_id: integer("user_id").references(() => userSchema.id).notNull(), + token: text("token").unique().notNull(), + expires_at: timestamp("expires_at").notNull(), + created_at: timestamp("created_at").defaultNow(), }); export type RegistrationToken = typeof registrationSchema.$inferSelect; diff --git a/backend/src/schemas/Relational/rolepoints.schema.ts b/backend/src/schemas/Relational/rolepoints.schema.ts index 03450be..9ed680e 100644 --- a/backend/src/schemas/Relational/rolepoints.schema.ts +++ b/backend/src/schemas/Relational/rolepoints.schema.ts @@ -1,12 +1,12 @@ -import { pgTable, serial, integer, primaryKey } from "drizzle-orm/pg-core"; +import { integer, pgTable, primaryKey } from "drizzle-orm/pg-core"; import { roleSchema } from "../Basic/role.schema"; export const rolePoints = pgTable("role_points", { - role_id: integer("role_points").references(() => roleSchema.id, { onDelete: "cascade" }).unique(), - points: integer("points"), + role_id: integer("role_points").references(() => roleSchema.id, { onDelete: "cascade" }).unique(), + points: integer("points"), }, -(table) => [ - primaryKey({ columns: [table.role_id]}) ], + (table) => [ + primaryKey({ columns: [table.role_id] })], ); export type RolePoints = typeof rolePoints.$inferSelect; diff --git a/backend/src/schemas/Relational/teamfaction.schema.ts b/backend/src/schemas/Relational/teamfaction.schema.ts index dadc619..3be8219 100644 --- a/backend/src/schemas/Relational/teamfaction.schema.ts +++ b/backend/src/schemas/Relational/teamfaction.schema.ts @@ -1,14 +1,13 @@ -import { pgTable, serial, integer, primaryKey } from "drizzle-orm/pg-core"; -import { teamSchema } from "../Basic/team.schema"; +import { integer, pgTable, primaryKey } from "drizzle-orm/pg-core"; import { factionSchema } from "../Basic/faction.schema"; +import { teamSchema } from "../Basic/team.schema"; -// Définition de la table `user_teams` pour gérer la relation many-to-many entre users et teams export const teamFactionSchema = pgTable("team_faction", { - faction_id: integer("faction_id").references(() => factionSchema.id, { onDelete: "cascade" }), - team_id: integer("team_id").references(() => teamSchema.id, { onDelete: "cascade" }), + faction_id: integer("faction_id").references(() => factionSchema.id, { onDelete: "cascade" }), + team_id: integer("team_id").references(() => teamSchema.id, { onDelete: "cascade" }), }, -(table) => [ - primaryKey({ columns: [table.faction_id, table.team_id]}) ], + (table) => [ + primaryKey({ columns: [table.faction_id, table.team_id] })], ); export type userFaction = typeof teamFactionSchema.$inferSelect; diff --git a/backend/src/schemas/Relational/teamshotgun.schema.ts b/backend/src/schemas/Relational/teamshotgun.schema.ts index 16f769f..a78ce00 100644 --- a/backend/src/schemas/Relational/teamshotgun.schema.ts +++ b/backend/src/schemas/Relational/teamshotgun.schema.ts @@ -7,4 +7,4 @@ export const teamShotgunSchema = pgTable("team_shotgun", { timestamp: timestamp("timestamp").defaultNow(), // Timecode du shotgun }); -export type TeamShotgun = typeof teamShotgunSchema.$inferSelect; \ No newline at end of file +export type TeamShotgun = typeof teamShotgunSchema.$inferSelect; diff --git a/backend/src/schemas/Relational/userpermanences.schema.ts b/backend/src/schemas/Relational/userpermanences.schema.ts index 2cbfe55..5f6e57e 100644 --- a/backend/src/schemas/Relational/userpermanences.schema.ts +++ b/backend/src/schemas/Relational/userpermanences.schema.ts @@ -1,25 +1,25 @@ -import { pgTable, integer, primaryKey, timestamp, boolean } from "drizzle-orm/pg-core"; -import { userSchema } from "../Basic/user.schema"; +import { boolean, integer, pgTable, primaryKey, timestamp } from "drizzle-orm/pg-core"; import { permanenceSchema } from "../Basic/permanence.schema"; +import { userSchema } from "../Basic/user.schema"; export const userPermanenceSchema = pgTable("user_permanences", { - user_id: integer("user_id").references(() => userSchema.id, { onDelete: "cascade" }), - permanence_id: integer("permanence_id").references(() => permanenceSchema.id, { onDelete: "cascade" }), - registered_at: timestamp("registered_at").defaultNow(), - claimed: boolean("claimed").default(false), -}, -(table) => [ - primaryKey({ columns: [table.user_id, table.permanence_id] }), -]); + user_id: integer("user_id").references(() => userSchema.id, { onDelete: "cascade" }), + permanence_id: integer("permanence_id").references(() => permanenceSchema.id, { onDelete: "cascade" }), + registered_at: timestamp("registered_at").defaultNow(), + claimed: boolean("claimed").default(false), +}, + (table) => [ + primaryKey({ columns: [table.user_id, table.permanence_id] }), + ]); export type UserPermanence = typeof userPermanenceSchema.$inferSelect; export const respoPermanenceSchema = pgTable("respo_permanences", { - user_id: integer("user_id").references(() => userSchema.id, { onDelete: "cascade" }), - permanence_id: integer("permanence_id").references(() => permanenceSchema.id, { onDelete: "cascade" }), -}, -(table) => [ - primaryKey({ columns: [table.user_id, table.permanence_id] }), -]); + user_id: integer("user_id").references(() => userSchema.id, { onDelete: "cascade" }), + permanence_id: integer("permanence_id").references(() => permanenceSchema.id, { onDelete: "cascade" }), +}, + (table) => [ + primaryKey({ columns: [table.user_id, table.permanence_id] }), + ]); export type RespoPermanence = typeof userPermanenceSchema.$inferSelect; diff --git a/backend/src/schemas/Relational/userroles.schema.ts b/backend/src/schemas/Relational/userroles.schema.ts index 532c9f5..99816f5 100644 --- a/backend/src/schemas/Relational/userroles.schema.ts +++ b/backend/src/schemas/Relational/userroles.schema.ts @@ -1,20 +1,19 @@ -import { pgTable, serial, integer, primaryKey } from "drizzle-orm/pg-core"; -import { userSchema } from "../Basic/user.schema"; +import { integer, pgTable, primaryKey } from "drizzle-orm/pg-core"; import { roleSchema } from "../Basic/role.schema"; +import { userSchema } from "../Basic/user.schema"; -// Définition de la table `user_roles` pour gérer la relation many-to-many entre users et roles export const userRolesSchema = pgTable("user_roles", { - user_id: integer("user_id").notNull().references(() => userSchema.id, { onDelete: "cascade" }), - role_id: integer("role_id").notNull().references(() => roleSchema.id, { onDelete: "cascade" }), + user_id: integer("user_id").notNull().references(() => userSchema.id, { onDelete: "cascade" }), + role_id: integer("role_id").notNull().references(() => roleSchema.id, { onDelete: "cascade" }), }); export type UserRole = typeof userRolesSchema.$inferSelect; export const userPreferencesSchema = pgTable("user_preferences", { - userId: integer("user_id").notNull().references(() => userSchema.id, { onDelete: "cascade" }), - roleId: integer("role_id").notNull().references(() => roleSchema.id, { onDelete: "cascade" }), + userId: integer("user_id").notNull().references(() => userSchema.id, { onDelete: "cascade" }), + roleId: integer("role_id").notNull().references(() => roleSchema.id, { onDelete: "cascade" }), }, (table) => [ - primaryKey({ columns: [table.userId, table.roleId]}) ], + primaryKey({ columns: [table.userId, table.roleId] })], ); export type UserPreference = typeof userPreferencesSchema.$inferSelect; diff --git a/backend/src/schemas/Relational/userteams.schema.ts b/backend/src/schemas/Relational/userteams.schema.ts index e477410..6288e4f 100644 --- a/backend/src/schemas/Relational/userteams.schema.ts +++ b/backend/src/schemas/Relational/userteams.schema.ts @@ -1,14 +1,13 @@ -import { pgTable, serial, integer, primaryKey } from "drizzle-orm/pg-core"; -import { userSchema } from "../Basic/user.schema"; +import { integer, pgTable, primaryKey } from "drizzle-orm/pg-core"; import { teamSchema } from "../Basic/team.schema"; +import { userSchema } from "../Basic/user.schema"; -// Définition de la table `user_teams` pour gérer la relation many-to-many entre users et teams export const userTeamsSchema = pgTable("user_teams", { - user_id: integer("user_id").references(() => userSchema.id, { onDelete: "cascade" }), - team_id: integer("team_id").references(() => teamSchema.id, { onDelete: "cascade" }), + user_id: integer("user_id").references(() => userSchema.id, { onDelete: "cascade" }), + team_id: integer("team_id").references(() => teamSchema.id, { onDelete: "cascade" }), }, -(table) => [ - primaryKey({ columns: [table.user_id, table.team_id]}) ], + (table) => [ + primaryKey({ columns: [table.user_id, table.team_id] })], ); export type UserTeam = typeof userTeamsSchema.$inferSelect; diff --git a/backend/src/schemas/Relational/usertent.schema.ts b/backend/src/schemas/Relational/usertent.schema.ts index 6991e3e..e011222 100644 --- a/backend/src/schemas/Relational/usertent.schema.ts +++ b/backend/src/schemas/Relational/usertent.schema.ts @@ -1,14 +1,13 @@ -import { pgTable, serial, integer, primaryKey, boolean, timestamp } from "drizzle-orm/pg-core"; +import { boolean, integer, pgTable, primaryKey, timestamp } from "drizzle-orm/pg-core"; import { userSchema } from "../Basic/user.schema"; export const userTentSchema = pgTable("user_tent", { - user_id_1: integer("user_id_1").references(() => userSchema.id, { onDelete: "cascade" }), - user_id_2: integer("user_id_2").references(() => userSchema.id, { onDelete: "cascade" }), - confirmed: boolean("confirmed").default(false), // optionnel : pour savoir si les deux ont validé - created_at: timestamp("created_at").defaultNow(), + user_id_1: integer("user_id_1").references(() => userSchema.id, { onDelete: "cascade" }), + user_id_2: integer("user_id_2").references(() => userSchema.id, { onDelete: "cascade" }), + confirmed: boolean("confirmed").default(false), // optionnel : pour savoir si les deux ont validé + created_at: timestamp("created_at").defaultNow(), }, (table) => [ - primaryKey({ columns: [table.user_id_1, table.user_id_2] }), + primaryKey({ columns: [table.user_id_1, table.user_id_2] }), ]); - -export type UserTent = typeof userTentSchema.$inferSelect; \ No newline at end of file +export type UserTent = typeof userTentSchema.$inferSelect; diff --git a/backend/src/services/auth.service.ts b/backend/src/services/auth.service.ts index ae48753..eee2e7f 100644 --- a/backend/src/services/auth.service.ts +++ b/backend/src/services/auth.service.ts @@ -1,180 +1,175 @@ -import { db } from '../database/db' import bcrypt from 'bcryptjs'; -import jwt from 'jsonwebtoken'; import { randomBytes } from "crypto"; +import { eq } from "drizzle-orm"; import { JSDOM } from 'jsdom'; -import { jwtSecret } from '../utils/secret'; -import * as user_service from './user.service'; -import * as role_service from './role.service'; +import jwt from 'jsonwebtoken'; +import { cas_validate_url } from '../../src/utils/secret'; +import { db } from '../database/db'; import { User, userSchema } from '../schemas/Basic/user.schema'; -import { and, desc, eq, isNotNull, isNull, sum } from "drizzle-orm"; import { registrationSchema } from '../schemas/Relational/registration.schema'; -import { cas_validate_url } from '../../src/utils/secret'; - +import { jwtSecret } from '../utils/secret'; +import * as role_service from './role.service'; +import * as user_service from './user.service'; // Fonction pour hacher le mot de passe export const hashPassword = async (password: string): Promise => { - const salt = await bcrypt.genSalt(10); - return bcrypt.hash(password, salt); + const salt = await bcrypt.genSalt(10); + return bcrypt.hash(password, salt); }; // Fonction pour vérifier le mot de passe export const comparePassword = async (password: string, hashedPassword: string): Promise => { - return bcrypt.compare(password, hashedPassword); + return bcrypt.compare(password, hashedPassword); }; // Fonction pour générer un JWT export const generateToken = (user: User & { roles: { roleId: number; roleName: string }[] }) => { - return jwt.sign( - { - userId: user.id, - userEmail: user.email, - userPermission: user.permission, - userRoles: user.roles, // Ajout des rôles dans le token - }, - jwtSecret, - { expiresIn: "1h" } - ); + return jwt.sign( + { + userId: user.id, + userEmail: user.email, + userPermission: user.permission, + userRoles: user.roles, // Ajout des rôles dans le token + }, + jwtSecret, + { expiresIn: "1h" } + ); }; - // Fonction de connexion export const loginUser = async (email: string, password: string) => { - // Chercher l'utilisateur par email - const user = await user_service.getUserByEmail(email); - if (!user) { - throw new Error("Utilisateur non trouvé"); - } - - // Vérifier le mot de passe - const isMatch = await comparePassword(password, user.password); - if (!isMatch) { - throw new Error("Mot de passe incorrect"); - } - - // Récupérer les rôles de l'utilisateur - const userRoles = await role_service.getUserRoles(user.id); // [{ roleId, roleName }] - - // Ajouter les rôles à l'objet utilisateur - const enrichedUser = { - ...user, - roles: userRoles, - }; - - // Générer un token JWT avec les rôles inclus - const token = generateToken(enrichedUser); - return token; -}; + // Chercher l'utilisateur par email + const user = await user_service.getUserByEmail(email); + if (!user) { + throw new Error("Utilisateur non trouvé"); + } + + // Vérifier le mot de passe + const isMatch = await comparePassword(password, user.password); + if (!isMatch) { + throw new Error("Mot de passe incorrect"); + } + + // Récupérer les rôles de l'utilisateur + const userRoles = await role_service.getUserRoles(user.id); // [{ roleId, roleName }] + // Ajouter les rôles à l'objet utilisateur + const enrichedUser = { + ...user, + roles: userRoles, + }; + + // Générer un token JWT avec les rôles inclus + const token = generateToken(enrichedUser); + return token; +}; // Fonction d'inscription export const registerUser = async (firstName: string, lastName: string, email: string, password: string) => { - // Vérifier si l'email est déjà pris - const existingUser = await user_service.getUserByEmail(email); - if (existingUser) { - throw new Error('L\'email est déjà pris'); - } - - // Hacher le mot de passe avant de l'enregistrer - const hashedPassword = await hashPassword(password); - - // Créer un nouvel utilisateur dans la base de données - const result = await db.insert(userSchema).values({ - first_name: firstName, - last_name: lastName, - email: email, - password: hashedPassword, - permission: 'Nouveau', - }); - - // Retourner le nouvel utilisateur - const newUser = result[0]; // `result` est un tableau avec l'utilisateur inséré - return newUser; -}; + // Vérifier si l'email est déjà pris + const existingUser = await user_service.getUserByEmail(email); + if (existingUser) { + throw new Error('L\'email est déjà pris'); + } + // Hacher le mot de passe avant de l'enregistrer + const hashedPassword = await hashPassword(password); + + // Créer un nouvel utilisateur dans la base de données + const result = await db.insert(userSchema).values({ + first_name: firstName, + last_name: lastName, + email: email, + password: hashedPassword, + permission: 'Nouveau', + }); + + // Retourner le nouvel utilisateur + const newUser = result[0]; // `result` est un tableau avec l'utilisateur inséré + return newUser; +}; export const validateCASTicket = async (ticket: string) => { - try { - const validateUrl = `${cas_validate_url}?service=${encodeURIComponent('https://integration.utt.fr/')}&ticket=${ticket}`; - const response = await fetch(validateUrl); - if (response.ok) { - const text = await response.text(); - /*console.log("====================validateCASTicket") - console.log(text) - console.log("validateCASTicket====================")*/ - const isValid = text.includes("authenticationSuccess"); - if (isValid) { - // User is authenticated - return await parseUsernameFromCASResponse(text); - } else { - console.error("CAS ticket validation failed"); - } - } else { - console.error("Failed to validate CAS ticket"); + try { + const validateUrl = `${cas_validate_url}?service=${encodeURIComponent('https://integration.utt.fr/')}&ticket=${ticket}`; + const response = await fetch(validateUrl); + if (response.ok) { + const text = await response.text(); + /*console.log("====================validateCASTicket") + console.log(text) + console.log("validateCASTicket====================")*/ + const isValid = text.includes("authenticationSuccess"); + if (isValid) { + // User is authenticated + return await parseUsernameFromCASResponse(text); + } else { + console.error("CAS ticket validation failed"); + } + } else { + console.error("Failed to validate CAS ticket"); + } + } catch (error) { + throw new Error("Failed to fetch CAS. Please try again later."); } - } catch (error) { - throw new Error("Failed to fetch CAS. Please try again later."); - } } export const parseUsernameFromCASResponse = async (response: string) => { - const dom = new JSDOM(response, { contentType: "application/xml" }); - const document = dom.window.document; - const authSuccessNode = document.getElementsByTagName("cas:authenticationSuccess")[0]; - if (authSuccessNode) { - const attributesNode = authSuccessNode.getElementsByTagName("cas:attributes")[0]; - if (attributesNode) { - return { - uid: attributesNode.getElementsByTagName("cas:uid")[0]?.textContent, - email: attributesNode.getElementsByTagName("cas:mail")[0]?.textContent, - sn: attributesNode.getElementsByTagName("cas:sn")[0]?.textContent, - givenName: attributesNode.getElementsByTagName("cas:givenName")[0]?.textContent, - }; + const dom = new JSDOM(response, { contentType: "application/xml" }); + const document = dom.window.document; + const authSuccessNode = document.getElementsByTagName("cas:authenticationSuccess")[0]; + if (authSuccessNode) { + const attributesNode = authSuccessNode.getElementsByTagName("cas:attributes")[0]; + if (attributesNode) { + return { + uid: attributesNode.getElementsByTagName("cas:uid")[0]?.textContent, + email: attributesNode.getElementsByTagName("cas:mail")[0]?.textContent, + sn: attributesNode.getElementsByTagName("cas:sn")[0]?.textContent, + givenName: attributesNode.getElementsByTagName("cas:givenName")[0]?.textContent, + }; + } } - } } - /*================================================================================================================*/ export const completeRegistration = async (token: string, password: string) => { - const [tokenRow] = await db.select().from(registrationSchema).where(eq(registrationSchema.token, token)); + const [tokenRow] = await db.select().from(registrationSchema).where(eq(registrationSchema.token, token)); - if (!tokenRow || new Date(tokenRow.expires_at) < new Date()) { - throw new Error("Token invalide ou expiré."); - } + if (!tokenRow || new Date(tokenRow.expires_at) < new Date()) { + throw new Error("Token invalide ou expiré."); + } - const hashedPassword = await bcrypt.hash(password, 10); + const hashedPassword = await bcrypt.hash(password, 10); - await db.update(userSchema) - .set({ password: hashedPassword, permission: "Nouveau" }) - .where(eq(userSchema.id, tokenRow.user_id)); + await db.update(userSchema) + .set({ password: hashedPassword, permission: "Nouveau" }) + .where(eq(userSchema.id, tokenRow.user_id)); - // Supprimer le token - await db.delete(registrationSchema).where(eq(registrationSchema.id, tokenRow.id)); + // Supprimer le token + await db.delete(registrationSchema).where(eq(registrationSchema.id, tokenRow.id)); } export const createRegistrationToken = async (userId: number) => { - const token = randomBytes(32).toString("hex"); // Jeton bien sécurisé - const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 90); // 90 jours + const token = randomBytes(32).toString("hex"); // Jeton bien sécurisé + const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 90); // 90 jours - await db.insert(registrationSchema).values({ - user_id: userId, - token, - expires_at: expiresAt, - }); + await db.insert(registrationSchema).values({ + user_id: userId, + token, + expires_at: expiresAt, + }); - return token; + return token; }; export const deleteUserRegistrationToken = async (userId: number) => { - try { - await db.delete(registrationSchema).where(eq(registrationSchema.user_id, userId)); - return; - } - catch (error) { - throw new Error(error); - } + try { + await db.delete(registrationSchema).where(eq(registrationSchema.user_id, userId)); + return; + } + catch (error) { + throw new Error(error); + } }; diff --git a/backend/src/services/bus.service.ts b/backend/src/services/bus.service.ts index 684e73e..7b15e73 100644 --- a/backend/src/services/bus.service.ts +++ b/backend/src/services/bus.service.ts @@ -1,83 +1,81 @@ -// src/services/bus.service.ts +import { eq } from "drizzle-orm"; import fs from "fs"; import Papa from "papaparse"; import { db } from "../database/db"; -import { busAttributionSchema } from "../schemas/Relational/busattribution.schema"; import { userSchema } from "../schemas/Basic/user.schema"; -import { eq } from "drizzle-orm"; +import { busAttributionSchema } from "../schemas/Relational/busattribution.schema"; export interface BusAttribution { - userId: number; - firstName: string | null; - lastName: string | null; - email: string; - bus: number; - departure_time: string; + userId: number; + firstName: string | null; + lastName: string | null; + email: string; + bus: number; + departure_time: string; } type CsvBus = { - user_id: number; - bus: number; - departure_time : string; + user_id: number; + bus: number; + departure_time: string; }; // Récupérer toutes les attributions bus + user export const getAllBusAttributions = async (): Promise => { - const results = await db - .select({ - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, - bus: busAttributionSchema.bus, - departure_time: busAttributionSchema.departure_time - }) - .from(busAttributionSchema) - .innerJoin(userSchema, eq(userSchema.id, busAttributionSchema.user_id)); + const results = await db + .select({ + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + bus: busAttributionSchema.bus, + departure_time: busAttributionSchema.departure_time + }) + .from(busAttributionSchema) + .innerJoin(userSchema, eq(userSchema.id, busAttributionSchema.user_id)); - return results; + return results; }; // Récupérer une attribution précise par userId export const getBusAttributionByUserId = async (userId: number): Promise => { - const result = await db - .select({ - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, - bus: busAttributionSchema.bus, - departure_time: busAttributionSchema.departure_time, - }) - .from(busAttributionSchema) - .innerJoin(userSchema, eq(userSchema.id, busAttributionSchema.user_id)) - .where(eq(busAttributionSchema.user_id, userId)); + const result = await db + .select({ + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + bus: busAttributionSchema.bus, + departure_time: busAttributionSchema.departure_time, + }) + .from(busAttributionSchema) + .innerJoin(userSchema, eq(userSchema.id, busAttributionSchema.user_id)) + .where(eq(busAttributionSchema.user_id, userId)); - return result.length > 0 ? result[0] : null; + return result.length > 0 ? result[0] : null; }; - export const importBusFromCSV = async ( - filePath: string + filePath: string ): Promise => { - const fileContent = fs.readFileSync(filePath, "utf8"); + const fileContent = fs.readFileSync(filePath, "utf8"); - const { data, errors } = Papa.parse(fileContent, { - header: true, - skipEmptyLines: true, - }); + const { data, errors } = Papa.parse(fileContent, { + header: true, + skipEmptyLines: true, + }); - if (errors.length > 0) { - console.error("CSV parsing errors:", errors); - throw new Error("Erreur lors du parsing du CSV."); - } + if (errors.length > 0) { + console.error("CSV parsing errors:", errors); + throw new Error("Erreur lors du parsing du CSV."); + } - const parsedData = data.map((r) => ({ - user_id: r.user_id, - bus: r.bus, - departure_time: r.departure_time, + const parsedData = data.map((r) => ({ + user_id: r.user_id, + bus: r.bus, + departure_time: r.departure_time, - })); + })); - await db.insert(busAttributionSchema).values(parsedData); -}; \ No newline at end of file + await db.insert(busAttributionSchema).values(parsedData); +}; diff --git a/backend/src/services/challenge.service.ts b/backend/src/services/challenge.service.ts index 601be30..44d7ece 100644 --- a/backend/src/services/challenge.service.ts +++ b/backend/src/services/challenge.service.ts @@ -1,26 +1,25 @@ +import { and, desc, eq, isNotNull, not, sum } from "drizzle-orm"; import { db } from "../database/db"; import { challengeSchema } from "../schemas/Basic/challenge.schema"; +import { factionSchema } from "../schemas/Basic/faction.schema"; +import { teamSchema } from "../schemas/Basic/team.schema"; +import { userSchema } from "../schemas/Basic/user.schema"; import { challengeValidationSchema } from "../schemas/Relational/challengevalidation.schema"; // Schéma combiné -import { and, desc, eq, isNotNull, isNull, sum, not } from "drizzle-orm"; import * as team_service from "./team.service"; -import * as faction_service from "./faction.service"; -import { userSchema } from "../schemas/Basic/user.schema"; -import { teamSchema } from "../schemas/Basic/team.schema"; -import { factionSchema } from "../schemas/Basic/faction.schema"; // 1. Créer un challenge export const createChallenge = async ( - title: string, - description: string, - category: string, - points: number, - created_by: number + title: string, + description: string, + category: string, + points: number, + created_by: number ) => { const challengeValues = { - title, - description, - category, - points, + title, + description, + category, + points, created_by }; const result = await db.insert(challengeSchema).values(challengeValues).returning(); @@ -67,14 +66,14 @@ export const validateChallenge = async ({ let target_team_id = null; let target_faction_id = null; - switch(type) { + switch (type) { case "user": target_team_id = await team_service.getUserTeam(targetId); target_faction_id = await team_service.getTeamFaction(target_team_id); break; case "team": target_faction_id = await team_service.getTeamFaction(targetId); - if(target_faction_id === 0)throw new Error("Il n'y a pas de faction associée"); + if (target_faction_id === 0) throw new Error("Il n'y a pas de faction associée"); break; case "faction": target_faction_id = targetId; @@ -103,23 +102,20 @@ export const validateChallenge = async ({ // 5. Ajouter ou retirer des points manuellement export const modifyFactionPoints = async ({ - title, - factionId, - points, - reason, + title, + factionId, + points, + reason, adminId }: { - title : string; + title: string; factionId: number; points: number; reason: string; adminId: number; challengeId?: number; // Optional, lié au challenge pour ajouter des points à une faction spécifique }) => { - - const newchall = await createChallenge(title, reason, "Free", points, adminId) - const newChallengeValidationPoints = { challenge_id: newchall.id, validated_by_admin_id: adminId, @@ -132,7 +128,6 @@ export const modifyFactionPoints = async ({ // 4. Insérer la validation du challenge dans la base de données const insert = await db.insert(challengeValidationSchema).values(newChallengeValidationPoints).returning(); - return insert[0]; }; @@ -171,7 +166,6 @@ export const modifyChallenge = async ({ return updatedChallenge[0]; }; - export const unvalidateChallenge = async ({ challengeId, factionId, @@ -184,72 +178,70 @@ export const unvalidateChallenge = async ({ userId: number; }) => { const rowToDelete = await db.select().from(challengeValidationSchema) - .where(and( - eq(challengeValidationSchema.challenge_id, challengeId), - eq(challengeValidationSchema.target_faction_id, factionId), - // Vérification de la présence de teamId et userId avant de les ajouter à la requête - ...(teamId ? [eq(challengeValidationSchema.target_team_id, teamId)] : []), - ...(userId ? [eq(challengeValidationSchema.target_user_id, userId)] : []) - )) - .limit(1); // Limite à une seule ligne + .where(and( + eq(challengeValidationSchema.challenge_id, challengeId), + eq(challengeValidationSchema.target_faction_id, factionId), + // Vérification de la présence de teamId et userId avant de les ajouter à la requête + ...(teamId ? [eq(challengeValidationSchema.target_team_id, teamId)] : []), + ...(userId ? [eq(challengeValidationSchema.target_user_id, userId)] : []) + )) + .limit(1); // Limite à une seule ligne if (rowToDelete.length > 0) { await db.delete(challengeValidationSchema) .where(eq(challengeValidationSchema.id, rowToDelete[0].id)); } -; + ; }; - export const getValidatedChallenges = async () => { try { - // Effectuer une jointure entre les tables pour récupérer toutes les informations pertinentes - const validatedChallenges = await db - .select({ - challenge_id: challengeValidationSchema.challenge_id, - challenge_name : challengeSchema.title, - challenge_categorie : challengeSchema.category, - challenge_description : challengeSchema.description, - points: challengeValidationSchema.points, - validated_at: challengeValidationSchema.validated_at, - target_user_id: challengeValidationSchema.target_user_id, - target_team_id: challengeValidationSchema.target_team_id, - target_faction_id: challengeValidationSchema.target_faction_id, - target_user_firstname: userSchema.first_name, // ou d'autres champs de user - target_user_lastname: userSchema.last_name, - target_team_name: teamSchema.name, // ou d'autres champs de team - target_faction_name: factionSchema.name, // ou d'autres champs de faction - }) - .from(challengeValidationSchema) - .leftJoin(challengeSchema, eq(challengeValidationSchema.challenge_id, challengeSchema.id)) - .leftJoin(userSchema, and(eq(challengeValidationSchema.target_user_id, userSchema.id), isNotNull(challengeValidationSchema.target_user_id))) - .leftJoin(teamSchema, and(eq(challengeValidationSchema.target_team_id,(teamSchema.id)), isNotNull(challengeValidationSchema.target_team_id))) - .leftJoin(factionSchema, and(eq(challengeValidationSchema.target_faction_id, factionSchema.id), isNotNull(challengeValidationSchema.target_faction_id))) - .orderBy(desc(challengeValidationSchema.validated_at)); // Trier par date de validation (si nécessaire) - - // Retourner les résultats - return validatedChallenges; + // Effectuer une jointure entre les tables pour récupérer toutes les informations pertinentes + const validatedChallenges = await db + .select({ + challenge_id: challengeValidationSchema.challenge_id, + challenge_name: challengeSchema.title, + challenge_categorie: challengeSchema.category, + challenge_description: challengeSchema.description, + points: challengeValidationSchema.points, + validated_at: challengeValidationSchema.validated_at, + target_user_id: challengeValidationSchema.target_user_id, + target_team_id: challengeValidationSchema.target_team_id, + target_faction_id: challengeValidationSchema.target_faction_id, + target_user_firstname: userSchema.first_name, // ou d'autres champs de user + target_user_lastname: userSchema.last_name, + target_team_name: teamSchema.name, // ou d'autres champs de team + target_faction_name: factionSchema.name, // ou d'autres champs de faction + }) + .from(challengeValidationSchema) + .leftJoin(challengeSchema, eq(challengeValidationSchema.challenge_id, challengeSchema.id)) + .leftJoin(userSchema, and(eq(challengeValidationSchema.target_user_id, userSchema.id), isNotNull(challengeValidationSchema.target_user_id))) + .leftJoin(teamSchema, and(eq(challengeValidationSchema.target_team_id, (teamSchema.id)), isNotNull(challengeValidationSchema.target_team_id))) + .leftJoin(factionSchema, and(eq(challengeValidationSchema.target_faction_id, factionSchema.id), isNotNull(challengeValidationSchema.target_faction_id))) + .orderBy(desc(challengeValidationSchema.validated_at)); // Trier par date de validation (si nécessaire) + + // Retourner les résultats + return validatedChallenges; } catch (error) { - console.error("Error retrieving validated challenges:", error); - throw new Error("Error retrieving validated challenges"); + console.error("Error retrieving validated challenges:", error); + throw new Error("Error retrieving validated challenges"); } }; - export const getTotalFactionPoints = async (factionId: number): Promise => { try { - const result = await db - .select({ - totalPoints: sum(challengeValidationSchema.points) // Somme des points - , // Filtre par l'ID de la faction - }) - .from(challengeValidationSchema).where(eq(challengeValidationSchema.target_faction_id, factionId)); - - // Récupérer le total des points - - const totalPoints = Number(result[0]?.totalPoints) || 0; - return totalPoints; + const result = await db + .select({ + totalPoints: sum(challengeValidationSchema.points) // Somme des points + , // Filtre par l'ID de la faction + }) + .from(challengeValidationSchema).where(eq(challengeValidationSchema.target_faction_id, factionId)); + + // Récupérer le total des points + + const totalPoints = Number(result[0]?.totalPoints) || 0; + return totalPoints; } catch (error) { - console.error("Erreur lors de la récupération des points de la faction:", error); - throw new Error("Impossible de récupérer les points de la faction"); + console.error("Erreur lors de la récupération des points de la faction:", error); + throw new Error("Impossible de récupérer les points de la faction"); } - }; \ No newline at end of file +}; diff --git a/backend/src/services/discord.service.ts b/backend/src/services/discord.service.ts index fb2c7b7..27d26e5 100644 --- a/backend/src/services/discord.service.ts +++ b/backend/src/services/discord.service.ts @@ -1,11 +1,10 @@ import axios from 'axios'; -import { db } from '../database/db'; // Import de la connexion PostgreSQL -import { userSchema } from '../schemas/Basic/user.schema'; import { eq } from 'drizzle-orm'; +import { db } from '../database/db'; // Import de la connexion PostgreSQL +import { userSchema } from '../schemas/Basic/user.schema'; import { discord_client_id, discord_client_secret, discord_redirect_uri } from '../utils/secret'; - -export const syncDiscordUserId = async (code : string, userId : number) => { +export const syncDiscordUserId = async (code: string, userId: number) => { // Étape 1 : échange le code contre un access token const tokenResponse = await axios.post('https://discord.com/api/oauth2/token', new URLSearchParams({ client_id: discord_client_id, @@ -31,8 +30,8 @@ export const syncDiscordUserId = async (code : string, userId : number) => { //Etape 3 : Update le discord_id de l'user const result = await db.update(userSchema) - .set({discord_id: userResponse.data.id,}) - .where(eq(userSchema.id, userId)); + .set({ discord_id: userResponse.data.id, }) + .where(eq(userSchema.id, userId)); return userResponse.data; // { id, username, discriminator, ... } -}; \ No newline at end of file +}; diff --git a/backend/src/services/email.service.ts b/backend/src/services/email.service.ts index 9439114..fbe972f 100644 --- a/backend/src/services/email.service.ts +++ b/backend/src/services/email.service.ts @@ -1,45 +1,44 @@ import nodemailer from 'nodemailer'; -import { zimbra_user, zimbra_password, zimbra_host } from '../utils/secret'; +import { zimbra_host, zimbra_password, zimbra_user } from '../utils/secret'; interface EmailOptions { - from: string; - to: string[]; - subject: string; - text?: string; - html?: string; - cc?: string[]; - bcc?: string[]; + from: string; + to: string[]; + subject: string; + text?: string; + html?: string; + cc?: string[]; + bcc?: string[]; } export const sendEmail = async (options: EmailOptions): Promise => { - try { + try { + const transporter = nodemailer.createTransport({ + host: zimbra_host, + port: 587, + secure: false, + auth: { + user: zimbra_user, + pass: zimbra_password, + }, + tls: { + rejectUnauthorized: false, + }, + }); - const transporter = nodemailer.createTransport({ - host: zimbra_host, - port: 587, - secure: false, - auth: { - user: zimbra_user, - pass: zimbra_password, - }, - tls: { - rejectUnauthorized: false, - }, - }); + const mailOptions = { + from: options.from, + to: options.to ? options.to.join(', ') : '', + subject: options.subject, + text: options.text, + html: options.html, + cc: options.cc ? options.cc.join(', ') : undefined, + bcc: options.bcc ? options.bcc.join(', ') : undefined, + }; - const mailOptions = { - from: options.from, - to: options.to ? options.to.join(', ') : '', - subject: options.subject, - text: options.text, - html: options.html, - cc: options.cc ? options.cc.join(', ') : undefined, - bcc: options.bcc ? options.bcc.join(', ') : undefined, - }; - - await transporter.sendMail(mailOptions); - } catch (error) { - console.log(error) - throw new Error('Erreur lors de l\'envoi de l\'email:'); - } + await transporter.sendMail(mailOptions); + } catch (error) { + console.log(error) + throw new Error('Erreur lors de l\'envoi de l\'email:'); + } }; diff --git a/backend/src/services/event.service.ts b/backend/src/services/event.service.ts index e6cac91..1457dec 100644 --- a/backend/src/services/event.service.ts +++ b/backend/src/services/event.service.ts @@ -1,83 +1,83 @@ -import { db } from "../database/db"; import { asc, eq } from "drizzle-orm"; +import { db } from "../database/db"; import { eventSchema } from "../schemas/Basic/event.schema"; -import { teamShotgunSchema } from "../schemas/Relational/teamshotgun.schema"; import { teamSchema } from "../schemas/Basic/team.schema"; +import { teamShotgunSchema } from "../schemas/Relational/teamshotgun.schema"; export const getEventsStatus = async () => { - const events = await db.select().from(eventSchema); - if (events.length > 0) { - return events[0]; // Renvoie le premier événement s'il existe - } else { - return null; // ou une valeur par défaut - } + const events = await db.select().from(eventSchema); + if (events.length > 0) { + return events[0]; // Renvoie le premier événement s'il existe + } else { + return null; // ou une valeur par défaut + } }; export const validateShotgun = async (teamId: number) => { - await db.transaction(async (tx) => { - await tx.insert(teamShotgunSchema).values({ team_id: teamId }); - }) + await db.transaction(async (tx) => { + await tx.insert(teamShotgunSchema).values({ team_id: teamId }); + }) }; export const alreadyShotgun = async (teamId: number) => { - const shotgunTeam = await db.select({ shotgunId: teamShotgunSchema.id }) - .from(teamShotgunSchema) - .where(eq(teamShotgunSchema.team_id, teamId)); + const shotgunTeam = await db.select({ shotgunId: teamShotgunSchema.id }) + .from(teamShotgunSchema) + .where(eq(teamShotgunSchema.team_id, teamId)); - if (shotgunTeam[0]) { - return true - } - else { - return false - } + if (shotgunTeam[0]) { + return true + } + else { + return false + } }; export const updatepreRegistrationStatus = async (preRegistrationOpen: boolean) => { - return await db.update(eventSchema) - .set({ pre_registration_open: preRegistrationOpen }) - .returning(); + return await db.update(eventSchema) + .set({ pre_registration_open: preRegistrationOpen }) + .returning(); }; export const updateShotgunStatus = async (shotgunOpen: boolean) => { - return await db.update(eventSchema) - .set({ shotgun_open: shotgunOpen }) - .returning(); + return await db.update(eventSchema) + .set({ shotgun_open: shotgunOpen }) + .returning(); }; export const getAllTeamShotguns = async () => { - return await db - .select({ - id: teamShotgunSchema.id, - teamId: teamShotgunSchema.team_id, - timestamp: teamShotgunSchema.timestamp, - teamName: teamSchema.name, - teamType: teamSchema.type, - }) - .from(teamShotgunSchema) - .leftJoin(teamSchema, eq(teamShotgunSchema.team_id, teamSchema.id)) - .orderBy(asc(teamShotgunSchema.timestamp), asc(teamShotgunSchema.id)); + return await db + .select({ + id: teamShotgunSchema.id, + teamId: teamShotgunSchema.team_id, + timestamp: teamShotgunSchema.timestamp, + teamName: teamSchema.name, + teamType: teamSchema.type, + }) + .from(teamShotgunSchema) + .leftJoin(teamSchema, eq(teamShotgunSchema.team_id, teamSchema.id)) + .orderBy(asc(teamShotgunSchema.timestamp), asc(teamShotgunSchema.id)); }; export const updateSDIStatus = async (sdiOpen: boolean) => { - return await db.update(eventSchema) - .set({ sdi_open: sdiOpen }) - .returning(); + return await db.update(eventSchema) + .set({ sdi_open: sdiOpen }) + .returning(); }; export const updateWEIStatus = async (weiOpen: boolean) => { - return await db.update(eventSchema) - .set({ wei_open: weiOpen }) - .returning(); + return await db.update(eventSchema) + .set({ wei_open: weiOpen }) + .returning(); }; export const updateFoodStatus = async (foodOpen: boolean) => { - return await db.update(eventSchema) - .set({ food_open: foodOpen }) - .returning(); + return await db.update(eventSchema) + .set({ food_open: foodOpen }) + .returning(); }; export const updateChallStatus = async (challOpen: boolean) => { - return await db.update(eventSchema) - .set({ chall_open: challOpen }) - .returning(); -}; \ No newline at end of file + return await db.update(eventSchema) + .set({ chall_open: challOpen }) + .returning(); +}; diff --git a/backend/src/services/faction.service.ts b/backend/src/services/faction.service.ts index 00722ca..7171252 100644 --- a/backend/src/services/faction.service.ts +++ b/backend/src/services/faction.service.ts @@ -1,37 +1,35 @@ -import { eq, and } from "drizzle-orm"; +import { eq } from "drizzle-orm"; import { db } from "../database/db"; import { factionSchema } from "../schemas/Basic/faction.schema"; -import { teamSchema } from "../schemas/Basic/team.schema"; -export const getFactions = async() => { +export const getFactions = async () => { + const factions = await db.select( + { + factionId: factionSchema.id, + name: factionSchema.name, + description: factionSchema.description + }).from(factionSchema); - const factions = await db.select( - {factionId : factionSchema.id, - name : factionSchema.name, - description : factionSchema.description}).from(factionSchema); - - return factions + return factions } -export const getFaction = async(factionId : any) => { - +export const getFaction = async (factionId: any) => { const faction = await db.select( - {factionId : factionSchema.id, - name : factionSchema.name, - description : factionSchema.description}).from(factionSchema).where(eq(factionSchema.id, factionId)); - + { + factionId: factionSchema.id, + name: factionSchema.name, + description: factionSchema.description + }).from(factionSchema).where(eq(factionSchema.id, factionId)); + return faction[0] } -export const createFaction = async(factionName : string) => { +export const createFaction = async (factionName: string) => { + const faction = await db.insert(factionSchema).values({ name: factionName }).returning(); - const faction = await db.insert(factionSchema).values({name : factionName}).returning(); - - return faction + return faction } -export const deleteFaction = async(factionId: number) => { - - await db.delete(factionSchema).where(eq(factionSchema.id, factionId)); - +export const deleteFaction = async (factionId: number) => { + await db.delete(factionSchema).where(eq(factionSchema.id, factionId)); } diff --git a/backend/src/services/im_export.service.ts b/backend/src/services/im_export.service.ts index e381d0f..7b5c282 100644 --- a/backend/src/services/im_export.service.ts +++ b/backend/src/services/im_export.service.ts @@ -1,87 +1,84 @@ -// src/services/googleSheets.service.ts -import { google } from 'googleapis'; -import { JWT } from 'google-auth-library'; import { existsSync, mkdirSync, writeFileSync } from "fs"; +import { JWT } from 'google-auth-library'; +import { google } from 'googleapis'; import { parse } from "json2csv"; import * as user_service from './user.service'; - const path = require('path'); const keyFilePath = path.resolve(__dirname, '../utils/google_credentials.json'); // Crée une instance JWT en utilisant la clé du service account const jwtClient = new JWT({ - keyFile: keyFilePath, - scopes: ['https://www.googleapis.com/auth/spreadsheets'], + keyFile: keyFilePath, + scopes: ['https://www.googleapis.com/auth/spreadsheets'], }); // Fonction pour écrire dans Google Sheets export const writeToGoogleSheet = async ( - spreadsheetId: string, - range: string, - values: any[][] + spreadsheetId: string, + range: string, + values: any[][] ) => { - try { - // Crée un client Sheets en utilisant JWT pour l'authentification - const sheets = google.sheets({ version: 'v4', auth: jwtClient }); + try { + // Crée un client Sheets en utilisant JWT pour l'authentification + const sheets = google.sheets({ version: 'v4', auth: jwtClient }); - // Appel pour écrire les données dans la feuille - await sheets.spreadsheets.values.update({ - spreadsheetId, - range, - valueInputOption: 'RAW', - requestBody: { - values, - }, - }); + // Appel pour écrire les données dans la feuille + await sheets.spreadsheets.values.update({ + spreadsheetId, + range, + valueInputOption: 'RAW', + requestBody: { + values, + }, + }); - console.log(`Données envoyées à Google Sheets dans la plage ${range}`); - } catch (error) { - console.error('Erreur lors de l\'écriture dans Google Sheets:', error); - throw error; - } + console.log(`Données envoyées à Google Sheets dans la plage ${range}`); + } catch (error) { + console.error('Erreur lors de l\'écriture dans Google Sheets:', error); + throw error; + } }; - export const exportUsersToCSV = async (): Promise => { - const users = await user_service.getUsersAll(); - - const formattedUsers = users.map(u => { - const isOrga = u.roles && u.roles.length > 0; - const isCE = u.permission === "Student" && u.teamId !== null; - const isBenevole = u.roles.some(role => role.roleName === "Bénévole"); + const users = await user_service.getUsersAll(); - return { - id: u.id, - prenom: u.first_name ?? "", - nom: u.last_name ?? "", - mail: u.email ?? "", - telephone: u.contact ?? "", - nouveau: u.permission === "Nouveau", - ce: isCE, - num_equipe: u.teamId ?? null, - benevole: isBenevole, - orga: isOrga, - majeur: u.majeur ?? false, - }; - }); + const formattedUsers = users.map(u => { + const isOrga = u.roles && u.roles.length > 0; + const isCE = u.permission === "Student" && u.teamId !== null; + const isBenevole = u.roles.some(role => role.roleName === "Bénévole"); - const csv = parse(formattedUsers, { - fields: [ - "id", "prenom", "nom", "mail", "telephone", - "nouveau", "ce", "num_equipe", "benevole", "orga", "majeur", "bus_manual" - ] - }); + return { + id: u.id, + prenom: u.first_name ?? "", + nom: u.last_name ?? "", + mail: u.email ?? "", + telephone: u.contact ?? "", + nouveau: u.permission === "Nouveau", + ce: isCE, + num_equipe: u.teamId ?? null, + benevole: isBenevole, + orga: isOrga, + majeur: u.majeur ?? false, + }; + }); + + const csv = parse(formattedUsers, { + fields: [ + "id", "prenom", "nom", "mail", "telephone", + "nouveau", "ce", "num_equipe", "benevole", "orga", "majeur", "bus_manual" + ] + }); - const exportDir = path.join(__dirname, "../../exports/bus"); - const filePath = path.join(exportDir, "bus.csv"); + const exportDir = path.join(__dirname, "../../exports/bus"); + const filePath = path.join(exportDir, "bus.csv"); - if (!existsSync(exportDir)) { - mkdirSync(exportDir, { recursive: true }); - } + if (!existsSync(exportDir)) { + mkdirSync(exportDir, { recursive: true }); + } - writeFileSync(filePath, csv); + writeFileSync(filePath, csv); - return filePath; + return filePath; }; diff --git a/backend/src/services/news.service.ts b/backend/src/services/news.service.ts index c586056..bd59f34 100644 --- a/backend/src/services/news.service.ts +++ b/backend/src/services/news.service.ts @@ -1,74 +1,69 @@ -// src/services/actus.service.ts +import { and, desc, eq } from "drizzle-orm"; import { db } from "../database/db"; import { News, newsSchema } from "../schemas/Basic/news.schema"; -import { eq, desc, and } from "drizzle-orm"; + // Créer une actu export const createNews = async ( - title: string, - description: string, - type: string, - published: boolean, - target: string, - image_url?: string + title: string, + description: string, + type: string, + published: boolean, + target: string, + image_url?: string ) => { - const newNews = { - title, - description, - type, - published, - target, - image_url - }; + const newNews = { + title, + description, + type, + published, + target, + image_url + }; - const result = await db.insert(newsSchema).values(newNews).returning(); - return result[0]; + const result = await db.insert(newsSchema).values(newNews).returning(); + return result[0]; }; // Lister les actus export const getAllNews = async () => { - return await db.select().from(newsSchema).orderBy(desc(newsSchema.created_at)); + return await db.select().from(newsSchema).orderBy(desc(newsSchema.created_at)); }; // Lister les actus publiées (pour l'onglet côté utilisateur) export const getPublishedNews = async () => { - return await db.select().from(newsSchema).where(eq(newsSchema.published, true)).orderBy(desc(newsSchema.created_at)); + return await db.select().from(newsSchema).where(eq(newsSchema.published, true)).orderBy(desc(newsSchema.created_at)); }; // Filtrer par type export const getPublishedNewsByType = async (type: string) => { - return await db.select().from(newsSchema) - .where(and(eq(newsSchema.published, true), eq(newsSchema.type, type))) - .orderBy(desc(newsSchema.created_at)); + return await db.select().from(newsSchema) + .where(and(eq(newsSchema.published, true), eq(newsSchema.type, type))) + .orderBy(desc(newsSchema.created_at)); }; // Publier une actu export const publishNews = async (id: number) => { - - await db.update(newsSchema).set({ published: true }).where(eq(newsSchema.id, id)).returning(); - + await db.update(newsSchema).set({ published: true }).where(eq(newsSchema.id, id)).returning(); }; export const deleteNews = async (newsId: number) => { - - await db.delete(newsSchema).where(eq(newsSchema.id, newsId)); - + await db.delete(newsSchema).where(eq(newsSchema.id, newsId)); } export const updateNews = async ( - id: number, - updates: Partial + id: number, + updates: Partial ) => { - const result = await db - .update(newsSchema) - .set(updates) - .where(eq(newsSchema.id, id)) - .returning(); + const result = await db + .update(newsSchema) + .set(updates) + .where(eq(newsSchema.id, id)) + .returning(); - return result[0] ?? null; + return result[0] ?? null; }; export const getNewsById = async (id: number) => { - const result = await db.select().from(newsSchema).where(eq(newsSchema.id, id)); - return result[0] ?? null; + const result = await db.select().from(newsSchema).where(eq(newsSchema.id, id)); + return result[0] ?? null; }; - diff --git a/backend/src/services/permanence.service.ts b/backend/src/services/permanence.service.ts index eeeb9d7..b47e69a 100644 --- a/backend/src/services/permanence.service.ts +++ b/backend/src/services/permanence.service.ts @@ -1,503 +1,495 @@ +import { and, eq, inArray, sql } from "drizzle-orm"; import fs from "fs"; import Papa from "papaparse"; -import { and, count, eq, inArray, sql } from "drizzle-orm"; -import { userSchema } from "../schemas/Basic/user.schema"; -import { permanenceSchema } from "../schemas/Basic/permanence.schema"; import { db } from "../database/db"; +import { permanenceSchema } from "../schemas/Basic/permanence.schema"; +import { userSchema } from "../schemas/Basic/user.schema"; import { respoPermanenceSchema, userPermanenceSchema } from "../schemas/Relational/userpermanences.schema"; type CsvPermanence = { - name: string; - description: string; - location: string; - start_at: string; - end_at: string; - capacity: string; - is_open: string; - difficulty: string; + name: string; + description: string; + location: string; + start_at: string; + end_at: string; + capacity: string; + is_open: string; + difficulty: string; }; // Classes d'erreurs personnalisées -class UnauthorizedError extends Error {} -class AlreadyRegisteredError extends Error {} -class PermanenceNotFoundError extends Error {} -class PermanenceClosedError extends Error {} -class PermanenceFullError extends Error {} -class UnregisterDeadlineError extends Error {} -class RegisterDeadlineError extends Error {} +class UnauthorizedError extends Error { } +class AlreadyRegisteredError extends Error { } +class PermanenceNotFoundError extends Error { } +class PermanenceClosedError extends Error { } +class PermanenceFullError extends Error { } +class UnregisterDeadlineError extends Error { } +class RegisterDeadlineError extends Error { } export const getPermanenceById = async (permId: number) => { - const permanence = await db.query.permanenceSchema.findFirst({ - where: eq(permanenceSchema.id, permId), - }); - if (!permanence) throw new PermanenceNotFoundError("Permanence introuvable"); - return permanence; + const permanence = await db.query.permanenceSchema.findFirst({ + where: eq(permanenceSchema.id, permId), + }); + if (!permanence) throw new PermanenceNotFoundError("Permanence introuvable"); + return permanence; }; // ➕ S'inscrire à une permanence export const registerUserToPermanence = async ( - userId: number, - permId: number + userId: number, + permId: number ) => { - try { - // Vérifications légères (sans verrouillage) - const user = await db.query.userSchema.findFirst({ - where: eq(userSchema.id, userId), - }); - if ( - !user || - (user.permission !== "Student" && user.permission !== "Admin") - ) { - throw new UnauthorizedError("Unauthorized"); - } - - const permanence = await getPermanenceById(permId); - - if (!permanence.is_open) - throw new PermanenceClosedError("Permanence not open"); - - const limitDate = new Date(String(permanence.start_at).replace(/Z$/, "")); - const now = new Date(); - - if (now > limitDate) { - throw new RegisterDeadlineError("Too late to register"); - } - - if (permanence.capacity == 0) { - throw new PermanenceFullError("Permanence full"); - } - - // Transaction avec verrouillage de table complet - await db.transaction(async (tx) => { - // 1. VERROUILLAGE COMPLET de la table permanences - await tx.execute(sql`LOCK TABLE permanences IN EXCLUSIVE MODE`); - - // 2. UPDATE atomique avec condition - décrémente seulement si capacity > 0 - const updateResult = await tx - .update(permanenceSchema) - .set({ capacity: sql`capacity - 1` }) - .where(and(eq(permanenceSchema.id, permId), sql`capacity > 0`)) - .returning({ newCapacity: permanenceSchema.capacity }); - - // 3. Si aucune ligne modifiée = pas de place disponible - if (updateResult.length === 0) { - throw new PermanenceFullError("Permanence full"); - } - - // 4. Insérer l'utilisateur (seulement si l'UPDATE a réussi) - await tx.insert(userPermanenceSchema).values({ - user_id: userId, - permanence_id: permId, - }); - }); - } catch (error: any) { - // Gestion des erreurs de contraintes de base de données - if ( - error.code === "23505" || // Contrainte unique PostgreSQL - error.code === "23000" || // Contrainte d'intégrité générale - error.message?.includes("UNIQUE constraint") || - error.message?.includes("duplicate key") || - error.message?.includes("PRIMARY KEY constraint") - ) { - throw new AlreadyRegisteredError("Already registered"); + try { + // Vérifications légères (sans verrouillage) + const user = await db.query.userSchema.findFirst({ + where: eq(userSchema.id, userId), + }); + if ( + !user || + (user.permission !== "Student" && user.permission !== "Admin") + ) { + throw new UnauthorizedError("Unauthorized"); + } + + const permanence = await getPermanenceById(permId); + + if (!permanence.is_open) + throw new PermanenceClosedError("Permanence not open"); + + const limitDate = new Date(String(permanence.start_at).replace(/Z$/, "")); + const now = new Date(); + + if (now > limitDate) { + throw new RegisterDeadlineError("Too late to register"); + } + + if (permanence.capacity == 0) { + throw new PermanenceFullError("Permanence full"); + } + + // Transaction avec verrouillage de table complet + await db.transaction(async (tx) => { + // 1. VERROUILLAGE COMPLET de la table permanences + await tx.execute(sql`LOCK TABLE permanences IN EXCLUSIVE MODE`); + + // 2. UPDATE atomique avec condition - décrémente seulement si capacity > 0 + const updateResult = await tx + .update(permanenceSchema) + .set({ capacity: sql`capacity - 1` }) + .where(and(eq(permanenceSchema.id, permId), sql`capacity > 0`)) + .returning({ newCapacity: permanenceSchema.capacity }); + + // 3. Si aucune ligne modifiée = pas de place disponible + if (updateResult.length === 0) { + throw new PermanenceFullError("Permanence full"); + } + + // 4. Insérer l'utilisateur (seulement si l'UPDATE a réussi) + await tx.insert(userPermanenceSchema).values({ + user_id: userId, + permanence_id: permId, + }); + }); + } catch (error: any) { + // Gestion des erreurs de contraintes de base de données + if ( + error.code === "23505" || // Contrainte unique PostgreSQL + error.code === "23000" || // Contrainte d'intégrité générale + error.message?.includes("UNIQUE constraint") || + error.message?.includes("duplicate key") || + error.message?.includes("PRIMARY KEY constraint") + ) { + throw new AlreadyRegisteredError("Already registered"); + } + // Re-lancer les autres erreurs + throw error; } - // Re-lancer les autres erreurs - throw error; - } }; // ❌ Se désinscrire d'une permanence export const unregisterUserFromPermanence = async ( - userId: number, - permId: number + userId: number, + permId: number ) => { - const permanence = await getPermanenceById(permId); - const now = new Date(); - const limitDate = new Date(permanence.start_at); - limitDate.setDate(limitDate.getDate() - 1); - - if (now > limitDate) - throw new UnregisterDeadlineError("Too late to unregister"); - - // Désinscrire l'utilisateur - await db - .delete(userPermanenceSchema) - .where( - and( - eq(userPermanenceSchema.user_id, userId), - eq(userPermanenceSchema.permanence_id, permId) - ) - ); - - await modifyPermCap(permId, 1); + const permanence = await getPermanenceById(permId); + const now = new Date(); + const limitDate = new Date(permanence.start_at); + limitDate.setDate(limitDate.getDate() - 1); + + if (now > limitDate) + throw new UnregisterDeadlineError("Too late to unregister"); + + // Désinscrire l'utilisateur + await db + .delete(userPermanenceSchema) + .where( + and( + eq(userPermanenceSchema.user_id, userId), + eq(userPermanenceSchema.permanence_id, permId) + ) + ); + + await modifyPermCap(permId, 1); }; // 🔎 Voir toutes les permanences ouvertes export const listOpenPermanences = async () => { - return await db.query.permanenceSchema.findMany({ - where: eq(permanenceSchema.is_open, true), - orderBy: permanenceSchema.start_at, - }); + return await db.query.permanenceSchema.findMany({ + where: eq(permanenceSchema.is_open, true), + orderBy: permanenceSchema.start_at, + }); }; // ➕ Créer une permanence export const createPermanence = async ( - name: string, - description: string, - location: string, - start_at: Date, - end_at: Date, - capacity: number, - difficulty: number, - respoId: number + name: string, + description: string, + location: string, + start_at: Date, + end_at: Date, + capacity: number, + difficulty: number, + respoId: number ) => { - // Étape 1 : Création de la permanence - const [newPermanence] = await db - .insert(permanenceSchema) - .values({ - name, - description, - location, - start_at, - end_at, - capacity, - is_open: false, - difficulty, - }) - .returning({ id: permanenceSchema.id }); - - // Étape 2 : Ajout du responsable - if (newPermanence?.id && respoId) { - await db.insert(respoPermanenceSchema).values({ - user_id: respoId, - permanence_id: newPermanence.id, - }); - } + // Étape 1 : Création de la permanence + const [newPermanence] = await db + .insert(permanenceSchema) + .values({ + name, + description, + location, + start_at, + end_at, + capacity, + is_open: false, + difficulty, + }) + .returning({ id: permanenceSchema.id }); + + // Étape 2 : Ajout du responsable + if (newPermanence?.id && respoId) { + await db.insert(respoPermanenceSchema).values({ + user_id: respoId, + permanence_id: newPermanence.id, + }); + } }; - export const deletePermanence = async (permId: number) => { - // Étape 1 : Supprimer les inscriptions des utilisateurs - await db - .delete(userPermanenceSchema) - .where(eq(userPermanenceSchema.permanence_id, permId)); - - // Étape 2 : Supprimer les responsables associés - await db - .delete(respoPermanenceSchema) - .where(eq(respoPermanenceSchema.permanence_id, permId)); - - // Étape 3 : Supprimer la permanence - await db - .delete(permanenceSchema) - .where(eq(permanenceSchema.id, permId)); + // Étape 1 : Supprimer les inscriptions des utilisateurs + await db + .delete(userPermanenceSchema) + .where(eq(userPermanenceSchema.permanence_id, permId)); + + // Étape 2 : Supprimer les responsables associés + await db + .delete(respoPermanenceSchema) + .where(eq(respoPermanenceSchema.permanence_id, permId)); + + // Étape 3 : Supprimer la permanence + await db + .delete(permanenceSchema) + .where(eq(permanenceSchema.id, permId)); }; - export const updatePermanence = async ( - permId: number, - name: string, - description: string, - location: string, - start_at: Date, - end_at: Date, - capacity: number, - difficulty: number, - respoId: number + permId: number, + name: string, + description: string, + location: string, + start_at: Date, + end_at: Date, + capacity: number, + difficulty: number, + respoId: number ) => { - // Étape 1 : Mise à jour de la permanence - await db - .update(permanenceSchema) - .set({ - name, - description, - location, - start_at, - end_at, - capacity, - is_open: false, - difficulty, - }) - .where(eq(permanenceSchema.id, permId)); - - // Étape 2 : Suppression des anciens responsables (si nécessaire) - await db - .delete(respoPermanenceSchema) - .where(eq(respoPermanenceSchema.permanence_id, permId)); - - // Étape 3 : Ajout du nouveau responsable - if(respoId){ - await db.insert(respoPermanenceSchema).values({ - user_id: respoId, - permanence_id: permId, - }); - } + // Étape 1 : Mise à jour de la permanence + await db + .update(permanenceSchema) + .set({ + name, + description, + location, + start_at, + end_at, + capacity, + is_open: false, + difficulty, + }) + .where(eq(permanenceSchema.id, permId)); + + // Étape 2 : Suppression des anciens responsables (si nécessaire) + await db + .delete(respoPermanenceSchema) + .where(eq(respoPermanenceSchema.permanence_id, permId)); + + // Étape 3 : Ajout du nouveau responsable + if (respoId) { + await db.insert(respoPermanenceSchema).values({ + user_id: respoId, + permanence_id: permId, + }); + } }; - // Ouvrir une permanence (Admin action) export const openPermanence = async (permId: number) => { - await db - .update(permanenceSchema) - .set({ is_open: true }) - .where(eq(permanenceSchema.id, permId)); + await db + .update(permanenceSchema) + .set({ is_open: true }) + .where(eq(permanenceSchema.id, permId)); }; // Fermer une permanence (Admin action) export const closePermanence = async (permId: number) => { - await db - .update(permanenceSchema) - .set({ is_open: false }) - .where(eq(permanenceSchema.id, permId)); + await db + .update(permanenceSchema) + .set({ is_open: false }) + .where(eq(permanenceSchema.id, permId)); }; // Modifier la capacité de la permanence export const modifyPermCap = async (permId: number, factor: number) => { - const perm = await getPermanenceById(permId); - const newPermCap = Number(perm.capacity) + factor; + const perm = await getPermanenceById(permId); + const newPermCap = Number(perm.capacity) + factor; - if (newPermCap < 0) throw new Error("Invalid capacity"); + if (newPermCap < 0) throw new Error("Invalid capacity"); - await db - .update(permanenceSchema) - .set({ capacity: newPermCap }) - .where(eq(permanenceSchema.id, permId)); + await db + .update(permanenceSchema) + .set({ capacity: newPermCap }) + .where(eq(permanenceSchema.id, permId)); }; // Voir ses permanences export const getMyPermanences = async (userId: number) => { - const userPerms = await db - .select({ - id: permanenceSchema.id, - name: permanenceSchema.name, - start_at: permanenceSchema.start_at, - end_at: permanenceSchema.end_at, - location: permanenceSchema.location, - }) - .from(userPermanenceSchema) - .innerJoin( - permanenceSchema, - eq(permanenceSchema.id, userPermanenceSchema.permanence_id) - ) - .where(eq(userPermanenceSchema.user_id, userId)); - - // Ajout du responsables - const results = await Promise.all( - userPerms.map(async (perm) => { - const [respo] = await db + const userPerms = await db .select({ - id: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, + id: permanenceSchema.id, + name: permanenceSchema.name, + start_at: permanenceSchema.start_at, + end_at: permanenceSchema.end_at, + location: permanenceSchema.location, }) - .from(respoPermanenceSchema) - .innerJoin(userSchema, eq(userSchema.id, respoPermanenceSchema.user_id)) - .where(eq(respoPermanenceSchema.permanence_id, perm.id)); - - return { - ...perm, - respo: respo ?? null, - }; - }) - ); + .from(userPermanenceSchema) + .innerJoin( + permanenceSchema, + eq(permanenceSchema.id, userPermanenceSchema.permanence_id) + ) + .where(eq(userPermanenceSchema.user_id, userId)); + + // Ajout du responsables + const results = await Promise.all( + userPerms.map(async (perm) => { + const [respo] = await db + .select({ + id: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + }) + .from(respoPermanenceSchema) + .innerJoin(userSchema, eq(userSchema.id, respoPermanenceSchema.user_id)) + .where(eq(respoPermanenceSchema.permanence_id, perm.id)); + + return { + ...perm, + respo: respo ?? null, + }; + }) + ); - return results; + return results; }; - export const getAllPermanences = async () => { - const perms = await db.select().from(permanenceSchema); - - const results = await Promise.all( - perms.map(async (perm) => { - const [respo] = await db - .select({ - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, + const perms = await db.select().from(permanenceSchema); + + const results = await Promise.all( + perms.map(async (perm) => { + const [respo] = await db + .select({ + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + }) + .from(respoPermanenceSchema) + .innerJoin(userSchema, eq(userSchema.id, respoPermanenceSchema.user_id)) + .where(eq(respoPermanenceSchema.permanence_id, perm.id)); + + return { + ...perm, + respo: respo ?? null, + }; }) - .from(respoPermanenceSchema) - .innerJoin(userSchema, eq(userSchema.id, respoPermanenceSchema.user_id)) - .where(eq(respoPermanenceSchema.permanence_id, perm.id)); - - return { - ...perm, - respo: respo ?? null, - }; - }) - ); + ); - return results; + return results; }; - export const getUsersInPermanence = async (permId: number) => { - return await db - .select({ - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - claimed: userPermanenceSchema.claimed, - }) - .from(userPermanenceSchema) - .innerJoin(userSchema, eq(userSchema.id, userPermanenceSchema.user_id)) - .where(eq(userPermanenceSchema.permanence_id, permId)); + return await db + .select({ + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + claimed: userPermanenceSchema.claimed, + }) + .from(userPermanenceSchema) + .innerJoin(userSchema, eq(userSchema.id, userPermanenceSchema.user_id)) + .where(eq(userPermanenceSchema.permanence_id, permId)); }; - export const addUserToPermanence = async (userId: number, permId: number) => { - // Désinscrire l'utilisateur - await db.insert(userPermanenceSchema).values({ - user_id: userId, - permanence_id: permId, - }); + // Désinscrire l'utilisateur + await db.insert(userPermanenceSchema).values({ + user_id: userId, + permanence_id: permId, + }); - await modifyPermCap(permId, -1); + await modifyPermCap(permId, -1); }; export const removeUserToPermanence = async ( - userId: number, - permId: number + userId: number, + permId: number ) => { - await db - .delete(userPermanenceSchema) - .where( - and( - eq(userPermanenceSchema.user_id, userId), - eq(userPermanenceSchema.permanence_id, permId) - ) - ); - - await modifyPermCap(permId, 1); + await db + .delete(userPermanenceSchema) + .where( + and( + eq(userPermanenceSchema.user_id, userId), + eq(userPermanenceSchema.permanence_id, permId) + ) + ); + + await modifyPermCap(permId, 1); }; export const getAllPermanencesWithUsers = async () => { - // Récupère toutes les permanences - const permanences = await getAllPermanences(); - - // Pour chaque permanence, on récupère les users associés avec leur statut claimed - const results = await Promise.all( - permanences.map(async (permanence) => { - const userRelations = await db - .select({ - id: userSchema.id, - first_name: userSchema.first_name, - last_name: userSchema.last_name, - email: userSchema.email, - claimed: userPermanenceSchema.claimed, + // Récupère toutes les permanences + const permanences = await getAllPermanences(); + + // Pour chaque permanence, on récupère les users associés avec leur statut claimed + const results = await Promise.all( + permanences.map(async (permanence) => { + const userRelations = await db + .select({ + id: userSchema.id, + first_name: userSchema.first_name, + last_name: userSchema.last_name, + email: userSchema.email, + claimed: userPermanenceSchema.claimed, + }) + .from(userPermanenceSchema) + .innerJoin(userSchema, eq(userSchema.id, userPermanenceSchema.user_id)) + .where(eq(userPermanenceSchema.permanence_id, permanence.id)); + + return { + ...permanence, + users: userRelations, + }; }) - .from(userPermanenceSchema) - .innerJoin(userSchema, eq(userSchema.id, userPermanenceSchema.user_id)) - .where(eq(userPermanenceSchema.permanence_id, permanence.id)); - - return { - ...permanence, - users: userRelations, - }; - }) - ); + ); - return results; + return results; }; export const importPermanencesFromCSV = async ( - filePath: string + filePath: string ): Promise => { - const fileContent = fs.readFileSync(filePath, "utf8"); - - const { data, errors } = Papa.parse(fileContent, { - header: true, - skipEmptyLines: true, - }); - - if (errors.length > 0) { - console.error("CSV parsing errors:", errors); - throw new Error("Erreur lors du parsing du CSV."); - } - - const parsedData = data.map((r) => ({ - name: r.name, - description: r.description, - location: r.location, - start_at: new Date(r.start_at), - end_at: new Date(r.end_at), - capacity: parseInt(r.capacity, 10), - difficulty: parseInt(r.difficulty, 10), - is_open: false, - - })); - - await db.insert(permanenceSchema).values(parsedData); + const fileContent = fs.readFileSync(filePath, "utf8"); + + const { data, errors } = Papa.parse(fileContent, { + header: true, + skipEmptyLines: true, + }); + + if (errors.length > 0) { + console.error("CSV parsing errors:", errors); + throw new Error("Erreur lors du parsing du CSV."); + } + + const parsedData = data.map((r) => ({ + name: r.name, + description: r.description, + location: r.location, + start_at: new Date(r.start_at), + end_at: new Date(r.end_at), + capacity: parseInt(r.capacity, 10), + difficulty: parseInt(r.difficulty, 10), + is_open: false, + + })); + + await db.insert(permanenceSchema).values(parsedData); }; export const isUserRespoOfPermanence = async ( - userId: number, + userId: number, ): Promise => { - const respo = await db - .select() - .from(respoPermanenceSchema) - .where( - eq(respoPermanenceSchema.user_id, userId) - ); + const respo = await db + .select() + .from(respoPermanenceSchema) + .where( + eq(respoPermanenceSchema.user_id, userId) + ); - return respo.length > 0; + return respo.length > 0; }; export const getPermanenceDetailsForRespo = async (respoId: number) => { - // Étape 1 : Trouver les permanences dont il est respo - const respos = await db - .select() - .from(respoPermanenceSchema) - .where(eq(respoPermanenceSchema.user_id, respoId)); - - const permanenceIds = respos.map((r) => r.permanence_id); - if (permanenceIds.length === 0) throw new Error("Pas de permanences");; - - // Étape 2 : Récupérer les permanences - const permanences = await db - .select() - .from(permanenceSchema) - .where(inArray(permanenceSchema.id, permanenceIds)); - - // Étape 3 : Récupérer les membres avec infos utiles - const results = await Promise.all( - permanences.map(async (perm) => { - const members = await db - .select({ - id: userSchema.id, - first_name: userSchema.first_name, - last_name: userSchema.last_name, - email: userSchema.email, - claimed: userPermanenceSchema.claimed, + // Étape 1 : Trouver les permanences dont il est respo + const respos = await db + .select() + .from(respoPermanenceSchema) + .where(eq(respoPermanenceSchema.user_id, respoId)); + + const permanenceIds = respos.map((r) => r.permanence_id); + if (permanenceIds.length === 0) throw new Error("Pas de permanences");; + + // Étape 2 : Récupérer les permanences + const permanences = await db + .select() + .from(permanenceSchema) + .where(inArray(permanenceSchema.id, permanenceIds)); + + // Étape 3 : Récupérer les membres avec infos utiles + const results = await Promise.all( + permanences.map(async (perm) => { + const members = await db + .select({ + id: userSchema.id, + first_name: userSchema.first_name, + last_name: userSchema.last_name, + email: userSchema.email, + claimed: userPermanenceSchema.claimed, + }) + .from(userPermanenceSchema) + .innerJoin(userSchema, eq(userSchema.id, userPermanenceSchema.user_id)) + .where(eq(userPermanenceSchema.permanence_id, perm.id)); + + return { + permanence: perm, + members, + }; }) - .from(userPermanenceSchema) - .innerJoin(userSchema, eq(userSchema.id, userPermanenceSchema.user_id)) - .where(eq(userPermanenceSchema.permanence_id, perm.id)); - - return { - permanence: perm, - members, - }; - }) - ); + ); - return results; + return results; }; -export const claimMember = async ( userId: number, permId: number, claimed: boolean ) => { - await db - .update(userPermanenceSchema) - .set({ claimed }) - .where( - and( - eq(userPermanenceSchema.user_id, userId), - eq(userPermanenceSchema.permanence_id, permId) - ) - ); +export const claimMember = async (userId: number, permId: number, claimed: boolean) => { + await db + .update(userPermanenceSchema) + .set({ claimed }) + .where( + and( + eq(userPermanenceSchema.user_id, userId), + eq(userPermanenceSchema.permanence_id, permId) + ) + ); }; - - diff --git a/backend/src/services/registration.service.ts b/backend/src/services/registration.service.ts index 64e55ca..1ffac87 100644 --- a/backend/src/services/registration.service.ts +++ b/backend/src/services/registration.service.ts @@ -1,14 +1,13 @@ -import { db } from '../database/db'; // Import de la connexion PostgreSQL import { eq } from 'drizzle-orm'; +import { db } from '../database/db'; // Import de la connexion PostgreSQL import { registrationSchema } from '../schemas/Relational/registration.schema'; - export const getRegistrationByUserId = async (userId: number) => { try { - const token = await db.select().from(registrationSchema).where(eq(registrationSchema.user_id, userId)); - return token[0] ? token[0].token : null; + const token = await db.select().from(registrationSchema).where(eq(registrationSchema.user_id, userId)); + return token[0] ? token[0].token : null; } catch (err) { - console.error('Erreur lors de la récupération du token d\'enregistrement:', err); - throw new Error('Erreur de base de données'); + console.error('Erreur lors de la récupération du token d\'enregistrement:', err); + throw new Error('Erreur de base de données'); } -}; \ No newline at end of file +}; diff --git a/backend/src/services/role.service.ts b/backend/src/services/role.service.ts index 4c3247d..426084b 100644 --- a/backend/src/services/role.service.ts +++ b/backend/src/services/role.service.ts @@ -1,204 +1,200 @@ -// services/role.service.ts +import { and, eq } from "drizzle-orm"; import { db } from "../database/db"; -import { eq, and } from "drizzle-orm"; -import { - userRolesSchema, - userPreferencesSchema, - UserRole, -} from "../schemas/Relational/userroles.schema"; import { roleSchema } from "../schemas/Basic/role.schema"; import { userSchema } from "../schemas/Basic/user.schema"; import { rolePoints } from "../schemas/Relational/rolepoints.schema"; +import { + userPreferencesSchema, + UserRole, + userRolesSchema, +} from "../schemas/Relational/userroles.schema"; // 1. Ajouter un rôle à un utilisateur export const assignRoleToUser = async (userId: number, roleId: number) => { - const existing = await db - .select() - .from(userRolesSchema) - .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); + const existing = await db + .select() + .from(userRolesSchema) + .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); - if (existing.length > 0) return; + if (existing.length > 0) return; - const newUserRole: UserRole = { user_id: userId, role_id: roleId }; - await db.insert(userRolesSchema).values(newUserRole); + const newUserRole: UserRole = { user_id: userId, role_id: roleId }; + await db.insert(userRolesSchema).values(newUserRole); }; // 2. Supprimer un rôle d'un utilisateur export const removeRoleFromUser = async (userId: number, roleId: number) => { - await db - .delete(userRolesSchema) - .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); + await db + .delete(userRolesSchema) + .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); }; // 3. Récupérer les rôles d'un utilisateur export const getUserRoles = async (userId: number) => { - return await db - .select({ - roleId: roleSchema.id, - roleName: roleSchema.name, - }) - .from(userRolesSchema) - .innerJoin(roleSchema, eq(userRolesSchema.role_id, roleSchema.id)) - .where(eq(userRolesSchema.user_id, userId)); + return await db + .select({ + roleId: roleSchema.id, + roleName: roleSchema.name, + }) + .from(userRolesSchema) + .innerJoin(roleSchema, eq(userRolesSchema.role_id, roleSchema.id)) + .where(eq(userRolesSchema.user_id, userId)); }; // 4. Récupérer tous les utilisateurs avec leurs rôles export const getUsersWithRoles = async () => { - return await db - .select({ - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - roleName: roleSchema.name, - }) - .from(userRolesSchema) - .innerJoin(userSchema, eq(userRolesSchema.user_id, userSchema.id)) - .innerJoin(roleSchema, eq(userRolesSchema.role_id, roleSchema.id)); + return await db + .select({ + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + roleName: roleSchema.name, + }) + .from(userRolesSchema) + .innerJoin(userSchema, eq(userRolesSchema.user_id, userSchema.id)) + .innerJoin(roleSchema, eq(userRolesSchema.role_id, roleSchema.id)); }; // 5. Récupérer les utilisateurs par nom de rôle export const getUsersByRoleName = async (roleName: string) => { - const role = await db - .select() - .from(roleSchema) - .where(eq(roleSchema.name, roleName)) - .limit(1); - - if (role.length === 0) throw new Error("Rôle non trouvé"); - - const roleId = role[0].id; - - return await db - .select({ - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, - contact: userSchema.contact, - roleName: roleSchema.name, - }) - .from(userPreferencesSchema) - .innerJoin(userSchema, eq(userPreferencesSchema.userId, userSchema.id)) - .innerJoin(roleSchema, eq(userPreferencesSchema.roleId, roleSchema.id)) - .where(eq(userPreferencesSchema.roleId, roleId)); + const role = await db + .select() + .from(roleSchema) + .where(eq(roleSchema.name, roleName)) + .limit(1); + + if (role.length === 0) throw new Error("Rôle non trouvé"); + + const roleId = role[0].id; + + return await db + .select({ + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + contact: userSchema.contact, + roleName: roleSchema.name, + }) + .from(userPreferencesSchema) + .innerJoin(userSchema, eq(userPreferencesSchema.userId, userSchema.id)) + .innerJoin(roleSchema, eq(userPreferencesSchema.roleId, roleSchema.id)) + .where(eq(userPreferencesSchema.roleId, roleId)); }; // 6. Mettre à jour les préférences d'un utilisateur export const updateUserPreferences = async (userId: number, roleIds: number[]) => { - await db.delete(userPreferencesSchema).where(eq(userPreferencesSchema.userId, userId)); + await db.delete(userPreferencesSchema).where(eq(userPreferencesSchema.userId, userId)); - const newPreferences = roleIds.map((roleId) => ({ userId, roleId })); - await db.insert(userPreferencesSchema).values(newPreferences); + const newPreferences = roleIds.map((roleId) => ({ userId, roleId })); + await db.insert(userPreferencesSchema).values(newPreferences); }; // 7. Récupérer les préférences d'un utilisateur export const getUserPreferences = async (userId: number) => { - return await db - .select({ - roleId: roleSchema.id, - roleName: roleSchema.name, - }) - .from(userPreferencesSchema) - .innerJoin(roleSchema, eq(userPreferencesSchema.roleId, roleSchema.id)) - .where(eq(userPreferencesSchema.userId, userId)); + return await db + .select({ + roleId: roleSchema.id, + roleName: roleSchema.name, + }) + .from(userPreferencesSchema) + .innerJoin(roleSchema, eq(userPreferencesSchema.roleId, roleSchema.id)) + .where(eq(userPreferencesSchema.userId, userId)); }; // 8. Récupérer tous les rôles disponibles export const getAllRoles = async () => { - return await db - .select({ - roleId: roleSchema.id, - name: roleSchema.name, - description: roleSchema.description, - }) - .from(roleSchema); + return await db + .select({ + roleId: roleSchema.id, + name: roleSchema.name, + description: roleSchema.description, + }) + .from(roleSchema); }; // 9. Ajouter des points à un rôle export const addPointsToRole = async (roleId: number, pointsToAdd: number) => { - const existing = await db - .select() - .from(rolePoints) - .where(eq(rolePoints.role_id, roleId)) - .limit(1); - - if (existing.length === 0) { - await db.insert(rolePoints).values({ role_id: roleId, points: pointsToAdd }); - } else { - const newPoints = existing[0].points + pointsToAdd; - await db - .update(rolePoints) - .set({ points: newPoints }) - .where(eq(rolePoints.role_id, roleId)); - } + const existing = await db + .select() + .from(rolePoints) + .where(eq(rolePoints.role_id, roleId)) + .limit(1); + + if (existing.length === 0) { + await db.insert(rolePoints).values({ role_id: roleId, points: pointsToAdd }); + } else { + const newPoints = existing[0].points + pointsToAdd; + await db + .update(rolePoints) + .set({ points: newPoints }) + .where(eq(rolePoints.role_id, roleId)); + } }; // 10. Retirer des points à un rôle export const removePointsFromRole = async (roleId: number, pointsToRemove: number) => { - const existing = await db - .select() - .from(rolePoints) - .where(eq(rolePoints.role_id, roleId)) - .limit(1); - - if (existing.length === 0) { - throw new Error("Rôle introuvable"); - } - - const newPoints = Math.max(0, existing[0].points - pointsToRemove); - await db - .update(rolePoints) - .set({ points: newPoints }) - .where(eq(rolePoints.role_id, roleId)); + const existing = await db + .select() + .from(rolePoints) + .where(eq(rolePoints.role_id, roleId)) + .limit(1); + + if (existing.length === 0) { + throw new Error("Rôle introuvable"); + } + + const newPoints = Math.max(0, existing[0].points - pointsToRemove); + await db + .update(rolePoints) + .set({ points: newPoints }) + .where(eq(rolePoints.role_id, roleId)); }; // 11. Récupérer les points de tous les rôles export const getAllRolePoints = async () => { - return await db.select().from(rolePoints); + return await db.select().from(rolePoints); }; // 12. Récupérer les points d'un rôle spécifique export const getRolePoints = async (roleId: number) => { - const result = await db - .select() - .from(rolePoints) - .where(eq(rolePoints.role_id, roleId)) - .limit(1); + const result = await db + .select() + .from(rolePoints) + .where(eq(rolePoints.role_id, roleId)) + .limit(1); - return result[0] ?? null; + return result[0] ?? null; }; // 13. Checker le role d'User export const checkRoleUser = async (userId: number, roleId: number) => { + const existingRole = await db + .select() + .from(userRolesSchema) + .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); - const existingRole = await db - .select() - .from(userRolesSchema) - .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); - - return existingRole; + return existingRole; }; // 13. Ajouter un role à l'User export const insertUserRole = async (userId: number, roleId: number) => { - - const newUserRole: UserRole = { user_id: userId, role_id: roleId } - await db.insert(userRolesSchema).values(newUserRole); + const newUserRole: UserRole = { user_id: userId, role_id: roleId } + await db.insert(userRolesSchema).values(newUserRole); } // 13. Recevoir tout les roles export const getRoles = async () => { - try { - const roles = await db.select({ - roleId: roleSchema.id, - name: roleSchema.name, - description: roleSchema.description - }).from(roleSchema); - return roles; - } - catch (error) { - throw new Error("Erreur lors de la récupération des rôles : " + error.message); - } - -} \ No newline at end of file + try { + const roles = await db.select({ + roleId: roleSchema.id, + name: roleSchema.name, + description: roleSchema.description + }).from(roleSchema); + return roles; + } + catch (error) { + throw new Error("Erreur lors de la récupération des rôles : " + error.message); + } +} diff --git a/backend/src/services/team.service.ts b/backend/src/services/team.service.ts index 9e3249b..1083479 100644 --- a/backend/src/services/team.service.ts +++ b/backend/src/services/team.service.ts @@ -1,279 +1,267 @@ import { eq } from "drizzle-orm"; import { db } from "../database/db"; +import { factionSchema } from "../schemas/Basic/faction.schema"; import { teamSchema } from "../schemas/Basic/team.schema"; -import { userTeamsSchema } from "../schemas/Relational/userteams.schema"; -import { teamFactionSchema } from "../schemas/Relational/teamfaction.schema"; import { userSchema } from "../schemas/Basic/user.schema"; -import { getFaction } from "./faction.service"; -import { factionSchema } from "../schemas/Basic/faction.schema"; +import { teamFactionSchema } from "../schemas/Relational/teamfaction.schema"; import { teamShotgunSchema } from "../schemas/Relational/teamshotgun.schema"; -import { permission } from "process"; +import { userTeamsSchema } from "../schemas/Relational/userteams.schema"; +import { getFaction } from "./faction.service"; export const createTeam = async (teamName: string, members: number[]) => { + const newTeam = await db.insert(teamSchema).values({ name: teamName }).returning(); + const teamId = newTeam[0].id; - const newTeam = await db.insert(teamSchema).values({ name: teamName }).returning(); - const teamId = newTeam[0].id; - await Promise.all(members.map((userId) => - db.insert(userTeamsSchema).values({ team_id: teamId, user_id: userId }) - )); + await Promise.all(members.map((userId) => + db.insert(userTeamsSchema).values({ team_id: teamId, user_id: userId }) + )); - return newTeam; + return newTeam; }; export const createTeamLight = async (teamName: string, factionId: number) => { + const newTeam = await db.insert(teamSchema).values({ name: teamName }).returning({ teamId: teamSchema.id }); - const newTeam = await db.insert(teamSchema).values({ name: teamName }).returning({ teamId: teamSchema.id }); - - if (factionId) { - await db.insert(teamFactionSchema).values({ faction_id: factionId, team_id: newTeam[0].teamId }); - } + if (factionId) { + await db.insert(teamFactionSchema).values({ faction_id: factionId, team_id: newTeam[0].teamId }); + } - return newTeam; + return newTeam; }; export const getUserTeam = async (userId: number) => { + const userTeam = await db.select({ userTeamId: userTeamsSchema.team_id }).from(userTeamsSchema).where(eq(userTeamsSchema.user_id, userId)); - const userTeam = await db.select({ userTeamId: userTeamsSchema.team_id }).from(userTeamsSchema).where(eq(userTeamsSchema.user_id, userId)); - - return userTeam[0]?.userTeamId + return userTeam[0]?.userTeamId } export const getTeams = async () => { - - const teams = await db.select( - { - teamId: teamSchema.id, - name: teamSchema.name, - description: teamSchema.description, - type: teamSchema.type - }).from(teamSchema); - - return teams + const teams = await db.select( + { + teamId: teamSchema.id, + name: teamSchema.name, + description: teamSchema.description, + type: teamSchema.type + }).from(teamSchema); + + return teams } export const getTeamsAll = async () => { - - const teams = await db.select().from(teamSchema); - - const teamsWithFaction = await Promise.all( - teams.map(async (team) => { - const teamFactionId = await getTeamFaction(team.id); - const teamFaction = await getFaction(teamFactionId); - return { - ...team, - teamFaction - }; - }) - ); - return teamsWithFaction; + const teams = await db.select().from(teamSchema); + + const teamsWithFaction = await Promise.all( + teams.map(async (team) => { + const teamFactionId = await getTeamFaction(team.id); + const teamFaction = await getFaction(teamFactionId); + return { + ...team, + teamFaction + }; + }) + ); + return teamsWithFaction; } export const modifyTeam = async (teamID: number, teamMembers: number[], factionID: number, name?: string, type?: string) => { + // 1. Mise à jour des champs de l'équipe + if (name !== undefined) { + await db + .update(teamSchema) + .set({ name: name }) + .where(eq(teamSchema.id, teamID)); + } + if (type !== undefined) { + await db + .update(teamSchema) + .set({ type: type }) + .where(eq(teamSchema.id, teamID)); + } - // 1. Mise à jour des champs de l'équipe - - - if (name !== undefined) { - await db - .update(teamSchema) - .set({ name: name }) - .where(eq(teamSchema.id, teamID)); - } - if (type !== undefined) { - await db - .update(teamSchema) - .set({ type: type }) - .where(eq(teamSchema.id, teamID)); - } - - // 2. Mise à jour des membres de l'équipe (remplace les anciens) - if (teamMembers.length !== 0) { - if (Array.isArray(teamMembers)) { - // Supprimer les anciens membres - await db.delete(userTeamsSchema).where(eq(userTeamsSchema.team_id, teamID)); - - // Ajouter les nouveaux - if (teamMembers.length > 0) { - await db.insert(userTeamsSchema).values( - teamMembers.map((userID) => ({ - user_id: userID, - team_id: teamID, - })) - ); - } + // 2. Mise à jour des membres de l'équipe (remplace les anciens) + if (teamMembers.length !== 0) { + if (Array.isArray(teamMembers)) { + // Supprimer les anciens membres + await db.delete(userTeamsSchema).where(eq(userTeamsSchema.team_id, teamID)); + + // Ajouter les nouveaux + if (teamMembers.length > 0) { + await db.insert(userTeamsSchema).values( + teamMembers.map((userID) => ({ + user_id: userID, + team_id: teamID, + })) + ); + } + } + } else { + await db.delete(userTeamsSchema).where(eq(userTeamsSchema.team_id, teamID)); } - } else { - await db.delete(userTeamsSchema).where(eq(userTeamsSchema.team_id, teamID)); - } - // 3. Mise à jour de la faction (remplace la relation précédente) - if (factionID !== undefined) { - // Supprimer l'ancienne relation - await db.delete(teamFactionSchema).where(eq(teamFactionSchema.team_id, teamID)); + // 3. Mise à jour de la faction (remplace la relation précédente) + if (factionID !== undefined) { + // Supprimer l'ancienne relation + await db.delete(teamFactionSchema).where(eq(teamFactionSchema.team_id, teamID)); - // Ajouter la nouvelle - await db.insert(teamFactionSchema).values({ - team_id: teamID, - faction_id: factionID, - }); - } + // Ajouter la nouvelle + await db.insert(teamFactionSchema).values({ + team_id: teamID, + faction_id: factionID, + }); + } - // 4. Retour de la team modifiée - const updatedTeam = await db - .select() - .from(teamSchema) - .where(eq(teamSchema.id, teamID)); + // 4. Retour de la team modifiée + const updatedTeam = await db + .select() + .from(teamSchema) + .where(eq(teamSchema.id, teamID)); - return updatedTeam[0]; + return updatedTeam[0]; }; - export const getTeamUsers = async (teamId: any) => { - - const users = await db - .select({ - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, - permission: userSchema.permission - }) - .from(userSchema) - .innerJoin(userTeamsSchema, eq(userSchema.id, userTeamsSchema.user_id)) - .where(eq(userTeamsSchema.team_id, teamId)); - - return users; + const users = await db + .select({ + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + permission: userSchema.permission + }) + .from(userSchema) + .innerJoin(userTeamsSchema, eq(userSchema.id, userTeamsSchema.user_id)) + .where(eq(userTeamsSchema.team_id, teamId)); + + return users; }; export const getAllTeamsWithUsers = async () => { - const results = await db - .select({ - teamId: teamSchema.id, - teamName: teamSchema.name, - teamType: teamSchema.type, - teamFaction: factionSchema.name, - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - discordId: userSchema.discord_id, - permission: userSchema.permission, - }) - .from(teamSchema) - .innerJoin(userTeamsSchema, eq(teamSchema.id, userTeamsSchema.team_id)) - .innerJoin(userSchema, eq(userSchema.id, userTeamsSchema.user_id)) - .innerJoin(teamFactionSchema, eq(teamSchema.id, teamFactionSchema.team_id)) - .innerJoin(factionSchema, eq(factionSchema.id, teamFactionSchema.faction_id)); - - const teamsMap = new Map; - }>(); - - for (const row of results) { - if (!teamsMap.has(row.teamId)) { - teamsMap.set(row.teamId, { - id: row.teamId, - name: row.teamName, - type: row.teamType, - faction: row.teamFaction, - users: [], - }); + const results = await db + .select({ + teamId: teamSchema.id, + teamName: teamSchema.name, + teamType: teamSchema.type, + teamFaction: factionSchema.name, + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + discordId: userSchema.discord_id, + permission: userSchema.permission, + }) + .from(teamSchema) + .innerJoin(userTeamsSchema, eq(teamSchema.id, userTeamsSchema.team_id)) + .innerJoin(userSchema, eq(userSchema.id, userTeamsSchema.user_id)) + .innerJoin(teamFactionSchema, eq(teamSchema.id, teamFactionSchema.team_id)) + .innerJoin(factionSchema, eq(factionSchema.id, teamFactionSchema.faction_id)); + + const teamsMap = new Map; + }>(); + + for (const row of results) { + if (!teamsMap.has(row.teamId)) { + teamsMap.set(row.teamId, { + id: row.teamId, + name: row.teamName, + type: row.teamType, + faction: row.teamFaction, + users: [], + }); + } + + teamsMap.get(row.teamId)!.users.push({ + id: row.userId, + firstName: row.firstName, + lastName: row.lastName, + discordId: row.discordId, + permission: row.permission + }); } - teamsMap.get(row.teamId)!.users.push({ - id: row.userId, - firstName: row.firstName, - lastName: row.lastName, - discordId: row.discordId, - permission: row.permission - }); - } - - return Array.from(teamsMap.values()); + return Array.from(teamsMap.values()); } export const getTeamFaction = async (teamId: any) => { - const teamFactionId = await db - .select({ faction_id: teamFactionSchema.faction_id }) - .from(teamFactionSchema) - .where(eq(teamFactionSchema.team_id, teamId)); - - if (teamFactionId.length > 0 && teamFactionId[0].faction_id) { - return teamFactionId[0].faction_id; - } else { - return 0; - } + const teamFactionId = await db + .select({ faction_id: teamFactionSchema.faction_id }) + .from(teamFactionSchema) + .where(eq(teamFactionSchema.team_id, teamId)); + + if (teamFactionId.length > 0 && teamFactionId[0].faction_id) { + return teamFactionId[0].faction_id; + } else { + return 0; + } }; export const deleteTeam = async (teamID: number) => { - // 1. Supprimer la relation avec les utilisateurs (table user_teams) - await db.delete(userTeamsSchema).where(eq(userTeamsSchema.team_id, teamID)); + // 1. Supprimer la relation avec les utilisateurs (table user_teams) + await db.delete(userTeamsSchema).where(eq(userTeamsSchema.team_id, teamID)); - // 2. Supprimer la relation avec la faction (table team_faction) - await db.delete(teamFactionSchema).where(eq(teamFactionSchema.team_id, teamID)); + // 2. Supprimer la relation avec la faction (table team_faction) + await db.delete(teamFactionSchema).where(eq(teamFactionSchema.team_id, teamID)); - // 3. Supprimer la relation avec les utilisateurs (table user_teams) - await db.delete(teamShotgunSchema).where(eq(teamShotgunSchema.team_id, teamID)); + // 3. Supprimer la relation avec les utilisateurs (table user_teams) + await db.delete(teamShotgunSchema).where(eq(teamShotgunSchema.team_id, teamID)); - // 4. Supprimer l'équipe de la table principale (teams) - const deletedTeam = await db - .delete(teamSchema) - .where(eq(teamSchema.id, teamID)) - .returning(); + // 4. Supprimer l'équipe de la table principale (teams) + const deletedTeam = await db + .delete(teamSchema) + .where(eq(teamSchema.id, teamID)) + .returning(); - // Si aucune équipe n'est supprimée, on retourne une erreur - if (deletedTeam.length === 0) { - throw new Error("Équipe non trouvée."); - } + // Si aucune équipe n'est supprimée, on retourne une erreur + if (deletedTeam.length === 0) { + throw new Error("Équipe non trouvée."); + } - return deletedTeam[0]; // Retourne les informations de l'équipe supprimée + return deletedTeam[0]; // Retourne les informations de l'équipe supprimée }; - export const addTeamMember = async (teamId: number, userId: number) => { + const newTeamMember = await db.insert(userTeamsSchema).values({ user_id: userId, team_id: teamId, }); - const newTeamMember = await db.insert(userTeamsSchema).values({ user_id: userId, team_id: teamId, }); - - return newTeamMember; + return newTeamMember; }; export const getUsersWithTeam = async () => { - try { - const userswithteam = await db.select( - { - userId: userTeamsSchema.user_id, - teamId: userTeamsSchema.team_id, - } - ).from(userTeamsSchema); - return userswithteam; - } catch (err) { - console.error('Erreur lors de la récupération des utilisateurs possédant une team ', err); - throw new Error('Erreur de base de données'); - } + try { + const userswithteam = await db.select( + { + userId: userTeamsSchema.user_id, + teamId: userTeamsSchema.team_id, + } + ).from(userTeamsSchema); + + return userswithteam; + } catch (err) { + console.error('Erreur lors de la récupération des utilisateurs possédant une team ', err); + throw new Error('Erreur de base de données'); + } }; export const getTeam = async (teamId: any) => { - try { - const team = await db.select( - { - teamId: teamSchema.id, - teamName: teamSchema.name - } - ).from(teamSchema).where(eq(teamSchema.id, teamId)); - - return team[0]; - - } catch (err) { - console.error('Erreur lors de la récupération des utilisateurs possédant une team ', err); - throw new Error('Erreur de base de données'); - } + try { + const team = await db.select( + { + teamId: teamSchema.id, + teamName: teamSchema.name + } + ).from(teamSchema).where(eq(teamSchema.id, teamId)); + + return team[0]; + } catch (err) { + console.error('Erreur lors de la récupération des utilisateurs possédant une team ', err); + throw new Error('Erreur de base de données'); + } }; diff --git a/backend/src/services/tent.service.ts b/backend/src/services/tent.service.ts index 758f1cb..ed9b32b 100644 --- a/backend/src/services/tent.service.ts +++ b/backend/src/services/tent.service.ts @@ -1,138 +1,135 @@ -import { db } from "../database/db"; import { and, eq, or } from "drizzle-orm"; -import { userTentSchema } from "../schemas/Relational/usertent.schema"; -import { userSchema } from "../schemas/Basic/user.schema"; import { alias } from "drizzle-orm/pg-core"; +import { db } from "../database/db"; +import { userSchema } from "../schemas/Basic/user.schema"; +import { userTentSchema } from "../schemas/Relational/usertent.schema"; /** * Créer une réservation de tente entre 2 utilisateurs. */ export const createTent = async (userId1: number, userId2: number) => { - if (userId1 === userId2) { - throw new Error("Impossible de réserver une tente avec soi-même."); - } - - // Vérifier si l'un des deux a déjà une tente - const existing = await db - .select() - .from(userTentSchema) - .where( - or( - eq(userTentSchema.user_id_1, userId1), - eq(userTentSchema.user_id_2, userId1), - eq(userTentSchema.user_id_1, userId2), - eq(userTentSchema.user_id_2, userId2) - ) - ); - - if (existing.length > 0) { - throw new Error("Un des utilisateurs a déjà une tente."); - } - - return await db.insert(userTentSchema).values({ - user_id_1: userId1, - user_id_2: userId2, - }); + if (userId1 === userId2) { + throw new Error("Impossible de réserver une tente avec soi-même."); + } + + // Vérifier si l'un des deux a déjà une tente + const existing = await db + .select() + .from(userTentSchema) + .where( + or( + eq(userTentSchema.user_id_1, userId1), + eq(userTentSchema.user_id_2, userId1), + eq(userTentSchema.user_id_1, userId2), + eq(userTentSchema.user_id_2, userId2) + ) + ); + + if (existing.length > 0) { + throw new Error("Un des utilisateurs a déjà une tente."); + } + + return await db.insert(userTentSchema).values({ + user_id_1: userId1, + user_id_2: userId2, + }); }; /** * Annuler une tente (par l'un ou l'autre des utilisateurs). */ export const cancelTent = async (userId1: number) => { - - return await db - .delete(userTentSchema) - .where( - or(eq(userTentSchema.user_id_1, userId1), eq(userTentSchema.user_id_2, userId1)), - ); + return await db + .delete(userTentSchema) + .where( + or(eq(userTentSchema.user_id_1, userId1), eq(userTentSchema.user_id_2, userId1)), + ); }; /** * Récupérer la tente d'un utilisateur. */ export const getTentByUser = async (userId: number) => { - return await db - .select() - .from(userTentSchema) - .where(or(eq(userTentSchema.user_id_1, userId), eq(userTentSchema.user_id_2, userId))); + return await db + .select() + .from(userTentSchema) + .where(or(eq(userTentSchema.user_id_1, userId), eq(userTentSchema.user_id_2, userId))); }; /** * Récupérer toutes les tentes (avec infos des 2 utilisateurs). */ export const getAllTents = async () => { - - const user2 = alias(userSchema, "user2"); - - return await db - .select({ - user1_id: userTentSchema.user_id_1, - user2_id: userTentSchema.user_id_2, - user1_first_name: userSchema.first_name, - user1_last_name: userSchema.last_name, - user1_email: userSchema.email, - user1_majeur: userSchema.majeur, - user2_first_name: user2.first_name, - user2_last_name: user2.last_name, - user2_email: user2.email, - user2_majeur: user2.majeur, - confirmed: userTentSchema.confirmed - }) - .from(userTentSchema) - .innerJoin(userSchema, eq(userTentSchema.user_id_1, userSchema.id)) - .innerJoin(user2, eq(userTentSchema.user_id_2, user2.id)); + const user2 = alias(userSchema, "user2"); + + return await db + .select({ + user1_id: userTentSchema.user_id_1, + user2_id: userTentSchema.user_id_2, + user1_first_name: userSchema.first_name, + user1_last_name: userSchema.last_name, + user1_email: userSchema.email, + user1_majeur: userSchema.majeur, + user2_first_name: user2.first_name, + user2_last_name: user2.last_name, + user2_email: user2.email, + user2_majeur: user2.majeur, + confirmed: userTentSchema.confirmed + }) + .from(userTentSchema) + .innerJoin(userSchema, eq(userTentSchema.user_id_1, userSchema.id)) + .innerJoin(user2, eq(userTentSchema.user_id_2, user2.id)); }; /** * Met à jour la confirmation(avec infos des 2 utilisateurs). */ export const toggleTentConfirmation = async ( - userId1: number, - userId2: number, - confirmed: boolean + userId1: number, + userId2: number, + confirmed: boolean ) => { - if (userId1 === userId2) { - throw new Error("Les deux utilisateurs doivent être différents."); - } - - // Vérifier si la tente existe - const existingTent = await db - .select() - .from(userTentSchema) - .where( - or( - and( - eq(userTentSchema.user_id_1, userId1), - eq(userTentSchema.user_id_2, userId2) - ), - and( - eq(userTentSchema.user_id_1, userId2), - eq(userTentSchema.user_id_2, userId1) - ) - ) - ); - - if (existingTent.length === 0) { - throw new Error("La tente entre ces deux utilisateurs n'existe pas."); - } - - // Mettre à jour la confirmation - await db - .update(userTentSchema) - .set({ confirmed }) - .where( - or( - and( - eq(userTentSchema.user_id_1, userId1), - eq(userTentSchema.user_id_2, userId2) - ), - and( - eq(userTentSchema.user_id_1, userId2), - eq(userTentSchema.user_id_2, userId1) - ) - ) - ); - - return { success: true, message: confirmed ? "Tente validée." : "Tente dévalidée." }; + if (userId1 === userId2) { + throw new Error("Les deux utilisateurs doivent être différents."); + } + + // Vérifier si la tente existe + const existingTent = await db + .select() + .from(userTentSchema) + .where( + or( + and( + eq(userTentSchema.user_id_1, userId1), + eq(userTentSchema.user_id_2, userId2) + ), + and( + eq(userTentSchema.user_id_1, userId2), + eq(userTentSchema.user_id_2, userId1) + ) + ) + ); + + if (existingTent.length === 0) { + throw new Error("La tente entre ces deux utilisateurs n'existe pas."); + } + + // Mettre à jour la confirmation + await db + .update(userTentSchema) + .set({ confirmed }) + .where( + or( + and( + eq(userTentSchema.user_id_1, userId1), + eq(userTentSchema.user_id_2, userId2) + ), + and( + eq(userTentSchema.user_id_1, userId2), + eq(userTentSchema.user_id_2, userId1) + ) + ) + ); + + return { success: true, message: confirmed ? "Tente validée." : "Tente dévalidée." }; }; - diff --git a/backend/src/services/user.service.ts b/backend/src/services/user.service.ts index 14a2a87..0641100 100644 --- a/backend/src/services/user.service.ts +++ b/backend/src/services/user.service.ts @@ -1,273 +1,269 @@ import bcrypt from 'bcryptjs'; -import { db } from '../database/db'; // Import de la connexion PostgreSQL -import { User, userSchema } from '../schemas/Basic/user.schema'; import { eq } from 'drizzle-orm'; -import { userTeamsSchema } from '../schemas/Relational/userteams.schema'; -import { getTeam, getTeamFaction, getUserTeam } from './team.service'; -import { getFaction } from './faction.service'; +import { db } from '../database/db'; // Import de la connexion PostgreSQL +import { User, userSchema } from '../schemas/Basic/user.schema'; import { registrationSchema } from '../schemas/Relational/registration.schema'; +import { getFaction } from './faction.service'; import { getUserRoles } from './role.service'; -import { permission } from 'process'; +import { getTeam, getTeamFaction, getUserTeam } from './team.service'; // Fonction pour récupérer un utilisateur par email export const getUserByEmail = async (email: string) => { - try { - const users = await db.select().from(userSchema).where(eq(userSchema.email, email)); - return users[0]; - } catch (err) { - console.error('Erreur lors de la récupération de l\'utilisateur par email:', err); - throw new Error('Erreur de base de données'); - } + try { + const users = await db.select().from(userSchema).where(eq(userSchema.email, email)); + return users[0]; + } catch (err) { + console.error('Erreur lors de la récupération de l\'utilisateur par email:', err); + throw new Error('Erreur de base de données'); + } }; export const getUserById = async (userId: number) => { - try { - const user = await db.select( - { - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, - majeur: userSchema.majeur, - branch : userSchema.branch, - contact : userSchema.contact, - permission : userSchema.permission, - discord_id : userSchema.discord_id - } - ).from(userSchema).where(eq(userSchema.id, userId)); - return user[0]; - } catch (err) { - console.error('Erreur lors de la récupération de l\'utilisateur par email:', err); - throw new Error('Erreur de base de données'); - } + try { + const user = await db.select( + { + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + majeur: userSchema.majeur, + branch: userSchema.branch, + contact: userSchema.contact, + permission: userSchema.permission, + discord_id: userSchema.discord_id + } + ).from(userSchema).where(eq(userSchema.id, userId)); + return user[0]; + } catch (err) { + console.error('Erreur lors de la récupération de l\'utilisateur par email:', err); + throw new Error('Erreur de base de données'); + } }; // Fonction pour enregistrer un nouvel utilisateur export const createUser = async ( - firstName: string, - lastName: string, - email: string, - majeur: boolean, - permission: string, - branch: string, - password: string) => { - try { - // Hacher le mot de passe - const hashedPassword = await bcrypt.hash(password, 10); + firstName: string, + lastName: string, + email: string, + majeur: boolean, + permission: string, + branch: string, + password: string) => { + try { + // Hacher le mot de passe + const hashedPassword = await bcrypt.hash(password, 10); - const newUser: Partial = { - first_name: firstName, - last_name: lastName, - email: email, - branch : branch === "CV_ING" ? "RI" : branch, - majeur: majeur, - password: hashedPassword, - permission: permission - }; - // Insérer un nouvel utilisateur dans la base de données - const result = await db.insert(userSchema).values(newUser).returning() + const newUser: Partial = { + first_name: firstName, + last_name: lastName, + email: email, + branch: branch === "CV_ING" ? "RI" : branch, + majeur: majeur, + password: hashedPassword, + permission: permission + }; + // Insérer un nouvel utilisateur dans la base de données + const result = await db.insert(userSchema).values(newUser).returning() - return result[0]; - } catch (err) { - console.error('Erreur lors de la création de l\'utilisateur:', err); - throw new Error('Erreur de base de données'); - } + return result[0]; + } catch (err) { + console.error('Erreur lors de la création de l\'utilisateur:', err); + throw new Error('Erreur de base de données'); + } }; // Fonction pour comparer les mots de passe (utilisée lors de la connexion) export const comparePassword = async (enteredPassword: string, storedPassword: string) => { - return await bcrypt.compare(enteredPassword, storedPassword); + return await bcrypt.compare(enteredPassword, storedPassword); }; -export const updateUserStudent = async(firstName: string, lastName: string, email: string) =>{ +export const updateUserStudent = async (firstName: string, lastName: string, email: string) => { try { const result = await db.update(userSchema) - .set({ - first_name: firstName, - last_name: lastName - }) - .where(eq(userSchema.email, email)); - + .set({ + first_name: firstName, + last_name: lastName + }) + .where(eq(userSchema.email, email)); + return result.rows[0]; - } catch (err) { + } catch (err) { console.error('Erreur lors de la récupération et de l\'update de l\'utilisateur par email:', err); throw new Error('Erreur de base de données'); - } + } } export const getUsersAdmin = async () => { - try { - const users = await db.select( - { - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, - majeur: userSchema.majeur, - branch : userSchema.branch, - contact : userSchema.contact, - permission : userSchema.permission, - discord_id : userSchema.discord_id - } - ).from(userSchema); - return users; - } catch (err) { - console.error('Erreur lors de la récupération des utilisateurs ', err); - throw new Error('Erreur de base de données'); - } + try { + const users = await db.select( + { + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + majeur: userSchema.majeur, + branch: userSchema.branch, + contact: userSchema.contact, + permission: userSchema.permission, + discord_id: userSchema.discord_id + } + ).from(userSchema); + return users; + } catch (err) { + console.error('Erreur lors de la récupération des utilisateurs ', err); + throw new Error('Erreur de base de données'); + } }; export const getUsers = async () => { - try { - const users = await db.select( - { - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - permission : userSchema.permission, - email: userSchema.email - } - ).from(userSchema); - return users; - } catch (err) { - console.error('Erreur lors de la récupération des utilisateurs ', err); - throw new Error('Erreur de base de données'); - } + try { + const users = await db.select( + { + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + permission: userSchema.permission, + email: userSchema.email + } + ).from(userSchema); + return users; + } catch (err) { + console.error('Erreur lors de la récupération des utilisateurs ', err); + throw new Error('Erreur de base de données'); + } }; export const getUsersAll = async () => { - try { - const users = await db.select().from(userSchema); + try { + const users = await db.select().from(userSchema); - const userWithTeam = await Promise.all( - users.map(async (user) => { - const roles = await getUserRoles(user.id); - let teamId = await getUserTeam(user.id); - teamId= teamId ?? null; + const userWithTeam = await Promise.all( + users.map(async (user) => { + const roles = await getUserRoles(user.id); + let teamId = await getUserTeam(user.id); + teamId = teamId ?? null; - let teamName: string | null = null; - let factionId: number | null = null; - let factionName: string | null = null; + let teamName: string | null = null; + let factionId: number | null = null; + let factionName: string | null = null; - if (teamId) { - const team = await getTeam(teamId); - teamName = team?.teamName ?? null; + if (teamId) { + const team = await getTeam(teamId); + teamName = team?.teamName ?? null; - factionId = await getTeamFaction(teamId); - const faction = await getFaction(factionId); - factionName = faction?.name ?? null; - } + factionId = await getTeamFaction(teamId); + const faction = await getFaction(factionId); + factionName = faction?.name ?? null; + } - return { - ...user, - teamId, - teamName, - factionId, - factionName, - roles, - }; - }) - ); + return { + ...user, + teamId, + teamName, + factionId, + factionName, + roles, + }; + }) + ); - return userWithTeam; - } catch (err) { - console.error('Erreur lors de la récupération des utilisateurs ', err); - throw new Error('Erreur de base de données'); - } + return userWithTeam; + } catch (err) { + console.error('Erreur lors de la récupération des utilisateurs ', err); + throw new Error('Erreur de base de données'); + } }; -export const getUsersbyPermission = async (permission : string) => { - try { - const users = await db.select( - { - userId: userSchema.id, - firstName: userSchema.first_name, - lastName: userSchema.last_name, - email: userSchema.email, - branch: userSchema.branch - } - ).from(userSchema).where(eq(userSchema.permission, permission)); - return users; - } catch (err) { - console.error('Erreur lors de la récupération des utilisateurs ', err); - throw new Error('Erreur de base de données'); - } +export const getUsersbyPermission = async (permission: string) => { + try { + const users = await db.select( + { + userId: userSchema.id, + firstName: userSchema.first_name, + lastName: userSchema.last_name, + email: userSchema.email, + branch: userSchema.branch + } + ).from(userSchema).where(eq(userSchema.permission, permission)); + return users; + } catch (err) { + console.error('Erreur lors de la récupération des utilisateurs ', err); + throw new Error('Erreur de base de données'); + } }; -export const updateUserPassword = async(userId: number, password: string) =>{ - try { - const result = await db.update(userSchema) - .set({ - password: password - }) - .where(eq(userSchema.id, userId)); - - return result.rows[0]; +export const updateUserPassword = async (userId: number, password: string) => { + try { + const result = await db.update(userSchema) + .set({ + password: password + }) + .where(eq(userSchema.id, userId)); + + return result.rows[0]; } catch (err) { - console.error('Erreur lors de la récupération et de l\'update de l\'utilisateur par email:', err); - throw new Error('Erreur de base de données'); + console.error('Erreur lors de la récupération et de l\'update de l\'utilisateur par email:', err); + throw new Error('Erreur de base de données'); } } export const updateUserInfoByUserId = async ( - userId: number, - branch?: string, - contact?: string + userId: number, + branch?: string, + contact?: string ) => { - try { - const result = await db.update(userSchema) - .set({ - branch : branch, - contact : contact }) - .where(eq(userSchema.id, userId)); + try { + const result = await db.update(userSchema) + .set({ + branch: branch, + contact: contact + }) + .where(eq(userSchema.id, userId)); - return result; - } catch (err) { - console.error('Erreur lors de la mise à jour des infos utilisateur:', err); - throw new Error('Erreur de base de données'); - } + return result; + } catch (err) { + console.error('Erreur lors de la mise à jour des infos utilisateur:', err); + throw new Error('Erreur de base de données'); + } }; export const updateUserByAdmin = async ( - userId: number, - updates: Partial + userId: number, + updates: Partial ) => { - try { + try { - if (Object.keys(updates).length === 0) { - throw new Error('Aucune donnée à mettre à jour'); - } + if (Object.keys(updates).length === 0) { + throw new Error('Aucune donnée à mettre à jour'); + } - const result = await db.update(userSchema) - .set( - updates - ) - .where(eq(userSchema.id, userId)); + const result = await db.update(userSchema) + .set( + updates + ) + .where(eq(userSchema.id, userId)); - return result; - } catch (err) { - console.error('Erreur lors de la mise à jour par l\'admin:', err); - throw new Error('Erreur de base de données'); - } + return result; + } catch (err) { + console.error('Erreur lors de la mise à jour par l\'admin:', err); + throw new Error('Erreur de base de données'); + } }; export const deleteUserById = async (userId: number) => { - try { + try { + + const user_registration_token = await db.select({ user_id: registrationSchema.user_id }).from(registrationSchema).where(eq(registrationSchema.user_id, userId)); - const user_registration_token = await db.select({user_id : registrationSchema.user_id}).from(registrationSchema).where(eq(registrationSchema.user_id, userId)); + if (user_registration_token.length > 0) { + await db.delete(registrationSchema).where(eq(registrationSchema.user_id, userId)); + } - if(user_registration_token.length > 0){ - await db.delete(registrationSchema).where(eq(registrationSchema.user_id, userId)); + const result = await db.delete(userSchema).where(eq(userSchema.id, userId)); + return result; + } catch (err) { + console.error('Erreur lors de la suppression de l\'utilisateur:', err); + throw new Error('Erreur de base de données'); } - - const result = await db.delete(userSchema).where(eq(userSchema.id, userId)); - return result; - } catch (err) { - console.error('Erreur lors de la suppression de l\'utilisateur:', err); - throw new Error('Erreur de base de données'); - } }; - - - diff --git a/backend/src/utils/emailtemplates.ts b/backend/src/utils/emailtemplates.ts index 1f76fc3..309068e 100644 --- a/backend/src/utils/emailtemplates.ts +++ b/backend/src/utils/emailtemplates.ts @@ -49,7 +49,6 @@ export const templateResetPassword = ` `; - export const templateNotebook = ` @@ -121,8 +120,6 @@ export const templateNotebook = ` - - `; export const templateAttributionBus = ` @@ -196,7 +193,6 @@ export const templateAttributionBus = ` `; - export const templateWelcome = ` @@ -361,8 +357,6 @@ export const templateNotifyTentConfirmation = ` `; - - // Fonction pour compiler le template export const compileTemplate = (data: any, templateName: string) => { const compiledTemplate = Handlebars.compile(templateName); diff --git a/backend/src/utils/no_sync_list.ts b/backend/src/utils/no_sync_list.ts index 46cd864..dcfe90e 100644 --- a/backend/src/utils/no_sync_list.ts +++ b/backend/src/utils/no_sync_list.ts @@ -1,4 +1,4 @@ //This list is to ignore user from UTT API who already have an account on the website but not with the same email address // OR People we don't want to sync like "BIO REF" or demissionary export const noSyncEmails = [ -]; \ No newline at end of file +]; diff --git a/backend/src/utils/responses.ts b/backend/src/utils/responses.ts index 12d1614..78de004 100644 --- a/backend/src/utils/responses.ts +++ b/backend/src/utils/responses.ts @@ -1,86 +1,85 @@ import { Response } from 'express'; export const Error = (res: Response, details: { error?: any, msg?: string }) => { - if (details.error) { - console.error('Error:', details.error); - } - const msg = details.msg || 'An error occurred'; - return res.status(Code.BAD_REQUEST).json(new HttpResponse(Code.BAD_REQUEST, msg)); + if (details.error) { + console.error('Error:', details.error); + } + const msg = details.msg || 'An error occurred'; + return res.status(Code.BAD_REQUEST).json(new HttpResponse(Code.BAD_REQUEST, msg)); }; export const Created = (res: Response, details: { data?: any, msg?: string }) => { - const msg = details.msg || 'Entity created'; - res.status(Code.CREATED).json(new HttpResponse(Code.CREATED, msg, details.data)); + const msg = details.msg || 'Entity created'; + res.status(Code.CREATED).json(new HttpResponse(Code.CREATED, msg, details.data)); }; export const Ok = (res: Response, details: { data?: any, msg?: string }) => { - const msg = details.msg || 'Ok'; - res.status(Code.OK).json(new HttpResponse(Code.OK, msg, details.data)); + const msg = details.msg || 'Ok'; + res.status(Code.OK).json(new HttpResponse(Code.OK, msg, details.data)); }; export const Accepted = (res: Response, details: { data?: any, msg?: string }) => { - const msg = details.msg || 'Accepted'; - res.status(Code.OK).json(new HttpResponse(Code.ACCEPTED, msg, details.data)); + const msg = details.msg || 'Accepted'; + res.status(Code.OK).json(new HttpResponse(Code.ACCEPTED, msg, details.data)); }; export const Unauthorized = (res: Response, details: { data?: any, msg?: string }) => { - const msg = details.msg || 'Unauthorized'; - res.status(Code.UNAUTHORIZED).json(new HttpResponse(Code.UNAUTHORIZED, msg, details.data)); + const msg = details.msg || 'Unauthorized'; + res.status(Code.UNAUTHORIZED).json(new HttpResponse(Code.UNAUTHORIZED, msg, details.data)); }; export const Conflict = (res: Response, details: { data?: any, msg?: string }) => { - const msg = details.msg || "Conflict"; - res.status(Code.CONFLICT).json(new HttpResponse(Code.CONFLICT, msg, details.data)); + const msg = details.msg || "Conflict"; + res.status(Code.CONFLICT).json(new HttpResponse(Code.CONFLICT, msg, details.data)); }; export const Teapot = (res: Response, details: { data?: any, msg?: string }) => { - const msg = details.msg || "I'm a teapot"; - res.status(Code.IM_A_TEAPOT).json(new HttpResponse(Code.IM_A_TEAPOT, msg, details.data)); + const msg = details.msg || "I'm a teapot"; + res.status(Code.IM_A_TEAPOT).json(new HttpResponse(Code.IM_A_TEAPOT, msg, details.data)); }; export enum Code { - OK = 200, - ACCEPTED = 202, - NOT_FOUND = 404, - BAD_REQUEST = 400, - UNAUTHORIZED = 401, - CREATED = 201, - CONFLICT = 409, - IM_A_TEAPOT = 418, - ISE = 500 + OK = 200, + ACCEPTED = 202, + NOT_FOUND = 404, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + CREATED = 201, + CONFLICT = 409, + IM_A_TEAPOT = 418, + ISE = 500 } export class HttpResponse { - private timeStamp: string; - private httpStatus: string; + private timeStamp: string; + private httpStatus: string; - constructor(private statusCode: Code, private message?: string, private data?: any) { - this.timeStamp = new Date().toLocaleString(); - this.statusCode = statusCode; - this.httpStatus = this.getStatus(); - this.message = message; - this.data = data; - } + constructor(private statusCode: Code, private message?: string, private data?: any) { + this.timeStamp = new Date().toLocaleString(); + this.statusCode = statusCode; + this.httpStatus = this.getStatus(); + this.message = message; + this.data = data; + } - private getStatus(): string { - switch (this.statusCode) { - case Code.OK: - return 'OK'; - case Code.NOT_FOUND: - return 'NOT_FOUND'; - case Code.BAD_REQUEST: - return 'BAD_REQUEST'; - case Code.CREATED: - return 'CREATED'; - case Code.ISE: - return 'INTERNAL_SERVER_ERROR'; - case Code.UNAUTHORIZED: - return 'UNAUTHORIZED'; - case Code.CONFLICT: - return 'CONFLICT'; - case Code.IM_A_TEAPOT: - return 'IM_A_TEAPOT'; + private getStatus(): string { + switch (this.statusCode) { + case Code.OK: + return 'OK'; + case Code.NOT_FOUND: + return 'NOT_FOUND'; + case Code.BAD_REQUEST: + return 'BAD_REQUEST'; + case Code.CREATED: + return 'CREATED'; + case Code.ISE: + return 'INTERNAL_SERVER_ERROR'; + case Code.UNAUTHORIZED: + return 'UNAUTHORIZED'; + case Code.CONFLICT: + return 'CONFLICT'; + case Code.IM_A_TEAPOT: + return 'IM_A_TEAPOT'; + } } - } } - diff --git a/backend/src/utils/siep.ts b/backend/src/utils/siep.ts index 8e56440..1dd7100 100644 --- a/backend/src/utils/siep.ts +++ b/backend/src/utils/siep.ts @@ -1,75 +1,75 @@ -import { api_utt_admis_url_ismajor, api_utt_auth_url, api_utt_password, api_utt_username } from "./secret"; import axios from 'axios'; +import { api_utt_admis_url_ismajor, api_utt_auth_url, api_utt_password, api_utt_username } from "./secret"; -export const getTokenUTTAPI = async() => { +export const getTokenUTTAPI = async () => { try { const response = await axios.post(api_utt_auth_url, { - login: api_utt_username, - password: api_utt_password, + login: api_utt_username, + password: api_utt_password, }, { - headers: { - 'accept': 'application/json', - 'Content-Type': 'application/json', - } + headers: { + 'accept': 'application/json', + 'Content-Type': 'application/json', + } }); return response.data.token; - } catch (error) { + } catch (error) { console.error('Error during POST request:', error); - } + } } -export const getNewStudentsFromUTTAPI_PAGE = async (token: string, date : string) => { - const allNewStudents: any[] = []; - let currentPage = 1; - let hasNextPage = true; +export const getNewStudentsFromUTTAPI_PAGE = async (token: string, date: string) => { + const allNewStudents: any[] = []; + let currentPage = 1; + let hasNextPage = true; - try { - while (hasNextPage) { - const response = await axios.get(api_utt_admis_url_ismajor+date+"?page="+currentPage, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); + try { + while (hasNextPage) { + const response = await axios.get(api_utt_admis_url_ismajor + date + "?page=" + currentPage, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); - const data = response.data; - const students = data['hydra:member']; - const nextPage = data['hydra:view']?.['hydra:next']; + const data = response.data; + const students = data['hydra:member']; + const nextPage = data['hydra:view']?.['hydra:next']; - allNewStudents.push(...students); + allNewStudents.push(...students); - if (nextPage) { - currentPage++; - } else { - hasNextPage = false; - } + if (nextPage) { + currentPage++; + } else { + hasNextPage = false; + } + } + console.log(allNewStudents); + return allNewStudents; + } catch (error) { + console.error('Error during GET request:', error); + return []; } - console.log(allNewStudents); - return allNewStudents; - } catch (error) { - console.error('Error during GET request:', error); - return []; - } }; -export const getNewStudentsFromUTTAPI_NOPAGE = async (token: string, date : string) => { - const allNewStudents: any[] = []; +export const getNewStudentsFromUTTAPI_NOPAGE = async (token: string, date: string) => { + const allNewStudents: any[] = []; - try { + try { - const response = await axios.get(api_utt_admis_url_ismajor+date, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); + const response = await axios.get(api_utt_admis_url_ismajor + date, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); - const data = response.data; - const students = data['hydra:member']; + const data = response.data; + const students = data['hydra:member']; - allNewStudents.push(...students); + allNewStudents.push(...students); - return allNewStudents; - } catch (error) { - console.error('Error during GET request:', error); - return []; - } -}; \ No newline at end of file + return allNewStudents; + } catch (error) { + console.error('Error during GET request:', error); + return []; + } +}; diff --git a/backend/src/utils/token.ts b/backend/src/utils/token.ts index 27e6eff..3a19ce2 100644 --- a/backend/src/utils/token.ts +++ b/backend/src/utils/token.ts @@ -1,9 +1,7 @@ -import { Request } from 'express'; import { verify } from 'jsonwebtoken'; import { jwtSecret } from '../utils/secret'; export const decodeToken = (token: string) => { - if (!token) { return null } @@ -12,4 +10,4 @@ export const decodeToken = (token: string) => { } catch (error) { return null } -}; \ No newline at end of file +}; diff --git a/backend/types/express.d.ts b/backend/types/express.d.ts index ddc24a9..12b2762 100644 --- a/backend/types/express.d.ts +++ b/backend/types/express.d.ts @@ -1,8 +1,8 @@ import { JwtPayload } from "jsonwebtoken"; declare module "express-serve-static-core" { - interface Request { - user?: JwtPayload | string; - permission?: JwtPayload | string; - } + interface Request { + user?: JwtPayload | string; + permission?: JwtPayload | string; + } } diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 092408a..47579c1 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -1,28 +1,28 @@ import js from '@eslint/js' -import globals from 'globals' import reactHooks from 'eslint-plugin-react-hooks' import reactRefresh from 'eslint-plugin-react-refresh' +import globals from 'globals' import tseslint from 'typescript-eslint' export default tseslint.config( - { ignores: ['dist'] }, - { - extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, - }, - rules: { - ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, }, - }, ) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9ca6d15..109d505 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,122 +1,117 @@ -// src/App.tsx import React from 'react'; -import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; - -import LoginPage from './pages/auth'; -import { HomePage } from './pages/home'; -import { ProfilPage } from './pages/profil'; -import { ShotgunPage } from './pages/shotgun'; +import { Route, BrowserRouter as Router, Routes } from 'react-router-dom'; +import AdminRoute from './components/utils/adminroute'; +import PrivateRoute from './components/utils/privateroute'; +import ProtectedRoute from './components/utils/protectedroute'; import { - AdminPageRole, - AdminPageTeam, - AdminPageEvents, - AdminPageExport, - AdminPageFaction, - AdminPagePerm, - AdminPageChall, - AdminPageEmail, - AdminPageUser, - AdminPageNews, - AdminPageGames, - AdminPageTent, - AdminPageBus, - AdminPageShotgun + AdminPageBus, + AdminPageChall, + AdminPageEmail, + AdminPageEvents, + AdminPageExport, + AdminPageFaction, + AdminPageGames, + AdminPageNews, + AdminPagePerm, + AdminPageRole, + AdminPageShotgun, + AdminPageTeam, + AdminPageTent, + AdminPageUser } from './pages/admin'; - -import ProtectedRoute from './components/utils/protectedroute'; -import AdminRoute from './components/utils/adminroute'; +import LoginPage from './pages/auth'; +import ChallPage from './pages/challenge'; +import DiscordPage from './pages/discord'; +import FoodPage from './pages/food'; +import GamesPage from './pages/games'; +import HomePage from './pages/home'; +import LegalsPage from './pages/legals'; +import NewsPage from './pages/news'; +import NotFoundPage from './pages/notFound'; +import ParrainagePage from './pages/parrainage'; import { AvailablePermanencesPage, MyPermanencesPage, RespoCallPage } from './pages/perm'; -import { ChallPage } from './pages/challenge'; -import { ParrainagePage } from './pages/parrainage'; -import { RegisterPage } from './pages/register'; -import { ResetPasswordPage } from './pages/resetPassword' -import { WeiPage } from './pages/wei'; -import { SdiPage } from './pages/sdi'; -import { NewsPage } from './pages/news'; -import { DiscordPage } from './pages/discord'; -import PrivateRoute from './components/utils/privateroute'; -import { GamesPage } from './pages/games'; -import { FoodPage } from './pages/food'; -import { PlanningsPage } from './pages/plannings'; -import { Roadbook } from './pages/roadbook'; -import { PrivacyPage } from './pages/privacy'; -import { LegalsPage } from './pages/legals'; -import { NotFoundPage } from './pages/notFound'; - +import PlanningsPage from './pages/plannings'; +import PrivacyPage from './pages/privacy'; +import ProfilPage from './pages/profil'; +import RegisterPage from './pages/register'; +import ResetPasswordPage from './pages/resetPassword'; +import Roadbook from './pages/roadbook'; +import SdiPage from './pages/sdi'; +import ShotgunPage from './pages/shotgun'; +import WeiPage from './pages/wei'; const App: React.FC = () => { - const VITE_ANALYTICS_WEBSITE_ID = import.meta.env.VITE_ANALYTICS_WEBSITE_ID; - - React.useEffect(() => { - const script = document.createElement('script'); - script.src = "https://analytics.uttnetgroup.fr/script.js"; - script.defer = true; - if (VITE_ANALYTICS_WEBSITE_ID) { - script.setAttribute('data-website-id', VITE_ANALYTICS_WEBSITE_ID); - } - document.body.appendChild(script); - return () => { - document.body.removeChild(script); - }; - }, [VITE_ANALYTICS_WEBSITE_ID]); + const VITE_ANALYTICS_WEBSITE_ID = import.meta.env.VITE_ANALYTICS_WEBSITE_ID; - return ( - - - {/* Public */} - } /> - } /> - } /> - } /> - } /> - } /> + React.useEffect(() => { + const script = document.createElement('script'); + script.src = "https://analytics.uttnetgroup.fr/script.js"; + script.defer = true; + if (VITE_ANALYTICS_WEBSITE_ID) { + script.setAttribute('data-website-id', VITE_ANALYTICS_WEBSITE_ID); + } + document.body.appendChild(script); + return () => { + document.body.removeChild(script); + }; + }, [VITE_ANALYTICS_WEBSITE_ID]); - {/* Utilisateurs connectés */} - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + return ( + + + {/* Public */} + } /> + } /> + } /> + } /> + } /> + } /> - {/* Étudiant et Admin */} - } /> - } /> - } /> - } /> - } /> + {/* Utilisateurs connectés */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - {/* ResposCE et Admin */} - } /> - } /> - } /> - } /> + {/* Étudiant et Admin */} + } /> + } /> + } /> + } /> + } /> - {/* ResposCE et Admin */} - } /> + {/* ResposCE et Admin */} + } /> + } /> + } /> + } /> - {/* Arbitre et Admin*/} - } /> - {/* Admin uniquement */} - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + {/* ResposCE et Admin */} + } /> - {/* Fallback */} - } /> - - + {/* Arbitre et Admin*/} + } /> + {/* Admin uniquement */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - ); + {/* Fallback */} + } /> + + + ); }; export default App; diff --git a/frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx b/frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx index b6f823e..a5c3c36 100644 --- a/frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx +++ b/frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx @@ -1,238 +1,238 @@ +import { CheckCircle2, Edit, Search, Trash2 } from "lucide-react"; import { useMemo, useState } from "react"; -import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; -import { Button } from "../../ui/button"; -import { Challenge } from "../../../interfaces/challenge.interface"; -import { deleteChallenge, validateChallenge } from "../../../services/requests/challenge.service"; -import { Trash2, Edit, CheckCircle2, Search } from "lucide-react"; import Select, { SingleValue } from "react-select"; -import { Team } from "../../../interfaces/team.interface"; +import Swal from "sweetalert2"; +import { Challenge } from "../../../interfaces/challenge.interface"; import { Faction } from "../../../interfaces/faction.interface"; +import { Team } from "../../../interfaces/team.interface"; import { User } from "../../../interfaces/user.interface"; -import Swal from "sweetalert2"; +import { deleteChallenge, validateChallenge } from "../../../services/requests/challenge.service"; +import { Button } from "../../ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; import { Input } from "../../ui/input"; interface Props { - challenges: Challenge[]; - refreshChallenges: () => void; - onEdit: (c: Challenge) => void; - teams: Team[]; - factions: Faction[]; - users: User[]; + challenges: Challenge[]; + refreshChallenges: () => void; + onEdit: (c: Challenge) => void; + teams: Team[]; + factions: Faction[]; + users: User[]; } type ValidationTarget = "user" | "team" | "faction"; const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, factions, users }: Props) => { - const [showValidationFormForId, setShowValidationFormForId] = useState(null); - const [validationType, setValidationType] = useState(null); - const [selectedTargetId, setSelectedTargetId] = useState(null); - const [searchTerm, setSearchTerm] = useState(""); - - - const filteredChallenges = useMemo(() => { - return challenges.filter( - (c) => - c.title.toLowerCase().includes(searchTerm.toLowerCase()) || - c.description.toLowerCase().includes(searchTerm.toLowerCase()) || - c.category.toLowerCase().includes(searchTerm.toLowerCase()) + const [showValidationFormForId, setShowValidationFormForId] = useState(null); + const [validationType, setValidationType] = useState(null); + const [selectedTargetId, setSelectedTargetId] = useState(null); + const [searchTerm, setSearchTerm] = useState(""); + + + const filteredChallenges = useMemo(() => { + return challenges.filter( + (c) => + c.title.toLowerCase().includes(searchTerm.toLowerCase()) || + c.description.toLowerCase().includes(searchTerm.toLowerCase()) || + c.category.toLowerCase().includes(searchTerm.toLowerCase()) + ); + }, [challenges, searchTerm]); + + const handleDelete = async (id: number) => { + const confirm = await Swal.fire({ + title: "Supprimer ce challenge ?", + text: "Cette action est irréversible 🚨", + icon: "warning", + showCancelButton: true, + confirmButtonColor: "#e3342f", + cancelButtonColor: "#6b7280", + confirmButtonText: "Oui, supprimer", + cancelButtonText: "Annuler", + }); + + if (!confirm.isConfirmed) return; + + try { + await deleteChallenge(id); + Swal.fire("Supprimé ✅", "Le challenge a bien été supprimé.", "success"); + refreshChallenges(); + } catch (err) { + Swal.fire("Erreur ❌", "Impossible de supprimer le challenge.", "error"); + } + }; + + const handleValidate = async () => { + if (!showValidationFormForId || !validationType || !selectedTargetId) return; + + try { + const res = await validateChallenge({ + challengeId: showValidationFormForId, + type: validationType, + targetId: selectedTargetId, + }); + + Swal.fire({ + icon: "success", + title: "Challenge validé ✅", + text: res.message, + timer: 2000, + showConfirmButton: false, + }); + + setShowValidationFormForId(null); + setValidationType(null); + setSelectedTargetId(null); + refreshChallenges(); + } catch (err) { + console.error("Erreur lors de la validation du challenge", err); + Swal.fire({ + icon: "error", + title: "Erreur ❌", + text: "Impossible de valider ce challenge. Réessaie plus tard.", + }); + } + }; + + return ( + + + + 📜 Challenges + + + + + {/* 🔎 Barre de recherche */} +
+ + setSearchTerm(e.target.value)} + className="flex-1" + /> +
+ + {/* Liste filtrée */} +
+ {filteredChallenges.length > 0 ? ( + filteredChallenges.map((c) => ( + + +
+

{c.title}

+

{c.description}

+

Catégorie : {c.category}

+

Points : {c.points}

+
+ +
+ + + +
+ + {showValidationFormForId === c.id && ( + + +

✅ Valider le challenge

+ + setSelectedTargetId(Number(option?.value))} + options={users.map((u: User) => ({ + value: u.userId, + label: `${u.firstName} ${u.lastName}`, + }))} + /> + )} + + {validationType === "team" && ( + setSelectedTargetId(Number(option?.value))} + options={factions.map((f: Faction) => ({ + value: f.factionId, + label: f.name, + }))} + /> + )} + +
+ + +
+
+
+ )} +
+
+ )) + ) : ( +

Aucun challenge trouvé.

+ )} +
+
+
); - }, [challenges, searchTerm]); - - const handleDelete = async (id: number) => { - const confirm = await Swal.fire({ - title: "Supprimer ce challenge ?", - text: "Cette action est irréversible 🚨", - icon: "warning", - showCancelButton: true, - confirmButtonColor: "#e3342f", - cancelButtonColor: "#6b7280", - confirmButtonText: "Oui, supprimer", - cancelButtonText: "Annuler", - }); - - if (!confirm.isConfirmed) return; - - try { - await deleteChallenge(id); - Swal.fire("Supprimé ✅", "Le challenge a bien été supprimé.", "success"); - refreshChallenges(); - } catch (err) { - Swal.fire("Erreur ❌", "Impossible de supprimer le challenge.", "error"); - } - }; - - const handleValidate = async () => { - if (!showValidationFormForId || !validationType || !selectedTargetId) return; - - try { - const res = await validateChallenge({ - challengeId: showValidationFormForId, - type: validationType, - targetId: selectedTargetId, - }); - - Swal.fire({ - icon: "success", - title: "Challenge validé ✅", - text: res.message, - timer: 2000, - showConfirmButton: false, - }); - - setShowValidationFormForId(null); - setValidationType(null); - setSelectedTargetId(null); - refreshChallenges(); - } catch (err) { - console.error("Erreur lors de la validation du challenge", err); - Swal.fire({ - icon: "error", - title: "Erreur ❌", - text: "Impossible de valider ce challenge. Réessaie plus tard.", - }); - } - }; - - return ( - - - - 📜 Challenges - - - - - {/* 🔎 Barre de recherche */} -
- - setSearchTerm(e.target.value)} - className="flex-1" - /> -
- - {/* Liste filtrée */} -
- {filteredChallenges.length > 0 ? ( - filteredChallenges.map((c) => ( - - -
-

{c.title}

-

{c.description}

-

Catégorie : {c.category}

-

Points : {c.points}

-
- -
- - - -
- - {showValidationFormForId === c.id && ( - - -

✅ Valider le challenge

- - setSelectedTargetId(Number(option?.value))} - options={users.map((u: User) => ({ - value: u.userId, - label: `${u.firstName} ${u.lastName}`, - }))} - /> - )} - - {validationType === "team" && ( - setSelectedTargetId(Number(option?.value))} - options={factions.map((f: Faction) => ({ - value: f.factionId, - label: f.name, - }))} - /> - )} - -
- - -
-
-
- )} -
-
- )) - ) : ( -

Aucun challenge trouvé.

- )} -
-
-
- ); }; export default AdminChallengeList; diff --git a/frontend/src/components/Admin/AdminChallenge/adminChallengeAddPointsForm.tsx b/frontend/src/components/Admin/AdminChallenge/adminChallengeAddPointsForm.tsx index 761c97c..94cfda7 100644 --- a/frontend/src/components/Admin/AdminChallenge/adminChallengeAddPointsForm.tsx +++ b/frontend/src/components/Admin/AdminChallenge/adminChallengeAddPointsForm.tsx @@ -1,107 +1,106 @@ -import { useState, useEffect } from "react"; -import { Input } from "../../ui/input"; -import { Button } from "../../ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; +import { useEffect, useState } from "react"; import Select from "react-select"; -import { getAllFactionsAdmin } from "../../../services/requests/faction.service"; -import { Faction } from "../../../interfaces/faction.interface"; import Swal from "sweetalert2"; +import { Faction } from "../../../interfaces/faction.interface"; import { addPointsToFaction } from "../../../services/requests/challenge.service"; - +import { getAllFactionsAdmin } from "../../../services/requests/faction.service"; +import { Button } from "../../ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; +import { Input } from "../../ui/input"; export const AdminChallengeAddPointsForm = () => { - const [factions, setFactions] = useState([]); - const [title, setTitle] = useState(""); - const [factionId, setFactionId] = useState(null); - const [points, setPoints] = useState(""); - const [reason, setReason] = useState(""); + const [factions, setFactions] = useState([]); + const [title, setTitle] = useState(""); + const [factionId, setFactionId] = useState(null); + const [points, setPoints] = useState(""); + const [reason, setReason] = useState(""); - useEffect(() => { - const fetchFactions = async () => { - try { - const response = await getAllFactionsAdmin(); - setFactions(response); - } catch (error) { - Swal.fire("Erreur", "Impossible de récupérer les factions", "error"); - } - }; - fetchFactions(); - }, []); + useEffect(() => { + const fetchFactions = async () => { + try { + const response = await getAllFactionsAdmin(); + setFactions(response); + } catch (error) { + Swal.fire("Erreur", "Impossible de récupérer les factions", "error"); + } + }; + fetchFactions(); + }, []); - const handleSubmit = async () => { - if (!title || !factionId || !points || !reason) { - Swal.fire("Champs manquants", "Tous les champs doivent être remplis", "warning"); - return; - } + const handleSubmit = async () => { + if (!title || !factionId || !points || !reason) { + Swal.fire("Champs manquants", "Tous les champs doivent être remplis", "warning"); + return; + } - const pointsNumber = Number(points); - if (isNaN(pointsNumber)) { - Swal.fire("Erreur", "Veuillez entrer un nombre valide pour les points", "error"); - return; - } + const pointsNumber = Number(points); + if (isNaN(pointsNumber)) { + Swal.fire("Erreur", "Veuillez entrer un nombre valide pour les points", "error"); + return; + } - try { - const result = await addPointsToFaction({ - title, - factionId, - points: pointsNumber, - reason, - }); + try { + const result = await addPointsToFaction({ + title, + factionId, + points: pointsNumber, + reason, + }); - Swal.fire({ - icon: "success", - title: "Succès", - text: result.message, - timer: 1500, - showConfirmButton: false, - }); + Swal.fire({ + icon: "success", + title: "Succès", + text: result.message, + timer: 1500, + showConfirmButton: false, + }); - setTitle(""); - setFactionId(null); - setPoints(""); - setReason(""); - } catch (error) { - Swal.fire("Erreur", "❌ Une erreur est survenue lors de l'ajout des points", "error"); - } - }; + setTitle(""); + setFactionId(null); + setPoints(""); + setReason(""); + } catch (error) { + Swal.fire("Erreur", "❌ Une erreur est survenue lors de l'ajout des points", "error"); + } + }; - return ( -
- - - - 🎯 Ajouter des points à une faction - - - + return ( +
+ + + + 🎯 Ajouter des points à une faction + + + - setTitle(e.target.value)} placeholder="Titre du challenge" /> + setTitle(e.target.value)} placeholder="Titre du challenge" /> - f.factionId === factionId)?.name || "Faction non trouvée", + } + : null + } + onChange={(option) => setFactionId(option ? option.value : null)} + options={factions.map((f) => ({ value: f.factionId, label: f.name }))} + placeholder="Sélectionner une faction" + /> - setPoints(e.target.value)} placeholder="Nombre de points" /> + setPoints(e.target.value)} placeholder="Nombre de points" /> - setReason(e.target.value)} placeholder="Raison" /> + setReason(e.target.value)} placeholder="Raison" /> -
- -
-
-
-
- ); +
+ +
+
+
+
+ ); }; diff --git a/frontend/src/components/Admin/AdminChallenge/adminChallengeEditor.tsx b/frontend/src/components/Admin/AdminChallenge/adminChallengeEditor.tsx index c707ee2..78b4090 100644 --- a/frontend/src/components/Admin/AdminChallenge/adminChallengeEditor.tsx +++ b/frontend/src/components/Admin/AdminChallenge/adminChallengeEditor.tsx @@ -1,112 +1,112 @@ -import { useState, useEffect } from "react"; -import { Button } from "../../ui/button"; -import { Input } from "../../ui/input"; -import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; +import { Loader2 } from "lucide-react"; +import { useEffect, useState } from "react"; import Select from "react-select"; -import { createChallenge, updateChallenge } from "../../../services/requests/challenge.service"; -import { Challenge } from "../../../interfaces/challenge.interface"; import Swal from "sweetalert2"; -import { Loader2 } from "lucide-react"; +import { Challenge } from "../../../interfaces/challenge.interface"; +import { createChallenge, updateChallenge } from "../../../services/requests/challenge.service"; +import { Button } from "../../ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; +import { Input } from "../../ui/input"; interface Props { - editingChallenge: Challenge | null; - setEditingChallenge: (c: Challenge | null) => void; - refreshChallenges: () => void; + editingChallenge: Challenge | null; + setEditingChallenge: (c: Challenge | null) => void; + refreshChallenges: () => void; } const categoryOptions = [ - { value: "Team", label: "Team" }, - { value: "Faction", label: "Faction" }, - { value: "User", label: "User" }, - { value: "Autre", label: "Autre" }, + { value: "Team", label: "Team" }, + { value: "Faction", label: "Faction" }, + { value: "User", label: "User" }, + { value: "Autre", label: "Autre" }, ]; const ChallengeEditor = ({ editingChallenge, setEditingChallenge, refreshChallenges }: Props) => { - const [form, setForm] = useState({ title: "", description: "", category: "", points: 0 }); - const [loading, setLoading] = useState(false); + const [form, setForm] = useState({ title: "", description: "", category: "", points: 0 }); + const [loading, setLoading] = useState(false); - useEffect(() => { - if (editingChallenge) { - setForm({ - title: editingChallenge.title, - description: editingChallenge.description, - category: editingChallenge.category, - points: editingChallenge.points, - }); - } - }, [editingChallenge]); + useEffect(() => { + if (editingChallenge) { + setForm({ + title: editingChallenge.title, + description: editingChallenge.description, + category: editingChallenge.category, + points: editingChallenge.points, + }); + } + }, [editingChallenge]); - const resetForm = () => { - setForm({ title: "", description: "", category: "", points: 0 }); - setEditingChallenge(null); - }; + const resetForm = () => { + setForm({ title: "", description: "", category: "", points: 0 }); + setEditingChallenge(null); + }; - const handleSubmit = async () => { - setLoading(true); - try { - if (editingChallenge) { - await updateChallenge({ id: editingChallenge.id, ...form }); - Swal.fire({ icon: "success", title: "Challenge mis à jour !" }); - } else { - await createChallenge(form); - Swal.fire({ icon: "success", title: "Challenge créé !" }); - } - refreshChallenges(); - resetForm(); - } catch { - Swal.fire({ icon: "error", title: "Erreur lors de l'enregistrement" }); - } finally { - setLoading(false); - } - }; + const handleSubmit = async () => { + setLoading(true); + try { + if (editingChallenge) { + await updateChallenge({ id: editingChallenge.id, ...form }); + Swal.fire({ icon: "success", title: "Challenge mis à jour !" }); + } else { + await createChallenge(form); + Swal.fire({ icon: "success", title: "Challenge créé !" }); + } + refreshChallenges(); + resetForm(); + } catch { + Swal.fire({ icon: "error", title: "Erreur lors de l'enregistrement" }); + } finally { + setLoading(false); + } + }; - return ( - - - - {editingChallenge ? "✏️ Modifier Challenge" : "🛠️ Créer Challenge"} - - - - setForm({ ...form, title: e.target.value })} - /> - setForm({ ...form, description: e.target.value })} - /> - setForm({ ...form, points: Number(e.target.value) })} - /> -
- - {editingChallenge && ( - - )} -
-
-
- ); + return ( + + + + {editingChallenge ? "✏️ Modifier Challenge" : "🛠️ Créer Challenge"} + + + + setForm({ ...form, title: e.target.value })} + /> + setForm({ ...form, description: e.target.value })} + /> + setForm({ ...form, points: Number(e.target.value) })} + /> +
+ + {editingChallenge && ( + + )} +
+
+
+ ); }; export default ChallengeEditor; diff --git a/frontend/src/components/Admin/AdminChallenge/adminChallengeValidatedList.tsx b/frontend/src/components/Admin/AdminChallenge/adminChallengeValidatedList.tsx index 5f70419..e914f21 100644 --- a/frontend/src/components/Admin/AdminChallenge/adminChallengeValidatedList.tsx +++ b/frontend/src/components/Admin/AdminChallenge/adminChallengeValidatedList.tsx @@ -1,138 +1,138 @@ +import { Search } from "lucide-react"; import { useMemo, useState } from "react"; +import Swal from "sweetalert2"; +import { ValidatedChallenge } from "../../../interfaces/challenge.interface"; +import { unvalidateChallenge } from "../../../services/requests/challenge.service"; import { Button } from "../../ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; import { Input } from "../../ui/input"; -import { unvalidateChallenge } from "../../../services/requests/challenge.service"; -import Swal from "sweetalert2"; -import { ValidatedChallenge } from "../../../interfaces/challenge.interface"; -import { Search } from "lucide-react"; interface Props { - validatedChallenges: ValidatedChallenge[]; - fetchValidatedChallenges: () => void | Promise; + validatedChallenges: ValidatedChallenge[]; + fetchValidatedChallenges: () => void | Promise; } export const AdminValidatedChallengesList = ({ - validatedChallenges, - fetchValidatedChallenges, + validatedChallenges, + fetchValidatedChallenges, }: Props) => { - const [search, setSearch] = useState(""); + const [search, setSearch] = useState(""); - const filtered = useMemo(() => { - const q = search.toLowerCase(); - return validatedChallenges.filter((c) => - [ - c.challenge_name, - c.challenge_categorie, - c.challenge_description, - c.target_user_firstname ?? "", - c.target_user_lastname ?? "", - c.target_team_name ?? "", - c.target_faction_name ?? "", - ] - .join(" ") - .toLowerCase() - .includes(q) - ); - }, [validatedChallenges, search]); + const filtered = useMemo(() => { + const q = search.toLowerCase(); + return validatedChallenges.filter((c) => + [ + c.challenge_name, + c.challenge_categorie, + c.challenge_description, + c.target_user_firstname ?? "", + c.target_user_lastname ?? "", + c.target_team_name ?? "", + c.target_faction_name ?? "", + ] + .join(" ") + .toLowerCase() + .includes(q) + ); + }, [validatedChallenges, search]); - const handleUnvalidate = async ( - challengeId: number, - factionId: number | null, - teamId: number | null, - userId: number | null - ) => { - const confirm = await Swal.fire({ - title: "Confirmer la dévalidation ?", - text: "Cette action retirera la validation du challenge.", - icon: "warning", - showCancelButton: true, - confirmButtonColor: "#e3342f", - cancelButtonColor: "#6b7280", - confirmButtonText: "Oui, dévalider", - cancelButtonText: "Annuler", - }); - if (!confirm.isConfirmed) return; + const handleUnvalidate = async ( + challengeId: number, + factionId: number | null, + teamId: number | null, + userId: number | null + ) => { + const confirm = await Swal.fire({ + title: "Confirmer la dévalidation ?", + text: "Cette action retirera la validation du challenge.", + icon: "warning", + showCancelButton: true, + confirmButtonColor: "#e3342f", + cancelButtonColor: "#6b7280", + confirmButtonText: "Oui, dévalider", + cancelButtonText: "Annuler", + }); + if (!confirm.isConfirmed) return; - try { - const res = await unvalidateChallenge({ - challengeId, - factionId: factionId ?? 0, - teamId: teamId ?? 0, - userId: userId ?? 0, - }); - Swal.fire({ icon: "success", title: "Dévalidé", text: res.message, timer: 1800, showConfirmButton: false }); - await fetchValidatedChallenges(); - } catch { - Swal.fire({ icon: "error", title: "Erreur", text: "Impossible de dévalider ce challenge." }); - } - }; + try { + const res = await unvalidateChallenge({ + challengeId, + factionId: factionId ?? 0, + teamId: teamId ?? 0, + userId: userId ?? 0, + }); + Swal.fire({ icon: "success", title: "Dévalidé", text: res.message, timer: 1800, showConfirmButton: false }); + await fetchValidatedChallenges(); + } catch { + Swal.fire({ icon: "error", title: "Erreur", text: "Impossible de dévalider ce challenge." }); + } + }; - return ( -
- - - - 📋 Challenges validés - - - + return ( +
+ + + + 📋 Challenges validés + + + - {/* Recherche */} -
- - setSearch(e.target.value)} - className="border-none focus:ring-0 bg-transparent flex-1" - /> -
+ {/* Recherche */} +
+ + setSearch(e.target.value)} + className="border-none focus:ring-0 bg-transparent flex-1" + /> +
- {/* Grille */} - {filtered.length === 0 ? ( -

Aucun challenge validé trouvé.

- ) : ( -
- {filtered.map((c) => ( -
-
-

{c.challenge_name}

-

{c.challenge_categorie}

-

{c.challenge_description}

-
+ {/* Grille */} + {filtered.length === 0 ? ( +

Aucun challenge validé trouvé.

+ ) : ( +
+ {filtered.map((c) => ( +
+
+

{c.challenge_name}

+

{c.challenge_categorie}

+

{c.challenge_description}

+
-
-

Points : {c.points}

-

- Validé le : {new Date(c.validated_at).toLocaleDateString()} -

-
+
+

Points : {c.points}

+

+ Validé le : {new Date(c.validated_at).toLocaleDateString()} +

+
-
-

Destinataire :

- {c.target_faction_name &&

{c.target_faction_name}

} - {c.target_team_name &&

{c.target_team_name}

} - {(c.target_user_firstname || c.target_user_lastname) && ( -

{c.target_user_firstname} {c.target_user_lastname}

- )} -
+
+

Destinataire :

+ {c.target_faction_name &&

{c.target_faction_name}

} + {c.target_team_name &&

{c.target_team_name}

} + {(c.target_user_firstname || c.target_user_lastname) && ( +

{c.target_user_firstname} {c.target_user_lastname}

+ )} +
- -
- ))} -
- )} - - -
- ); + +
+ ))} +
+ )} +
+
+
+ ); }; diff --git a/frontend/src/components/Admin/AdminPerm/adminPermAction.tsx b/frontend/src/components/Admin/AdminPerm/adminPermAction.tsx index c7a35ce..b0ca232 100644 --- a/frontend/src/components/Admin/AdminPerm/adminPermAction.tsx +++ b/frontend/src/components/Admin/AdminPerm/adminPermAction.tsx @@ -1,99 +1,98 @@ +import Swal from "sweetalert2"; import { Button } from "../../../components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "../../../components/ui/card"; -import Swal from "sweetalert2"; - -import { openPermanence, closePermanence } from "../../../services/requests/permanence.service"; import { Permanence } from "../../../interfaces/permanence.interface"; +import { closePermanence, openPermanence } from "../../../services/requests/permanence.service"; interface PermanenceActionsProps { - permanences: Permanence[]; - onRefresh: () => void; + permanences: Permanence[]; + onRefresh: () => void; } // Fonction utilitaire pour "normaliser" une date au début de journée (00:00:00) const normalizeDate = (d: Date): Date => { - return new Date(d.getFullYear(), d.getMonth(), d.getDate()); + return new Date(d.getFullYear(), d.getMonth(), d.getDate()); }; const inSevenDays = (): Date => { - const d = new Date(); - d.setDate(d.getDate() + 7); - return normalizeDate(d); // → seuil à J+7 mais à 00h00 + const d = new Date(); + d.setDate(d.getDate() + 7); + return normalizeDate(d); // → seuil à J+7 mais à 00h00 }; const PermanenceActions: React.FC = ({ permanences, onRefresh }) => { - const handleOpenAll = async (): Promise => { - const confirm = await Swal.fire({ - title: "Ouvrir toutes à J+7 ?", - text: "Toutes les permanences commençant avant J+7 seront ouvertes.", - icon: "question", - showCancelButton: true, - confirmButtonText: "Ouvrir", - cancelButtonText: "Annuler", - }); - if (!confirm.isConfirmed) return; + const handleOpenAll = async (): Promise => { + const confirm = await Swal.fire({ + title: "Ouvrir toutes à J+7 ?", + text: "Toutes les permanences commençant avant J+7 seront ouvertes.", + icon: "question", + showCancelButton: true, + confirmButtonText: "Ouvrir", + cancelButtonText: "Annuler", + }); + if (!confirm.isConfirmed) return; - const today = normalizeDate(new Date()).getTime(); - const threshold = inSevenDays().getTime(); + const today = normalizeDate(new Date()).getTime(); + const threshold = inSevenDays().getTime(); - const toOpen = permanences.filter((p) => { - const permDate = normalizeDate(new Date(p.start_at)).getTime(); - return permDate > today && permDate <= threshold && !p.is_open; - }); + const toOpen = permanences.filter((p) => { + const permDate = normalizeDate(new Date(p.start_at)).getTime(); + return permDate > today && permDate <= threshold && !p.is_open; + }); - try { - await Promise.all(toOpen.map((p) => openPermanence(p.id))); - await Swal.fire("Ouvertes", "Toutes les permanences ont été ouvertes !", "success"); - onRefresh(); - } catch { - Swal.fire("Erreur", "Une erreur est survenue lors de l'ouverture", "error"); - } - }; + try { + await Promise.all(toOpen.map((p) => openPermanence(p.id))); + await Swal.fire("Ouvertes", "Toutes les permanences ont été ouvertes !", "success"); + onRefresh(); + } catch { + Swal.fire("Erreur", "Une erreur est survenue lors de l'ouverture", "error"); + } + }; - const handleCloseAll = async (): Promise => { - const confirm = await Swal.fire({ - title: "Fermer toutes à J+7 ?", - text: "Toutes les permanences commençant avant J+7 seront fermées.", - icon: "warning", - showCancelButton: true, - confirmButtonText: "Fermer", - cancelButtonText: "Annuler", - }); - if (!confirm.isConfirmed) return; + const handleCloseAll = async (): Promise => { + const confirm = await Swal.fire({ + title: "Fermer toutes à J+7 ?", + text: "Toutes les permanences commençant avant J+7 seront fermées.", + icon: "warning", + showCancelButton: true, + confirmButtonText: "Fermer", + cancelButtonText: "Annuler", + }); + if (!confirm.isConfirmed) return; - const threshold = inSevenDays().getTime(); - const toClose = permanences.filter((p) => { - const permDate = normalizeDate(new Date(p.start_at)).getTime(); - return permDate <= threshold; - }); - try { - await Promise.all(toClose.map((p) => closePermanence(p.id))); - await Swal.fire("Fermées", "Toutes les permanences ont été fermées !", "success"); - onRefresh(); - } catch { - Swal.fire("Erreur", "Une erreur est survenue lors de la fermeture", "error"); - } - }; + const threshold = inSevenDays().getTime(); + const toClose = permanences.filter((p) => { + const permDate = normalizeDate(new Date(p.start_at)).getTime(); + return permDate <= threshold; + }); + try { + await Promise.all(toClose.map((p) => closePermanence(p.id))); + await Swal.fire("Fermées", "Toutes les permanences ont été fermées !", "success"); + onRefresh(); + } catch { + Swal.fire("Erreur", "Une erreur est survenue lors de la fermeture", "error"); + } + }; - return ( - - - - ⚡ Actions rapides - - - -
- - -
-
-
- ); + return ( + + + + ⚡ Actions rapides + + + +
+ + +
+
+
+ ); }; export default PermanenceActions; diff --git a/frontend/src/components/Admin/AdminPerm/adminPermForm.tsx b/frontend/src/components/Admin/AdminPerm/adminPermForm.tsx index ed10c23..0edd6b1 100644 --- a/frontend/src/components/Admin/AdminPerm/adminPermForm.tsx +++ b/frontend/src/components/Admin/AdminPerm/adminPermForm.tsx @@ -1,14 +1,14 @@ -import { useState, useEffect } from "react"; -import { Card, CardHeader, CardContent, CardTitle } from "../../../components/ui/card"; -import { Input } from "../../../components/ui/input"; -import { Textarea } from "../../../components/ui/textarea"; -import { Button } from "../../../components/ui/button"; +import { useEffect, useState } from "react"; import Select from "react-select"; import Swal from "sweetalert2"; +import { Button } from "../../../components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "../../../components/ui/card"; +import { Input } from "../../../components/ui/input"; +import { Textarea } from "../../../components/ui/textarea"; import { - createPermanence, - updatePermanence, + createPermanence, + updatePermanence, } from "../../../services/requests/permanence.service"; import { getUsers } from "../../../services/requests/user.service"; @@ -18,215 +18,215 @@ import { User } from "../../../interfaces/user.interface"; import { formatDateForDB, formatDateForInput } from "../../utils/datetime_utils"; interface PermanenceFormProps { - editMode: boolean; - editPermanence: Permanence | null; - onRefresh: () => void; - onCancelEdit: () => void; + editMode: boolean; + editPermanence: Permanence | null; + onRefresh: () => void; + onCancelEdit: () => void; } const PermanenceForm = ({ - editMode, - editPermanence, - onRefresh, - onCancelEdit, + editMode, + editPermanence, + onRefresh, + onCancelEdit, }: PermanenceFormProps) => { - const [name, setName] = useState(""); - const [desc, setDesc] = useState(""); - const [location, setLocation] = useState(""); - const [startAt, setStartAt] = useState(Date); - const [endAt, setEndAt] = useState(Date); - const [capacity, setCapacity] = useState(0); - const [difficulty, setDifficulty] = useState(0); - const [respo, setRespo] = useState(); - const [users, setUsers] = useState([]); - - useEffect(() => { - const fetchUsers = async () => { - try { - const data = await getUsers(); - setUsers(data); - } catch { - Swal.fire("Erreur", "Impossible de charger les utilisateurs", "error"); - } + const [name, setName] = useState(""); + const [desc, setDesc] = useState(""); + const [location, setLocation] = useState(""); + const [startAt, setStartAt] = useState(Date); + const [endAt, setEndAt] = useState(Date); + const [capacity, setCapacity] = useState(0); + const [difficulty, setDifficulty] = useState(0); + const [respo, setRespo] = useState(); + const [users, setUsers] = useState([]); + + useEffect(() => { + const fetchUsers = async () => { + try { + const data = await getUsers(); + setUsers(data); + } catch { + Swal.fire("Erreur", "Impossible de charger les utilisateurs", "error"); + } + }; + + fetchUsers(); + }, []); + + useEffect(() => { + if (editMode && editPermanence) { + setName(editPermanence.name); + setDesc(editPermanence.description); + setLocation(editPermanence.location); + setStartAt(formatDateForInput(editPermanence.start_at)); + setEndAt(formatDateForInput(editPermanence.end_at)); + setCapacity(editPermanence.capacity); + setDifficulty(editPermanence.difficulty); + if (editPermanence.respo) { + setRespo(editPermanence.respo); + } + } + }, [editMode, editPermanence]); + + const handleSubmit = async () => { + if (!editMode) { + if (!name || !desc || !location || !startAt || !endAt) { + Swal.fire("Erreur", "Veuillez remplir tous les champs", "warning"); + return; + } + + if (capacity < 0 || difficulty < 0) { + Swal.fire("Erreur", "Capacité et difficulté doivent être positives", "warning"); + return; + } + } + + + let respoId = respo && !isNaN(Number(respo.userId)) ? Number(respo.userId) : null; + + + try { + const payload = { + name, + description: desc, + location, + start_at: formatDateForDB(startAt), // ✅ en UTC + end_at: formatDateForDB(endAt), // ✅ en UTC + capacity, + difficulty, + respoId, + }; + + if (editMode && editPermanence) { + await updatePermanence(editPermanence.id, payload); + Swal.fire("Succès", "Permanence mise à jour", "success"); + onCancelEdit(); + } else { + await createPermanence(payload); + Swal.fire("Succès", "Permanence créée", "success"); + } + + resetForm(); + onRefresh(); + } catch (err: any) { + Swal.fire("Erreur", err.response.data.message, "error"); + } }; - fetchUsers(); - }, []); - - useEffect(() => { - if (editMode && editPermanence) { - setName(editPermanence.name); - setDesc(editPermanence.description); - setLocation(editPermanence.location); - setStartAt(formatDateForInput(editPermanence.start_at)); - setEndAt(formatDateForInput(editPermanence.end_at)); - setCapacity(editPermanence.capacity); - setDifficulty(editPermanence.difficulty); - if (editPermanence.respo) { - setRespo(editPermanence.respo); - } - } - }, [editMode, editPermanence]); - - const handleSubmit = async () => { - if (!editMode) { - if (!name || !desc || !location || !startAt || !endAt) { - Swal.fire("Erreur", "Veuillez remplir tous les champs", "warning"); - return; - } - - if (capacity < 0 || difficulty < 0) { - Swal.fire("Erreur", "Capacité et difficulté doivent être positives", "warning"); - return; - } - } - - - let respoId = respo && !isNaN(Number(respo.userId)) ? Number(respo.userId) : null; - - - try { - const payload = { - name, - description: desc, - location, - start_at: formatDateForDB(startAt), // ✅ en UTC - end_at: formatDateForDB(endAt), // ✅ en UTC - capacity, - difficulty, - respoId, - }; - - if (editMode && editPermanence) { - await updatePermanence(editPermanence.id, payload); - Swal.fire("Succès", "Permanence mise à jour", "success"); - onCancelEdit(); - } else { - await createPermanence(payload); - Swal.fire("Succès", "Permanence créée", "success"); - } - - resetForm(); - onRefresh(); - } catch (err: any) { - Swal.fire("Erreur", err.response.data.message, "error"); - } - }; - - const resetForm = () => { - setName(""); - setDesc(""); - setLocation(""); - setStartAt(""); - setEndAt(""); - setCapacity(0); - setDifficulty(0); - setRespo(null); - }; - - const respoOptions = users.map((user) => ({ - value: user.userId, - label: `${user.firstName} ${user.lastName}`, - })); - - const selectedRespoOption = respo - ? { value: respo.userId, label: `${respo.firstName} ${respo.lastName}` } - : null; - - return ( - - - - {editMode ? "✏️ Modifier une permanence" : "➕ Créer une permanence"} - - - - setName(e.target.value)} - /> -