Merge branch 'master' of https://codefirst.iut.uca.fr/git/Crypteam/Cryptid into deducGrid
@ -0,0 +1,34 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: node:20
|
||||
commands:
|
||||
- cd cryptide_project
|
||||
- npm install --legacy-peer-deps
|
||||
- CI=false npm run build
|
||||
|
||||
- name: code-analysis
|
||||
image: node:20
|
||||
environment:
|
||||
SONAR_TOKEN:
|
||||
from_secret: SECRET_SONAR_LOGIN_CRYPTIDE
|
||||
commands:
|
||||
- export SONAR_SCANNER_VERSION=4.7.0.2747
|
||||
- export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux
|
||||
- curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
|
||||
- unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
|
||||
- export PATH=$SONAR_SCANNER_HOME/bin:$PATH
|
||||
- export SONAR_SCANNER_OPTS="-server"
|
||||
- sonar-scanner -D sonar.projectKey=Cryptid -D sonar.sources=. -D sonar.host.url=https://codefirst.iut.uca.fr/sonar
|
||||
secrets: [SECRET_SONAR_LOGIN_CRYPTIDE]
|
||||
settings:
|
||||
sonar_host: https://codefirst.iut.uca.fr/sonar/
|
||||
sonar_token:
|
||||
from_secret: SECRET_SONAR_LOGIN_CRYPTIDE
|
@ -1,2 +1,110 @@
|
||||
# Cryptid
|
||||
(:construction: ***Le readme n'est pas a sa version final et est susceptible de changer*** :construction:)
|
||||
|
||||
# Présentation :
|
||||
|
||||
Bienvenue dans notre jeu de déduction captivant, où l'intrigue et la malice se rejoignent dans une aventure palpitante ! Plongez-vous dans un monde de mystère et d'intrigue, où chaque interaction compte, et chaque indice vous rapproche de la vérité.Imaginez un graphique complexe où chaque sommet représente une personne, chaque axe une relation, et chaque détail compte. Vous êtes plongé dans un défi stimulant pour découvrir qui parmi ces individus est le mystérieux tueur. Chaque joueur détient un indice crucial, et seul le partage stratégique de ces indices vous mènera à la résolution du mystère. Explorez notre page de règles pour comprendre les subtilités du jeu, découvrez les indices qui peuvent vous guider, et élaborez des stratégies intelligentes pour identifier le coupable. Manipuler vos amis, afin d'être le premier à découvrir qui est le meurtrier ! Êtes-vous prêt à relever le défi et à démasquer le tueur caché dans le graphe ? Que l'enquête commence !
|
||||
|
||||
## Comment utiliser notre projet :
|
||||
|
||||
*Prérequis :* Avoir **git** et **npm** (et donc **Node.js**) sur son poste.
|
||||
|
||||
### Etape 1 : Récupérer le projet
|
||||
Dans un premier terminal, exécutez les commandes suivantes :
|
||||
```bash
|
||||
git clone https://codefirst.iut.uca.fr/git/Crypteam/Cryptid.git
|
||||
cd cryptide_project
|
||||
npm i --force
|
||||
```
|
||||
|
||||
### Etape 2 : Configurer le réseau
|
||||
|
||||
1. Pour jouer en distant, vous devez vous trouver sur le même réseau. (Il faut quand même éviter les réseaux tels qu' eduroam)
|
||||
|
||||
2. Récupérez votre adresse IP :
|
||||
|
||||
Sur **Windows**
|
||||
> - Ouvrez l'invite de commandes (`cmd`) et tapez la commande `ipconfig`. Repérez la section de votre connexion sans fil et notez l'adresse IPv4.
|
||||
|
||||
Sur **MacOS** / **Linux**
|
||||
> - Ouvrez le terminal et tapez la commande `ifconfig` ou `ip addr`. Recherchez la section de votre connexion sans fil et notez l'adresse IP.
|
||||
|
||||
### Etape 3 : Configurer les serveurs
|
||||
|
||||
1. **Ouvrez le fichier `./src/AdressSetup.ts` :**
|
||||
- Localisez le fichier dans le répertoire de votre application.
|
||||
|
||||
2. **Modifiez l'adresse des serveurs :**
|
||||
- Remplacez les adresses IP existantes par celle que vous avez notée à l'étape 2.
|
||||
|
||||
Exemple :
|
||||
```typescript
|
||||
// ./AdressSetup.ts
|
||||
const ADRESSE_WEBSERVER = "http://{VOTRE_IP}:3002"
|
||||
const ADRESSE_DBSERVER = "http://{VOTRE_IP}:3003"
|
||||
const ADRESSE_WEBSITE = ""
|
||||
|
||||
export {ADRESSE_DBSERVER, ADRESSE_WEBSERVER, ADRESSE_WEBSITE}
|
||||
```
|
||||
|
||||
3. **Ouvrez le fichier `./server/server.js` :**
|
||||
- Localisez le fichier dans le répertoire de votre application.
|
||||
|
||||
4. **Modifiez les adresses qui peuvent accèder aux serveurs :**
|
||||
- Ajoutez votre adresse notée à l'étape 2 dans le cors.
|
||||
|
||||
Exemple :
|
||||
```typescript
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
const io = socketIO(server, {
|
||||
cors: {
|
||||
origin: ["http://{VOTRE_IP}:3000", "http://localhost:3000"], // Remplacez par l'URL de votre application React
|
||||
methods: ["GET", "POST"],
|
||||
credentials: true
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
5. **Apportez la même modification au fichier `./src/server/server.js` :**
|
||||
- Une fois le fichier ouvert, appuyez-vous sur l'exemple précédent pour apporter les modifications necéssaire.
|
||||
|
||||
### Etape 4 : Démarrer les serveurs
|
||||
|
||||
1. **Ouvrez un second terminal :**
|
||||
- Exécutez le script `./startServer.sh`.
|
||||
|
||||
### Etape 5 : Démarrer l'application
|
||||
|
||||
1. Revenez dans le premier terminal et exécutez la commande suivante :
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
2. L'application va s'ouvrir dans votre navigateur et vous pouvez jouez à notre jeu.
|
||||
|
||||
### Etape bonus : Jouez avec ces amis
|
||||
|
||||
1. Sur la première page de l'application, cliquez sur **Jouez** > **Créer une partie**.
|
||||
2. Une fois dans le lobby, **copiez le lien** de la partie et l'envoyez à vos amis.
|
||||
3. Lancez la partie et **amusez-vous** !
|
||||
|
||||
|
||||
# ::construction_worker:: Développeurs
|
||||
|
||||
- Thomas Chazot : thomas.chazot@etu.uca.fr
|
||||
- Pierre Ferreira : pierre.ferreira@etu.uca.fr
|
||||
- Baptiste Marcel : baptise.marcel@etu.uca.fr
|
||||
|
||||
<div align="center">
|
||||
<a href = "https://codefirst.iut.uca.fr/git/thomas.chazot2">
|
||||
<img src="https://codefirst.iut.uca.fr/git/avatars/4ce5054250e693b04367d853b3c469b1?size=870" width="50" >
|
||||
</a>
|
||||
<a href = "https://codefirst.iut.uca.fr/git/pierre.ferreira">
|
||||
<img src="https://codefirst.iut.uca.fr/git/avatars/edbacace5f621ae77077f206ebdcee27?size=870" width="50" >
|
||||
</a>
|
||||
<a href = "https://codefirst.iut.uca.fr/git/baptiste.marcel">
|
||||
<img src="https://codefirst.iut.uca.fr/git/avatars/6b1f2a8b8f636d8f4d315b060075578f?size=870" width="50" >
|
||||
</a>
|
||||
|
||||
© IUT - Auvergne
|
||||
</div>
|
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Vérifier si l'adresse IP est fournie en tant que paramètre
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <adresse_ip>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Stocker l'adresse IP fournie en tant que variable
|
||||
adresse_ip="$1"
|
||||
|
||||
# Utiliser l'adresse IP dans la commande find avec Perl
|
||||
find . -type f -exec perl -pi -e 's|http://[0-9.]+:([0-9]+)|http://localhost:$1|g' {} +
|
||||
|
||||
find . -type f -exec perl -pi -e "s|http://localhost:([0-9]+)|http://$adresse_ip:\$1|g" {} +
|
@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
if lsof -Pi :3000 -sTCP:LISTEN -t >/dev/null; then
|
||||
# Tuer le processus associé au port
|
||||
pid=$(lsof -Pi :3000 -sTCP:LISTEN -t)
|
||||
kill -9 $pid
|
||||
fi
|
||||
|
||||
|
||||
if lsof -Pi :3002 -sTCP:LISTEN -t >/dev/null; then
|
||||
# Tuer le processus associé au port
|
||||
pid=$(lsof -Pi :3002 -sTCP:LISTEN -t)
|
||||
kill -9 $pid
|
||||
fi
|
||||
|
||||
if lsof -Pi :3003 -sTCP:LISTEN -t >/dev/null; then
|
||||
# Tuer le processus associé au port
|
||||
pid=$(lsof -Pi :3003 -sTCP:LISTEN -t)
|
||||
kill -9 $pid
|
||||
fi
|
||||
|
||||
|
||||
npm start &
|
||||
|
||||
node server/server.js &
|
||||
|
||||
node src/server/server.js
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
const ADRESSE_WEBSERVER = "http://localhost:3002"
|
||||
|
||||
const ADRESSE_DBSERVER = "http://localhost:3003"
|
||||
|
||||
const tmp = ADRESSE_DBSERVER
|
||||
const tmp2 = ADRESSE_WEBSERVER
|
||||
|
||||
const ADRESSE_WEBSITE = ""
|
||||
|
||||
|
||||
export {ADRESSE_DBSERVER, ADRESSE_WEBSERVER, ADRESSE_WEBSITE}
|
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
|
||||
import { NavDropdown } from 'react-bootstrap';
|
||||
import LanguageNavItem from './LangNavItem';
|
||||
import { HiLanguage } from 'react-icons/hi2';
|
||||
|
||||
// @ts-ignore
|
||||
const LangDropdown = ({ changeLocale }) => {
|
||||
return (
|
||||
<NavDropdown title={<HiLanguage/>} id="language-dropdown" align='end' drop='down-centered'>
|
||||
<LanguageNavItem
|
||||
countryCode="FR"
|
||||
languageKey="languageSelector.french"
|
||||
onClick={() => changeLocale('fr')}
|
||||
/>
|
||||
<LanguageNavItem
|
||||
countryCode="GB"
|
||||
languageKey="languageSelector.english"
|
||||
onClick={() => changeLocale('en')}
|
||||
/>
|
||||
{/* Ajoutez d'autres langues selon vos besoins */}
|
||||
</NavDropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default LangDropdown;
|
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { NavDropdown } from 'react-bootstrap';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import ReactCountryFlag from 'react-country-flag';
|
||||
|
||||
/*@ts-ignore*/
|
||||
const LanguageNavItem = ({ countryCode, languageKey, onClick }) => {
|
||||
return (
|
||||
<NavDropdown.Item onClick={onClick}>
|
||||
<ReactCountryFlag
|
||||
className="custom-flag"
|
||||
countryCode={countryCode}
|
||||
svg
|
||||
style={{ margin: 'auto 10px 3px auto' }}
|
||||
/>
|
||||
<FormattedMessage id={languageKey} />
|
||||
</NavDropdown.Item>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguageNavItem;
|
@ -0,0 +1,92 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTheme } from '../Style/ThemeContext';
|
||||
import Player from '../model/Player';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
import Offcanvas from 'react-bootstrap/Offcanvas';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import { socket } from '../SocketConfig';
|
||||
|
||||
interface LobbyContainerProps {
|
||||
roomNum : string
|
||||
HeadPlayer : Player
|
||||
nbPlayer : number
|
||||
//? mettre un "nbplayermax" si le nombre de joueur max peut etre fixé ?
|
||||
}
|
||||
|
||||
const LobbyContainer: React.FC<LobbyContainerProps> = ({roomNum, HeadPlayer, nbPlayer}) => {
|
||||
const theme=useTheme();
|
||||
|
||||
|
||||
|
||||
|
||||
const navigate = useNavigate();
|
||||
const dest = '/lobby?room=' + roomNum;
|
||||
|
||||
//* Modal
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
const handleClose = () => setShow(false);
|
||||
const handleShow = () => setShow(true);
|
||||
|
||||
const handleContainerClick = () => {
|
||||
|
||||
if (show){
|
||||
handleClose()
|
||||
}
|
||||
else{
|
||||
if (nbPlayer < 6) {
|
||||
socket.off("request lobbies")
|
||||
navigate(dest);
|
||||
} else {
|
||||
handleShow()
|
||||
//alert('La salle est pleine. Impossible de rejoindre.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let stylefull;
|
||||
let colorfull;
|
||||
if (nbPlayer >= 6) {
|
||||
stylefull = "darkred"
|
||||
colorfull = "darkred"
|
||||
}
|
||||
else {
|
||||
stylefull = "whitesmoke"
|
||||
colorfull = "black"
|
||||
}
|
||||
|
||||
return(
|
||||
<div className='lobbyMainContainer' onClick={handleContainerClick} style={{borderColor:stylefull}}>
|
||||
<header style={{height:'20%', display:'flex', justifyContent:'end'}}>
|
||||
<h6><i>Room : {roomNum}</i></h6>
|
||||
</header>
|
||||
<hr/>
|
||||
<h3><b>{HeadPlayer.pseudo}</b></h3>
|
||||
<div style={{display:'flex', justifyContent:'end', alignItems:'end'}}>
|
||||
<h2 style={{color:colorfull}}>{nbPlayer}/6</h2>
|
||||
</div>
|
||||
{/*
|
||||
<Button onClick={handleContainerClick} variant='danger'>
|
||||
click
|
||||
</Button> */}
|
||||
|
||||
<Modal show={show} onHide={handleClose}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Salle pleine</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>La salle est pleine, il est impossible d'y aller pour le moment !</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={handleClose}>
|
||||
Ok
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LobbyContainer;
|
@ -0,0 +1,52 @@
|
||||
import React, { useState } from 'react';
|
||||
import '../Pages/Profile.css'
|
||||
import dl from '../res/icon/download.png'
|
||||
import defaultImg from '../res/img/Person.png'
|
||||
import { useAuth } from '../Contexts/AuthContext';
|
||||
|
||||
//@ts-ignore
|
||||
const ProfilePDP = () => {
|
||||
const [selectedFile, setSelectedFile] = useState(null);
|
||||
|
||||
|
||||
const {user} = useAuth()
|
||||
// @ts-ignores
|
||||
const handleFileChange = (event) => {
|
||||
let file = event.target.files[0];
|
||||
|
||||
setSelectedFile(file);
|
||||
if (file) {
|
||||
const pdpUrl = URL.createObjectURL(file);
|
||||
if (user!=null){
|
||||
user.profilePicture = pdpUrl
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='mainPDPContainer'>
|
||||
{selectedFile ? (
|
||||
<div >
|
||||
{/* @ts-ignore */}
|
||||
{/* <p>Selected File: {selectedFile.name}</p> */}
|
||||
<img src={URL.createObjectURL(selectedFile)} alt="Preview" className='imgContainer' width='100px' height='100px' />
|
||||
</div>
|
||||
) : (
|
||||
<div >
|
||||
<img src={user?.profilePicture} alt="Preview" className='imgContainer' width='100px' height='100px' />
|
||||
</div>
|
||||
)}
|
||||
<div className="parent">
|
||||
<div className="file-upload">
|
||||
<img src={dl} alt="upload" width='35px' height='35px'/>
|
||||
{/* <h6>Cliquer ici pour ajouter une image</h6> */}
|
||||
{/* <p>Taille recommandée : 100px</p> */}
|
||||
<input type="file" accept="image/*" onChange={handleFileChange}/>
|
||||
</div>
|
||||
</div>
|
||||
{/* <input type="file" accept="image/*" onChange={handleFileChange} /> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfilePDP;
|
@ -0,0 +1,62 @@
|
||||
// AuthContext.js
|
||||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
import DbUserService from '../model/DataManagers/DbUserService';
|
||||
import Manager from '../model/DataManagers/Manager';
|
||||
import Player from '../model/Player';
|
||||
import User from '../model/User';
|
||||
import AuthService from '../services/AuthService';
|
||||
|
||||
interface AuthContextProps {
|
||||
isLoggedIn: boolean;
|
||||
login: () => void;
|
||||
logout: () => void;
|
||||
user: User | null
|
||||
setUserData: (newPlayer: User) => void
|
||||
manager: Manager
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextProps | undefined>(undefined);
|
||||
|
||||
const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
|
||||
const [user, setUser] = useState<User| null>(null)
|
||||
const [manager] = useState<Manager>(new Manager(new DbUserService()))
|
||||
|
||||
const login = async () => {
|
||||
setIsLoggedIn(true);
|
||||
const [u, bool] = await manager.userService.fetchUserInformation()
|
||||
setUser(u)
|
||||
};
|
||||
|
||||
const setUserData = (newPlayer: User) => {
|
||||
setUser(newPlayer)
|
||||
}
|
||||
|
||||
const logout = async() => {
|
||||
try {
|
||||
await AuthService.logout();
|
||||
setIsLoggedIn(false);
|
||||
const [u, bool] = await manager.userService.fetchUserInformation()
|
||||
setUser(u)
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ isLoggedIn, login, logout, user, setUserData, manager }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useAuth = (): AuthContextProps => {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth must be used within an AuthProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { AuthProvider, useAuth };
|
@ -0,0 +1,14 @@
|
||||
async function loadImageAsync(url: string): Promise<string> {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const blob = await response.blob();
|
||||
|
||||
// Faire quelque chose avec le blob, par exemple, créer une URL blob
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
return blobUrl
|
||||
} catch (error) {
|
||||
throw new Error("Erreur lors du chargement de l'image :");
|
||||
}
|
||||
}
|
||||
|
||||
export {loadImageAsync}
|
@ -0,0 +1,42 @@
|
||||
.lobbyMainContainer{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: white;
|
||||
|
||||
border: solid 1px whitesmoke;
|
||||
border-radius: 15px;
|
||||
box-shadow: 5px 5px 5px rgb(246, 246, 246);
|
||||
|
||||
width: 300px;
|
||||
/* max-width: 300px; */
|
||||
/* max-width: 20%; */
|
||||
max-height: 200px;
|
||||
|
||||
padding: 20px;
|
||||
margin: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.lobbyList{
|
||||
display: flex;
|
||||
/* justify-content: space-around; */
|
||||
/* columns: 4; */
|
||||
flex-wrap: wrap;
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.searchLobby{
|
||||
border: solid 1px whitesmoke;
|
||||
border-radius: 15px;
|
||||
padding: 15px;
|
||||
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
|
||||
h1{
|
||||
margin: 50px !important;
|
||||
font-family: "Raleway", sans-serif;
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
/* Style */
|
||||
import './Lobbies.css';
|
||||
import "../Style/Global.css"
|
||||
import { useTheme } from '../Style/ThemeContext';
|
||||
import LobbyContainer from '../Components/LobbyContainer';
|
||||
import Player from '../model/Player';
|
||||
import User from '../model/User';
|
||||
import { socket } from '../SocketConfig';
|
||||
import JSONParser from '../JSONParser';
|
||||
import Person from '../model/Person';
|
||||
|
||||
|
||||
class LobbyDataProps {
|
||||
roomNum : string
|
||||
headPlayer: Player
|
||||
nbPlayer: number
|
||||
|
||||
constructor(roomNum: string, player: Player, nbPlayer: number){
|
||||
this.roomNum = roomNum
|
||||
this.headPlayer = player
|
||||
this.nbPlayer = nbPlayer
|
||||
}
|
||||
}
|
||||
|
||||
function Lobbies() {
|
||||
const theme=useTheme();
|
||||
|
||||
|
||||
|
||||
|
||||
const [lobbyData, setLobbyData] = useState<LobbyDataProps[]>([])
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
const filteredLobbies = lobbyData.filter((lobby) =>
|
||||
lobby.roomNum.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
lobby.headPlayer.pseudo.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
socket.emit("request lobbies")
|
||||
|
||||
|
||||
socket.on("request lobbies", (map) => {
|
||||
const jsonMap = JSON.parse(map)
|
||||
const tmpTab: LobbyDataProps[]=[]
|
||||
for(const item of jsonMap){
|
||||
tmpTab.push(new LobbyDataProps(item.key, JSONParser.JSONToPlayer(item.value[0]), item.value.length))
|
||||
}
|
||||
setLobbyData(tmpTab )
|
||||
})
|
||||
})
|
||||
|
||||
return(
|
||||
<div style={{display:'flex', flexDirection:'column', alignItems:'center'}}>
|
||||
<h1>Bienvenue dans le lobby des lobbies</h1>
|
||||
<input
|
||||
type="text"
|
||||
className='searchLobby'
|
||||
placeholder="Rechercher un lobby..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<div className="lobbyList">
|
||||
{filteredLobbies.map((lobby, index) => (
|
||||
<LobbyContainer
|
||||
key={index}
|
||||
roomNum={lobby.roomNum}
|
||||
HeadPlayer={lobby.headPlayer}
|
||||
nbPlayer={lobby.nbPlayer}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Lobbies;
|
@ -1,52 +1,114 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { AiOutlineSend } from 'react-icons/ai';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../Contexts/AuthContext';
|
||||
import AuthService from '../services/AuthService';
|
||||
import '../Style/Global.css';
|
||||
|
||||
export default class Login extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="form-container">
|
||||
<form>
|
||||
<br/>
|
||||
<h3>Log In</h3>
|
||||
<div className="mb-3">
|
||||
<label>Pseudo</label>
|
||||
<input
|
||||
const SignIn = () => {
|
||||
const navigate = useNavigate();
|
||||
const { login } = useAuth();
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
const data = {
|
||||
pseudo: (event.target as any).pseudo.value,
|
||||
password: (event.target as any).password.value,
|
||||
remember: (event.target as any).remember.checked
|
||||
};
|
||||
|
||||
const validation = await AuthService.validateSignIn(data);
|
||||
|
||||
if (!validation.valid) {
|
||||
setError(validation.error);
|
||||
} else {
|
||||
setError(null);
|
||||
|
||||
const result = await AuthService.signIn(data);
|
||||
|
||||
// console.log(result);
|
||||
|
||||
setShowConfirmation(true);
|
||||
setTimeout(async () => {
|
||||
await login();
|
||||
navigate('/play'); // 3 secondes avant de rediriger vers la page de connexion
|
||||
}, 3000);
|
||||
}
|
||||
} catch (error: any) {
|
||||
setError(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setShowConfirmation(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="form-container">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<br/>
|
||||
<h3>Log In</h3>
|
||||
<div className="mb-3">
|
||||
<label>Pseudo</label>
|
||||
<input
|
||||
type="pseudo"
|
||||
name='pseudo'
|
||||
className="form-control"
|
||||
placeholder="Entrez votre pseudo ici"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label>Password</label>
|
||||
<input
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label>Password</label>
|
||||
<input
|
||||
type="password"
|
||||
name='password'
|
||||
className="form-control"
|
||||
placeholder="Entrez votre mot de passe ici"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="custom-control custom-checkbox">
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="custom-control custom-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
name='remember'
|
||||
className="custom-control-input"
|
||||
id="customCheck1"
|
||||
/>
|
||||
<label className="custom-control-label" htmlFor="customCheck1">
|
||||
Se souvenir de moi
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-grid">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
</div>
|
||||
<div className="d-grid">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Soumettre <AiOutlineSend/>
|
||||
</button>
|
||||
</div>
|
||||
<p className="forgot-password text-right">
|
||||
<a href="#">Mot de passe</a> oublié ?
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
<p className="forgot-password text-right">
|
||||
<a href="#">Mot de passe</a> oublié ?
|
||||
</p>
|
||||
</form>
|
||||
|
||||
{error && (
|
||||
<div className="alert alert-danger" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showConfirmation && (
|
||||
<div className="alert alert-success" role="alert">
|
||||
Connexion réussie ! Vous serez redirigé vers votre profil dans 3 secondes.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignIn;
|
||||
|
@ -0,0 +1,123 @@
|
||||
.mainContainer{
|
||||
display: flex;
|
||||
/* flex-direction: column; */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 50px;
|
||||
}
|
||||
|
||||
.mainPDPContainer{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 2px solid whitesmoke;
|
||||
border-radius: 15px;
|
||||
|
||||
background-color: white;
|
||||
|
||||
margin: 10px;
|
||||
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
/*Lpart*/
|
||||
|
||||
.imgContainer{
|
||||
border: 5px solid black;
|
||||
border-radius: 50px;
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*Rpart*/
|
||||
|
||||
.Rpart{
|
||||
|
||||
min-width: 40%;
|
||||
min-height: 250px;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
|
||||
background-color: white;
|
||||
|
||||
border: solid 1px whitesmoke;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
|
||||
.username-display{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.editbutton{
|
||||
border-color: white;
|
||||
background-color: white;
|
||||
border: none;
|
||||
|
||||
margin-left: 15px;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
.inputpseudo{
|
||||
display: 'flex';
|
||||
justify-content: 'flex-start';
|
||||
align-items: 'center';
|
||||
flex-direction: 'row';
|
||||
|
||||
width: 20vw;
|
||||
padding: 5;
|
||||
|
||||
border:none;
|
||||
border-bottom: solid 2px gray;
|
||||
border-radius: 5;
|
||||
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.bottom{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: end;
|
||||
align-items: end;
|
||||
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
/*File upload*/
|
||||
.parent {
|
||||
/* width: 250px; */
|
||||
/* margin: auto; */
|
||||
margin: 2rem;
|
||||
background: #ffffff;
|
||||
border-radius: 25px;
|
||||
/* box-shadow: 7px 20px 20px rgb(210, 227, 244); */
|
||||
}
|
||||
.file-upload {
|
||||
text-align: center;
|
||||
border: 3px dashed rgb(210, 227, 244);
|
||||
/* padding: 1.5rem; */
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
|
||||
}
|
||||
.file-upload p {
|
||||
font-size: 0.5rem;
|
||||
/* margin-top: 10px; */
|
||||
color: #bbcada;
|
||||
}
|
||||
.file-upload input {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ProfilePDP from '../Components/ProfilePDP';
|
||||
|
||||
import SessionService from '../services/SessionService';
|
||||
import AuthService from '../services/AuthService';
|
||||
import { PlayerProps } from '../types/Player';
|
||||
import { delay, update } from 'lodash';
|
||||
import { socket } from '../SocketConfig';
|
||||
|
||||
|
||||
/* Style */
|
||||
import './Profile.css'
|
||||
import Edit from "../res/icon/edit-pen.png"
|
||||
import Coche from '../res/icon/coche.png'
|
||||
import Cancel from '../res/icon/cancel.png'
|
||||
|
||||
/* Nav */
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
/* Model */
|
||||
import User from '../model/User';
|
||||
|
||||
/* Context */
|
||||
import { useAuth } from '../Contexts/AuthContext';
|
||||
|
||||
/* Boostrap */
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Alert from 'react-bootstrap/Alert';
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
import Form from 'react-bootstrap/Form';
|
||||
|
||||
|
||||
//@ts-ignore
|
||||
const Profile = () => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
//let player;
|
||||
const {user, logout} = useAuth()
|
||||
|
||||
// let pseudoNotNull;
|
||||
// if(user?.pseudo != null){
|
||||
// pseudoNotNull = user.pseudo;
|
||||
// }
|
||||
|
||||
const [editingUsername, setEditingUsername] = useState(false);
|
||||
const [newUsername, setNewUsername] = useState(user?.pseudo);
|
||||
|
||||
//@ts-ignore
|
||||
const onUsernameChange = (newUsername) => {
|
||||
if(user?.pseudo != null){
|
||||
SessionService.UpdatePseudo(user.pseudo, newUsername)
|
||||
user.pseudo = newUsername;
|
||||
}
|
||||
}
|
||||
|
||||
const handleUsernameChange = () => {
|
||||
// Maj du pseudo
|
||||
onUsernameChange(newUsername);
|
||||
|
||||
// Désactiver le mode d'édition
|
||||
setEditingUsername(false);
|
||||
};
|
||||
|
||||
|
||||
//* Gestion Modal de suppression :
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
|
||||
const handleShowDeleteModal = () => {
|
||||
setShowDeleteModal(true);
|
||||
};
|
||||
|
||||
const handleCloseDeleteModal = () => {
|
||||
setShowDeleteModal(false);
|
||||
};
|
||||
|
||||
//* Confirmation avec la phrase :
|
||||
const [confirmationPhrase, setConfirmationPhrase] = useState('');
|
||||
|
||||
const [showWrong, setShowWrong] = useState(false);
|
||||
|
||||
//@ts-ignore
|
||||
const handleConfirmationPhraseChange = (e) => {
|
||||
setConfirmationPhrase(e.target.value);
|
||||
};
|
||||
|
||||
const handleDeleteAccount = () => {
|
||||
// Verification de la phrase
|
||||
if (confirmationPhrase.toLowerCase() === 'supprimer mon compte') {
|
||||
console.log('Compte supprimé !');
|
||||
|
||||
if(user!= null){
|
||||
const pseudo = user.pseudo;
|
||||
AuthService.delAccount(pseudo);
|
||||
AuthService.logout();
|
||||
logout();
|
||||
}
|
||||
else{
|
||||
console.error("l'utilisateur ne peut pas être null")
|
||||
}
|
||||
handleCloseDeleteModal();
|
||||
|
||||
navigate("/play")
|
||||
|
||||
} else {
|
||||
console.error('Phrase de confirmation incorrecte.');
|
||||
setShowWrong(true);
|
||||
setTimeout(async () => {
|
||||
setShowWrong(false);
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<center><h1>Mon Compte</h1></center>
|
||||
<div className='mainContainer'>
|
||||
<div>
|
||||
<ProfilePDP/>
|
||||
</div>
|
||||
<div className='Rpart'>
|
||||
{editingUsername ? (
|
||||
<div className='username-edit'>
|
||||
<input
|
||||
type='text'
|
||||
className='inputpseudo'
|
||||
value={newUsername}
|
||||
onChange={(e) => setNewUsername(e.target.value)}
|
||||
/>
|
||||
<button className='editbutton' onClick={handleUsernameChange}>
|
||||
<img src={Coche} alt='edit' width='25' height='25'/>
|
||||
</button>
|
||||
<button className='editbutton' onClick={() => setEditingUsername(false)}>
|
||||
<img src={Cancel} alt='edit' width='25' height='25'/>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className='username-display'>
|
||||
<h1>{user?.pseudo}</h1>
|
||||
<button className='editbutton' onClick={() => setEditingUsername(true)}>
|
||||
<img src={Edit} alt='edit' width='25' height='25'/>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<hr/>
|
||||
{!editingUsername ? (
|
||||
<Button variant="secondary">Modifier le mot de passe</Button>
|
||||
) : (
|
||||
<Alert key='info' variant='info' style={{width:'100%'}}>
|
||||
Vous êtes en mode "édition".
|
||||
</Alert>
|
||||
)}
|
||||
<div className='bottom'>
|
||||
<>
|
||||
<Button variant="danger" onClick={handleShowDeleteModal}>Supprimer</Button>
|
||||
|
||||
<Modal show={showDeleteModal} onHide={handleCloseDeleteModal}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Confirmation de suppression</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<p>
|
||||
Pour confirmer la suppression de votre compte, veuillez
|
||||
entrer la phrase : "supprimer mon compte".
|
||||
</p>
|
||||
<Form.Control
|
||||
type='text'
|
||||
placeholder='Entrez la phrase de confirmation'
|
||||
value={confirmationPhrase}
|
||||
onChange={handleConfirmationPhraseChange}
|
||||
/>
|
||||
{
|
||||
showWrong &&
|
||||
<Alert key='infomodel' variant='danger' style={{width:'100%'}}>
|
||||
La phrase de confirmation est incorrecte.
|
||||
</Alert>
|
||||
}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant='secondary' onClick={handleCloseDeleteModal}>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button variant='danger' onClick={handleDeleteAccount}>Supprimer mon compte</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Profile;
|
@ -1,48 +1,111 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { AiOutlineSend } from 'react-icons/ai';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import AuthService from '../services/AuthService';
|
||||
import '../Style/Global.css';
|
||||
|
||||
export default class SignUp extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="form-container">
|
||||
<form>
|
||||
<br/>
|
||||
<h3>Sign Up</h3>
|
||||
<div className="mb-3">
|
||||
<label>Pseudo</label>
|
||||
<input
|
||||
type="pseudo"
|
||||
const SignUp = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
const data = {
|
||||
pseudo: (event.target as any).pseudo.value,
|
||||
password: (event.target as any).password.value,
|
||||
Cpassword: (event.target as any).Cpassword.value,
|
||||
};
|
||||
|
||||
const validation = await AuthService.validateSignUp(data);
|
||||
|
||||
if (!validation.valid) {
|
||||
setError(validation.error);
|
||||
} else {
|
||||
setError(null);
|
||||
|
||||
const result = await AuthService.signUp(data);
|
||||
console.log(result);
|
||||
|
||||
setShowConfirmation(true);
|
||||
setTimeout(() => {
|
||||
navigate('/login'); // 3 secondes avant de rediriger vers la page de connexion
|
||||
}, 3000);
|
||||
}
|
||||
} catch (error: any) {
|
||||
setError(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setShowConfirmation(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="form-container">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<br/>
|
||||
<h3>Sign Up</h3>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="pseudo">Pseudo</label>
|
||||
<input
|
||||
type="text"
|
||||
id="pseudo"
|
||||
name="pseudo"
|
||||
className="form-control"
|
||||
placeholder="Entrez votre pseudo ici"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label>Password</label>
|
||||
<input
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
className="form-control"
|
||||
placeholder="Entrez votre mot de passe ici"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label>Confirm Password</label>
|
||||
<input
|
||||
type="Cpassword"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="Cpassword">Confirm Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="Cpassword"
|
||||
name="Cpassword"
|
||||
className="form-control"
|
||||
placeholder="Confirmez votre mot de passe ici"
|
||||
/>
|
||||
</div>
|
||||
<div className="d-grid">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="d-grid">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Soumettre <AiOutlineSend/>
|
||||
</button>
|
||||
</div>
|
||||
<p className="forgot-password text-right">
|
||||
Vous avez déjà un <a href="/login">compte</a> ?
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
<p className="forgot-password text-right">
|
||||
Vous avez déjà un <a href="/login">compte</a> ?
|
||||
</p>
|
||||
</form>
|
||||
|
||||
{error && (
|
||||
<div className="alert alert-danger" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showConfirmation && (
|
||||
<div className="alert alert-success" role="alert">
|
||||
Inscription réussie ! Vous serez redirigé vers la page de connexion dans 3 secondes.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignUp;
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 254 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
@ -1,36 +0,0 @@
|
||||
import {
|
||||
Network,
|
||||
Options,
|
||||
Data,
|
||||
Edge,
|
||||
Node
|
||||
} from "vis-network/standalone/esm/vis-network";
|
||||
import React, { useState, useLayoutEffect, useRef } from "react";
|
||||
|
||||
export interface UseVisNetworkOptions {
|
||||
options: Options;
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
}
|
||||
|
||||
export default (props: UseVisNetworkOptions) => {
|
||||
const { edges, nodes, options } = props;
|
||||
|
||||
const [network, addNetwork] = useState<Network | null>(null);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const data: Data = { nodes, edges };
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (ref.current) {
|
||||
const instance = new Network(ref.current, data, options);
|
||||
addNetwork(instance);
|
||||
}
|
||||
return () => network?.destroy();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
network,
|
||||
ref
|
||||
};
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import { io } from "socket.io-client";
|
||||
import { ADRESSE_WEBSERVER } from "./AdressSetup";
|
||||
|
||||
|
||||
const socket = io("http://localhost:3002");
|
||||
const socket = io(ADRESSE_WEBSERVER);
|
||||
|
||||
export {socket}
|
@ -0,0 +1,74 @@
|
||||
import SessionService from "../../services/SessionService";
|
||||
import { socket } from "../../SocketConfig";
|
||||
import User from "../User";
|
||||
import IUserService from "./IUserService";
|
||||
|
||||
class DbUserService implements IUserService{
|
||||
async fetchUserInformation(): Promise<[User | null, boolean]> {
|
||||
try {
|
||||
const sessionData = await SessionService.getSession();
|
||||
|
||||
// Vérifie si il y a une session
|
||||
if (sessionData.user) {
|
||||
// Il y a une session on récupère les infos du joueur
|
||||
const updatedPlayer: User = new User(socket.id, sessionData.user.pseudo, sessionData.user.profilePicture, {
|
||||
nbGames: sessionData.user.soloStats.nbGames,
|
||||
bestScore: sessionData.user.soloStats.bestScore,
|
||||
avgNbTry: sessionData.user.soloStats.avgNbTry,
|
||||
},
|
||||
{
|
||||
nbGames: sessionData.user.onlineStats.nbGames,
|
||||
nbWins: sessionData.user.onlineStats.nbWins,
|
||||
ratio: sessionData.user.onlineStats.ratio,
|
||||
})
|
||||
return [updatedPlayer, true]
|
||||
} else {
|
||||
// Pas de session on génère un guest random
|
||||
const guestPlayer: User = new User(socket.id, 'Guest_' + Math.floor(Math.random() * 1000000), '',
|
||||
{
|
||||
nbGames: 0,
|
||||
bestScore: 0,
|
||||
avgNbTry: 0,
|
||||
},
|
||||
{
|
||||
nbGames: 0,
|
||||
nbWins: 0,
|
||||
ratio: 0,
|
||||
})
|
||||
return [guestPlayer, false]
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [null, false]
|
||||
}
|
||||
}
|
||||
|
||||
async updateSoloStats(pseudo: string, nbGames: number, bestScore: number, avgNbTry: number): Promise<void> {
|
||||
try {
|
||||
const result = await SessionService.updateSoloStats(pseudo, nbGames, bestScore, avgNbTry);
|
||||
if (result) {
|
||||
console.log("Stats solo updated");
|
||||
} else {
|
||||
console.log("Stats solo not updated");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async updateOnlineStats(pseudo: string, nbGames: number, bestScore: number, ratio: number): Promise<void> {
|
||||
try {
|
||||
const result = await SessionService.updateOnlineStats(pseudo, nbGames, bestScore, ratio);
|
||||
if (result) {
|
||||
console.log("Stats online updated");
|
||||
} else {
|
||||
console.log("Stats online not updated");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DbUserService
|
@ -0,0 +1,10 @@
|
||||
import User from "../User";
|
||||
|
||||
interface IUserService{
|
||||
fetchUserInformation(): Promise<[User | null, boolean]>
|
||||
updateSoloStats(pseudo: string, nbGames: number, bestScore: number, avgNbTry: number): Promise<void>
|
||||
updateOnlineStats(pseudo: string, nbGames: number, bestScore: number, ratio: number): Promise<void>
|
||||
}
|
||||
|
||||
|
||||
export default IUserService
|
@ -0,0 +1,12 @@
|
||||
import IUserService from "./IUserService";
|
||||
|
||||
class Manager{
|
||||
|
||||
public userService: IUserService
|
||||
|
||||
constructor(userService: IUserService){
|
||||
this.userService = userService
|
||||
}
|
||||
}
|
||||
|
||||
export default Manager
|
@ -0,0 +1,72 @@
|
||||
import IndiceTesterFactory from "./Factory/IndiceTesterFactory";
|
||||
import Indice from "./Indices/Indice";
|
||||
import Pair from "./Pair";
|
||||
import Person from "./Person";
|
||||
import PersonNetwork from "./PersonsNetwork";
|
||||
|
||||
class EnigmeDuJourCreator{
|
||||
|
||||
|
||||
static createEnigme(personNetwork: PersonNetwork, choosenIndices: Indice[], choosenPerson: Person, allIndices: Indice[]): Map<number, Pair<Indice, boolean>[]>{
|
||||
const map = new Map<number, Pair<Indice, boolean>[]>()
|
||||
personNetwork.getPersons().forEach((p) =>{
|
||||
map.set(p.getId(), [])
|
||||
})
|
||||
|
||||
choosenIndices.forEach((choosenIndice) => {
|
||||
const choosenIndiceTester = IndiceTesterFactory.Create(choosenIndice)
|
||||
const modifiedPersons: Pair<Person, boolean>[] = []
|
||||
let possibleIndices: Indice[] = [...allIndices]
|
||||
|
||||
let i = 0
|
||||
|
||||
while (possibleIndices.length != 1){
|
||||
let tmpPossibleIndices: Indice[] = [...possibleIndices]
|
||||
let choosenPair : Pair<Person, boolean> = new Pair(personNetwork.getPersons()[0], true)
|
||||
for(const person of personNetwork.getPersons().filter((p) => p.getId() !== choosenPerson.getId())){
|
||||
const veryTmpIndice = [...possibleIndices]
|
||||
if (!choosenIndiceTester.Works(person)){
|
||||
possibleIndices.forEach((possibleIndice, index) =>{
|
||||
const tester = IndiceTesterFactory.Create(possibleIndice)
|
||||
if (tester.Works(person)){
|
||||
const t = veryTmpIndice.findIndex((tmpIndice) => tmpIndice.getId() == possibleIndice.getId())
|
||||
if (t != -1){
|
||||
veryTmpIndice.splice(t, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (veryTmpIndice.length<tmpPossibleIndices.length){
|
||||
tmpPossibleIndices = veryTmpIndice
|
||||
choosenPair = new Pair(person, false)
|
||||
}
|
||||
}
|
||||
else{
|
||||
possibleIndices.forEach((possibleIndice, index) =>{
|
||||
const tester = IndiceTesterFactory.Create(possibleIndice)
|
||||
if (!tester.Works(person)){
|
||||
const t = veryTmpIndice.findIndex((tmpIndice) => tmpIndice.getId() == possibleIndice.getId())
|
||||
if (t != -1){
|
||||
veryTmpIndice.splice(t, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (veryTmpIndice.length<tmpPossibleIndices.length){
|
||||
tmpPossibleIndices = veryTmpIndice
|
||||
choosenPair = new Pair(person, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
possibleIndices = [...tmpPossibleIndices]
|
||||
modifiedPersons.push(choosenPair)
|
||||
console.log(possibleIndices)
|
||||
}
|
||||
console.log("choosenIndice => " + choosenIndice.ToString("fr"))
|
||||
console.log("possibleIndices => " + possibleIndices[0].ToString("fr"))
|
||||
modifiedPersons.forEach((pair) =>{
|
||||
map.get(pair.first.getId())?.push(new Pair(choosenIndice, pair.second))
|
||||
})
|
||||
})
|
||||
return map
|
||||
}
|
||||
}
|
||||
export default EnigmeDuJourCreator
|
@ -1,18 +0,0 @@
|
||||
import Player from "./Player";
|
||||
|
||||
class Human extends Player{
|
||||
|
||||
constructor(id: string, name: string){
|
||||
super(id, name)
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
type: "Human",
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Human
|
@ -0,0 +1,27 @@
|
||||
import Player from "./Player";
|
||||
import defaultImg from "../res/img/Person.png"
|
||||
class User extends Player{
|
||||
|
||||
public soloStats: any
|
||||
public onlineStats: any
|
||||
|
||||
constructor(id: string, pseudo: string, profilePicture: string, soloStats: any, onlineStats: any){
|
||||
super(id, pseudo, profilePicture || defaultImg)
|
||||
this.soloStats=soloStats
|
||||
this.onlineStats=onlineStats
|
||||
}
|
||||
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
type: "User",
|
||||
id: this.id,
|
||||
profilePicture: this.profilePicture,
|
||||
pseudo: this.pseudo,
|
||||
soloStats: this.soloStats,
|
||||
onlineStats: this.onlineStats
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default User
|
After Width: | Height: | Size: 1010 B |
After Width: | Height: | Size: 302 B |
After Width: | Height: | Size: 613 B |
@ -0,0 +1,137 @@
|
||||
const UserService = require('../services/UserService');
|
||||
const DatabaseService = require('../services/DatabaseService');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
const sqlite3 = require('sqlite3');
|
||||
|
||||
class AuthController {
|
||||
static async signUp(req, res) {
|
||||
const databaseService = new DatabaseService();
|
||||
const pseudo = req.body.pseudo;
|
||||
const date = new Date();
|
||||
const hour = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
try {
|
||||
await databaseService.connect();
|
||||
|
||||
// Vérifier que le pseudo n'existe pas déjà
|
||||
const verif = await databaseService.getUserByPseudo(pseudo);
|
||||
if (verif) {
|
||||
res.status(400).json({ error: 'Le pseudo est déjà utilisé.' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Créer un nouvel utilisateur
|
||||
const currentUser = await UserService.createUser(req.body);
|
||||
const insertedUser = await databaseService.insertUser(currentUser);
|
||||
|
||||
const user = await databaseService.getUserByPseudo(pseudo);
|
||||
|
||||
// Initialiser les stats de l'utilisateur
|
||||
await databaseService.initSoloStats(user.idUser);
|
||||
await databaseService.initOnlineStats(user.idUser);
|
||||
|
||||
const soloStats = await databaseService.getSoloStatsByUserId(user.idUser);
|
||||
const onlineStats = await databaseService.getOnlineStatsByUserId(user.idUser);
|
||||
|
||||
await databaseService.updateUserIDStats(user.idUser, soloStats.idSoloStats, onlineStats.idOnlineStats);
|
||||
|
||||
console.log("[" + hour + ":" + minutes + "] " + user.pseudo + " have been registered.");
|
||||
res.status(201).json({ message: 'Inscription réussie', user: insertedUser});
|
||||
}
|
||||
catch (error) {
|
||||
// Gérer les erreurs
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Erreur lors de l\'inscription.' });
|
||||
}
|
||||
finally {
|
||||
await databaseService.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
static async signIn(req, res) {
|
||||
const databaseService = new DatabaseService();
|
||||
const date = new Date();
|
||||
const hour = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
try{
|
||||
await databaseService.connect();
|
||||
|
||||
// Vérifier que le pseudo existe
|
||||
const pseudo = req.body.pseudo;
|
||||
const user = await databaseService.getUserByPseudo(pseudo);
|
||||
if (!user) {
|
||||
res.status(400).json({ error: 'Le pseudo n\'existe pas.' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier que le mot de passe est correct
|
||||
const password = req.body.password;
|
||||
const validPassword = await bcrypt.compare(password, user.password);
|
||||
if (!validPassword) {
|
||||
res.status(400).json({ error: 'Le mot de passe est incorrect.' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Stocker l'utilisateur dans la session)
|
||||
req.session.user = user;
|
||||
|
||||
// Envoyer une réponse réussie
|
||||
console.log("[" + hour + ":" + minutes + "] " + user.pseudo + " have been connected.");
|
||||
res.status(200).json({ message: 'Connexion réussie', user: user });
|
||||
}
|
||||
catch(error){
|
||||
// Gérer les erreurs
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Erreur lors de la connexion.' });
|
||||
}
|
||||
finally{
|
||||
await databaseService.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
static async logout(req, res) {
|
||||
const pseudo = req.session.user.pseudo;
|
||||
const date = new Date();
|
||||
const hour = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
// Détruire la session pour déconnecter l'utilisateur
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Erreur lors de la déconnexion.' });
|
||||
} else {
|
||||
console.log("[" + hour + ":" + minutes + "] " + pseudo + " have been disconnected.");
|
||||
res.status(200).json({ message: 'Déconnexion réussie' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static async delAccount(req, res){
|
||||
const db = new DatabaseService();
|
||||
try{
|
||||
await db.connect();
|
||||
|
||||
const user = await db.getUserByPseudo(req.body.pseudo);
|
||||
|
||||
if(!user){
|
||||
res.status(400).json({ error: 'Le pseudo n\'existe pas.' });
|
||||
return;
|
||||
}
|
||||
await db.deleteSoloStat(user.idUser);
|
||||
await db.deleteOnlineStat(user.idUser);
|
||||
await db.deleteUser(user.idUser);
|
||||
}
|
||||
catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Erreur lors de la supression du compte.' });
|
||||
}
|
||||
finally{
|
||||
db.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthController;
|
@ -0,0 +1,146 @@
|
||||
const DatabaseService = require('../services/DatabaseService');
|
||||
|
||||
class SessionController {
|
||||
static async getUserInformation(req, res) {
|
||||
const db = new DatabaseService();
|
||||
const date = new Date();
|
||||
const hour = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
try{
|
||||
await db.connect();
|
||||
|
||||
if (!req.session.user) {
|
||||
res.status(200).json({ error: "true", message: 'User not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
req.session.user.soloStats = await db.getSoloStatsByUserId(req.session.user.idUser);
|
||||
req.session.user.onlineStats = await db.getOnlineStatsByUserId(req.session.user.idUser);
|
||||
|
||||
console.log("[" + hour + ":" + minutes + "] " + req.session.user.pseudo + " have a session.");
|
||||
res.status(200).json({ user: req.session.user });
|
||||
}
|
||||
catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Erreur lors de la récupération de l\'utilisateur.' });
|
||||
}
|
||||
finally{
|
||||
await db.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
static async updateSoloStats(req, res) {
|
||||
const db = new DatabaseService();
|
||||
const date = new Date();
|
||||
const hour = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
try{
|
||||
await db.connect();
|
||||
|
||||
const user = await db.getUserByPseudo(req.body.pseudo);
|
||||
|
||||
if (!user) {
|
||||
res.status(200).json({ error: "true", message: 'User not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const soloStats = await db.getSoloStatsByUserId(user.idUser);
|
||||
|
||||
if (!soloStats) {
|
||||
res.status(200).json({ error: "true", message: 'Solo stats not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
await db.updateSoloStats(user.idUser, req.body.nbGames, req.body.bestScore, req.body.avgNbTry);
|
||||
|
||||
const newSoloStats = await db.getSoloStatsByUserId(user.idUser);
|
||||
|
||||
req.session.user.soloStats = newSoloStats;
|
||||
|
||||
console.log("[" + hour + ":" + minutes + "] " + req.session.user.pseudo + "'s solot_stats are updated.");
|
||||
res.status(200).json({ user: req.session.user });
|
||||
}
|
||||
catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Erreur lors de la mise à jour des statistiques solo.' });
|
||||
}
|
||||
finally{
|
||||
await db.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
static async updateOnlineStats(req, res) {
|
||||
const db = new DatabaseService();
|
||||
const date = new Date();
|
||||
const hour = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
try{
|
||||
await db.connect();
|
||||
|
||||
const user = await db.getUserByPseudo(req.body.pseudo);
|
||||
|
||||
if (!user) {
|
||||
res.status(200).json({ error: "true", message: 'User not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const onlineStats = await db.getOnlineStatsByUserId(user.idUser);
|
||||
|
||||
if (!onlineStats) {
|
||||
res.status(200).json({ error: "true", message: 'Online stats not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
await db.updateOnlineStats(user.idUser, req.body.nbGames, req.body.nbWins, req.body.ratio);
|
||||
|
||||
const newOnlineStats = await db.getOnlineStatsByUserId(user.idUser);
|
||||
|
||||
req.session.user.onlineStats = newOnlineStats;
|
||||
|
||||
console.log("[" + hour + ":" + minutes + "] " + req.session.user.pseudo + "'s online_stats are updated.");
|
||||
res.status(200).json({ user: req.session.user });
|
||||
}
|
||||
catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Erreur lors de la mise à jour des statistiques en ligne.' });
|
||||
}
|
||||
finally{
|
||||
await db.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
static async UpdatePseudo(req, res){
|
||||
const db = new DatabaseService();
|
||||
|
||||
try{
|
||||
await db.connect();
|
||||
|
||||
const user = await db.getUserByPseudo(req.body.pseudo);
|
||||
console.log("utilisateur" + user.idUser + " pseudo" + user.pseudo)
|
||||
if (!user) {
|
||||
res.status(200).json({ error: "true", message: 'User not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
await db.updatePseudo(user.idUser, req.body.newPseudo); //* update
|
||||
|
||||
const updatedUser = await db.getUserByPseudo(req.body.newPseudo);
|
||||
console.log("updaetdutilisateur" + updatedUser.idUser + " pseudo" + updatedUser.pseudo)
|
||||
req.session.user.pseudo = updatedUser.pseudo;
|
||||
console.log("req.session.user.pseudo" + req.session.user.pseudo)
|
||||
res.status(200).json({ user: req.session.user }); //verif rep
|
||||
}
|
||||
catch(error){
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Erreur lors de la modification du pseudo de l\'utilisateur.' });
|
||||
}
|
||||
finally{
|
||||
await db.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SessionController;
|
@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const AuthController = require('../controllers/AuthController');
|
||||
const SessionController = require('../controllers/SessionController');
|
||||
|
||||
// Routes pour l'authentification
|
||||
router.post('/auth/signup', AuthController.signUp);
|
||||
router.post('/auth/signin', AuthController.signIn);
|
||||
router.delete('/auth/logout', AuthController.logout)
|
||||
router.delete('/auth/delAccount', AuthController.delAccount)
|
||||
|
||||
// Routes pour les sessions
|
||||
router.get('/session', SessionController.getUserInformation);
|
||||
router.put('/session/updatePseudo', SessionController.UpdatePseudo);
|
||||
router.put('/session/updateSoloStats', SessionController.updateSoloStats);
|
||||
router.put('/session/updateOnlineStats', SessionController.updateOnlineStats);
|
||||
|
||||
module.exports = router;
|
@ -0,0 +1,59 @@
|
||||
const express = require('express');
|
||||
const session = require('express-session');
|
||||
const cors = require('cors');
|
||||
const bodyParser = require('body-parser');
|
||||
const crypto = require('crypto');
|
||||
const authRoutes = require('./routes/authRoutes');
|
||||
const DatabaseService = require('./services/DatabaseService');
|
||||
|
||||
|
||||
const app = express();
|
||||
const port = 3003;
|
||||
|
||||
// Middleware
|
||||
app.use(cors(
|
||||
{
|
||||
origin: ["http://localhost:3000", "http://localhost:3000"],
|
||||
credentials: true
|
||||
}
|
||||
)); // Autoriser les requêtes cross-origin
|
||||
app.use(bodyParser.json()); // Parser le body des requêtes en JSON
|
||||
|
||||
// Session
|
||||
const secret = crypto.randomBytes(32).toString('hex');
|
||||
app.use(session({
|
||||
secret: secret,
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === 'production' ? true : false,
|
||||
maxAge: 60 * 60 * 1000
|
||||
}
|
||||
}));
|
||||
|
||||
// Middleware pour les routes
|
||||
app.use('/', authRoutes);
|
||||
|
||||
// Middleware de gestion des erreurs
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
res.status(500).send('Erreur interne du serveur!');
|
||||
});
|
||||
|
||||
// Middleware pour la gestion des erreurs 404
|
||||
app.use((req, res) => {
|
||||
res.status(404).send('Page non trouvée');
|
||||
});
|
||||
|
||||
// Connexion à la base de données sqlite3 au démarrage du serveur
|
||||
const databaseService = new DatabaseService();
|
||||
databaseService.connect()
|
||||
.then(() => {
|
||||
// Lancer le serveur
|
||||
app.listen(port, () => {
|
||||
console.log(`Serveur lancé sur le port ${port}`);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
@ -0,0 +1,231 @@
|
||||
const sqlite3 = require('sqlite3');
|
||||
const path = require('path');
|
||||
const { rejects } = require('assert');
|
||||
|
||||
class DatabaseService {
|
||||
constructor(){
|
||||
this.db_name = "socialgraph";
|
||||
}
|
||||
|
||||
async connect(client){
|
||||
const dbPath = path.resolve(__dirname, `../db/${this.db_name}.db`)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client = new sqlite3.Database(dbPath,
|
||||
sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE,
|
||||
(err) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async disconnect(){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.close((err) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Récupère l'utilisateur par son pseudo
|
||||
async getUserByPseudo(pseudo){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.get('SELECT * FROM users WHERE pseudo = ?', pseudo, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Récupère l'utilisateur par son id
|
||||
async getUserByID(id){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.get('SELECT * FROM users WHERE idUser = ?', id, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Récupère stats solo de l'utilisateur
|
||||
async getSoloStatsByUserId(userId){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.get('SELECT * FROM solo_stats WHERE idUser = ?', userId, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Récupère stats online de l'utilisateur
|
||||
async getOnlineStatsByUserId(userId){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.get('SELECT * FROM online_stats WHERE idUser = ?', userId, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// insère un utilisateur dans la base de données
|
||||
async insertUser(user) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { pseudo, password } = user;
|
||||
this.client.run('INSERT INTO users (pseudo, password) VALUES (?, ?)', [pseudo, password], (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Mettre à jour l'id de stats solo et online de l'utilisateur
|
||||
async updateUserIDStats(userId, soloStatsId, onlineStatsId){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('UPDATE users SET idSoloStats = ?, idOnlineStats = ? WHERE idUser = ?', [soloStatsId, onlineStatsId, userId], (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Mettre à jour les stats solo de l'utilisateur
|
||||
async updateSoloStats(userId, nbGames, bestScore, avgNbTry){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('UPDATE solo_stats SET nbGames = ?, bestScore = ?, avgNbTry = ? WHERE idUser = ?', [nbGames, bestScore, avgNbTry, userId], (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Mettre à jour les stats online de l'utilisateur
|
||||
async updateOnlineStats(userId, nbGames, nbWins, ratio){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('UPDATE online_stats SET nbGames = ?, nbWins = ?, ratio = ? WHERE idUser = ?', [nbGames, nbWins, ratio, userId], (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async initSoloStats(userId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('INSERT INTO solo_stats (nbGames, bestScore, avgNbTry, idUser) VALUES (?, ?, ?, ?)', 0, 0, 0.0, userId, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async initOnlineStats(userId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('INSERT INTO online_stats (nbGames, nbWins, ratio, idUser) VALUES (?, ?, ?, ?)', 0, 0, 0.0, userId, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async deleteUser(userId){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('DELETE FROM users WHERE idUser=?', userId, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async deleteSoloStat(userId){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('DELETE FROM solo_stats WHERE idUser=?', userId, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async deleteOnlineStat(userId){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('DELETE FROM online_stats WHERE idUser=?', userId, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async updatePseudo(userId, newPseudo){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.run('UPDATE users SET pseudo = ? WHERE idUser = ?', newPseudo, userId, (err, result) => {
|
||||
if(err){
|
||||
reject(err);
|
||||
}
|
||||
else{
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DatabaseService;
|
@ -0,0 +1,17 @@
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
class UserService {
|
||||
static async createUser(userData) {
|
||||
// Hacher le mot de passe avant de le stocker dans la base de données
|
||||
const hashedPassword = await bcrypt.hash(userData.password, 10);
|
||||
|
||||
// Retournez l'utilisateur créé
|
||||
return { pseudo: userData.pseudo, password: hashedPassword };
|
||||
}
|
||||
|
||||
static async initUserStats(idUser) {
|
||||
return { nbGames: 0, nbWins: 0, ratio: 0.0, idUser: idUser };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserService;
|
@ -0,0 +1,113 @@
|
||||
import VerificationService from './VerificationService';
|
||||
import {ADRESSE_DBSERVER} from "../AdressSetup"
|
||||
import User from '../model/User';
|
||||
|
||||
|
||||
class AuthService{
|
||||
// Méthode pour vérifier les données de connexion
|
||||
static async validateSignUp(data: any): Promise<{valid: boolean, error: string}> {
|
||||
return VerificationService.validateSignUpData(data);
|
||||
}
|
||||
|
||||
static async validateSignIn(data: any): Promise<{valid: boolean, error: string}> {
|
||||
return VerificationService.validateSignInData(data);
|
||||
}
|
||||
|
||||
static async signUp(data: any) {
|
||||
try {
|
||||
const response = await fetch(ADRESSE_DBSERVER + '/auth/signup', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} else {
|
||||
const errorResponse = await response.json();
|
||||
throw new Error(errorResponse.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async signIn(data: any) {
|
||||
try {
|
||||
const response = await fetch(ADRESSE_DBSERVER + '/auth/signin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} else {
|
||||
const errorResponse = await response.json();
|
||||
throw new Error(errorResponse.error);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async logout() {
|
||||
try {
|
||||
const response = await fetch(ADRESSE_DBSERVER + '/auth/logout', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} else {
|
||||
const errorResponse = await response.json();
|
||||
throw new Error(errorResponse.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async delAccount(pseudo: string){
|
||||
try {
|
||||
const response = await fetch(ADRESSE_DBSERVER + '/auth/delAccount', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ pseudo }),
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} else {
|
||||
const errorResponse = await response.json();
|
||||
throw new Error(errorResponse.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthService;
|
@ -0,0 +1,116 @@
|
||||
import {ADRESSE_DBSERVER} from "../AdressSetup"
|
||||
|
||||
class SessionService {
|
||||
static async getSession() {
|
||||
try {
|
||||
const response = await fetch(ADRESSE_DBSERVER + '/session', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} else {
|
||||
const errorResponse = await response.json();
|
||||
throw new Error(errorResponse.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async updateSoloStats(pseudo: string, nbGames: number, bestScore: number, avgNbTry: number){
|
||||
try {
|
||||
const response = await fetch(ADRESSE_DBSERVER + '/session/updateSoloStats', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
pseudo,
|
||||
nbGames,
|
||||
bestScore,
|
||||
avgNbTry
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} else {
|
||||
const errorResponse = await response.json();
|
||||
throw new Error(errorResponse.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async updateOnlineStats(pseudo: string, nbGames: number, nbWins: number, ratio: number){
|
||||
try {
|
||||
|
||||
console.log("updateOnlineStats : ", pseudo, nbGames, nbWins, ratio);
|
||||
const response = await fetch(ADRESSE_DBSERVER + '/session/updateOnlineStats', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
pseudo,
|
||||
nbGames,
|
||||
nbWins,
|
||||
ratio
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} else {
|
||||
const errorResponse = await response.json();
|
||||
throw new Error(errorResponse.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async UpdatePseudo(pseudo : string, newPseudo : string) {
|
||||
console.log("pseudo : " + pseudo + " newpseudo : " + newPseudo)
|
||||
try {
|
||||
const response = await fetch(ADRESSE_DBSERVER + '/session/updatePseudo', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
pseudo,
|
||||
newPseudo
|
||||
}),
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} else {
|
||||
const errorResponse = await response.json();
|
||||
throw new Error(errorResponse.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SessionService;
|
@ -0,0 +1,31 @@
|
||||
class ValidationService {
|
||||
public static validateSignUpData(data: any): {valid: boolean, error: string} {
|
||||
if(!data.pseudo || !data.password || !data.Cpassword) {
|
||||
return {valid: false, error: 'Veuillez remplir tous les champs.'};
|
||||
}
|
||||
|
||||
if(data.password.length < 8) {
|
||||
return {valid: false, error: 'Le mot de passe doit contenir au moins 8 caractères.'};
|
||||
}
|
||||
|
||||
if(data.password !== data.Cpassword) {
|
||||
return {valid: false, error: 'Les mots de passe ne correspondent pas.'};
|
||||
}
|
||||
|
||||
return {valid: true, error: ''};
|
||||
}
|
||||
|
||||
public static validateSignInData(data: any): {valid: boolean, error: string} {
|
||||
if(!data.pseudo || !data.password) {
|
||||
return {valid: false, error: 'Veuillez remplir tous les champs.'};
|
||||
}
|
||||
|
||||
if(data.password.length < 8) {
|
||||
return {valid: false, error: 'Le mot de passe doit contenir au moins 8 caractères.'};
|
||||
}
|
||||
|
||||
return {valid: true, error: ''};
|
||||
}
|
||||
}
|
||||
|
||||
export default ValidationService;
|
@ -0,0 +1,18 @@
|
||||
export interface PlayerSoloStats {
|
||||
nbGames: number;
|
||||
bestScore: number;
|
||||
avgNbTry: number;
|
||||
};
|
||||
|
||||
export interface PlayerOnlineStats {
|
||||
nbGames: number;
|
||||
nbWins: number;
|
||||
ratio: number;
|
||||
};
|
||||
|
||||
export interface PlayerProps {
|
||||
profilePicture: string;
|
||||
pseudo: string;
|
||||
soloStats: PlayerSoloStats;
|
||||
onlineStats: PlayerOnlineStats;
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
\relax
|
||||
\@writefile{toc}{\contentsline {paragraph}{Première énigme}{2}{}\protected@file@percent }
|
||||
\gdef \@abspage@last{2}
|
@ -1,132 +0,0 @@
|
||||
\documentclass[11pt]{article}
|
||||
\usepackage{fullpage}
|
||||
\usepackage{times}
|
||||
\usepackage{tikz}
|
||||
\usepackage{paralist}
|
||||
\usetikzlibrary {shapes.multipart}
|
||||
\newcommand{\Basketball}{\includegraphics[width=.5cm]{ballon-de-basket.png}}
|
||||
\newcommand{\Football}{\includegraphics[width=.4cm]{ballon-de-foot.png}}
|
||||
\newcommand{\Bowling}{\includegraphics[width=.5cm]{bowling.png}}
|
||||
\newcommand{\Baseball}{\includegraphics[width=.5cm]{baseball.png}}
|
||||
\newcommand{\Tennis}{\includegraphics[width=.5cm]{tennis.png}}
|
||||
\begin{document}
|
||||
\thispagestyle{empty}
|
||||
Voici le graphe de SocialGraphe
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[scale=.9]
|
||||
\node[draw, circle split] (0) at (0,0) { Alexander \nodepart{lower} \Football{} \Bowling{}};
|
||||
\node[draw, circle split] (1) at (4,0) { Wyatt \nodepart{lower} \Baseball{} \Tennis{}};
|
||||
\node[draw, circle split] (2) at (8,0) { Mia \nodepart{lower} \Basketball{}};
|
||||
\node[draw, circle split] (3) at (12,0) { William \nodepart{lower} \Baseball{} \Football{}};
|
||||
\node[draw, circle split] (4) at (16,0) { Zoey \nodepart{lower} \Basketball{} \Bowling{} \Tennis{}};
|
||||
\node[draw, circle split] (5) at (0,4) { Isabella \nodepart{lower} \Tennis{}};
|
||||
\node[draw, circle split] (6) at (4,4) { Abigail \nodepart{lower} \Baseball{}};
|
||||
\node[draw, circle split] (7) at (8,4) { Savannah \nodepart{lower} \Bowling{} \Basketball{} \Football{}};
|
||||
\node[draw, circle split] (8) at (12,4) { Peyton \nodepart{lower} \Football{}};
|
||||
\node[draw, circle split] (9) at (16,4) { Alice \nodepart{lower} \Tennis{} \Baseball{}};
|
||||
\node[draw, circle split] (10) at (0,8) { Sophia \nodepart{lower} \Bowling{} \Basketball{} \Bowling{}};
|
||||
\node[draw, circle split] (11) at (4,8) { Layla \nodepart{lower} \Tennis{} \Baseball{} \Football{}};
|
||||
\node[draw, circle split] (12) at (8,8) { Ava \nodepart{lower} \Basketball{}};
|
||||
\node[draw, circle split] (13) at (12,8) { Harper \nodepart{lower} \Bowling{}};
|
||||
\node[draw, circle split] (14) at (16,8) { Sebastian \nodepart{lower} \Tennis{} \Basketball{} \Baseball{}};
|
||||
\node[draw, circle split] (15) at (0,12) { Michael \nodepart{lower} \Football{}};
|
||||
\node[draw, circle split] (16) at (4,12) { Natalie \nodepart{lower} \Bowling{} \Football{} \Baseball{}};
|
||||
\node[draw, circle split] (17) at (8,12) { Penelope \nodepart{lower} \Basketball{}};
|
||||
\node[draw, circle split] (18) at (12,12) { Lily \nodepart{lower} \Tennis{} \Tennis{}};
|
||||
\node[draw, circle split] (19) at (16,12) { Eleanor \nodepart{lower} \Football{}};
|
||||
\node[draw, circle split] (20) at (0,16) { Henry \nodepart{lower} \Bowling{} \Basketball{}};
|
||||
\node[draw, circle split] (21) at (4,16) { Claire \nodepart{lower} \Baseball{} \Basketball{}};
|
||||
\node[draw, circle split] (22) at (8,16) { Caleb \nodepart{lower} \Baseball{}};
|
||||
\node[draw, circle split] (23) at (12,16) { Charlotte \nodepart{lower} \Bowling{} \Football{} \Tennis{}};
|
||||
\node[draw, circle split] (24) at (16,16) { Luke \nodepart{lower} \Football{}};
|
||||
\node[draw, circle split] (25) at (0,20) { Connor \nodepart{lower} \Baseball{} \Tennis{}};
|
||||
\node[draw, circle split] (26) at (4,20) { Aiden \nodepart{lower} \Basketball{} \Bowling{} \Tennis{}};
|
||||
\node[draw, circle split] (27) at (8,20) { Aurora \nodepart{lower} \Football{}};
|
||||
\node[draw, circle split] (28) at (12,20) { Nathan \nodepart{lower} \Bowling{} \Baseball{}};
|
||||
\node[draw, circle split] (29) at (16,20) { Aurora \nodepart{lower} \Basketball{}};
|
||||
\draw (0) -- (11);
|
||||
\draw (0) -- (13);
|
||||
\draw (0) -- (18);
|
||||
\draw (1) -- (13);
|
||||
\draw (1) -- (24);
|
||||
\draw (2) -- (22);
|
||||
\draw (2) -- (16);
|
||||
\draw (2) -- (9);
|
||||
\draw (2) -- (6);
|
||||
\draw (3) -- (4);
|
||||
\draw (3) -- (20);
|
||||
\draw (4) -- (28);
|
||||
\draw (4) -- (3);
|
||||
\draw (5) -- (17);
|
||||
\draw (5) -- (15);
|
||||
\draw (5) -- (24);
|
||||
\draw (6) -- (2);
|
||||
\draw (7) -- (17);
|
||||
\draw (7) -- (24);
|
||||
\draw (7) -- (22);
|
||||
\draw (7) -- (11);
|
||||
\draw (8) -- (25);
|
||||
\draw (8) -- (21);
|
||||
\draw (8) -- (24);
|
||||
\draw (8) -- (11);
|
||||
\draw (9) -- (2);
|
||||
\draw (10) -- (25);
|
||||
\draw (10) -- (26);
|
||||
\draw (10) -- (27);
|
||||
\draw (11) -- (0);
|
||||
\draw (11) -- (7);
|
||||
\draw (11) -- (8);
|
||||
\draw (12) -- (20);
|
||||
\draw (12) -- (27);
|
||||
\draw (13) -- (0);
|
||||
\draw (13) -- (1);
|
||||
\draw (14) -- (15);
|
||||
\draw (15) -- (5);
|
||||
\draw (15) -- (14);
|
||||
\draw (15) -- (20);
|
||||
\draw (15) -- (17);
|
||||
\draw (16) -- (2);
|
||||
\draw (16) -- (26);
|
||||
\draw (17) -- (5);
|
||||
\draw (17) -- (7);
|
||||
\draw (17) -- (15);
|
||||
\draw (17) -- (20);
|
||||
\draw (18) -- (0);
|
||||
\draw (19) -- (23);
|
||||
\draw (20) -- (3);
|
||||
\draw (20) -- (12);
|
||||
\draw (20) -- (15);
|
||||
\draw (20) -- (17);
|
||||
\draw (21) -- (8);
|
||||
\draw (22) -- (2);
|
||||
\draw (22) -- (7);
|
||||
\draw (22) -- (23);
|
||||
\draw (23) -- (19);
|
||||
\draw (23) -- (22);
|
||||
\draw (24) -- (1);
|
||||
\draw (24) -- (5);
|
||||
\draw (24) -- (7);
|
||||
\draw (24) -- (8);
|
||||
\draw (25) -- (8);
|
||||
\draw (25) -- (10);
|
||||
\draw (26) -- (10);
|
||||
\draw (26) -- (16);
|
||||
\draw (26) -- (29);
|
||||
\draw (27) -- (10);
|
||||
\draw (27) -- (12);
|
||||
\draw (28) -- (4);
|
||||
\draw (29) -- (26);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
|
||||
|
||||
\paragraph{Première énigme}
|
||||
Trouver qui est le coupable avec les indices suivants.
|
||||
\begin{compactitem}
|
||||
\item Indice 1 : Le suspect pratique au moins du Baseball et/ou du Basketball .
|
||||
\item Indice 2 : Le suspect pratique 2 ou 1 sport(s).
|
||||
\item Indice 3 : Le suspect a les cheveux Roux ou Blond .
|
||||
\item Indice 4 : Le suspect a au moins un ami avec les cheveux Roux .
|
||||
\end{compactitem}
|
||||
% Solution : Nathan
|
||||
\end{document}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Cryptid",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{}
|