diff --git a/.github/workflows/create-docs.yml b/.github/workflows/create-docs.yml new file mode 100644 index 0000000..468df44 --- /dev/null +++ b/.github/workflows/create-docs.yml @@ -0,0 +1,31 @@ +name: Creates docs and deploy to gh-pages +on: + workflow_call: + workflow_dispatch: + push: + branches: [ main ] + +jobs: + build: + name: Deploy docs + runs-on: ubuntu-latest + steps: + - name: Checkout main + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v5 + + - name: Install dependencies + working-directory: ./documentation + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Build docs + working-directory: ./documentation + run: mkdocs build --verbose --clean + + - name: Push docs to gh-pages + working-directory: ./documentation + run: python deploy.py \ No newline at end of file diff --git a/.gitignore b/.gitignore index 41bbba6..551aa14 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,7 @@ web_modules/ .yarn-integrity # dotenv environment variable files -.env +server/.env .env.development.local .env.test.local .env.production.local @@ -130,4 +130,4 @@ dist .pnp.* db-backup/ -deployments \ No newline at end of file +.venv \ No newline at end of file diff --git a/documentation/.gitignore b/documentation/.gitignore new file mode 100644 index 0000000..d97e100 --- /dev/null +++ b/documentation/.gitignore @@ -0,0 +1 @@ +site \ No newline at end of file diff --git a/documentation/deploy.py b/documentation/deploy.py new file mode 100644 index 0000000..abdc3c4 --- /dev/null +++ b/documentation/deploy.py @@ -0,0 +1,2 @@ +from ghp_import import ghp_import +ghp_import('site', push=True, force=True) \ No newline at end of file diff --git a/documentation/docs/developpeur/backend/api.md b/documentation/docs/developpeur/backend/api.md new file mode 100644 index 0000000..faa9ab4 --- /dev/null +++ b/documentation/docs/developpeur/backend/api.md @@ -0,0 +1,12 @@ +## À Propos + +Ce projet utilise Node.js Express pour créer un backend simple pour l'application. + +## Routes API + +Vous pouvez consulter toutes les routes utilisables du backend ici + +* User : https://documenter.getpostman.com/view/32663805/2sA2rCU28v#e942a4f4-321c-465b-bf88-e6c1f1d6f6c8 +* Quiz : https://documenter.getpostman.com/view/32663805/2sA2rCU28v#732d980b-02fd-4807-b5bc-72725098b9b0 +* Folders : https://documenter.getpostman.com/view/32663805/2sA2rCU28v#49ecd432-ccfc-4c8a-8390-b3962f0d5fd7 +* Images : https://documenter.getpostman.com/view/32663805/2sA2rCU28v#58382180-d6f0-492d-80c3-e09de1c368b8 \ No newline at end of file diff --git a/documentation/docs/developpeur/backend/auth.md b/documentation/docs/developpeur/backend/auth.md new file mode 100644 index 0000000..82b72c9 --- /dev/null +++ b/documentation/docs/developpeur/backend/auth.md @@ -0,0 +1,372 @@ +# Authentification + +## Introduction + +Le but du module d'authentification est de pouvoir facilement faire des blocks de code permettant une authentification personalisée. Il est possible de le faire grâce a cette architecture. Pour la première version de cette fonctionalitée, l'introduction de OICD et de OAuth sont priorisé ainsi que la migration du module d'authentification simple. + + +## Déconstruction simple de la structure +La structure est la suivante : + +Le AuthManager s'occupe de centraliser les requetes d'authentifications. Ce qui veux dire d'initialiser les autres modules et d'être la source de véritée dans au sujet de l'authentification. Les modules sont automatiquement chargé par l'utilisation de variables d'environment. + +Le module s'occupe de creer les routes nécéssaires pour son fonctionnement et de créer les utilisateurs. Ces modules vont appeller le AuthManager afin de confirmer leurs actions avec le login/register de celui-ci + +Dans le cas de modules plus complexe, tels que le module Passport, la chaine peut être prolongée afin de maintenir centralisée les actions. Chaque connecteur de PassportJs est initialisé par le module de PassportJs. + + +## Besoins exprimés + + +Modularité et généricité : + +- Le système d'authentification doit être adaptable à diverses configurations, notamment pour répondre aux exigences spécifiques des différentes universités ou institutions. + +Utilisation de différentes méthodes d'authentification : + +- L'application doit permettre de gérer plusieurs fournisseurs d'authentification (SSO, LDAP, OAuth, etc.) de manière centralisée et flexible. + +Facilité de configuration : + +- Le système doit permettre une configuration simple et flexible, adaptée à différents environnements (développement, production, etc.). + +Gestion des permissions : + +- Il doit être possible de définir et de mapper facilement les permissions et les rôles des utilisateurs pour sécuriser l’accès aux différentes fonctionnalités de l’application. + +Maintien de la connexion : + +- Le système doit garantir la persistance de la connexion pendant toute la durée de l'utilisation de l'application (exemple : quiz), avec la possibilité de se reconnecter sans perte de données en cas de déconnexion temporaire. + +## Recis utilisateurs pris en comptes + +- En tant qu'utilisateur de projet FOSS, je veux que le module d'authentifcation soit modulaire et générique afin de l'adapter a mes besoins. +- En tant qu'administrateur, je veux que les droits des utilisateurs soient inférés par l'authentificateur de l'établissement. +- En tant qu'administrateur, je veux que la configuration des authentificateur soit simple +- En tant qu'administrateur, je veux configurer les connections a partir de variables d'env ou fichier de config. +- En tant qu'utilisateur, je veux que ma connexion soit stable. +- En tant qu'utilisateur, je veux pouvoir me reconnecter a une salle s'il y arrive un problème de connexion. + +## Diagrammes + +### Structure +```plantuml +@startuml + +package Backend { +class AuthManager{ + +IAuthModule[] auths + #userInfos + + -load() + -registerAuths() + +showAuths() + + +authStatus() + +logIn(UserInfos) + +register(UserInfos) + +logOut() +} + +interface IAuthModule{ + +registerAuth() + +authenticate() + +register() + +showAuth() +} + +class SimpleFormAuthModule{ + +} + +class PassportAuthModule{ + IPassportProviderDefinition[] providers +} + +Interface IPassportProviderDefinition{ + +name + +type +} + +class OAuthPassportProvider{ + +clientId + +clientSecret + +configUrl + +authorizeUrl + +tokenUrl + +userinfoUrl + +logoutUrl + +JWKSUrl +} + +IAuthModule <|-- SimpleFormAuthModule +IAuthModule <|-- PassportAuthModule +IPassportProviderDefinition <|-- OAuthPassportProvider + +AuthManager -> IAuthModule +PassportAuthModule -> IPassportProviderDefinition +} + +package Frontend{ + class AuthDrawer{ + +IAuthVisual[] getAuthsVisual() + +drawAuths() + } + + Interface IAuthVisual{ + +draw() + } + + class FormVisual{ + +FormInput[] formInputs + } + + interface FormInput{ + +name + +label + +type + +value + } + + AuthDrawer -> IAuthVisual + IAuthVisual <|-- FormVisual + FormVisual -> FormInput +} + +@enduml +``` + + +### Explication des communications : Passport Js +```plantuml +@startuml + +box "Frontend" +participant User +Participant App +end box + +box "Backend" +participant PassportAuthModule +participant Db +participant AuthManager +end box + +box "Auth Server" +participant AuthServer +end box + +User -> App : Get auth page +App -> User : auth page + +User -> App : click OAuth button +App -> User : redirect to OAuth + +User -> AuthServer: Login +AuthServer -> User: Redirect to Auth endpoint with token + +User -> PassportAuthModule: Authenticate with token + +PassportAuthModule -> AuthServer: get user info +AuthServer -> PassportAuthModule: userInfo + +alt login + PassportAuthModule -> Db : fetch local userInfo + Db->PassportAuthModule: userInfo + PassportAuthModule -> PassportAuthModule: Merge userInfo definition + PassportAuthModule -> Db : update user profile + Db->PassportAuthModule: userInfo +end + +alt register + PassportAuthModule -> Db : fetch local userInfo + Db->PassportAuthModule: null + PassportAuthModule -> Db : create user profile + Db->PassportAuthModule: userInfo +end + +PassportAuthModule -> AuthManager : login(userInfos) + +AuthManager -> User: Give refresh token + Redirect to page +User -> App: get / +App -> User: Show Authenticated / +@enduml +``` + +### Explication des communications : SimpleAuth +```plantuml +@startuml + +box "Frontend" +participant User +Participant App +end box + +box "Backend" +participant SimpleAuthModule +participant Db +participant AuthManager +end box + +User -> App : Get auth page +App -> User : auth page + + +alt Login + User -> App : Send Login/Pass + + App -> SimpleAuthModule: Send login/pass + + SimpleAuthModule -> Db: get user info + Db->SimpleAuthModule: user info + SimpleAuthModule -> SimpleAuthModule: Validate Hash +end + +alt register + User -> App : Send Username + Password + Email + + App -> SimpleAuthModule: Send Username + Password + Email + + SimpleAuthModule -> Db: get user info + Db -> SimpleAuthModule : null + + SimpleAuthModule -> Db: put user info +end + +SimpleAuthModule -> AuthManager: userInfo +AuthManager -> User: Give refresh token + Redirect to page +User -> App: get / +App -> User: Show Authenticated / +@enduml +``` + +### Comment les boutons sont affichés +```plantuml +@startuml + +box "FrontEnd" +participant User +Participant FrontEnd +Participant AuthDrawer +end box + +box "BackEnd" +participant API +participant AuthManager +participant Db +participant IAuthModule +end box + +API -> API : load global configurations + +create AuthManager +API -> AuthManager : instanciate with auth configurations + + +create IAuthModule +AuthManager -> IAuthModule : instanciate array + +loop For each auth in auths + AuthManager -> IAuthModule : register + IAuthModule -> API : register routes + API -> IAuthModule : route registration confirmation + IAuthModule -> AuthManager : module registration confirmation +end + +User -> FrontEnd : get login page + +alt already logged in + FrontEnd -> User: redirected to authenticated page +end + +FrontEnd -> AuthDrawer : get auth visual +AuthDrawer -> API : get auth form data + +API -> AuthManager : get auth form data + + +loop For each auth in auths + AuthManager -> IAuthModule : get form data + IAuthModule -> AuthManager : form data +end + +AuthManager -> API : auth fom data +API -> AuthDrawer : auth form data + +AuthDrawer -> AuthDrawer : make auth html +AuthDrawer -> FrontEnd : auth HTML +FrontEnd -> User : show auth page + + +@enduml +``` + +### Comment les sessions sont conservées +```plantuml +@startuml + box "Frontend" + participant User + Participant App + end box + + box "Backend" + participant AuthManager + participant IAuthModules + end box + + App -> AuthManager : send refresh token + + AuthManager -> IAuthModules: ForEach check if logged + IAuthModules -> AuthManager: is authenticated ? + + alt one logged in + AuthManager -> App : send new token + end + + alt all logged out + AuthManager -> App : send error + App -> App : destroy token + App -> User : redirect to login page + end + +@enduml +``` + +## Configuration des variables d'environnement + +Example de configuration du fichier : `server/auth_config.json` : + +```json +{ + "auth": { + "passportjs": // Module + [ + { + "gmatte": { // Nom du sous-module Passport + "type": "oauth", // type + "OAUTH_AUTHORIZATION_URL": "https://auth.gmatte.xyz/application/o/authorize/", + "OAUTH_TOKEN_URL": "https://auth.gmatte.xyz/application/o/token/", + "OAUTH_USERINFO_URL": "https://auth.gmatte.xyz/application/o/userinfo/", + "OAUTH_CLIENT_ID": "--redacted--", + "OAUTH_CLIENT_SECRET": "--Redacted--", + "OAUTH_ADD_SCOPE": "groups", // scopes supplémentaire nécéssaire pour le pivot + "OAUTH_ROLE_TEACHER_VALUE": "groups_evaluetonsavoir-prof", // valeur de pivot afin de définir un enseignant + "OAUTH_ROLE_STUDENT_VALUE": "groups_evaluetonsavoir" // valeur de pivot afin de définir un étudiant + } + }, + { + "etsmtl":{ + "type":"oidc", + "OIDC_CONFIG_URL":"https://login.microsoftonline.com/70aae3b7-9f3b-484d-8f95-49e8fbb783c0/v2.0/.well-known/openid-configuration", + "OIDC_CLIENT_ID": "--redacted--", + "OIDC_CLIENT_SECRET": "--redacted--", + "OIDC_ADD_SCOPE": "", + "OIDC_ROLE_TEACHER_VALUE": "groups_evaluetonsavoir-prof", + "OIDC_ROLE_STUDENT_VALUE": "groups_evaluetonsavoir" + } + } + ], + "simpleauth":{} + } +} +``` \ No newline at end of file diff --git a/documentation/docs/developpeur/backend/base-de-donnees.md b/documentation/docs/developpeur/backend/base-de-donnees.md new file mode 100644 index 0000000..52bdb12 --- /dev/null +++ b/documentation/docs/developpeur/backend/base-de-donnees.md @@ -0,0 +1,11 @@ +# Type de base de données +La base de données est une mongoDB + +# Collections disponibles +* Files : Ceci est la collection qui contient les différents quiz et leurs questions. +* Folders : Ceci est la collection qui contient les dossiers qui servent à la gestion des différents quiz +* Images : C'est dans cette collection que sont stockées les images utilisées dans les quiz +* Users : Cette collection est utilisée pour la gestion des utilisateurs + +# Information sur la création +Lors du démarrage du projet, la base de données est créée automatiquement. diff --git a/documentation/docs/developpeur/backend/katex.md b/documentation/docs/developpeur/backend/katex.md new file mode 100644 index 0000000..73a6e5d --- /dev/null +++ b/documentation/docs/developpeur/backend/katex.md @@ -0,0 +1,39 @@ +# KaTeX + +KaTeX est le module qui s'occupe de formater les formules mathématiques selon la configuration donnée. + +Les formules entourées de $$ s'afficheront centrées sur leur propre ligne + +`.replace(/\$\$(.*?)\$\$/g, (_, inner) => katex.renderToString(inner, { displayMode: true }))` + +alors que les formules entourées de $ s'afficheront sur la même ligne + +`.replace(/\$(.*?)\$/g, (_, inner) => katex.renderToString(inner, { displayMode: false }))` + +La configuration du formatage peut être trouvée dans le fichier TextType.ts situé dans le dossier EvalueTonSavoir/client/src/components/GiftTemplate/templates + +C'est aussi dans ce fichier que le format markdown est pris en charge. + +## Éditeur de quiz +Pour l'affichage dans l'éditeur de quiz, on peut retrouver la classe TextType être appliquée sur différents éléments du dossier templates, par exemple la classe Numerical.ts. + +On peut voir ici que le TextType est appliqué sur le contenu de la question: +```typescript +
${TextType({text: stem })}
+``` + +Selon ce qui avait été écrit dans la question, la classe s'occupera de formatter les bonnes sections. + +## Affichage de questions + +Le module React-latex était utilisé pour le formatage des questions durant un quiz, mais cela a apporté le problème de disparité d'affichage entre la création et l'affichage des questions avec des formules mathématiques. +Les classes affichant les questions durant un quiz peuvent utiliser ce format, mais avec une manipulation de plus. + +Les variables contenant la question doivent d'abord avoir un type TextFormat pour pouvoir faire appel à la classe qui s'occupe du format sous le module KaTeX. +Puis, étant sur un environnement React, il faut utiliser la propriété dangerouslySetInnerHTML pour afficher la question correctement. + + +` + ` + +Ce type de manipulation peut être utilisé dans d'autre environnement React si on veut éviter d'utiliser React-latex. \ No newline at end of file diff --git a/documentation/docs/developpeur/backend/quiz.md b/documentation/docs/developpeur/backend/quiz.md new file mode 100644 index 0000000..ce3c666 --- /dev/null +++ b/documentation/docs/developpeur/backend/quiz.md @@ -0,0 +1,54 @@ +# Example de Quiz + +```gift +//-----------------------------------------// +// Examples from gift/format.php. +//-----------------------------------------// + +Who's buried in Grant's tomb?{~Grant ~Jefferson =no one} + +Grant is {~buried =entombed ~living} in Grant's tomb. + +Grant is buried in Grant's tomb.{FALSE} + +Who's buried in Grant's tomb?{=no one =nobody} + +When was Ulysses S. Grant born?{#1822:5} + +Match the following countries with their corresponding capitals. { + =Canada -> Ottawa + =Italy -> Rome + =Japan -> Tokyo + =India -> New Delhi + ####It's good to know the capitals +} + +//-----------------------------------------// +// More complicated examples. +//-----------------------------------------// + +::Grant's Tomb::Grant is { + ~buried#No one is buried there. + =entombed#Right answer! + ~living#We hope not! +} in Grant's tomb. + +Difficult multiple choice question.{ + ~wrong answer #comment on wrong answer + ~%50%half credit answer #comment on answer + =full credit answer #well done!} + +::Jesus' hometown (Short answer ex.):: Jesus Christ was from { + =Nazareth#Yes! That's right! + =%75%Nazereth#Right, but misspelled. + =%25%Bethlehem#He was born here, but not raised here. +}. + +//this comment will be ignored by the filter +::Numerical example:: +When was Ulysses S. Grant born? {# + =1822:0 #Correct! 100% credit + =%50%1822:2 #He was born in 1822. + You get 50% credit for being close. +} +``` \ No newline at end of file diff --git a/documentation/docs/developpeur/backend/salle-de-quiz-swagger.json b/documentation/docs/developpeur/backend/salle-de-quiz-swagger.json new file mode 100644 index 0000000..2084a39 --- /dev/null +++ b/documentation/docs/developpeur/backend/salle-de-quiz-swagger.json @@ -0,0 +1,146 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Room API" + }, + "servers":[ + { + "url": "http://localhost", + "description": "Via Docker" + }, + { + "url": "http://localhost:3000", + "description": "Via npm" + } + ], + "security": [ + { + "bearerAuth": [] + } + ], + "paths": { + "/api/room": { + "get": { + "summary": "Get all rooms", + "description": "Returns a list of rooms", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Room" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a new room", + "description": "Creates a new room, returns the created room", + "responses": { + "200": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Room" + } + } + } + } + } + } + }, + "/api/room/{roomId}": { + "get": { + "summary": "Get a room by id", + "description": "Returns a room by id", + "parameters": [ + { + "name": "roomId", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Room" + } + } + } + } + } + }, + "delete": { + "summary": "Delete a room by id", + "description": "Deletes a room by id", + "parameters": [ + { + "name": "roomId", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, + "schemas": { + "Room": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "default": "autoincrement" + }, + "name": { + "type": "string" + }, + "host": { + "type": "string" + }, + "nbStudents": { + "type": "integer", + "default": 0 + }, + "mustBeCleaned": { + "type": "boolean", + "default": false + } + }, + "required": [ + "id", + "name", + "host" + ] + } + } + } + } \ No newline at end of file diff --git a/documentation/docs/developpeur/backend/salle-de-quiz.md b/documentation/docs/developpeur/backend/salle-de-quiz.md new file mode 100644 index 0000000..d1adc03 --- /dev/null +++ b/documentation/docs/developpeur/backend/salle-de-quiz.md @@ -0,0 +1,185 @@ +# Salles de Quiz + +## Introduction + +Les salles de quiz ont été extraites dans leur propre container afin de limiter les dégats liés soit a une surutilisation d'une salle ou une attaque sur le logiciel. + +En éffet, le découplement permet a un quiz de: + + - Survivre même si le backend est non-fonctionnel + - Mourir sans entrainer toute l'application avec elle + - Créer/Supprimer des salles automatiquement dépendant de la demande + + Pour éffectuer ceci il faut éffectuer une petite gymnastique. Il y a une route dans l'api servant à gerer les salles. Lorsqu'un utilisateur demande le socket d'une salle : "/api/rooms/{id}/socket", la requette rebondit sur le proxy Nginx. Celui-ci contacte le backend afin d'obtenir l'addresse de l'ordinateur auquel envoyer la requette et redirige le socket vers ce pc. + +## Déconstruction simple de la structure + +Un module supplémnetaire a été ajouté à la structure : Rooms. + +L'objet `room` est la définition d'une salle de façon minimaliste, cette définission est aggrandie avec l'information récotlé du "provider". +Le `provider` est le système gérant les différentes salles. Dans l'implémentation éffectuée, il s'agit de docker. + +Lorsque l'api des salles est instantié, celui-ci est lié avec un "provider", définissant comment les salles seront créées. +L'api des salles permet de les ajouter, les supprimer, et les consulter les salles. + +L'api lance deux "jobs": + +- Une vérification de l'état de santé des salles. Celle-ci roule tous les 10 secondes et met a jour les salles. +- Une suppression des salles. Celle-ci roule tous les 30 secondes et supprimme automatiquement les salles ayant la mention de suppression. + +## Besoins exprimés + +Fiabilite : + +- Nous voulons s'assurer qu'il soit possible d'avoir un grand nombre d'élèves présent sans qu'il y ait des problèmes de déconnexions +- Nous voulons que le temps de réponse soit bas +- Nous voulons que le système soit capable de fonctionner de facon indépendante + +## Recis utilisateurs pris en comptes + +- En tant qu'enseignant, je veux que tous mes élèves soient capable de se connecter a la salle de classe rapidement +- En tant qu'enseignant, je veux que la salle de quiz puisse survivre des pannes liées aux autres modules de l'aplication +- En tant qu'administrateur, je veux que les salles soient indépendantes et qui ne touche pas aux performances des autres salles +- En tant qu'administrateur, je veux que les salles puissent être hébergées séparément du projet + +## Diagrammes + +### Structure +```plantuml +@startuml +class Room{ + +id + +name + +host + +nbStudents + +mustBeCleaned +} + +class RoomRepository { + +get(id) + +create(room) + +delete(id) + +update(room,id) + +getAll() +} + +class RoomController { + +setupRoom(options) + +deleteRoom(roomId) + +listRooms() + +getRoomStatus(roomId) + +updateRoom(room,roomId) +} + +class RoomRouter{ + + / : GET + + /:id : GET + + / : POST + + /:id : PUT + + /:id : DELETE +} + +class BaseRoomProvider { + +createRoom(roomid,options) + +deleteRoom(roomId) + +getRoomInfo(roomId) + +getRoomStatus(roomId) + +listRooms() + -cleanup() + -syncInstantiatedRooms() + #updateRoomInfos() +} + +class DockerRoomProvider +circle Dockerode + + +Room - RoomRepository +BaseRoomProvider o-- RoomRepository +DockerRoomProvider --|> BaseRoomProvider +DockerRoomProvider - Dockerode +Dockerode o-- QuizRoom +RoomController o-- BaseRoomProvider +RoomRouter o-- RoomController + +class QuizRoom{ + +/health: GET + +create-room() + +join-room() + +next-question() + +launch-student-mode() + +end-quiz() + +submit-answers() + -disconnect() +} +@enduml +``` +Remarque: Les signatures de fonctions semblent un peu partout car il y a des fonctions de classes standard, des appels HTTPS et des appels de sockets dans le même diagramme. + +### Diagramme de séquence démontrant les communications +```plantuml +@startuml + actor Teacher + actor Student + entity Nginx + entity Frontend + entity Api + entity Docker + entity Database + +group Quiz Creation + Teacher -> Frontend : Create a quizroom + Frontend -> Api : Create a quizroom + Api -> Docker : Create a quizroom + Docker -> QuizRoom ** + QuizRoom -> Docker : creation successful + Docker -> Api : Creation Successful + + loop every seconds until healthy or 30s: + Api -> QuizRoom : Checking Health via /health + QuizRoom -> Api : Doesn't answer, answer healthy or unhealthy + end + + Api -> Database : Create Room + Database -> Api : Room created + Api -> Teacher : Route to room socket +end + +group Quiz Joining: + Teacher -> Nginx : Join Room + Nginx -> Api : Get room infos from id + Api -> Nginx : Ip:port of room + Nginx -> QuizRoom: Give teacher's connexion + + Student -> Frontend: Join Room X + Frontend -> Nginx : Join Room X + Nginx -> Api : Get room infos from id + Api -> Nginx : Ip:port of room + Nginx -> QuizRoom: Give student's connexion + + QuizRoom -> QuizRoom : Give Quiz ... (Multiple actions) + + Student -> QuizRoom: Disconnect + Teacher -> QuizRoom: Disconect +end + +group QuizManagement (Every 10 seconds) + Api -> QuizRoom : Checking number of people in the room + QuizRoom -> Api : Number of people (0) or Unhealthy + Api -> Database : Mark room to deletion +end + +group Quiz Deletion (Every 30 seconds) + Api -> Database : Give all rooms marked for deletion + Database -> Api : rooms + Api -> Docker : delete rooms + Docker -> QuizRoom : delete + Docker -> Api : Deleted +end + +@enduml +``` + +## API + +