mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
added permission for teacher routes.
coté backend doit etre fait
This commit is contained in:
parent
8770726873
commit
deaaa8bc9b
8 changed files with 87 additions and 34 deletions
9
client/package-lock.json
generated
9
client/package-lock.json
generated
|
|
@ -22,6 +22,7 @@
|
||||||
"esbuild": "^0.23.1",
|
"esbuild": "^0.23.1",
|
||||||
"gift-pegjs": "^1.0.2",
|
"gift-pegjs": "^1.0.2",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"katex": "^0.16.11",
|
"katex": "^0.16.11",
|
||||||
"marked": "^14.1.2",
|
"marked": "^14.1.2",
|
||||||
"nanoid": "^5.0.2",
|
"nanoid": "^5.0.2",
|
||||||
|
|
@ -9417,6 +9418,14 @@
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jwt-decode": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/katex": {
|
"node_modules/katex": {
|
||||||
"version": "0.16.11",
|
"version": "0.16.11",
|
||||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz",
|
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz",
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
"esbuild": "^0.23.1",
|
"esbuild": "^0.23.1",
|
||||||
"gift-pegjs": "^1.0.2",
|
"gift-pegjs": "^1.0.2",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"katex": "^0.16.11",
|
"katex": "^0.16.11",
|
||||||
"marked": "^14.1.2",
|
"marked": "^14.1.2",
|
||||||
"nanoid": "^5.0.2",
|
"nanoid": "^5.0.2",
|
||||||
|
|
|
||||||
|
|
@ -26,23 +26,24 @@ import ApiService from './services/ApiService';
|
||||||
import OAuthCallback from './pages/AuthManager/callback/AuthCallback';
|
import OAuthCallback from './pages/AuthManager/callback/AuthCallback';
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState(ApiService.isLoggedIn()); // Initial check
|
const [isAuthenticated, setIsAuthenticated] = useState(ApiService.isLoggedIn());
|
||||||
const location = useLocation(); // Hook to detect route changes
|
const [isTeacherAuthenticated, setIsTeacherAuthenticated] = useState(ApiService.isLoggedInTeacher());
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
// Check login status every time the route changes
|
// Check login status every time the route changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkLoginStatus = () => {
|
const checkLoginStatus = () => {
|
||||||
const loggedIn = ApiService.isLoggedIn();
|
setIsAuthenticated(ApiService.isLoggedIn());
|
||||||
setIsAuthenticated(loggedIn); // Update state if login status changes
|
setIsTeacherAuthenticated(ApiService.isLoggedInTeacher());
|
||||||
console.log('App.tsx - Login status:', loggedIn);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
checkLoginStatus(); // Check login status whenever the route changes
|
checkLoginStatus();
|
||||||
}, [location]); // Re-run when the location (route) changes
|
}, [location]);
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
ApiService.logout();
|
ApiService.logout();
|
||||||
setIsAuthenticated(false); // Ensure we log out the user in the state as well
|
setIsAuthenticated(false);
|
||||||
|
setIsTeacherAuthenticated(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -57,19 +58,19 @@ const App: React.FC = () => {
|
||||||
{/* Pages espace enseignant */}
|
{/* Pages espace enseignant */}
|
||||||
<Route
|
<Route
|
||||||
path="/teacher/dashboard"
|
path="/teacher/dashboard"
|
||||||
element={isAuthenticated ? <Dashboard /> : <Navigate to="/login" />}
|
element={isTeacherAuthenticated ? <Dashboard /> : <Navigate to="/login" />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/teacher/share/:id"
|
path="/teacher/share/:id"
|
||||||
element={isAuthenticated ? <Share /> : <Navigate to="/login" />}
|
element={isTeacherAuthenticated ? <Share /> : <Navigate to="/login" />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/teacher/editor-quiz/:id"
|
path="/teacher/editor-quiz/:id"
|
||||||
element={isAuthenticated ? <QuizForm /> : <Navigate to="/login" />}
|
element={isTeacherAuthenticated ? <QuizForm /> : <Navigate to="/login" />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/teacher/manage-room/:id"
|
path="/teacher/manage-room/:id"
|
||||||
element={isAuthenticated ? <ManageRoom /> : <Navigate to="/login" />}
|
element={isTeacherAuthenticated ? <ManageRoom /> : <Navigate to="/login" />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Pages espace étudiant */}
|
{/* Pages espace étudiant */}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import apiService from '../../../services/ApiService';
|
import apiService from '../../../services/ApiService';
|
||||||
|
import { jwtDecode } from 'jwt-decode';
|
||||||
|
|
||||||
const OAuthCallback: React.FC = () => {
|
const OAuthCallback: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
@ -15,6 +16,10 @@ const OAuthCallback: React.FC = () => {
|
||||||
console.log(user);
|
console.log(user);
|
||||||
apiService.saveToken(user);
|
apiService.saveToken(user);
|
||||||
|
|
||||||
|
const decodedToken = jwtDecode(user);
|
||||||
|
const { email } = decodedToken as { email: string;};
|
||||||
|
console.log(email + " connected!");
|
||||||
|
|
||||||
// Navigate to the dashboard or another page
|
// Navigate to the dashboard or another page
|
||||||
navigate('/');
|
navigate('/');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
// JoinRoom.tsx
|
// JoinRoom.tsx
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { TextField } from '@mui/material';
|
import { TextField, FormLabel, RadioGroup, FormControlLabel, Radio, Box } from '@mui/material';
|
||||||
import LoadingButton from '@mui/lab/LoadingButton';
|
import LoadingButton from '@mui/lab/LoadingButton';
|
||||||
|
|
||||||
import LoginContainer from '../../../../components/LoginContainer/LoginContainer'
|
import LoginContainer from '../../../../components/LoginContainer/LoginContainer';
|
||||||
import ApiService from '../../../../services/ApiService';
|
import ApiService from '../../../../services/ApiService';
|
||||||
|
|
||||||
const Register: React.FC = () => {
|
const Register: React.FC = () => {
|
||||||
|
|
@ -15,33 +14,31 @@ const Register: React.FC = () => {
|
||||||
|
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
const [role, setRole] = useState('etudiant');
|
||||||
|
|
||||||
const [connectionError, setConnectionError] = useState<string>('');
|
const [connectionError, setConnectionError] = useState<string>('');
|
||||||
const [isConnecting] = useState<boolean>(false);
|
const [isConnecting] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => { };
|
||||||
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const register = async () => {
|
const register = async () => {
|
||||||
const result = await ApiService.register(email, password);
|
const result = await ApiService.register(email, password, role);
|
||||||
|
|
||||||
if (result != true) {
|
if (result !== true) {
|
||||||
setConnectionError(result);
|
setConnectionError(result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate("/login")
|
navigate("/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoginContainer
|
<LoginContainer
|
||||||
title='Créer un compte'
|
title="Créer un compte"
|
||||||
error={connectionError}>
|
error={connectionError}
|
||||||
|
>
|
||||||
<TextField
|
<TextField
|
||||||
label="Email"
|
label="Email"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|
@ -63,6 +60,20 @@ const Register: React.FC = () => {
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', marginBottom: '1rem' }}>
|
||||||
|
<FormLabel component="legend" sx={{ marginRight: '1rem' }}>Choisir votre rôle</FormLabel>
|
||||||
|
<RadioGroup
|
||||||
|
row
|
||||||
|
aria-label="role"
|
||||||
|
name="role"
|
||||||
|
value={role}
|
||||||
|
onChange={(e) => setRole(e.target.value)}
|
||||||
|
>
|
||||||
|
<FormControlLabel value="etudiant" control={<Radio />} label="Étudiant" />
|
||||||
|
<FormControlLabel value="professeur" control={<Radio />} label="Professeur" />
|
||||||
|
</RadioGroup>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
loading={isConnecting}
|
loading={isConnecting}
|
||||||
onClick={register}
|
onClick={register}
|
||||||
|
|
@ -72,9 +83,7 @@ const Register: React.FC = () => {
|
||||||
>
|
>
|
||||||
S'inscrire
|
S'inscrire
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
|
|
||||||
</LoginContainer>
|
</LoginContainer>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ const Register: React.FC = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const register = async () => {
|
const register = async () => {
|
||||||
const result = await ApiService.register(email, password);
|
const result = await ApiService.register(email, password, "temp"); // "temp", car ce fichier n'est plus utilisé. il est déplacer dans /AuthManager
|
||||||
|
|
||||||
if (result != true) {
|
if (result != true) {
|
||||||
setConnectionError(result);
|
setConnectionError(result);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import axios, { AxiosError, AxiosResponse } from 'axios';
|
import axios, { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import {jwtDecode} from 'jwt-decode';
|
||||||
import { ENV_VARIABLES } from '../constants';
|
import { ENV_VARIABLES } from '../constants';
|
||||||
|
|
||||||
import { QuizType } from '../Types/QuizType';
|
import { QuizType } from '../Types/QuizType';
|
||||||
|
|
@ -66,8 +67,6 @@ class ApiService {
|
||||||
public isLoggedIn(): boolean {
|
public isLoggedIn(): boolean {
|
||||||
const token = this.getToken()
|
const token = this.getToken()
|
||||||
|
|
||||||
console.log("Check if loggedIn : " + token);
|
|
||||||
|
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +77,35 @@ class ApiService {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isLoggedInTeacher(): boolean {
|
||||||
|
const token = this.getToken();
|
||||||
|
|
||||||
|
console.log("Check if loggedIn : " + token);
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decodedToken = jwtDecode(token) as { role: string };
|
||||||
|
|
||||||
|
const userRole = decodedToken.role;
|
||||||
|
const requiredRole = 'professeur';
|
||||||
|
|
||||||
|
if (userRole !== requiredRole) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update token expiry
|
||||||
|
this.saveToken(token);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error decoding token:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public logout(): void {
|
public logout(): void {
|
||||||
return localStorage.removeItem("jwt");
|
return localStorage.removeItem("jwt");
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +116,7 @@ class ApiService {
|
||||||
* @returns true if successful
|
* @returns true if successful
|
||||||
* @returns A error string if unsuccessful,
|
* @returns A error string if unsuccessful,
|
||||||
*/
|
*/
|
||||||
public async register(email: string, password: string): Promise<any> {
|
public async register(email: string, password: string, role: string): Promise<any> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
|
|
@ -97,7 +125,7 @@ class ApiService {
|
||||||
|
|
||||||
const url: string = this.constructRequestUrl(`/user/register`);
|
const url: string = this.constructRequestUrl(`/user/register`);
|
||||||
const headers = this.constructRequestHeaders();
|
const headers = this.constructRequestHeaders();
|
||||||
const body = { email, password };
|
const body = { email, password, role };
|
||||||
|
|
||||||
const result: AxiosResponse = await axios.post(url, body, { headers: headers });
|
const result: AxiosResponse = await axios.post(url, body, { headers: headers });
|
||||||
|
|
||||||
|
|
@ -881,4 +909,4 @@ class ApiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiService = new ApiService();
|
const apiService = new ApiService();
|
||||||
export default apiService;
|
export default apiService;
|
||||||
|
|
@ -4,7 +4,7 @@ services:
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
build:
|
build:
|
||||||
context: ./server
|
context: ./client
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue