From f5d76ca5899fc9cb5f4f8fa38004204742ddc1e9 Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sat, 1 Aug 2026 17:09:18 +0200 Subject: [PATCH] feat: cleaner types architecture --- backend/src/controllers/auth.controller.ts | 8 +- backend/src/controllers/banned.controller.ts | 2 +- backend/src/controllers/bus.controller.ts | 4 +- .../src/controllers/challenge.controller.ts | 2 +- backend/src/controllers/discord.controller.ts | 2 +- backend/src/controllers/email.controller.ts | 8 +- backend/src/controllers/event.controller.ts | 4 +- backend/src/controllers/faction.controller.ts | 2 +- .../src/controllers/im_export.controller.ts | 6 +- backend/src/controllers/news.controller.ts | 4 +- .../src/controllers/permanence.controller.ts | 2 +- backend/src/controllers/role.controller.ts | 2 +- backend/src/controllers/team.controller.ts | 2 +- backend/src/controllers/tent.controller.ts | 14 +- backend/src/controllers/user.controller.ts | 10 +- backend/src/database/db.ts | 16 +- backend/src/database/initdb/initUser.ts | 14 +- backend/src/dto/email.dto.ts | 11 +- backend/src/dto/team.dto.ts | 36 --- backend/src/dto/user.dto.ts | 6 +- backend/src/email/email.preview-data.ts | 4 +- backend/src/email/email.registry.ts | 2 +- backend/src/middlewares/auth.middleware.ts | 14 +- .../src/middlewares/automation.middleware.ts | 4 +- backend/src/middlewares/multer.middleware.ts | 4 +- .../src/middlewares/respoperm.middleware.ts | 16 +- backend/src/middlewares/user.middleware.ts | 24 +- backend/src/services/auth.service.ts | 4 +- backend/src/services/discord.service.ts | 38 +-- backend/src/services/email.service.ts | 4 +- backend/src/services/permanence.service.ts | 266 +++++++----------- backend/src/services/team.service.ts | 2 +- backend/src/services/team/assignation.team.ts | 2 +- backend/src/services/team/email.team.ts | 8 +- backend/src/services/user.service.ts | 30 +- .../src/{utils => shared/http}/responses.ts | 0 .../integrations}/billetweb.ts | 2 +- .../{utils => shared/integrations}/siep.ts | 29 +- .../secret.ts => shared/secrets/secrets.ts} | 0 .../storage}/uploadDocuments.ts | 0 backend/src/{ => shared}/utils/pLimit.ts | 0 backend/src/{ => shared}/utils/token.ts | 4 +- backend/src/types/{auth.ts => auth.d.ts} | 0 backend/{ => src}/types/billetweb.d.ts | 0 backend/{ => src}/types/email.d.ts | 11 + backend/src/types/{http.ts => http.d.ts} | 0 backend/{ => src}/types/permanence.d.ts | 0 backend/src/types/team.d.ts | 35 +++ backend/src/types/user.d.ts | 17 ++ 49 files changed, 300 insertions(+), 375 deletions(-) rename backend/src/{utils => shared/http}/responses.ts (100%) rename backend/src/{utils => shared/integrations}/billetweb.ts (96%) rename backend/src/{utils => shared/integrations}/siep.ts (78%) rename backend/src/{utils/secret.ts => shared/secrets/secrets.ts} (100%) rename backend/src/{utils => shared/storage}/uploadDocuments.ts (100%) rename backend/src/{ => shared}/utils/pLimit.ts (100%) rename backend/src/{ => shared}/utils/token.ts (72%) rename backend/src/types/{auth.ts => auth.d.ts} (100%) rename backend/{ => src}/types/billetweb.d.ts (100%) rename backend/{ => src}/types/email.d.ts (78%) rename backend/src/types/{http.ts => http.d.ts} (100%) rename backend/{ => src}/types/permanence.d.ts (100%) create mode 100644 backend/src/types/team.d.ts create mode 100644 backend/src/types/user.d.ts diff --git a/backend/src/controllers/auth.controller.ts b/backend/src/controllers/auth.controller.ts index b53fded..924f21d 100644 --- a/backend/src/controllers/auth.controller.ts +++ b/backend/src/controllers/auth.controller.ts @@ -1,7 +1,7 @@ import bcrypt from 'bcryptjs'; import bigInt from 'big-integer'; import { sign, verify } from 'jsonwebtoken'; -import type { EmailOptions } from '../../types/email'; +import type { EmailOptions } from '../types/email'; import { templateResetPassword } from '../email/email.registry'; import { compileTemplate } from '../email/email.renderer'; import * as auth_service from '../services/auth.service'; @@ -10,9 +10,9 @@ import * as email_service from '../services/email.service'; import * as registration_service from '../services/registration.service'; import * as role_service from '../services/role.service'; import * as user_service from '../services/user.service'; -import { Error, Ok, Unauthorized } from '../utils/responses'; -import { email_from, jwtSecret, service_url } from '../utils/secret'; -import { decodeToken } from '../utils/token'; +import { Error, Ok, Unauthorized } from '../shared/http/responses'; +import { email_from, jwtSecret, service_url } from '../shared/secrets/secrets'; +import { decodeToken } from '../shared/utils/token'; import type { AppRequestHandler } from '../types/http'; import type { LoginBody, diff --git a/backend/src/controllers/banned.controller.ts b/backend/src/controllers/banned.controller.ts index e3ffa8d..8d1f827 100644 --- a/backend/src/controllers/banned.controller.ts +++ b/backend/src/controllers/banned.controller.ts @@ -1,5 +1,5 @@ import * as banned_service from '../services/banned.service'; -import { Error, Ok } from '../utils/responses'; +import { Error, Ok } from '../shared/http/responses'; import type { AppRequestHandler } from '../types/http'; import type { BannedBody, BannedIdParams } from '../dto/banned.dto'; diff --git a/backend/src/controllers/bus.controller.ts b/backend/src/controllers/bus.controller.ts index 5d5e2c7..55f2394 100644 --- a/backend/src/controllers/bus.controller.ts +++ b/backend/src/controllers/bus.controller.ts @@ -1,7 +1,7 @@ import * as bus_service from '../services/bus.service'; import { generateEmailHtml, sendEmail } from '../services/email.service'; -import { Error, Ok } from '../utils/responses'; -import { email_from } from '../utils/secret'; +import { Error, Ok } from '../shared/http/responses'; +import { email_from } from '../shared/secrets/secrets'; import type { AppRequestHandler } from '../types/http'; export const sendBusAttributionEmails: AppRequestHandler = async (_req, res) => { diff --git a/backend/src/controllers/challenge.controller.ts b/backend/src/controllers/challenge.controller.ts index c17da21..505a512 100644 --- a/backend/src/controllers/challenge.controller.ts +++ b/backend/src/controllers/challenge.controller.ts @@ -1,5 +1,5 @@ import * as challenge_service from '../services/challenge.service'; -import { Created, Error, Ok, Unauthorized } from '../utils/responses'; +import { Created, Error, Ok, Unauthorized } from '../shared/http/responses'; import type { AppRequestHandler } from '../types/http'; import type { CreateChallengeBody, diff --git a/backend/src/controllers/discord.controller.ts b/backend/src/controllers/discord.controller.ts index 225acaf..6ad9bd1 100644 --- a/backend/src/controllers/discord.controller.ts +++ b/backend/src/controllers/discord.controller.ts @@ -1,5 +1,5 @@ import * as discord_service from '../services/discord.service'; -import { Error, Ok, ServiceUnavailable } from '../utils/responses'; +import { Error, Ok, ServiceUnavailable } from '../shared/http/responses'; import type { AppRequestHandler } from '../types/http'; import type { DiscordBody } from '../dto/discord.dto'; diff --git a/backend/src/controllers/email.controller.ts b/backend/src/controllers/email.controller.ts index 4c7526d..f449470 100644 --- a/backend/src/controllers/email.controller.ts +++ b/backend/src/controllers/email.controller.ts @@ -1,11 +1,11 @@ -import type { EmailOptions } from '../../types/email'; +import type { EmailOptions } from '../types/email'; import { defaultPreviewData } from '../email/email.preview-data'; import { generateEmailHtml, getRecipients, sendEmail } from '../services/email.service'; import * as registration_service from '../services/registration.service'; import * as user_service from '../services/user.service'; -import { Error, Ok } from '../utils/responses'; -import { email_from, service_url } from '../utils/secret'; -import { getLatestUploadedDocument } from '../utils/uploadDocuments'; +import { Error, Ok } from '../shared/http/responses'; +import { email_from, service_url } from '../shared/secrets/secrets'; +import { getLatestUploadedDocument } from '../shared/storage/uploadDocuments'; import type { AppRequestHandler } from '../types/http'; import type { EmailRequestBody } from '../dto/email.dto'; diff --git a/backend/src/controllers/event.controller.ts b/backend/src/controllers/event.controller.ts index fea9b5f..b32b11c 100644 --- a/backend/src/controllers/event.controller.ts +++ b/backend/src/controllers/event.controller.ts @@ -1,7 +1,7 @@ import * as event_service from '../services/event.service'; import * as team_service from '../services/team.service'; -import { Conflict, Error, Ok, Teapot, Unauthorized } from '../utils/responses'; -import { shotgun_password } from '../utils/secret'; +import { Conflict, Error, Ok, Teapot, Unauthorized } from '../shared/http/responses'; +import { shotgun_password } from '../shared/secrets/secrets'; import type { AppRequestHandler } from '../types/http'; import type { ShotgunBody, ToggleStatusBody } from '../dto/event.dto'; diff --git a/backend/src/controllers/faction.controller.ts b/backend/src/controllers/faction.controller.ts index 811d0cb..03c4520 100644 --- a/backend/src/controllers/faction.controller.ts +++ b/backend/src/controllers/faction.controller.ts @@ -1,5 +1,5 @@ import * as faction_service from '../services/faction.service'; -import { Error, Ok } from '../utils/responses'; +import { Error, Ok } from '../shared/http/responses'; import type { AppRequestHandler } from '../types/http'; import type { FactionQuery, FactionBody } from '../dto/faction.dto'; diff --git a/backend/src/controllers/im_export.controller.ts b/backend/src/controllers/im_export.controller.ts index ba1df4d..979e6d1 100644 --- a/backend/src/controllers/im_export.controller.ts +++ b/backend/src/controllers/im_export.controller.ts @@ -5,14 +5,14 @@ import * as export_service from '../services/im_export.service'; import * as permanence_service from '../services/permanence.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 { Error, Ok } from '../shared/http/responses'; +import { spreadsheet_id } from '../shared/secrets/secrets'; import { getLatestUploadedDocument, isSafeUploadSegment, removeUploadedDocuments, toUploadedDocumentStatus, -} from '../utils/uploadDocuments'; +} from '../shared/storage/uploadDocuments'; import type { AppRequestHandler } from '../types/http'; import type { UploadedDocumentParams } from '../dto/im_export.dto'; diff --git a/backend/src/controllers/news.controller.ts b/backend/src/controllers/news.controller.ts index 7756d1f..3ff6ae7 100644 --- a/backend/src/controllers/news.controller.ts +++ b/backend/src/controllers/news.controller.ts @@ -4,8 +4,8 @@ import * as email_service from '../services/email.service'; import { generateEmailHtml } from '../services/email.service'; import * as news_service from '../services/news.service'; import * as user_service from '../services/user.service'; -import { Error, Ok } from '../utils/responses'; -import { email_from } from '../utils/secret'; +import { Error, Ok } from '../shared/http/responses'; +import { email_from } from '../shared/secrets/secrets'; import type { AppRequestHandler } from '../types/http'; import type { NewsBody, NewsQuery } from '../dto/news.dto'; diff --git a/backend/src/controllers/permanence.controller.ts b/backend/src/controllers/permanence.controller.ts index 5220048..92ca549 100644 --- a/backend/src/controllers/permanence.controller.ts +++ b/backend/src/controllers/permanence.controller.ts @@ -1,5 +1,5 @@ import * as permanence_service from '../services/permanence.service'; -import { Error, Ok } from '../utils/responses'; +import { Error, Ok } from '../shared/http/responses'; import type { AppRequestHandler } from '../types/http'; import type { PermanenceBody, PermQuery } from '../dto/permanence.dto'; diff --git a/backend/src/controllers/role.controller.ts b/backend/src/controllers/role.controller.ts index bf946aa..3f788f9 100644 --- a/backend/src/controllers/role.controller.ts +++ b/backend/src/controllers/role.controller.ts @@ -1,5 +1,5 @@ import * as role_service from '../services/role.service'; -import { Error, Ok } from '../utils/responses'; +import { Error, Ok } from '../shared/http/responses'; import type { AppRequestHandler } from '../types/http'; import type { PermissionBody, diff --git a/backend/src/controllers/team.controller.ts b/backend/src/controllers/team.controller.ts index 302b67f..5377d9a 100644 --- a/backend/src/controllers/team.controller.ts +++ b/backend/src/controllers/team.controller.ts @@ -2,7 +2,7 @@ import { type 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 { Error, Ok } from '../utils/responses'; +import { Error, Ok } from '../shared/http/responses'; import type { AppRequestHandler } from '../types/http'; import type { CreateTeamBody, CreateTeamLightBody, ModifyTeamBody, TeamQuery } from '../dto/team.dto'; diff --git a/backend/src/controllers/tent.controller.ts b/backend/src/controllers/tent.controller.ts index 9c68870..410345d 100644 --- a/backend/src/controllers/tent.controller.ts +++ b/backend/src/controllers/tent.controller.ts @@ -1,8 +1,8 @@ import { generateEmailHtml, 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 { email_from } from '../utils/secret'; +import { Error, Ok } from '../shared/http/responses'; +import { email_from } from '../shared/secrets/secrets'; import type { AppRequestHandler } from '../types/http'; import type { CreateTentBody, ToggleTentBody } from '../dto/tent.dto'; @@ -89,10 +89,8 @@ export const toggleTentConfirmation: AppRequestHandler = async ( const emailOptions = { from: email_from, to: [user1.email, user2.email], - subject: confirmed - ? "🎉 Votre tente a été validée !" - : "⛺ Votre tente a été invalidée", - text: "", // optionnel + subject: confirmed ? '🎉 Votre tente a été validée !' : '⛺ Votre tente a été invalidée', + text: '', // optionnel html: htmlEmail, }; @@ -100,9 +98,7 @@ export const toggleTentConfirmation: AppRequestHandler = async ( await sendEmail(emailOptions); Ok(res, { - msg: confirmed - ? "Tente validée et email envoyé." - : "Tente invalidée et email envoyé.", + msg: confirmed ? 'Tente validée et email envoyé.' : 'Tente invalidée et email envoyé.', }); } catch (err: any) { console.error(err); diff --git a/backend/src/controllers/user.controller.ts b/backend/src/controllers/user.controller.ts index f10ef74..b983c1e 100644 --- a/backend/src/controllers/user.controller.ts +++ b/backend/src/controllers/user.controller.ts @@ -1,14 +1,14 @@ import type { - AdminCreateUserDto, - CreateUserContactInformationDto, + CreateUserContactInformationBody, PermissionParams, ProfileBody, SyncBody, UserIdParams, VssSubmissionPayload, + AdminCreateUserBody, } from '../dto/user.dto'; import * as user_service from '../services/user.service'; -import { Error, Ok } from '../utils/responses'; +import { Error, Ok } from '../shared/http/responses'; import type { AppRequestHandler } from '../types/http'; export const getUsersAdmin: AppRequestHandler = async (_req, res) => { @@ -85,7 +85,7 @@ export const getUserContactInformation: AppRequestHandler = async (req, res) => } }; -export const createUserContactInformation: AppRequestHandler = async (req, res) => { +export const createUserContactInformation: AppRequestHandler = async (req, res) => { const userId = req.user?.userId; const contact = req.body; @@ -153,7 +153,7 @@ export const adminUpdateUser: AppRequestHandler } }; -export const adminCreateUser: AppRequestHandler = async (req, res) => { +export const adminCreateUser: AppRequestHandler = async (req, res) => { try { const user = await user_service.adminCreateUser(req.body); diff --git a/backend/src/database/db.ts b/backend/src/database/db.ts index 75c5005..af9e389 100644 --- a/backend/src/database/db.ts +++ b/backend/src/database/db.ts @@ -1,6 +1,6 @@ -import { drizzle } from "drizzle-orm/node-postgres"; -import { Client } from "pg"; -import { postgres_db, postgres_host, postgres_password, postgres_port, postgres_user } from '../utils/secret'; +import { drizzle } from 'drizzle-orm/node-postgres'; +import { Client } from 'pg'; +import { postgres_db, postgres_host, postgres_password, postgres_port, postgres_user } from '../shared/secrets/secrets'; // ✅ Import de tous tes schémas ici import * as challenge from '../schemas/Basic/challenge.schema'; @@ -11,16 +11,16 @@ 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 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 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"; +import * as tent from '../schemas/Relational/usertent.schema'; const schema = { ...user, @@ -40,7 +40,7 @@ const schema = { ...busattribution, ...registration, ...tent, - ...rolepoints + ...rolepoints, }; const client = new Client({ diff --git a/backend/src/database/initdb/initUser.ts b/backend/src/database/initdb/initUser.ts index 48f4fb8..7131f5c 100644 --- a/backend/src/database/initdb/initUser.ts +++ b/backend/src/database/initdb/initUser.ts @@ -1,7 +1,7 @@ -import { userSchema } from "../../schemas/Basic/user.schema"; -import { hashPassword } from "../../services/auth.service"; -import { email_password } from "../../utils/secret"; -import { db } from "../db"; // Assurez-vous que votre instance db est correcte +import { userSchema } from '../../schemas/Basic/user.schema'; +import { hashPassword } from '../../services/auth.service'; +import { email_password } from '../../shared/secrets/secrets'; +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); @@ -11,9 +11,9 @@ export const initUser = async () => { // 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", + first_name: 'Integration UTT', + last_name: 'Integration UTT', + email: 'integration@utt.fr', majeur: true, password: hashedPassword, permission: 'Admin', diff --git a/backend/src/dto/email.dto.ts b/backend/src/dto/email.dto.ts index e423efc..2d734e4 100644 --- a/backend/src/dto/email.dto.ts +++ b/backend/src/dto/email.dto.ts @@ -1,13 +1,4 @@ -type EmailPayload = { - subject: string; - templateName: string; - recipientsGroups?: string[]; - sendTo?: string[]; - html?: string; - title?: string; - content?: string; - text?: string; -}; +import { type EmailPayload } from '../types/email'; export type EmailRequestBody = { payload?: EmailPayload; diff --git a/backend/src/dto/team.dto.ts b/backend/src/dto/team.dto.ts index ca34e97..43d5ca8 100644 --- a/backend/src/dto/team.dto.ts +++ b/backend/src/dto/team.dto.ts @@ -21,39 +21,3 @@ export type TeamQuery = { teamId?: string; teamID?: string; }; - -export type TeamMemberRow = { - userId: number; -}; - -export type StudentRow = { - userId: number; - email: string; - branch: string; - male?: boolean; -}; - -export type TeamAssignmentNotification = { - email: string; - teamId: number; -}; - -export type TeamRow = { - teamId: number; - name: string; - description: string; - type: string; - socialLink: string; - riCompatible: boolean; -}; - -export type TeamDistributionState = TeamRow & { - size: number; - girlsCount: number; -}; - -export type TeamSizeRow = { - teamId: number; - teamName: string; - size: number; -}; diff --git a/backend/src/dto/user.dto.ts b/backend/src/dto/user.dto.ts index 7e0107a..6d0056b 100644 --- a/backend/src/dto/user.dto.ts +++ b/backend/src/dto/user.dto.ts @@ -1,6 +1,6 @@ -import { type VssSubmissionAnswer } from '../services/user.service'; +import { type VssSubmissionAnswer } from '../types/user'; -export interface AdminCreateUserDto { +export interface AdminCreateUserBody { firstName: string; lastName: string; email: string; @@ -26,7 +26,7 @@ export type ProfileBody = { contact: string; }; -export type CreateUserContactInformationDto = { +export type CreateUserContactInformationBody = { emergency_contact_name: string; emergency_contact_phone: string; }; diff --git a/backend/src/email/email.preview-data.ts b/backend/src/email/email.preview-data.ts index b866057..6a656e8 100644 --- a/backend/src/email/email.preview-data.ts +++ b/backend/src/email/email.preview-data.ts @@ -1,5 +1,5 @@ -import type { TemplateData } from '../../types/email'; -import { service_url } from '../utils/secret'; +import type { TemplateData } from '../types/email'; +import { service_url } from '../shared/secrets/secrets'; export const defaultPreviewData: Record = { custom: { diff --git a/backend/src/email/email.registry.ts b/backend/src/email/email.registry.ts index 140074e..f582ca5 100644 --- a/backend/src/email/email.registry.ts +++ b/backend/src/email/email.registry.ts @@ -1,4 +1,4 @@ -import type { PermanenceEmailData, TemplateRenderer, TeamAssignmentEmailData } from '../../types/email'; +import type { PermanenceEmailData, TemplateRenderer, TeamAssignmentEmailData } from '../types/email'; export const templateResetPassword = 'reset-password.html'; const templateNotebook = 'notebook.html'; diff --git a/backend/src/middlewares/auth.middleware.ts b/backend/src/middlewares/auth.middleware.ts index a185d88..74330bd 100644 --- a/backend/src/middlewares/auth.middleware.ts +++ b/backend/src/middlewares/auth.middleware.ts @@ -1,21 +1,21 @@ -import { type NextFunction, type Request, type Response } from "express"; -import { Unauthorized } from "../utils/responses"; // Assurez-vous que cette fonction est bien définie -import { decodeToken } from "../utils/token"; +import { type NextFunction, type Request, type Response } from 'express'; +import { Unauthorized } from '../shared/http/responses'; // Assurez-vous que cette fonction est bien définie +import { decodeToken } from '../shared/utils/token'; export const authenticateUser = (req: Request, res: Response, next: NextFunction) => { try { const authHeader = req.headers.authorization; - if (!authHeader || !authHeader.startsWith("Bearer ")) { - return Unauthorized(res, { msg: "Unauthorized: Missing or malformed token" }); + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return Unauthorized(res, { msg: 'Unauthorized: Missing or malformed token' }); } - const token = authHeader.split(" ")[1]; + const token = authHeader.split(' ')[1]; const decoded = decodeToken(token); req.user = decoded; // Ajoute les données du token à l'objet `req` next(); } catch { - return Unauthorized(res, { msg: "Unauthorized: Invalid or expired token" }); + return Unauthorized(res, { msg: 'Unauthorized: Invalid or expired token' }); } }; diff --git a/backend/src/middlewares/automation.middleware.ts b/backend/src/middlewares/automation.middleware.ts index 606e75d..655f267 100644 --- a/backend/src/middlewares/automation.middleware.ts +++ b/backend/src/middlewares/automation.middleware.ts @@ -1,6 +1,6 @@ import { type NextFunction, type Request, type Response } from 'express'; -import { Unauthorized } from '../utils/responses'; // Assurez-vous que cette fonction est bien définie -import { automation_token } from '../utils/secret'; +import { Unauthorized } from '../shared/http/responses'; // Assurez-vous que cette fonction est bien définie +import { automation_token } from '../shared/secrets/secrets'; export const authenticateAutomation = (req: Request, res: Response, next: NextFunction) => { try { diff --git a/backend/src/middlewares/multer.middleware.ts b/backend/src/middlewares/multer.middleware.ts index e654a40..f21d24f 100644 --- a/backend/src/middlewares/multer.middleware.ts +++ b/backend/src/middlewares/multer.middleware.ts @@ -2,8 +2,8 @@ import { type NextFunction, type Request, type Response } from 'express'; import fs from 'fs/promises'; import multer from 'multer'; import path from 'path'; -import { Error } from '../utils/responses'; -import { isSafeUploadSegment, removeUploadedDocuments } from '../utils/uploadDocuments'; +import { Error } from '../shared/http/responses'; +import { isSafeUploadSegment, removeUploadedDocuments } from '../shared/storage/uploadDocuments'; export enum MIMEType { PDF = 'application/pdf', diff --git a/backend/src/middlewares/respoperm.middleware.ts b/backend/src/middlewares/respoperm.middleware.ts index b5f5939..a9e3eed 100644 --- a/backend/src/middlewares/respoperm.middleware.ts +++ b/backend/src/middlewares/respoperm.middleware.ts @@ -1,16 +1,12 @@ -import { type NextFunction, type Request, type Response } from "express"; -import { isUserRespoOfPermanence } from "../services/permanence.service"; -import { Error } from "../utils/responses"; +import { type NextFunction, type Request, type Response } from 'express'; +import { isUserRespoOfPermanence } from '../services/permanence.service'; +import { Error } from '../shared/http/responses'; -export const isRespoMiddleware = async ( - req: Request, - res: Response, - next: NextFunction -) => { +export const isRespoMiddleware = async (req: Request, res: Response, next: NextFunction) => { const userId = req.user?.userId; if (!userId) { - Error(res, { msg: "Utilisateur ou permanence non spécifié" }); + Error(res, { msg: 'Utilisateur ou permanence non spécifié' }); return; } @@ -23,6 +19,6 @@ export const isRespoMiddleware = async ( next(); // ✅ L'utilisateur est bien respo, on continue } catch (err) { console.error(err); - Error(res, { msg: "Erreur lors de la vérification du responsable" }); + 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 b8b83ba..b37c70f 100644 --- a/backend/src/middlewares/user.middleware.ts +++ b/backend/src/middlewares/user.middleware.ts @@ -1,41 +1,35 @@ -import { type NextFunction, type Request, type Response } from "express"; -import { Unauthorized } from "../utils/responses"; // adapte selon ton projet +import { type NextFunction, type Request, type Response } from 'express'; +import { Unauthorized } from '../shared/http/responses'; // adapte selon ton projet -export const checkRole = ( - requiredPermission?: string, - requiredRoles?: string[] -) => { +export const checkRole = (requiredPermission?: string, requiredRoles?: string[]) => { return (req: Request, res: Response, next: NextFunction) => { const user = req.user; if (!user) { - Unauthorized(res, { msg: "Accès non autorisé" }); + Unauthorized(res, { msg: 'Accès non autorisé' }); return; } try { - const isAdmin = user.userPermission === "Admin"; + const isAdmin = user.userPermission === 'Admin'; - const hasPermission = - !requiredPermission || user.userPermission === requiredPermission; + const hasPermission = !requiredPermission || user.userPermission === requiredPermission; const hasRole = !requiredRoles || (Array.isArray(user.userRoles) && - user.userRoles.some((role: { roleName: string }) => - requiredRoles.includes(role.roleName) - )); + user.userRoles.some((role: { roleName: string }) => requiredRoles.includes(role.roleName))); if (!isAdmin && !(hasPermission || hasRole)) { Unauthorized(res, { - msg: "Accès interdit, rôle ou permission insuffisants", + msg: 'Accès interdit, rôle ou permission insuffisants', }); return; } next(); } catch { - Unauthorized(res, { msg: "Token invalide ou expiré" }); + Unauthorized(res, { msg: 'Token invalide ou expiré' }); } }; }; diff --git a/backend/src/services/auth.service.ts b/backend/src/services/auth.service.ts index c62fd13..665738a 100644 --- a/backend/src/services/auth.service.ts +++ b/backend/src/services/auth.service.ts @@ -3,11 +3,11 @@ import { randomBytes } from 'crypto'; import { eq } from 'drizzle-orm'; import { JSDOM } from 'jsdom'; import jwt from 'jsonwebtoken'; -import { cas_validate_url } from '../../src/utils/secret'; +import { cas_validate_url } from '../shared/secrets/secrets'; import { db } from '../database/db'; import { userSchema } from '../schemas/Basic/user.schema'; import { registrationSchema } from '../schemas/Relational/registration.schema'; -import { jwtSecret } from '../utils/secret'; +import { jwtSecret } from '../shared/secrets/secrets'; import type { AuthTokenUser } from '../types/auth'; import * as role_service from './role.service'; import * as user_service from './user.service'; diff --git a/backend/src/services/discord.service.ts b/backend/src/services/discord.service.ts index 07602c4..a31452f 100644 --- a/backend/src/services/discord.service.ts +++ b/backend/src/services/discord.service.ts @@ -2,36 +2,38 @@ import axios from 'axios'; 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'; +import { discord_client_id, discord_client_secret, discord_redirect_uri } from '../shared/secrets/secrets'; 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, - client_secret: discord_client_secret, - grant_type: 'authorization_code', - code, - redirect_uri: discord_redirect_uri, - scope: 'identify' - }), { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }); + const tokenResponse = await axios.post( + 'https://discord.com/api/oauth2/token', + new URLSearchParams({ + client_id: discord_client_id, + client_secret: discord_client_secret, + grant_type: 'authorization_code', + code, + redirect_uri: discord_redirect_uri, + scope: 'identify', + }), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }, + ); const access_token = tokenResponse.data.access_token; // Étape 2 : récupère les infos utilisateur avec le token const userResponse = await axios.get('https://discord.com/api/users/@me', { headers: { - Authorization: `Bearer ${access_token}` - } + Authorization: `Bearer ${access_token}`, + }, }); //Etape 3 : Update le discord_id de l'user - await db.update(userSchema) - .set({ discord_id: userResponse.data.id, }) - .where(eq(userSchema.id, userId)); + await db.update(userSchema).set({ discord_id: userResponse.data.id }).where(eq(userSchema.id, userId)); return userResponse.data; // { id, username, discriminator, ... } }; diff --git a/backend/src/services/email.service.ts b/backend/src/services/email.service.ts index a345b5b..a4a5e65 100644 --- a/backend/src/services/email.service.ts +++ b/backend/src/services/email.service.ts @@ -1,9 +1,9 @@ import nodemailer from 'nodemailer'; -import type { EmailOptions, TemplateData } from '../../types/email'; +import type { EmailOptions, TemplateData } from '../types/email'; import { templateRenderers } from '../email/email.registry'; import { compileTemplate } from '../email/email.renderer'; import * as user_service from '../services/user.service'; -import { email_from, email_host, email_password, email_user } from '../utils/secret'; +import { email_from, email_host, email_password, email_user } from '../shared/secrets/secrets'; export const getRecipients = async ( recipientsGroups: string[] | undefined, diff --git a/backend/src/services/permanence.service.ts b/backend/src/services/permanence.service.ts index ae76a0b..aa9a36e 100644 --- a/backend/src/services/permanence.service.ts +++ b/backend/src/services/permanence.service.ts @@ -1,55 +1,56 @@ -import { and, eq, inArray, sql } from "drizzle-orm"; -import fs from "fs"; -import Papa from "papaparse"; -import type { PermanenceEmailData } from "../../types/email"; -import type { CsvPermanence, LightUser, Notification, Permanence } from "../../types/permanence"; -import { db } from "../database/db"; -import { AlreadyRegisteredError, PermanenceClosedError, PermanenceFullError, PermanenceNotFoundError, RegisterDeadlineError, UnauthorizedError, UnregisterDeadlineError } from "../errors/permanence.error"; -import { permanenceSchema } from "../schemas/Basic/permanence.schema"; -import { userSchema } from "../schemas/Basic/user.schema"; -import { respoPermanenceSchema, userPermanenceSchema } from "../schemas/Relational/userpermanences.schema"; -import { email_from } from "../utils/secret"; -import { generateEmailHtml, sendEmail } from "./email.service"; +import { and, eq, inArray, sql } from 'drizzle-orm'; +import fs from 'fs'; +import Papa from 'papaparse'; +import type { PermanenceEmailData } from '../types/email'; +import type { CsvPermanence, LightUser, Notification, Permanence } from '../types/permanence'; +import { db } from '../database/db'; +import { + AlreadyRegisteredError, + PermanenceClosedError, + PermanenceFullError, + PermanenceNotFoundError, + RegisterDeadlineError, + UnauthorizedError, + UnregisterDeadlineError, +} from '../errors/permanence.error'; +import { permanenceSchema } from '../schemas/Basic/permanence.schema'; +import { userSchema } from '../schemas/Basic/user.schema'; +import { respoPermanenceSchema, userPermanenceSchema } from '../schemas/Relational/userpermanences.schema'; +import { email_from } from '../shared/secrets/secrets'; +import { generateEmailHtml, sendEmail } from './email.service'; 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"); + if (!permanence) throw new PermanenceNotFoundError('Permanence introuvable'); return permanence; }; // ➕ S'inscrire à une permanence -export const registerUserToPermanence = async ( - userId: number, - permId: number -) => { +export const registerUserToPermanence = async (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"); + 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"); + if (!permanence.is_open) throw new PermanenceClosedError('Permanence not open'); - const limitDate = new Date(String(permanence.start_at).replace(/Z$/, "")); + const limitDate = new Date(String(permanence.start_at).replace(/Z$/, '')); const now = new Date(); if (now > limitDate) { - throw new RegisterDeadlineError("Too late to register"); + throw new RegisterDeadlineError('Too late to register'); } if (permanence.capacity == 0) { - throw new PermanenceFullError("Permanence full"); + throw new PermanenceFullError('Permanence full'); } // Transaction avec verrouillage de table complet @@ -66,7 +67,7 @@ export const registerUserToPermanence = async ( // 3. Si aucune ligne modifiée = pas de place disponible if (updateResult.length === 0) { - throw new PermanenceFullError("Permanence full"); + throw new PermanenceFullError('Permanence full'); } // 4. Insérer l'utilisateur (seulement si l'UPDATE a réussi) @@ -78,13 +79,13 @@ export const registerUserToPermanence = async ( } 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") + 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"); + throw new AlreadyRegisteredError('Already registered'); } // Re-lancer les autres erreurs throw error; @@ -92,27 +93,18 @@ export const registerUserToPermanence = async ( }; // ❌ Se désinscrire d'une permanence -export const unregisterUserFromPermanence = async ( - userId: number, - permId: number -) => { +export const unregisterUserFromPermanence = async (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"); + 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) - ) - ); + .where(and(eq(userPermanenceSchema.user_id, userId), eq(userPermanenceSchema.permanence_id, permId))); await modifyPermCap(permId, 1); }; @@ -134,7 +126,7 @@ export const createPermanence = async ( end_at: Date, capacity: number, difficulty: number, - respoId: number + respoId: number, ) => { // Étape 1 : Création de la permanence const [newPermanence] = await db @@ -162,19 +154,13 @@ export const createPermanence = async ( export const deletePermanence = async (permId: number) => { // Étape 1 : Supprimer les inscriptions des utilisateurs - await db - .delete(userPermanenceSchema) - .where(eq(userPermanenceSchema.permanence_id, permId)); + 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)); + await db.delete(respoPermanenceSchema).where(eq(respoPermanenceSchema.permanence_id, permId)); // Étape 3 : Supprimer la permanence - await db - .delete(permanenceSchema) - .where(eq(permanenceSchema.id, permId)); + await db.delete(permanenceSchema).where(eq(permanenceSchema.id, permId)); }; export const updatePermanence = async ( @@ -186,7 +172,7 @@ export const updatePermanence = async ( end_at: Date, capacity: number, difficulty: number, - respoId: number + respoId: number, ) => { // Étape 1 : Mise à jour de la permanence await db @@ -204,9 +190,7 @@ export const updatePermanence = async ( .where(eq(permanenceSchema.id, permId)); // Étape 2 : Suppression des anciens responsables (si nécessaire) - await db - .delete(respoPermanenceSchema) - .where(eq(respoPermanenceSchema.permanence_id, permId)); + await db.delete(respoPermanenceSchema).where(eq(respoPermanenceSchema.permanence_id, permId)); // Étape 3 : Ajout du nouveau responsable if (respoId) { @@ -219,18 +203,12 @@ export const updatePermanence = async ( // 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 @@ -238,12 +216,9 @@ export const modifyPermCap = async (permId: number, factor: number) => { 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 @@ -257,10 +232,7 @@ export const getMyPermanences = async (userId: number) => { location: permanenceSchema.location, }) .from(userPermanenceSchema) - .innerJoin( - permanenceSchema, - eq(permanenceSchema.id, userPermanenceSchema.permanence_id) - ) + .innerJoin(permanenceSchema, eq(permanenceSchema.id, userPermanenceSchema.permanence_id)) .where(eq(userPermanenceSchema.user_id, userId)); // Ajout du responsables @@ -281,10 +253,9 @@ export const getMyPermanences = async (userId: number) => { ...perm, respo: respo ?? null, }; - }) + }), ); - return results; }; @@ -308,10 +279,9 @@ export const getAllPermanences = async () => { ...perm, respo: respo ?? null, }; - }) + }), ); - return results; }; @@ -338,18 +308,10 @@ export const addUserToPermanence = async (userId: number, permId: number) => { await modifyPermCap(permId, -1); }; -export const removeUserToPermanence = async ( - userId: number, - permId: number -) => { +export const removeUserToPermanence = async (userId: number, permId: number) => { await db .delete(userPermanenceSchema) - .where( - and( - eq(userPermanenceSchema.user_id, userId), - eq(userPermanenceSchema.permanence_id, permId) - ) - ); + .where(and(eq(userPermanenceSchema.user_id, userId), eq(userPermanenceSchema.permanence_id, permId))); await modifyPermCap(permId, 1); }; @@ -377,16 +339,14 @@ export const getAllPermanencesWithUsers = async () => { ...permanence, users: userRelations, }; - }) + }), ); return results; }; -export const importPermanencesFromCSV = async ( - filePath: string -): Promise => { - const fileContent = fs.readFileSync(filePath, "utf8"); +export const importPermanencesFromCSV = async (filePath: string): Promise => { + const fileContent = fs.readFileSync(filePath, 'utf8'); const { data, errors } = Papa.parse(fileContent, { header: true, @@ -394,8 +354,8 @@ export const importPermanencesFromCSV = async ( }); if (errors.length > 0) { - console.error("CSV parsing errors:", errors); - throw new Error("Erreur lors du parsing du CSV."); + console.error('CSV parsing errors:', errors); + throw new Error('Erreur lors du parsing du CSV.'); } const parsedData = data.map((r) => ({ @@ -407,40 +367,26 @@ export const importPermanencesFromCSV = async ( 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, -): Promise => { - const respo = await db - .select() - .from(respoPermanenceSchema) - .where( - eq(respoPermanenceSchema.user_id, userId) - ); +export const isUserRespoOfPermanence = async (userId: number): Promise => { + const respo = await db.select().from(respoPermanenceSchema).where(eq(respoPermanenceSchema.user_id, userId)); 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 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");; + 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)); + 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( @@ -461,7 +407,7 @@ export const getPermanenceDetailsForRespo = async (respoId: number) => { permanence: perm, members, }; - }) + }), ); return results; @@ -471,15 +417,10 @@ export const claimMember = async (userId: number, permId: number, claimed: boole await db .update(userPermanenceSchema) .set({ claimed }) - .where( - and( - eq(userPermanenceSchema.user_id, userId), - eq(userPermanenceSchema.permanence_id, permId) - ) - ); + .where(and(eq(userPermanenceSchema.user_id, userId), eq(userPermanenceSchema.permanence_id, permId))); }; -export const getDailyNotifications = async (): Promise => { +export const getDailyNotifications = async (): Promise => { const permanences = await db.query.permanenceSchema.findMany({ where: sql` ${permanenceSchema.start_at} >= CURRENT_DATE + INTERVAL '1 day' @@ -489,7 +430,7 @@ export const getDailyNotifications = async (): Promise => { }); return getMembersFromPermanences(permanences); -} +}; export const getHourlyNotifications = async (): Promise => { const permanences = await db.query.permanenceSchema.findMany({ @@ -504,10 +445,14 @@ export const getHourlyNotifications = async (): Promise => { }; // Cette fonction est vouée à disparaitre lors du passage à Prisma, avec un simple "with" -export const getMembersFromPermanences = async (permanences: Permanence[]): Promise<{ - permanence: Permanence, - members: LightUser[] -}[]> => { +export const getMembersFromPermanences = async ( + permanences: Permanence[], +): Promise< + { + permanence: Permanence; + members: LightUser[]; + }[] +> => { return await Promise.all( permanences.map(async (perm) => { const members = await db @@ -523,57 +468,46 @@ export const getMembersFromPermanences = async (permanences: Permanence[]): Prom return { permanence: perm, - members: members + members: members, }; - }) + }), ); -} +}; -export const sendNotifications = async ( - notifications: Notification[] -) => { +export const sendNotifications = async (notifications: Notification[]) => { for (const notification of notifications) { - const permanenceEmailData: PermanenceEmailData = { permName: notification.permanence.name, - permBeginDate: - new Intl.DateTimeFormat("fr-FR", { - day: "2-digit", - month: "long", - }).format(notification.permanence.start_at), - permBeginHour: - new Intl.DateTimeFormat("fr-FR", { - hour: "2-digit", - minute: "2-digit", - }).format(notification.permanence.start_at), - permEndDate: - new Intl.DateTimeFormat("fr-FR", { - day: "2-digit", - month: "long", - }).format(notification.permanence.end_at), - permEndHour: - new Intl.DateTimeFormat("fr-FR", { - hour: "2-digit", - minute: "2-digit", - }).format(notification.permanence.end_at), + permBeginDate: new Intl.DateTimeFormat('fr-FR', { + day: '2-digit', + month: 'long', + }).format(notification.permanence.start_at), + permBeginHour: new Intl.DateTimeFormat('fr-FR', { + hour: '2-digit', + minute: '2-digit', + }).format(notification.permanence.start_at), + permEndDate: new Intl.DateTimeFormat('fr-FR', { + day: '2-digit', + month: 'long', + }).format(notification.permanence.end_at), + permEndHour: new Intl.DateTimeFormat('fr-FR', { + hour: '2-digit', + minute: '2-digit', + }).format(notification.permanence.end_at), permLocation: notification.permanence.location, - permDescription: notification.permanence.description - } - const subject = `[RAPPEL] Permanence - ${notification.permanence.name}` + permDescription: notification.permanence.description, + }; + const subject = `[RAPPEL] Permanence - ${notification.permanence.name}`; - const htmlEmail = generateEmailHtml( - "templateNotifyPermanenceReminder", - permanenceEmailData - ); + const htmlEmail = generateEmailHtml('templateNotifyPermanenceReminder', permanenceEmailData); for (const member of notification.members) { try { - const emailOptions = { from: email_from, to: [member.email], subject: subject, - text: "", + text: '', html: htmlEmail, }; diff --git a/backend/src/services/team.service.ts b/backend/src/services/team.service.ts index 9a66594..aba6cf5 100644 --- a/backend/src/services/team.service.ts +++ b/backend/src/services/team.service.ts @@ -8,7 +8,7 @@ import { teamShotgunSchema } from '../schemas/Relational/teamshotgun.schema'; import { userTeamsSchema } from '../schemas/Relational/userteams.schema'; import { getFaction } from './faction.service'; import * as user_service from '../services/user.service'; -import type { StudentRow, TeamMemberRow, TeamRow, TeamAssignmentNotification } from '../dto/team.dto'; +import type { StudentRow, TeamMemberRow, TeamRow, TeamAssignmentNotification } from '../types/team'; import sendEmailToNewAssignedStudents from './team/email.team'; import assignUsersToTeams from './team/assignation.team'; diff --git a/backend/src/services/team/assignation.team.ts b/backend/src/services/team/assignation.team.ts index 6e0e009..9d11b28 100644 --- a/backend/src/services/team/assignation.team.ts +++ b/backend/src/services/team/assignation.team.ts @@ -2,7 +2,7 @@ import { eq, inArray } from 'drizzle-orm'; import { db } from '../../database/db'; import { userSchema } from '../../schemas/Basic/user.schema'; import { userTeamsSchema } from '../../schemas/Relational/userteams.schema'; -import type { StudentRow, TeamRow, TeamDistributionState, TeamAssignmentNotification } from '../../dto/team.dto'; +import type { StudentRow, TeamRow, TeamDistributionState, TeamAssignmentNotification } from '../../types/team'; /** * Détermine si un(e) étudiant(e) est une fille. diff --git a/backend/src/services/team/email.team.ts b/backend/src/services/team/email.team.ts index a147162..8e6cf33 100644 --- a/backend/src/services/team/email.team.ts +++ b/backend/src/services/team/email.team.ts @@ -4,10 +4,10 @@ import { factionSchema } from '../../schemas/Basic/faction.schema'; import { teamSchema } from '../../schemas/Basic/team.schema'; import { teamFactionSchema } from '../../schemas/Relational/teamfaction.schema'; import { generateEmailHtml, sendEmail } from '../email.service'; -import type { TeamAssignmentNotification } from '../../dto/team.dto'; -import { email_from, email_concurrency } from '../../utils/secret'; -import type { TeamAssignmentEmailData } from '../../../types/email'; -import getPLimit from '../../utils/pLimit'; +import type { TeamAssignmentNotification } from '../../types/team'; +import { email_from, email_concurrency } from '../../shared/secrets/secrets'; +import type { TeamAssignmentEmailData } from '../../types/email'; +import getPLimit from '../../shared/utils/pLimit'; /** * Envoie un email de notification à chaque étudiant venant d'être diff --git a/backend/src/services/user.service.ts b/backend/src/services/user.service.ts index 11d9092..08844e3 100644 --- a/backend/src/services/user.service.ts +++ b/backend/src/services/user.service.ts @@ -2,40 +2,22 @@ import bcrypt from 'bcryptjs'; import { eq } from 'drizzle-orm'; import * as randomstring from 'randomstring'; import { db } from '../database/db'; // Import de la connexion PostgreSQL -import type { AdminCreateUserDto, CreateUserContactInformationDto, VssSubmissionPayload } from '../dto/user.dto'; +import type { AdminCreateUserBody, CreateUserContactInformationBody, VssSubmissionPayload } from '../dto/user.dto'; import { type User, userSchema } from '../schemas/Basic/user.schema'; import { vssqcmquestionSchema } from '../schemas/Basic/vssqcmquestion.schema'; import { vssqcmanswerSchema } from '../schemas/Relational/vssqcmanswer.schema'; import { registrationSchema } from '../schemas/Relational/registration.schema'; import * as auth_service from '../services/auth.service'; -import * as SIEP_Utils from '../utils/siep'; +import * as SIEP_Utils from '../shared/integrations/siep'; import * as Banned_Service from './banned.service'; import { createRegistrationToken } from './auth.service'; import { getFaction } from './faction.service'; import { getUserRoles } from './role.service'; import { getTeam, getTeamFaction, getUserTeam } from './team.service'; import { userInformationSchema } from '../schemas/Relational/userinformation.schema'; -import { addUserToRespondentStudentsList } from '../utils/billetweb'; +import { addUserToRespondentStudentsList } from '../shared/integrations/billetweb'; import { generateEmailHtml, sendEmail } from './email.service'; -import { email_from } from '../utils/secret'; - -export type VssQuestionnaireAnswer = { - id: number; - answer: string; -}; - -export type VssQuestionnaireQuestion = { - id: number; - question: string; - points: number; - type: 'single_choice' | 'multiple_choice'; - answers: VssQuestionnaireAnswer[]; -}; - -export type VssSubmissionAnswer = { - questionId: number; - answerIds: number[]; -}; +import { email_from } from '../shared/secrets/secrets'; // Fonction pour récupérer un utilisateur par email export const getUserByEmail = async (email: string) => { @@ -161,7 +143,7 @@ export const updateUserStudent = async (firstName: string, lastName: string, ema } }; -export const adminCreateUser = async (data: AdminCreateUserDto) => { +export const adminCreateUser = async (data: AdminCreateUserBody) => { const email = data.email.toLowerCase(); const userInDb = await getUserByEmail(email); @@ -268,7 +250,7 @@ export const getUserContactInformation = async (userId: number) => { } }; -export const createUserContactInformation = async (userId: number, contact: CreateUserContactInformationDto) => { +export const createUserContactInformation = async (userId: number, contact: CreateUserContactInformationBody) => { try { if (!contact.emergency_contact_name || !contact.emergency_contact_phone) { throw new Error("Le nom et le numéro de téléphone du contact d'urgence sont requis."); diff --git a/backend/src/utils/responses.ts b/backend/src/shared/http/responses.ts similarity index 100% rename from backend/src/utils/responses.ts rename to backend/src/shared/http/responses.ts diff --git a/backend/src/utils/billetweb.ts b/backend/src/shared/integrations/billetweb.ts similarity index 96% rename from backend/src/utils/billetweb.ts rename to backend/src/shared/integrations/billetweb.ts index a5135d8..9d5d86f 100644 --- a/backend/src/utils/billetweb.ts +++ b/backend/src/shared/integrations/billetweb.ts @@ -1,5 +1,5 @@ import axios from 'axios'; -import { api_billetweb_token, api_billetweb_url, api_billetweb_respondent_students_list_id } from './secret'; +import { api_billetweb_token, api_billetweb_url, api_billetweb_respondent_students_list_id } from '../secrets/secrets'; import type { BilletwebUser } from '../../types/billetweb'; const headers = { diff --git a/backend/src/utils/siep.ts b/backend/src/shared/integrations/siep.ts similarity index 78% rename from backend/src/utils/siep.ts rename to backend/src/shared/integrations/siep.ts index 1dd7100..2b5e941 100644 --- a/backend/src/utils/siep.ts +++ b/backend/src/shared/integrations/siep.ts @@ -1,22 +1,26 @@ import axios from 'axios'; -import { api_utt_admis_url_ismajor, api_utt_auth_url, api_utt_password, api_utt_username } from "./secret"; +import { api_utt_admis_url_ismajor, api_utt_auth_url, api_utt_password, api_utt_username } from '../secrets/secrets'; export const getTokenUTTAPI = async () => { try { - const response = await axios.post(api_utt_auth_url, { - login: api_utt_username, - password: api_utt_password, - }, { - headers: { - 'accept': 'application/json', - 'Content-Type': 'application/json', - } - }); + const response = await axios.post( + api_utt_auth_url, + { + login: api_utt_username, + password: api_utt_password, + }, + { + headers: { + accept: 'application/json', + 'Content-Type': 'application/json', + }, + }, + ); return response.data.token; } catch (error) { console.error('Error during POST request:', error); } -} +}; export const getNewStudentsFromUTTAPI_PAGE = async (token: string, date: string) => { const allNewStudents: any[] = []; @@ -25,7 +29,7 @@ export const getNewStudentsFromUTTAPI_PAGE = async (token: string, date: string) try { while (hasNextPage) { - const response = await axios.get(api_utt_admis_url_ismajor + date + "?page=" + currentPage, { + const response = await axios.get(api_utt_admis_url_ismajor + date + '?page=' + currentPage, { headers: { Authorization: `Bearer ${token}`, }, @@ -55,7 +59,6 @@ export const getNewStudentsFromUTTAPI_NOPAGE = async (token: string, date: strin const allNewStudents: any[] = []; try { - const response = await axios.get(api_utt_admis_url_ismajor + date, { headers: { Authorization: `Bearer ${token}`, diff --git a/backend/src/utils/secret.ts b/backend/src/shared/secrets/secrets.ts similarity index 100% rename from backend/src/utils/secret.ts rename to backend/src/shared/secrets/secrets.ts diff --git a/backend/src/utils/uploadDocuments.ts b/backend/src/shared/storage/uploadDocuments.ts similarity index 100% rename from backend/src/utils/uploadDocuments.ts rename to backend/src/shared/storage/uploadDocuments.ts diff --git a/backend/src/utils/pLimit.ts b/backend/src/shared/utils/pLimit.ts similarity index 100% rename from backend/src/utils/pLimit.ts rename to backend/src/shared/utils/pLimit.ts diff --git a/backend/src/utils/token.ts b/backend/src/shared/utils/token.ts similarity index 72% rename from backend/src/utils/token.ts rename to backend/src/shared/utils/token.ts index f77f74b..c2ea62f 100644 --- a/backend/src/utils/token.ts +++ b/backend/src/shared/utils/token.ts @@ -1,6 +1,6 @@ import { verify } from 'jsonwebtoken'; -import type { AuthTokenPayload } from '../types/auth'; -import { jwtSecret } from '../utils/secret'; +import type { AuthTokenPayload } from '../../types/auth'; +import { jwtSecret } from '../secrets/secrets'; export const decodeToken = (token: string): AuthTokenPayload | null => { if (!token) { diff --git a/backend/src/types/auth.ts b/backend/src/types/auth.d.ts similarity index 100% rename from backend/src/types/auth.ts rename to backend/src/types/auth.d.ts diff --git a/backend/types/billetweb.d.ts b/backend/src/types/billetweb.d.ts similarity index 100% rename from backend/types/billetweb.d.ts rename to backend/src/types/billetweb.d.ts diff --git a/backend/types/email.d.ts b/backend/src/types/email.d.ts similarity index 78% rename from backend/types/email.d.ts rename to backend/src/types/email.d.ts index 4e6c2fe..6688387 100644 --- a/backend/types/email.d.ts +++ b/backend/src/types/email.d.ts @@ -32,3 +32,14 @@ export interface TeamAssignmentEmailData extends TemplateData { } export type TemplatesRecordByNumber = Record; + +export type EmailPayload = { + subject: string; + templateName: string; + recipientsGroups?: string[]; + sendTo?: string[]; + html?: string; + title?: string; + content?: string; + text?: string; +}; diff --git a/backend/src/types/http.ts b/backend/src/types/http.d.ts similarity index 100% rename from backend/src/types/http.ts rename to backend/src/types/http.d.ts diff --git a/backend/types/permanence.d.ts b/backend/src/types/permanence.d.ts similarity index 100% rename from backend/types/permanence.d.ts rename to backend/src/types/permanence.d.ts diff --git a/backend/src/types/team.d.ts b/backend/src/types/team.d.ts new file mode 100644 index 0000000..01b0d0b --- /dev/null +++ b/backend/src/types/team.d.ts @@ -0,0 +1,35 @@ +export type TeamMemberRow = { + userId: number; +}; + +export type StudentRow = { + userId: number; + email: string; + branch: string; + male?: boolean; +}; + +export type TeamAssignmentNotification = { + email: string; + teamId: number; +}; + +export type TeamRow = { + teamId: number; + name: string; + description: string; + type: string; + socialLink: string; + riCompatible: boolean; +}; + +export type TeamDistributionState = TeamRow & { + size: number; + girlsCount: number; +}; + +export type TeamSizeRow = { + teamId: number; + teamName: string; + size: number; +}; diff --git a/backend/src/types/user.d.ts b/backend/src/types/user.d.ts new file mode 100644 index 0000000..4132e01 --- /dev/null +++ b/backend/src/types/user.d.ts @@ -0,0 +1,17 @@ +export type VssQuestionnaireAnswer = { + id: number; + answer: string; +}; + +export type VssQuestionnaireQuestion = { + id: number; + question: string; + points: number; + type: 'single_choice' | 'multiple_choice'; + answers: VssQuestionnaireAnswer[]; +}; + +export type VssSubmissionAnswer = { + questionId: number; + answerIds: number[]; +};