From 6bcdcedc66dd4d2ab1ba31566fdfa72a19064844 Mon Sep 17 00:00:00 2001 From: Mathis Date: Fri, 11 Apr 2025 19:48:50 +0200 Subject: [PATCH 01/11] Ajout de la page de profil --- .../java/com/example/starter/AuthHandler.java | 7 +- .../com/example/starter/MainVerticle.java | 4 + .../java/com/example/starter/QueryUsers.java | 64 +++- .../java/com/example/starter/SetUser.java | 142 ++++++-- Front-end/src/App.jsx | 6 +- Front-end/src/components/Header.jsx | 12 +- Front-end/src/pages/Gestion/Gestion.jsx | 1 - Front-end/src/pages/Login.jsx | 57 +++- Front-end/src/pages/Profil.jsx | 317 ++++++++++++++++++ Front-end/src/pages/Settings.jsx | 161 --------- Front-end/src/pages/Signup.jsx | 24 +- 11 files changed, 581 insertions(+), 214 deletions(-) create mode 100644 Front-end/src/pages/Profil.jsx delete mode 100644 Front-end/src/pages/Settings.jsx diff --git a/Back-end/src/main/java/com/example/starter/AuthHandler.java b/Back-end/src/main/java/com/example/starter/AuthHandler.java index e3a31c3..1bc0490 100644 --- a/Back-end/src/main/java/com/example/starter/AuthHandler.java +++ b/Back-end/src/main/java/com/example/starter/AuthHandler.java @@ -30,8 +30,9 @@ public class AuthHandler { String email = body.getString("email"); String gender = body.getString("gender"); String password = body.getString("password"); + String pseudo = body.getString("pseudo"); - if (name == null || surname == null || email == null || gender == null || password == null) { + if (name == null || surname == null || email == null || gender == null || password == null || pseudo == null) { context.response() .setStatusCode(400) .end(new JsonObject().put("error", "Tous les champs sont requis").encode()); @@ -41,8 +42,8 @@ public class AuthHandler { String hashedPassword = BCrypt.withDefaults().hashToString(12, password.toCharArray()); databaseService.pool - .preparedQuery("INSERT INTO users (name, surname, email, gender, password) VALUES (?, ?, ?, ?, ?)") - .execute(Tuple.of(name, surname, email, gender, hashedPassword)) + .preparedQuery("INSERT INTO users (name, surname, email, gender, password, pseudo) VALUES (?, ?, ?, ?, ?, ?)") + .execute(Tuple.of(name, surname, email, gender, hashedPassword,pseudo)) .onSuccess(result -> { context.response() .setStatusCode(201) diff --git a/Back-end/src/main/java/com/example/starter/MainVerticle.java b/Back-end/src/main/java/com/example/starter/MainVerticle.java index b669774..b45f367 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -56,12 +56,16 @@ public class MainVerticle extends AbstractVerticle { router.post("/modifRangeData").handler(setWeatherData::setRangeData); router.post("/deleteObject").handler(setObjects::deleteObject); router.get("/users").handler(queryUsers::getUsers); + router.post("/user").handler(queryUsers::getUser); router.post("/setUserPoints").handler(setUser::setUserPoints); router.post("/deleteUser").handler(setUser::deleteUser); + router.post("/updateProfil").handler(setUser::updateUserProfile); + router.post("/changePassword").handler(setUser::changeUserPassword); // Routes d'authentification router.post("/signup").handler(authHandler::handleSignup); router.post("/login").handler(authHandler::handleLogin); + // Création du serveur HTTP vertx.createHttpServer() diff --git a/Back-end/src/main/java/com/example/starter/QueryUsers.java b/Back-end/src/main/java/com/example/starter/QueryUsers.java index e065c02..6305e81 100644 --- a/Back-end/src/main/java/com/example/starter/QueryUsers.java +++ b/Back-end/src/main/java/com/example/starter/QueryUsers.java @@ -4,6 +4,7 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.Tuple; public class QueryUsers { private DatabaseService databaseService; @@ -25,19 +26,20 @@ public class QueryUsers { .onSuccess(rows -> { JsonArray users = new JsonArray(); for (Row row : rows) { - int points=row.getInteger("points"); + int points = row.getInteger("points"); JsonObject user = new JsonObject() .put("id", row.getInteger("id")) .put("name", row.getString("name")) .put("surname", row.getString("surname")) .put("email", row.getString("email")) .put("gender", row.getString("gender")) - .put("points",points); - if(points<=60){ + .put("pseudo",row.getString("pseudo")) + .put("points", points); + if (points <= 60) { user.put("role", "user"); - }else if(points<=100){ + } else if (points <= 100) { user.put("role", "complexe"); - }else if(points>=200){ + } else if (points >= 200) { user.put("role", "admin"); } users.add(user); @@ -48,4 +50,56 @@ public class QueryUsers { }); } + + public void getUser(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); + return; + } + //Integer idUser = body.getInteger("idUser"); + Integer idUser = 4; + databaseService.pool + .preparedQuery("SELECT * FROM users WHERE id=?;") + .execute(Tuple.of(idUser)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.size() == 0) { + context.response() + .setStatusCode(404) + .end(new JsonObject().put("error", "Utilisateur non trouvé").encode()); + return; + } + + Row row = rows.iterator().next(); + int points = row.getInteger("points"); + JsonObject user = new JsonObject() + .put("id", row.getInteger("id")) + .put("name", row.getString("name")) + .put("surname", row.getString("surname")) + .put("email", row.getString("email")) + .put("gender", row.getString("gender")) + .put("pseudo",row.getString("pseudo")) + .put("points", points); + + if (points <= 60) { + user.put("role", "user"); + } else if (points <= 100) { + user.put("role", "complexe"); + } else if (points >= 200) { + user.put("role", "admin"); + } + + context.response() + .putHeader("content-type", "application/json; charset=UTF-8") + .end(user.encode()); + }); + } } diff --git a/Back-end/src/main/java/com/example/starter/SetUser.java b/Back-end/src/main/java/com/example/starter/SetUser.java index 6bcd9b2..47aa996 100644 --- a/Back-end/src/main/java/com/example/starter/SetUser.java +++ b/Back-end/src/main/java/com/example/starter/SetUser.java @@ -1,5 +1,6 @@ package com.example.starter; +import at.favre.lib.crypto.bcrypt.BCrypt; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import io.vertx.sqlclient.Tuple; @@ -10,6 +11,7 @@ public class SetUser { public SetUser(DatabaseService ddbs) { this.databaseService = ddbs; } + public void updateUserPoints(Integer userId, Integer points) { databaseService.pool .preparedQuery("UPDATE users SET points=points+? WHERE id=?") @@ -25,6 +27,105 @@ public class SetUser { } }); } + + public void changeUserPassword(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); + return; + } + Integer id = body.getInteger("id"); + String oldPassword = body.getString("oldPassword"); + String newPassword = body.getString("newPassword"); + + databaseService.pool + .preparedQuery("SELECT password FROM users WHERE id=?") + .execute(Tuple.of(id)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + context.response() + .setStatusCode(404) + .end(new JsonObject().put("error", "Utilisateur non trouvé").encode()); + return; + } + + String currentPassword = rows.iterator().next().getString("password"); + BCrypt.Result verification = BCrypt.verifyer().verify(oldPassword.toCharArray(), currentPassword); + + if (!verification.verified) { + context.response() + .setStatusCode(401) + .end(new JsonObject().put("error", "Ancien mot de passe incorrect").encode()); + return; + } + String hashedPassword = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray()); + + databaseService.pool + .preparedQuery("UPDATE users SET password=? WHERE id=?") + .execute(Tuple.of(hashedPassword, id)) + .onFailure(e -> { + System.err.println("Erreur lors de la mise à jour du mot de passe :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject() + .put("error", "Erreur lors de la mise à jour du mot de passe") + .encode()); + }) + .onSuccess(updateRows -> { + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject().put("success", "Le mot de passe a bien été mis à jour") + .encode()); + }); + }); + } + + public void updateUserProfile(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); + return; + } + Integer id = body.getInteger("id"); + String name = body.getString("name"); + String surname = body.getString("surname"); + String pseudo = body.getString("pseudo"); + + databaseService.pool + .preparedQuery("UPDATE users SET name=?, surname=?, pseudo=? WHERE id=?") + .execute(Tuple.of(name, surname,pseudo, id)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + context.response() + .setStatusCode(404) + .end(new JsonObject().put("error", "Utilisateur non trouvé").encode()); + return; + } + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject() + .put("success", "Les informations de l'utilisateur ont bien été mises à jour") + .encode()); + return; + }); + } + public void setUserPoints(RoutingContext context) { JsonObject body = context.body().asJsonObject(); if (body == null) { @@ -59,36 +160,37 @@ public class SetUser { return; }); } - public void deleteUser(RoutingContext context){ + + public void deleteUser(RoutingContext context) { JsonObject body = context.body().asJsonObject(); - if(body== null){ + if (body == null) { context.response() - .setStatusCode(400) - .end(new JsonObject().put("error","Corps de la requête manquant").encode()); + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); return; } Integer id = body.getInteger("id"); databaseService.pool - .preparedQuery("DELETE FROM users WHERE id=?") - .execute(Tuple.of(id)) - .onFailure(e->{ - System.err.println("Erreur de récupération de la BDD :"+e.getMessage()); - context.response() - .setStatusCode(500) - .end(new JsonObject().put("error","Erreur de récupération de la BDD").encode()); - }) - .onSuccess(rows -> { - if(rows.rowCount()==0){ + .preparedQuery("DELETE FROM users WHERE id=?") + .execute(Tuple.of(id)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + context.response() .setStatusCode(404) .end(new JsonObject().put("error", "Utilisateur non trouvé").encode()); return; - } - context.response() - .putHeader("content-type","application/json: charset=UTF-8") - .end(new JsonObject().put("success", "L'utilisateur à bien été supprimé").encode()); - return; - }); + } + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject().put("success", "L'utilisateur à bien été supprimé").encode()); + return; + }); } } diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index 18df2c9..46f897a 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -9,7 +9,7 @@ import Objet from "./pages/Gestion/Objet.jsx"; import AddObject from "./pages/Gestion/AddObject.jsx"; import Signup from "./pages/Signup.jsx"; import Login from "./pages/Login.jsx"; -import Settings from "./pages/Settings.jsx"; +import Profil from "./pages/Profil.jsx"; import Sidebar from "./pages/Admin/sidebar.jsx"; import User from "./pages/Admin/User.jsx"; import Dashboard from "./pages/Admin/Dashboard.jsx"; @@ -31,9 +31,9 @@ function App() { } /> } /> } />} /> - } /> + }/>} /> } /> - } /> + }/>} /> } /> } />} /> diff --git a/Front-end/src/components/Header.jsx b/Front-end/src/components/Header.jsx index 52dada0..35f0d20 100644 --- a/Front-end/src/components/Header.jsx +++ b/Front-end/src/components/Header.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { X, Menu, LogIn, UserPlus, LogOut, Settings } from "lucide-react"; +import { X, Menu, LogIn, UserPlus, LogOut, User } from "lucide-react"; import { Link } from "react-router-dom"; import { useAuth } from "../AuthContext"; @@ -80,12 +80,12 @@ function Header() { <>
  • setIsMenuOpen(false)} className="flex items-center gap-2 text-gray-600 hover:text-indigo-600" > - - Paramètres + + Profil
  • @@ -127,11 +127,11 @@ function Header() { ) : (
    setIsMenuOpen(false)} className="flex items-center gap-2 text-gray-600 hover:text-indigo-600" > - + +
    + + +
    +
    + +
    +
    +

    {userData.name} {userData.surname} ({userData.pseudo})

    +

    {userData.email}

    +
    +
    + +
    +

    Points de fidélité: {userData.points} ({userData.role})

    +
    + + {editMode ? ( + <> +
    + + +
    + +
    + + +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    +
    + + + + ) : ( +
    +
    +

    Prénom

    +

    {userData.name}

    +
    +
    +

    Nom

    +

    {userData.surname}

    +
    +
    +

    Email

    +

    {userData.email}

    +
    +
    + )} + + + + {/* Changement de mot de passe */} +
    +

    Modifier le mot de passe

    +
    +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + + +
    +
    + + + + ); +} + +export default Profil; \ No newline at end of file diff --git a/Front-end/src/pages/Settings.jsx b/Front-end/src/pages/Settings.jsx deleted file mode 100644 index 924a6b4..0000000 --- a/Front-end/src/pages/Settings.jsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, { useState } from 'react'; -import { Mail, User, Lock } from 'lucide-react'; -import { useNavigate, Link} from 'react-router-dom'; // Importation du hook useNavigate - -function Settings() { - const [formData, setFormData] = useState({ - name: '', - surname: '', - email: '', - gender: '', - password: '', - confirmPassword: '' - }); - const navigate = useNavigate(); // Initialisation de useNavigate - - const handleChange = (e) => { - const { name, value } = e.target; - setFormData(prev => ({ - ...prev, - [name]: value - })); - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - - if (formData.password !== formData.confirmPassword) { - alert("Les mots de passe ne correspondent pas !"); - return; - } - - try { - const response = await fetch(`${API_BASE_URL}/settings`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(formData), - }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data.error || "Erreur lors de la modification"); - } - - alert("Modification réussie !"); - - // Redirection vers la page d'accueil après une inscription réussie - navigate("/home"); // Remplace "/home" par l'URL de ta page d'accueil - } catch (error) { - alert(error.message); - } - }; - - return ( -
    -
    -

    Settings

    -
    - {/* (Formulaire changement Email, Mot de passe) */} - - - - {/* Email */} -
    - -
    -
    - -
    - -
    -
    - - {/* Mot de passe */} -
    - -
    -
    - -
    - -
    -
    - - {/* nouveau mot de passe */} -
    - -
    -
    - -
    - -
    -
    -
    - -
    -
    - -
    - -
    -
    - {/* Bouton d'inscription */} -
    - -
    -
    - -
    -
    - ); -} - -export default Settings; diff --git a/Front-end/src/pages/Signup.jsx b/Front-end/src/pages/Signup.jsx index e18140f..c6703db 100644 --- a/Front-end/src/pages/Signup.jsx +++ b/Front-end/src/pages/Signup.jsx @@ -7,6 +7,7 @@ function Signup() { const [formData, setFormData] = useState({ name: '', surname: '', + pseudo:'', email: '', gender: '', password: '', @@ -62,7 +63,7 @@ function Signup() { {/* Formulaire (Nom, Prénom, Sexe, Email, Mot de passe) */}
    @@ -81,7 +82,7 @@ function Signup() {
    @@ -98,6 +99,25 @@ function Signup() {
    +
    + +
    +
    + +
    + +
    +
    + {/* Sexe */}
    +
    +

    Pseudo

    +

    {userData.pseudo}

    +

    Email

    {userData.email}

    From dcdcdee10372a64c8513d96b056810ceaeba1901 Mon Sep 17 00:00:00 2001 From: Charles Mendiburu Date: Fri, 11 Apr 2025 19:51:48 +0200 Subject: [PATCH 03/11] Protection route selon role --- .../java/com/example/starter/AuthHandler.java | 87 ++++++------ Front-end/package-lock.json | 126 +++++++++++++++++- Front-end/package.json | 1 + Front-end/src/App.jsx | 29 ++-- Front-end/src/AuthContext.jsx | 33 +++-- Front-end/src/ProtectedRoute.jsx | 18 ++- Front-end/src/pages/Home.jsx | 12 +- 7 files changed, 231 insertions(+), 75 deletions(-) diff --git a/Back-end/src/main/java/com/example/starter/AuthHandler.java b/Back-end/src/main/java/com/example/starter/AuthHandler.java index e3a31c3..2e62e0e 100644 --- a/Back-end/src/main/java/com/example/starter/AuthHandler.java +++ b/Back-end/src/main/java/com/example/starter/AuthHandler.java @@ -77,46 +77,53 @@ public class AuthHandler { } databaseService.pool - .preparedQuery("SELECT password,points FROM users WHERE email = ?") - .execute(Tuple.of(email)) - .onSuccess(result -> { - if (result.rowCount() == 0) { - context.response() - .setStatusCode(401) - .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); - return; - } + .preparedQuery("SELECT name, surname, password, points FROM users WHERE email = ?") // Ajout de name et surname + .execute(Tuple.of(email)) + .onSuccess(result -> { + if (result.rowCount() == 0) { + context.response() + .setStatusCode(401) + .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); + return; + } - String storedHashedPassword = result.iterator().next().getString("password"); - Integer nbPointsUser = result.iterator().next().getInteger("points"); + var row = result.iterator().next(); + String storedHashedPassword = row.getString("password"); + Integer nbPointsUser = row.getInteger("points"); + String name = row.getString("name"); + String surname = row.getString("surname"); - BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword); + BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword); - if (verification.verified) { - JsonObject claims = new JsonObject().put("sub", email); - if(nbPointsUser<=60){ - claims.put("role", "user"); - }else if(nbPointsUser<=100){ - claims.put("role", "complexe"); - }else if(nbPointsUser>=200){ - claims.put("role", "admin"); - } - - String token = jwtAuth.generateToken(claims); - context.response() - .setStatusCode(200) - .end(new JsonObject().put("token", token).encode()); - } else { - context.response() - .setStatusCode(401) - .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); - } - }) - .onFailure(err -> { - System.err.println("Erreur de connexion : " + err.getMessage()); - context.response() - .setStatusCode(500) - .end(new JsonObject().put("error", "Erreur serveur").encode()); - }); - } -} + if (verification.verified) { + JsonObject claims = new JsonObject() + .put("sub", email) + .put("name", name) + .put("surname", surname); + + if (nbPointsUser <= 60) { + claims.put("role", "user"); + } else if (nbPointsUser <= 100) { + claims.put("role", "complexe"); + } else if (nbPointsUser >= 200) { + claims.put("role", "admin"); + } + + String token = jwtAuth.generateToken(claims); + + context.response() + .setStatusCode(200) + .end(new JsonObject().put("token", token).encode()); + } else { + context.response() + .setStatusCode(401) + .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); + } + }) + .onFailure(err -> { + System.err.println("Erreur de connexion : " + err.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur serveur").encode()); + }); +}} \ No newline at end of file diff --git a/Front-end/package-lock.json b/Front-end/package-lock.json index d61485a..8494b4e 100644 --- a/Front-end/package-lock.json +++ b/Front-end/package-lock.json @@ -13,6 +13,7 @@ "@emotion/styled": "^11.14.0", "@mui/material": "^7.0.1", "axios": "^1.8.4", + "jwt-decode": "^4.0.0", "lucide-react": "^0.427.0", "react": "^18.3.1", "react-charts": "^3.0.0-beta.57", @@ -2070,7 +2071,6 @@ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2819,6 +2819,71 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -3788,12 +3853,63 @@ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", "license": "ISC" }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "license": "MIT" }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4269,6 +4385,14 @@ "node": ">=4.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/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/Front-end/package.json b/Front-end/package.json index ca037c9..36bfe68 100644 --- a/Front-end/package.json +++ b/Front-end/package.json @@ -14,6 +14,7 @@ "@emotion/styled": "^11.14.0", "@mui/material": "^7.0.1", "axios": "^1.8.4", + "jwt-decode": "^4.0.0", "lucide-react": "^0.427.0", "react": "^18.3.1", "react-charts": "^3.0.0-beta.57", diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index 18df2c9..c80fc95 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -14,7 +14,7 @@ import Sidebar from "./pages/Admin/sidebar.jsx"; import User from "./pages/Admin/User.jsx"; import Dashboard from "./pages/Admin/Dashboard.jsx"; import AdminObjet from "./pages/Admin/AdminObjet.jsx"; -import ProtectedRoute from './ProtectedRoute.jsx'; // Correction de l'import +import ProtectedRoute from "./ProtectedRoute.jsx"; function App() { return ( @@ -23,19 +23,28 @@ function App() {
    + {/* Routes publiques */} } /> } /> - } />} /> - } />} /> - } />} /> } /> } /> - } />} /> - } /> - } /> - } /> - } /> - } />} /> + + {/* Routes protégées pour tous les utilisateurs connectés */} + } allowedRoles={['admin', 'complexe', 'user']} />} /> + } allowedRoles={['admin', 'complexe', 'user']} />} /> + } allowedRoles={['admin', 'complexe', 'user']} />} /> + + {/* Routes protégées pour les admins et complexes */} + } allowedRoles={['admin', 'complexe']} />} /> + } allowedRoles={['admin', 'complexe']} />} /> + + {/* Routes protégées pour tous les utilisateurs connectés */} + } allowedRoles={['admin', 'complexe', 'user']} />} /> + } allowedRoles={['admin', 'complexe', 'user']} />} /> + + {/* Routes protégées pour les admins uniquement */} + } allowedRoles={['admin']} />} /> + } allowedRoles={['admin']} />} />
    diff --git a/Front-end/src/AuthContext.jsx b/Front-end/src/AuthContext.jsx index ca5abc9..545e406 100644 --- a/Front-end/src/AuthContext.jsx +++ b/Front-end/src/AuthContext.jsx @@ -1,5 +1,6 @@ -// src/AuthContext.js import React, { createContext, useContext, useState, useEffect } from "react"; +import { jwtDecode } from "jwt-decode"; + // Créer le contexte const AuthContext = createContext(); @@ -7,22 +8,25 @@ const AuthContext = createContext(); // Hook pour accéder facilement au contexte export const useAuth = () => useContext(AuthContext); -// Fournisseur de contexte qui gère l'état du token +// Fournisseur de contexte qui gère l'état du token et de l'utilisateur export const AuthProvider = ({ children }) => { const [token, setToken] = useState(localStorage.getItem("token")); + const [user, setUser] = useState(null); - // Met à jour le token lorsque localStorage change + // Met à jour le token et décode l'utilisateur useEffect(() => { - const handleStorageChange = () => { - setToken(localStorage.getItem("token")); - }; - - window.addEventListener("storage", handleStorageChange); - - return () => { - window.removeEventListener("storage", handleStorageChange); - }; - }, []); + if (token) { + try { + const decoded = jwtDecode(token); + setUser(decoded); + } catch (error) { + console.error("Erreur lors du décodage du token:", error); + setUser(null); + } + } else { + setUser(null); + } + }, [token]); const login = (newToken) => { localStorage.setItem("token", newToken); @@ -32,10 +36,11 @@ export const AuthProvider = ({ children }) => { const logout = () => { localStorage.removeItem("token"); setToken(null); + setUser(null); }; return ( - + {children} ); diff --git a/Front-end/src/ProtectedRoute.jsx b/Front-end/src/ProtectedRoute.jsx index 9df0306..02d7308 100644 --- a/Front-end/src/ProtectedRoute.jsx +++ b/Front-end/src/ProtectedRoute.jsx @@ -1,16 +1,22 @@ -import { useAuth } from './AuthContext'; // Utilisation du contexte d'authentification -import { Navigate } from 'react-router-dom'; // Utilisation de React Router pour la redirection +import React from "react"; +import { Navigate } from "react-router-dom"; +import { useAuth } from "./AuthContext"; // Utilisation du contexte d'authentification -function ProtectedRoute({ element }) { - const { token } = useAuth(); // Vérifier si un token existe, donc si l'utilisateur est authentifié +function ProtectedRoute({ element, allowedRoles }) { + const { token, user } = useAuth(); // Vérifier si un token existe, donc si l'utilisateur est authentifié // Si l'utilisateur n'est pas authentifié, redirigez-le vers la page de login if (!token) { return ; } - // Si l'utilisateur est authentifié, permettez l'accès à la route + // Si l'utilisateur est authentifié mais n'a pas le bon rôle + if (allowedRoles && !allowedRoles.includes(user?.role)) { + return ; + } + + // Si l'utilisateur est authentifié et a les bons rôles, permettez l'accès à la route return element; } -export default ProtectedRoute; // Export de la fonction +export default ProtectedRoute; diff --git a/Front-end/src/pages/Home.jsx b/Front-end/src/pages/Home.jsx index 885b92b..a97f1d3 100644 --- a/Front-end/src/pages/Home.jsx +++ b/Front-end/src/pages/Home.jsx @@ -8,6 +8,7 @@ function Home() { const [activeFilter, setActiveFilter] = useState('all'); const [name, setName] = useState([]); const { token, logout } = useAuth(); + const { user } = useAuth(); return (
    @@ -15,10 +16,13 @@ function Home() {

    Bienvenue dans ta ville intelligente.

    - {token ? ( - <>

    Tu es connecté

    - - ):( + {user ? ( + <> +

    Bienvenue, {user.name} {user.surname}!

    +

    Email : {user.sub}

    +

    Rôle : {user.role}

    + + ):(

    Non connecté

    )}

    From c82414b295bb5279575c1822d3faf56ce682089f Mon Sep 17 00:00:00 2001 From: Charles Mendiburu Date: Fri, 11 Apr 2025 20:01:28 +0200 Subject: [PATCH 04/11] Protection route en fct des roles --- Front-end/src/App.jsx | 2 +- Front-end/src/components/Header.jsx | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index ff6cf89..6aab120 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -36,7 +36,7 @@ function App() { {/* Routes protégées pour les admins et complexes */} } allowedRoles={['admin', 'complexe']} />} /> - } allowedRoles={['admin', 'complexe']} />} /> + } allowedRoles={['admin', 'complexe']} />} /> {/* Routes protégées pour tous les utilisateurs connectés */} } allowedRoles={['admin', 'complexe', 'user']} />} /> diff --git a/Front-end/src/components/Header.jsx b/Front-end/src/components/Header.jsx index 52dada0..35f0d20 100644 --- a/Front-end/src/components/Header.jsx +++ b/Front-end/src/components/Header.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { X, Menu, LogIn, UserPlus, LogOut, Settings } from "lucide-react"; +import { X, Menu, LogIn, UserPlus, LogOut, User } from "lucide-react"; import { Link } from "react-router-dom"; import { useAuth } from "../AuthContext"; @@ -80,12 +80,12 @@ function Header() { <>

  • setIsMenuOpen(false)} className="flex items-center gap-2 text-gray-600 hover:text-indigo-600" > - - Paramètres + + Profil
  • @@ -127,11 +127,11 @@ function Header() { ) : (
    setIsMenuOpen(false)} className="flex items-center gap-2 text-gray-600 hover:text-indigo-600" > - +
    setProprio(e.target.value)} + type="number" + value={proprio_id} + onChange={(e) => setProprio_id(e.target.value)} required disabled={verif||!isAdmin} /> diff --git a/Front-end/src/pages/Admin/AdminObjet.jsx b/Front-end/src/pages/Admin/AdminObjet.jsx index 8ad524b..336f876 100644 --- a/Front-end/src/pages/Admin/AdminObjet.jsx +++ b/Front-end/src/pages/Admin/AdminObjet.jsx @@ -205,7 +205,7 @@ function AdminObjet() { {obj.location} - {obj.proprio} + {obj.proprio_id} {obj.status} diff --git a/Front-end/src/pages/Home.jsx b/Front-end/src/pages/Home.jsx index a97f1d3..83dfd9d 100644 --- a/Front-end/src/pages/Home.jsx +++ b/Front-end/src/pages/Home.jsx @@ -21,6 +21,8 @@ function Home() {

    Bienvenue, {user.name} {user.surname}!

    Email : {user.sub}

    Rôle : {user.role}

    +

    Rôle : {user.id}

    + ):(

    Non connecté

    diff --git a/Front-end/src/pages/Profil.jsx b/Front-end/src/pages/Profil.jsx index fb60988..aa22826 100644 --- a/Front-end/src/pages/Profil.jsx +++ b/Front-end/src/pages/Profil.jsx @@ -2,11 +2,17 @@ import React, { useState, useEffect } from 'react'; import { Mail, User, Lock, Edit, Save } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { API_BASE_URL } from "../config"; +import { useAuth } from "../AuthContext"; import axios from "axios"; function Profil() { const [userData, setUserData] = useState({}); - const identifiant=4; + const { user } = useAuth(); + useEffect(() => { + if (user) { + console.log("user.role:", user.id); + } + }, [user]); const [formData, setFormData] = useState({ oldPassword: '', newPassword: '', @@ -21,7 +27,7 @@ function Profil() { useEffect(() => { axios .post(`${API_BASE_URL}/user`, { - id: identifiant, + id: user.id, }) .then((response) => { setUserData(response.data); @@ -30,7 +36,7 @@ function Profil() { .catch((error) => { console.error("Erreur lors de la récupération :", error); }); - }, []); + }, [user]); const handleChange = (e) => { const { name, value } = e.target; @@ -61,7 +67,7 @@ function Profil() { try { axios .post(`${API_BASE_URL}/changePassword`, { - id: userData.id, // Il faudrait s'assurer que userData contient l'ID de l'utilisateur + id: userData.id, oldPassword: formData.oldPassword, newPassword: formData.newPassword }) From 23f15fcf752e87026e9a52a7f970ca73c753028c Mon Sep 17 00:00:00 2001 From: Mathis Date: Fri, 11 Apr 2025 23:20:29 +0200 Subject: [PATCH 07/11] =?UTF-8?q?Avancement=20de=20la=20mise=20=C3=A0=20jo?= =?UTF-8?q?ur=20des=20points=20pour=20les=20users?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/starter/MainVerticle.java | 2 +- .../com/example/starter/QueryObjects.java | 22 +++- .../java/com/example/starter/QueryUsers.java | 3 +- .../java/com/example/starter/SetObjects.java | 109 ++++++++++-------- .../com/example/starter/SetWeatherData.java | 3 +- Front-end/src/App.jsx | 1 - Front-end/src/ProtectedRoute.jsx | 14 +-- Front-end/src/components/AlertInactive.jsx | 4 +- Front-end/src/components/Header.jsx | 45 +++++--- Front-end/src/components/InfoObject.jsx | 4 + Front-end/src/components/ModifObject.jsx | 4 + Front-end/src/components/ParticularMeteo.jsx | 5 +- Front-end/src/pages/Gestion/Gestion.jsx | 6 +- .../src/pages/Gestion/ObjectManagement.jsx | 5 +- Front-end/src/pages/Gestion/Objet.jsx | 20 +++- Front-end/src/pages/Login.jsx | 10 +- Front-end/src/pages/Profil.jsx | 1 + 17 files changed, 154 insertions(+), 104 deletions(-) diff --git a/Back-end/src/main/java/com/example/starter/MainVerticle.java b/Back-end/src/main/java/com/example/starter/MainVerticle.java index 1c64300..cf0ea07 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -47,7 +47,7 @@ public class MainVerticle extends AbstractVerticle { // Déclaration des routes router.get("/objets").handler(queryObjects::getObjects); - router.get("/objet").handler(queryObjects::getParticularObject); + router.post("/objet").handler(queryObjects::getParticularObject); router.post("/modifObjet").handler(setObjects::setInfoObjet); router.get("/wind").handler(queryWeather::getWindInfos); router.get("/meteo").handler(queryWeather::getMeteoInfos); diff --git a/Back-end/src/main/java/com/example/starter/QueryObjects.java b/Back-end/src/main/java/com/example/starter/QueryObjects.java index f984225..95a3427 100644 --- a/Back-end/src/main/java/com/example/starter/QueryObjects.java +++ b/Back-end/src/main/java/com/example/starter/QueryObjects.java @@ -41,9 +41,16 @@ public class QueryObjects { } public void getParticularObject(RoutingContext context) { - String id = context.request().getParam("id"); - // Integer idUser = body.getInteger("idUser"); - Integer idUser = 4; + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); + return; + } + String id = body.getString("id"); + + String idUser = body.getString("userId"); if (id == null) { context.response() .setStatusCode(400) @@ -66,10 +73,13 @@ public class QueryObjects { .end(new JsonObject().put("error", "Objet non trouvé").encode()); return; } - if (idUser != null) { - setUser.updateUserPoints(idUser, 1); + System.out.println(idUser); + + Boolean shouldUpdatePoints = body.getBoolean("shouldUpdatePoints", false); + + if (Boolean.TRUE.equals(shouldUpdatePoints) && idUser != null) { + setUser.updateUserPoints(Integer.parseInt(idUser), 1); } - ; context.response() .putHeader("content-type", "application/json: charset=UTF-8") .end(getInfosObjects(rows).encode()); diff --git a/Back-end/src/main/java/com/example/starter/QueryUsers.java b/Back-end/src/main/java/com/example/starter/QueryUsers.java index f9f744a..be4dae7 100644 --- a/Back-end/src/main/java/com/example/starter/QueryUsers.java +++ b/Back-end/src/main/java/com/example/starter/QueryUsers.java @@ -109,8 +109,7 @@ public class QueryUsers { .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); return; } - //Integer idUser = body.getInteger("idUser"); - Integer idUser = 4; + Integer idUser = body.getInteger("idUser"); databaseService.pool .preparedQuery("SELECT * FROM users WHERE id=?;") .execute(Tuple.of(idUser)) diff --git a/Back-end/src/main/java/com/example/starter/SetObjects.java b/Back-end/src/main/java/com/example/starter/SetObjects.java index 30045e4..ad16b15 100644 --- a/Back-end/src/main/java/com/example/starter/SetObjects.java +++ b/Back-end/src/main/java/com/example/starter/SetObjects.java @@ -7,12 +7,15 @@ import io.vertx.sqlclient.Tuple; public class SetObjects { private DatabaseService databaseService; private SetUser setUser; + public SetObjects(DatabaseService ddbs) { this.databaseService = ddbs; } - public void setUserHandler(SetUser setUser){ + + public void setUserHandler(SetUser setUser) { this.setUser = setUser; } + public void setInfoObjet(RoutingContext context) { JsonObject body = context.body().asJsonObject(); if (body == null) { @@ -22,8 +25,7 @@ public class SetObjects { return; } Integer id = body.getInteger("id"); - // Integer idUser = body.getInteger("idUser"); - Integer idUser = 4; + Integer idUser = body.getInteger("idUser"); String description = body.getString("description"); String type = body.getString("type"); String location = body.getString("location"); @@ -46,57 +48,60 @@ public class SetObjects { .end(new JsonObject().put("error", "Objet non trouvé").encode()); return; } - if(idUser!=null){ - setUser.updateUserPoints(idUser,1); - }; + Boolean shouldUpdatePoints = body.getBoolean("shouldUpdatePoints", false); + + if (Boolean.TRUE.equals(shouldUpdatePoints) && idUser != null) { + setUser.updateUserPoints(idUser, 1); + } context.response() .putHeader("content-type", "application/json: charset=UTF-8") .end(new JsonObject().put("success", "L'objet à bien été mis à jour").encode()); return; }); } - public void deleteObject(RoutingContext context){ + + public void deleteObject(RoutingContext context) { JsonObject body = context.body().asJsonObject(); - if(body== null){ + if (body == null) { context.response() - .setStatusCode(400) - .end(new JsonObject().put("error","Corps de la requête manquant").encode()); + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); return; } String id = body.getString("id"); databaseService.pool - .preparedQuery("DELETE FROM weather_objects WHERE id=?") - .execute(Tuple.of(Integer.parseInt(id))) - .onFailure(e->{ - System.err.println("Erreur de récupération de la BDD :"+e.getMessage()); - context.response() - .setStatusCode(500) - .end(new JsonObject().put("error","Erreur de récupération de la BDD").encode()); - }) - .onSuccess(rows -> { - if(rows.rowCount()==0){ + .preparedQuery("DELETE FROM weather_objects WHERE id=?") + .execute(Tuple.of(Integer.parseInt(id))) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + context.response() .setStatusCode(404) .end(new JsonObject().put("error", "Objet non trouvé").encode()); return; - } - context.response() - .putHeader("content-type","application/json: charset=UTF-8") - .end(new JsonObject().put("success", "L'objet à bien été supprimé").encode()); - return; - }); + } + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject().put("success", "L'objet à bien été supprimé").encode()); + return; + }); } - public void newObject(RoutingContext context){ + + public void newObject(RoutingContext context) { JsonObject body = context.body().asJsonObject(); - if(body== null){ + if (body == null) { context.response() - .setStatusCode(400) - .end(new JsonObject().put("error","Corps de la requête manquant").encode()); + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); return; } - // Integer idUser = body.getInteger("idUser"); - Integer idUser = 4; + Integer idUser = body.getInteger("proprio_id"); String name = body.getString("nom"); String description = body.getString("description"); String type = body.getString("type"); @@ -105,29 +110,31 @@ public class SetObjects { String batterieType = body.getString("batterieType"); Integer proprio_id = body.getInteger("proprio_id"); databaseService.pool - .preparedQuery("INSERT INTO weather_objects (name,description,type,location,status,type_batterie,proprio_id) VALUES (?,?,?,?,?,?,?)") - .execute(Tuple.of(name,description,type,location,status,batterieType,proprio_id)) - .onFailure(e->{ - System.err.println("Erreur de récupération de la BDD :"+e.getMessage()); - context.response() - .setStatusCode(500) - .end(new JsonObject().put("error","Erreur de récupération de la BDD").encode()); - }) - .onSuccess(rows -> { - if(rows.rowCount()==0){ + .preparedQuery( + "INSERT INTO weather_objects (name,description,type,location,status,type_batterie,proprio_id) VALUES (?,?,?,?,?,?,?)") + .execute(Tuple.of(name, description, type, location, status, batterieType, proprio_id)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + context.response() .setStatusCode(404) .end(new JsonObject().put("error", "Objet non trouvé").encode()); return; - } - if(idUser!=null){ - setUser.updateUserPoints(idUser,2); - }; - context.response() - .putHeader("content-type","application/json: charset=UTF-8") - .end(new JsonObject().put("success", "L'objet à bien été ajouté").encode()); - return; - }); + } + if (idUser != null) { + setUser.updateUserPoints(idUser, 2); + } + ; + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject().put("success", "L'objet à bien été ajouté").encode()); + return; + }); } } diff --git a/Back-end/src/main/java/com/example/starter/SetWeatherData.java b/Back-end/src/main/java/com/example/starter/SetWeatherData.java index d6ca45f..b6fb7c8 100644 --- a/Back-end/src/main/java/com/example/starter/SetWeatherData.java +++ b/Back-end/src/main/java/com/example/starter/SetWeatherData.java @@ -41,8 +41,7 @@ public class SetWeatherData { return; } String query = String.format("UPDATE range_data SET %s_min=?, %s_max=? WHERE station_id=?", type, type); - // Integer idUser = body.getInteger("idUser"); - Integer idUser = 4; + Integer idUser = body.getInteger("idUser"); databaseService.pool .preparedQuery( query) diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index fde0836..fbf874c 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -41,7 +41,6 @@ function App() { {/* Routes protégées pour tous les utilisateurs connectés */} } allowedRoles={['admin', 'complexe', 'user']} />} /> } allowedRoles={['admin', 'complexe', 'user']} />} /> - {/* Routes protégées pour les admins uniquement */} } allowedRoles={['admin']} />} /> } allowedRoles={['admin']} />} /> diff --git a/Front-end/src/ProtectedRoute.jsx b/Front-end/src/ProtectedRoute.jsx index 02d7308..b329341 100644 --- a/Front-end/src/ProtectedRoute.jsx +++ b/Front-end/src/ProtectedRoute.jsx @@ -4,19 +4,17 @@ import { useAuth } from "./AuthContext"; // Utilisation du contexte d'authentif function ProtectedRoute({ element, allowedRoles }) { const { token, user } = useAuth(); // Vérifier si un token existe, donc si l'utilisateur est authentifié - + // Si l'utilisateur n'est pas authentifié, redirigez-le vers la page de login if (!token) { return ; } - - // Si l'utilisateur est authentifié mais n'a pas le bon rôle - if (allowedRoles && !allowedRoles.includes(user?.role)) { - return ; + if(user){ + if (allowedRoles && !allowedRoles.includes(user?.role)) { + return ; + } + return element; } - - // Si l'utilisateur est authentifié et a les bons rôles, permettez l'accès à la route - return element; } export default ProtectedRoute; diff --git a/Front-end/src/components/AlertInactive.jsx b/Front-end/src/components/AlertInactive.jsx index 5f06a76..72df6b0 100644 --- a/Front-end/src/components/AlertInactive.jsx +++ b/Front-end/src/components/AlertInactive.jsx @@ -1,9 +1,11 @@ import React, {useState} from "react"; import { TriangleAlert,X } from "lucide-react"; +import { useAuth } from "../AuthContext"; function AlertInactive({affAlert,setAffAlert}) { + const { user } = useAuth(); return ( - (affAlert&&( + (affAlert&&(user?.role!=="user")&&(
    - + )}
    +

    Bienvenue dans le module Gestion.

    +

    - Ce module vous permet de gérer les capteur et stations connectés de France de manière simple et efficace. + Ce module vous permet de gérer les capteurs et stations connectés de France de manière simple et efficace.

    @@ -42,6 +45,7 @@ function Gestion() { Explorer les objets
    +
    diff --git a/Front-end/src/pages/Gestion/ObjectManagement.jsx b/Front-end/src/pages/Gestion/ObjectManagement.jsx index a7fedd2..513bc0c 100644 --- a/Front-end/src/pages/Gestion/ObjectManagement.jsx +++ b/Front-end/src/pages/Gestion/ObjectManagement.jsx @@ -3,8 +3,9 @@ import { Search, ArrowRight, RadioTower,Plus } from "lucide-react"; import { useEffect, useState } from "react"; import axios from "axios"; import { API_BASE_URL } from "../../config"; - +import { useAuth } from "../../AuthContext"; function ObjectManagement() { + const {user} = useAuth(); const [searchQuery, setSearchQuery] = useState(""); const [activeFilter, setActiveFilter] = useState(""); const [objects, setObjects] = useState([]); @@ -34,7 +35,7 @@ function ObjectManagement() {

    - Gestion des Objets connectés. + {(user?.role!=="user")?("Gestion"):("Visualisation")} des Objets connectés.

    diff --git a/Front-end/src/pages/Gestion/Objet.jsx b/Front-end/src/pages/Gestion/Objet.jsx index cd40f07..2d484bd 100644 --- a/Front-end/src/pages/Gestion/Objet.jsx +++ b/Front-end/src/pages/Gestion/Objet.jsx @@ -12,7 +12,9 @@ import WindInfo from "../../components/WindInfo"; import MeteoInfos from "../../components/MeteoInfos"; import MeteoGraph from "../../components/MeteoGraph"; import BatterieInfo from "../../components/BatterieInfo"; +import { useAuth } from "../../AuthContext"; function Objet() { + const {user} =useAuth(); const identifiant = new URLSearchParams(window.location.search).get("id"); const [object, setObject] = useState({}); const [graphStates, setGraphStates] = useState({ @@ -28,12 +30,20 @@ function Objet() { humidity: useRef(null), wind: useRef(null), }; - useEffect(() => { - axios.get(`${API_BASE_URL}/objet?id=${identifiant}`).then((response) => { - setObject(response.data[0]); - }); - }, [identifiant]); + axios + .post(`${API_BASE_URL}/objet`, { + id: identifiant, + userId:user.id, + shouldUpdatePoints:true, + }) + .then((response) => { + setObject(response.data[0]); + }) + .catch((error) => { + console.error("Erreur lors de la récupération :", error); + }); + }, [user]); return object && object.id ? (
    diff --git a/Front-end/src/pages/Login.jsx b/Front-end/src/pages/Login.jsx index 21e6fae..8eca535 100644 --- a/Front-end/src/pages/Login.jsx +++ b/Front-end/src/pages/Login.jsx @@ -11,8 +11,8 @@ function Login() { password: "", }); const [error, setError] = useState(""); - const { login } = useAuth(); // Utilisation du hook useAuth pour accéder à la fonction login - const navigate = useNavigate(); // Initialisation de useNavigate + const { login } = useAuth(); + const navigate = useNavigate(); const handleChange = (e) => { const { name, value } = e.target; @@ -20,13 +20,12 @@ function Login() { ...prev, [name]: value, })); - // Réinitialiser l'erreur lorsque l'utilisateur commence à modifier le formulaire if (error) setError(""); }; const handleSubmit = async (e) => { e.preventDefault(); - setError(""); // Réinitialiser les erreurs à chaque tentative + setError(""); try { const response = await axios.post(`${API_BASE_URL}/login`, formData, { @@ -46,7 +45,6 @@ function Login() { console.error("Erreur lors de la connexion", error); if (error.response) { - // Le serveur a répondu avec un statut d'erreur (4xx, 5xx) if (error.response.status === 401) { setError("Email ou mot de passe incorrect"); } else if (error.response.status === 422) { @@ -60,12 +58,10 @@ function Login() { ); } } else if (error.request) { - // La requête a été faite mais pas de réponse reçue setError( "Impossible de joindre le serveur. Vérifiez votre connexion internet." ); } else { - // Une erreur s'est produite lors de la configuration de la requête setError("Une erreur s'est produite. Veuillez réessayer."); } } diff --git a/Front-end/src/pages/Profil.jsx b/Front-end/src/pages/Profil.jsx index aa22826..fc1d89d 100644 --- a/Front-end/src/pages/Profil.jsx +++ b/Front-end/src/pages/Profil.jsx @@ -7,6 +7,7 @@ import axios from "axios"; function Profil() { const [userData, setUserData] = useState({}); + const { user } = useAuth(); useEffect(() => { if (user) { From acfebdaf0fc910b138289226366d67bb0bbd3ee8 Mon Sep 17 00:00:00 2001 From: Mathis Date: Fri, 11 Apr 2025 23:39:18 +0200 Subject: [PATCH 08/11] Ajout des points pour les limites --- Back-end/src/main/java/com/example/starter/MainVerticle.java | 1 + .../src/main/java/com/example/starter/SetWeatherData.java | 5 +++-- Front-end/src/components/ParticularMeteo.jsx | 1 + Front-end/src/components/UserInfosObject.jsx | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Back-end/src/main/java/com/example/starter/MainVerticle.java b/Back-end/src/main/java/com/example/starter/MainVerticle.java index cf0ea07..e467323 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -43,6 +43,7 @@ public class MainVerticle extends AbstractVerticle { SetUser setUser = new SetUser(databaseService); setObjects.setUserHandler(setUser); queryObjects.setUserHandler(setUser); + setWeatherData.setUserHandler(setUser); // Déclaration des routes diff --git a/Back-end/src/main/java/com/example/starter/SetWeatherData.java b/Back-end/src/main/java/com/example/starter/SetWeatherData.java index b6fb7c8..d998e73 100644 --- a/Back-end/src/main/java/com/example/starter/SetWeatherData.java +++ b/Back-end/src/main/java/com/example/starter/SetWeatherData.java @@ -13,8 +13,8 @@ public class SetWeatherData { this.databaseService = ddbs; } public void setUserHandler(SetUser setUser) { - this.setUser = setUser; - } + this.setUser = setUser; + } public void setRangeData(RoutingContext context) { JsonObject body = context.body().asJsonObject(); if (body == null) { @@ -42,6 +42,7 @@ public class SetWeatherData { } String query = String.format("UPDATE range_data SET %s_min=?, %s_max=? WHERE station_id=?", type, type); Integer idUser = body.getInteger("idUser"); + System.out.println("User : "+idUser); databaseService.pool .preparedQuery( query) diff --git a/Front-end/src/components/ParticularMeteo.jsx b/Front-end/src/components/ParticularMeteo.jsx index 6f16343..2a9f233 100644 --- a/Front-end/src/components/ParticularMeteo.jsx +++ b/Front-end/src/components/ParticularMeteo.jsx @@ -73,6 +73,7 @@ function ParticularMeteo({ axios .post(`${API_BASE_URL}/modifRangeData`, { id: identifiant, + idUser:user.id, min: parseFloat(rangeValue[0]), max: parseFloat(rangeValue[1]), type, diff --git a/Front-end/src/components/UserInfosObject.jsx b/Front-end/src/components/UserInfosObject.jsx index 1821e50..f26f051 100644 --- a/Front-end/src/components/UserInfosObject.jsx +++ b/Front-end/src/components/UserInfosObject.jsx @@ -4,7 +4,7 @@ import axios from "axios"; import { API_BASE_URL } from "../config"; -function InfoObject({ object,defafficherModif }) { +function UserInfosObject({ object,defafficherModif }) { axios .post(`${API_BASE_URL}/publicUser`, { id: object.user, @@ -58,4 +58,4 @@ function InfoObject({ object,defafficherModif }) { ); } -export default InfoObject; +export default UserInfosObject; From 41e8ed63dc7034b43a1a719719f1c1cfa3682fcf Mon Sep 17 00:00:00 2001 From: Mathis Date: Sat, 12 Apr 2025 00:11:50 +0200 Subject: [PATCH 09/11] =?UTF-8?q?Ajout=20de=20l'information=20du=20propri?= =?UTF-8?q?=C3=A9taire=20de=20l'objet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/starter/QueryUsers.java | 2 +- Front-end/src/components/UserInfosObject.jsx | 51 ++++++++----------- Front-end/src/pages/Gestion/Objet.jsx | 3 ++ 3 files changed, 24 insertions(+), 32 deletions(-) diff --git a/Back-end/src/main/java/com/example/starter/QueryUsers.java b/Back-end/src/main/java/com/example/starter/QueryUsers.java index be4dae7..792f575 100644 --- a/Back-end/src/main/java/com/example/starter/QueryUsers.java +++ b/Back-end/src/main/java/com/example/starter/QueryUsers.java @@ -109,7 +109,7 @@ public class QueryUsers { .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); return; } - Integer idUser = body.getInteger("idUser"); + Integer idUser = body.getInteger("id"); databaseService.pool .preparedQuery("SELECT * FROM users WHERE id=?;") .execute(Tuple.of(idUser)) diff --git a/Front-end/src/components/UserInfosObject.jsx b/Front-end/src/components/UserInfosObject.jsx index f26f051..0ea3c13 100644 --- a/Front-end/src/components/UserInfosObject.jsx +++ b/Front-end/src/components/UserInfosObject.jsx @@ -1,58 +1,47 @@ -import React from "react"; -import { Info } from "lucide-react"; +import React,{useEffect, useState} from "react"; +import { User } from "lucide-react"; import axios from "axios"; import { API_BASE_URL } from "../config"; -function UserInfosObject({ object,defafficherModif }) { - axios +function UserInfosObject({ user}) { + const [userInfo,setuserInfo]=useState({}); + useEffect(()=>{ + console.log(user); + axios .post(`${API_BASE_URL}/publicUser`, { - id: object.user, + id: user, }) .then((response) => { + setuserInfo(response.data); console.log("Modification réussie :", response.data); }) .catch((error) => { console.error("Erreur lors de la modification :", error); }); - defafficherModif(false); - window.location.reload(); + },[user]); + return ( -
    +
    - +
    -

    Informations

    +

    Propriétaire

    -

    Description :

    -

    {object.description}

    +

    Pseudo :

    +

    {userInfo.pseudo}

    -

    Type :

    -

    {object.type}

    +

    Genre :

    +

    {userInfo.gender}

    -

    Localisation :

    -

    {object.location}

    -
    - -
    -

    Status :

    -

    {object.status}

    -
    - -
    -

    - Derniere mise à jour : -

    -

    {object.last_update}

    -
    -
    - defafficherModif(true))}>Modifier ces infos +

    Nombre de points :

    +

    {userInfo.points}

    ); diff --git a/Front-end/src/pages/Gestion/Objet.jsx b/Front-end/src/pages/Gestion/Objet.jsx index 2d484bd..7b76169 100644 --- a/Front-end/src/pages/Gestion/Objet.jsx +++ b/Front-end/src/pages/Gestion/Objet.jsx @@ -13,6 +13,7 @@ import MeteoInfos from "../../components/MeteoInfos"; import MeteoGraph from "../../components/MeteoGraph"; import BatterieInfo from "../../components/BatterieInfo"; import { useAuth } from "../../AuthContext"; +import UserInfosObject from "../../components/UserInfosObject"; function Objet() { const {user} =useAuth(); const identifiant = new URLSearchParams(window.location.search).get("id"); @@ -71,6 +72,8 @@ function Objet() { graphRefs={graphRefs} /> + +
    {graphStates.wind && } From 4a43437b47644b57aec8a15f13e513514adf0401 Mon Sep 17 00:00:00 2001 From: Mathis Date: Sat, 12 Apr 2025 00:27:04 +0200 Subject: [PATCH 10/11] Ajout du responsive sur les filtres --- .../src/pages/Gestion/ObjectManagement.jsx | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/Front-end/src/pages/Gestion/ObjectManagement.jsx b/Front-end/src/pages/Gestion/ObjectManagement.jsx index 513bc0c..3daf55d 100644 --- a/Front-end/src/pages/Gestion/ObjectManagement.jsx +++ b/Front-end/src/pages/Gestion/ObjectManagement.jsx @@ -1,15 +1,17 @@ import React from "react"; -import { Search, ArrowRight, RadioTower,Plus } from "lucide-react"; +import { Search, ArrowRight, RadioTower, Plus } from "lucide-react"; import { useEffect, useState } from "react"; import axios from "axios"; import { API_BASE_URL } from "../../config"; import { useAuth } from "../../AuthContext"; + function ObjectManagement() { const {user} = useAuth(); const [searchQuery, setSearchQuery] = useState(""); const [activeFilter, setActiveFilter] = useState(""); const [objects, setObjects] = useState([]); - const [nbAffObject,setnbAffObject] = useState(6); + const [nbAffObject, setnbAffObject] = useState(6); + const filteredDATA = objects.filter((node) => { const matchesSearchQuery = searchQuery === "" || @@ -30,6 +32,7 @@ function ObjectManagement() { setObjects(response.data); }); }, []); + return (
    @@ -52,10 +55,12 @@ function ObjectManagement() { onChange={(e) => setSearchQuery(e.target.value)} />
    -
    + + {/* Filtres responsifs - utilisation de flex-wrap et responsive spacing */} +
    -
    + + {/* Grille responsive pour les objets */} +
    {filteredDATA.length === 0 ? ( -

    Aucun objet trouvé

    +

    Aucun objet trouvé

    ) : ( - filteredDATA.slice(0,nbAffObject).map((object) => ( + filteredDATA.slice(0, nbAffObject).map((object) => (
    {object.status === "active" ? (
    @@ -144,14 +151,21 @@ function ObjectManagement() { )) )}
    - {(nbAffObject - - + +
    )}
    ); } -export default ObjectManagement; + +export default ObjectManagement; \ No newline at end of file From 72d3211d5e5e9be4a63692bf0d0bfd3022e5a019 Mon Sep 17 00:00:00 2001 From: Mathis Date: Sat, 12 Apr 2025 10:25:23 +0200 Subject: [PATCH 11/11] =?UTF-8?q?Ajout=20de=20la=20requete=20pour=20les=20?= =?UTF-8?q?cat=C3=A9gorie=20et=20impl=C3=A9mentation=20dans=20creer=20un?= =?UTF-8?q?=20objet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/starter/MainVerticle.java | 1 + .../com/example/starter/QueryObjects.java | 20 ++++++++++++ Front-end/src/components/FormNewObject.jsx | 32 +++++++++++++------ 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Back-end/src/main/java/com/example/starter/MainVerticle.java b/Back-end/src/main/java/com/example/starter/MainVerticle.java index e467323..fa171c3 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -63,6 +63,7 @@ public class MainVerticle extends AbstractVerticle { router.post("/updateProfil").handler(setUser::updateUserProfile); router.post("/changePassword").handler(setUser::changeUserPassword); router.post("/publicUser").handler(queryUsers::getPublicUser); + router.get("/getCategories").handler(queryObjects::getCategories); // Routes d'authentification router.post("/signup").handler(authHandler::handleSignup); router.post("/login").handler(authHandler::handleLogin); diff --git a/Back-end/src/main/java/com/example/starter/QueryObjects.java b/Back-end/src/main/java/com/example/starter/QueryObjects.java index 95a3427..abc5bca 100644 --- a/Back-end/src/main/java/com/example/starter/QueryObjects.java +++ b/Back-end/src/main/java/com/example/starter/QueryObjects.java @@ -40,6 +40,26 @@ public class QueryObjects { }); } + public void getCategories(RoutingContext context) { + databaseService.pool + .query("SELECT DISTINCT type FROM weather_objects;") + .execute() + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + JsonArray types = new JsonArray(); + rows.forEach(row -> types.add(row.getString("type"))); + + context.response() + .putHeader("content-type", "application/json; charset=UTF-8") + .end(types.encode()); + }); + } + public void getParticularObject(RoutingContext context) { JsonObject body = context.body().asJsonObject(); if (body == null) { diff --git a/Front-end/src/components/FormNewObject.jsx b/Front-end/src/components/FormNewObject.jsx index c9913b8..8d138f1 100644 --- a/Front-end/src/components/FormNewObject.jsx +++ b/Front-end/src/components/FormNewObject.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { BadgePlus } from "lucide-react"; import axios from "axios"; import { API_BASE_URL } from "../config"; @@ -6,12 +6,12 @@ import { useAuth } from "../AuthContext"; function FormNewObject({ isAdmin }) { const { user } = useAuth(); - + const [categorie, setCategorie] = useState({}); const [description, setDescription] = useState(""); const [type, setType] = useState(""); const [location, setLocalisation] = useState(""); - const [proprio_id,setProprio_id] = useState(user?.id); - const [batterieType,setBatterieType] = useState(""); + const [proprio_id, setProprio_id] = useState(user?.id); + const [batterieType, setBatterieType] = useState(""); const [status, setStatus] = useState("active"); const [nom, setNom] = useState(""); const [Response, setResponse] = useState(null); @@ -32,7 +32,7 @@ function FormNewObject({ isAdmin }) { location, status, batterieType, - proprio_id + proprio_id, }) .then((response) => { setMessRequete("Votre objet à bien été enregistré !"); @@ -49,6 +49,12 @@ function FormNewObject({ isAdmin }) { setVerif(true); } } + useEffect(() => { + axios.get(`${API_BASE_URL}/getCategories`).then((response) => { + setCategorie(response.data); + console.log(response.data); + }); + }, []); function resetForm() { setNom(""); setStatus("active"); @@ -56,7 +62,7 @@ function FormNewObject({ isAdmin }) { setType(""); setLocalisation(""); setBatterieType(""); - if(isAdmin)set_id(""); + if (isAdmin) set_id(""); setActive(true); } function handleCancel() { @@ -136,15 +142,21 @@ function FormNewObject({ isAdmin }) { > Type : - setType(e.target.value)} required disabled={verif} - /> + > + + {categorie.map((cat, index) => ( + + ))} +
    @@ -195,7 +207,7 @@ function FormNewObject({ isAdmin }) { value={proprio_id} onChange={(e) => setProprio_id(e.target.value)} required - disabled={verif||!isAdmin} + disabled={verif || !isAdmin} />