From 00c5b9ce5307f5e78279bc28c677304b3faa7ee1 Mon Sep 17 00:00:00 2001 From: Charles Mendiburu Date: Mon, 7 Apr 2025 00:10:27 +0200 Subject: [PATCH] =?UTF-8?q?Merge=20et=20cr=C3=A9ation=20page=20de=20settin?= =?UTF-8?q?gs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/starter/AuthHandler.java | 113 +++++++ .../com/example/starter/DatabaseService.java | 2 + .../com/example/starter/JwtAuthProvider.java | 18 + .../com/example/starter/MainVerticle.java | 315 ++++-------------- Front-end/src/App.jsx | 2 + Front-end/src/components/Header.jsx | 11 +- Front-end/src/pages/Home.jsx | 216 ++++++------ Front-end/src/pages/Settings.jsx | 161 +++++++++ Front-end/src/pages/Signup.jsx | 2 + 9 files changed, 473 insertions(+), 367 deletions(-) create mode 100644 Back-end/src/main/java/com/example/starter/AuthHandler.java create mode 100644 Back-end/src/main/java/com/example/starter/JwtAuthProvider.java create 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 new file mode 100644 index 0000000..2052b71 --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/AuthHandler.java @@ -0,0 +1,113 @@ +package com.example.starter; + +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import at.favre.lib.crypto.bcrypt.BCrypt; +import io.vertx.ext.auth.jwt.JWTAuth; +import com.example.starter.auth.JwtAuthProvider; +import io.vertx.sqlclient.Tuple; + +public class AuthHandler { + private final DatabaseService databaseService; + private final JWTAuth jwtAuth; + + public AuthHandler(DatabaseService databaseService, JWTAuth jwtAuth) { + this.databaseService = databaseService; + this.jwtAuth = jwtAuth; + } + + public void handleSignup(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Requête invalide").encode()); + return; + } + + String name = body.getString("name"); + String surname = body.getString("surname"); + String email = body.getString("email"); + String gender = body.getString("gender"); + String password = body.getString("password"); + + if (name == null || surname == null || email == null || gender == null || password == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Tous les champs sont requis").encode()); + return; + } + + 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)) + .onSuccess(result -> { + context.response() + .setStatusCode(201) + .end(new JsonObject().put("message", "Utilisateur inscrit avec succès").encode()); + }) + .onFailure(err -> { + System.err.println("Erreur d'inscription : " + err.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur d'inscription").encode()); + }); + } + + public void handleLogin(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Requête invalide").encode()); + return; + } + + String email = body.getString("email"); + String password = body.getString("password"); + + if (email == null || password == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Email et mot de passe requis").encode()); + return; + } + + databaseService.pool + .preparedQuery("SELECT password 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; + } + + String storedHashedPassword = result.iterator().next().getString("password"); + BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword); + + if (verification.verified) { + JsonObject claims = new JsonObject().put("sub", email).put("role", "user"); + 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()); + }); + } +} diff --git a/Back-end/src/main/java/com/example/starter/DatabaseService.java b/Back-end/src/main/java/com/example/starter/DatabaseService.java index e10ab51..f4e05d5 100644 --- a/Back-end/src/main/java/com/example/starter/DatabaseService.java +++ b/Back-end/src/main/java/com/example/starter/DatabaseService.java @@ -3,6 +3,8 @@ package com.example.starter; import io.vertx.core.Vertx; import io.vertx.jdbcclient.JDBCConnectOptions; import io.vertx.jdbcclient.JDBCPool; +import io.vertx.ext.auth.jwt.JWTAuth; +import io.vertx.ext.auth.jwt.JWTAuthOptions; import io.vertx.sqlclient.PoolOptions; public class DatabaseService { diff --git a/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java b/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java new file mode 100644 index 0000000..01d26d9 --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java @@ -0,0 +1,18 @@ +package com.example.starter.auth; + +import io.vertx.core.Vertx; +import io.vertx.ext.auth.jwt.JWTAuth; +import io.vertx.ext.auth.jwt.JWTAuthOptions; +import io.vertx.ext.auth.KeyStoreOptions; +import com.example.starter.auth.JwtAuthProvider; + + +public class JwtAuthProvider { + + public static JWTAuth createJwtAuth(Vertx vertx) { + return JWTAuth.create(vertx, new JWTAuthOptions() + .setKeyStore(new KeyStoreOptions() + .setPath("keystore.jceks") + .setPassword("secret"))); + } +} 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 971c6e2..519759e 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -1,274 +1,71 @@ package com.example.starter; -import io.vertx.ext.web.handler.BodyHandler; -import io.vertx.ext.web.handler.BodyHandler; -import io.vertx.ext.web.handler.CorsHandler; -import io.vertx.core.http.HttpMethod; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; - +import io.vertx.core.http.HttpMethod; import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; -import at.favre.lib.crypto.bcrypt.BCrypt; +import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.ext.web.handler.CorsHandler; import io.vertx.ext.auth.jwt.JWTAuth; -import io.vertx.ext.auth.jwt.JWTAuthOptions; -import io.vertx.ext.auth.KeyStoreOptions; -import io.vertx.ext.auth.authentication.TokenCredentials; +import com.example.starter.auth.JwtAuthProvider; import io.vertx.ext.web.handler.JWTAuthHandler; public class MainVerticle extends AbstractVerticle { - private DatabaseService databaseService; - private Router router; // Déclaration du router en variable de classe - private JWTAuth jwtAuth; // Déclaration au niveau de la classe + private DatabaseService databaseService; + private Router router; + @Override + public void start(Promise startPromise) throws Exception { + databaseService = new DatabaseService(vertx); + + // Initialisation du fournisseur JWT + JWTAuth jwtAuth = JwtAuthProvider.createJwtAuth(vertx); + - @Override - public void start(Promise startPromise) throws Exception { - databaseService = new DatabaseService(vertx); + // Initialisation du routeur + router = Router.router(vertx); + router.route().handler(BodyHandler.create()); + router.route().handler(CorsHandler.create() + .addOrigin("*") + .allowedMethod(HttpMethod.GET) + .allowedMethod(HttpMethod.POST) + .allowedHeader("Content-Type") + .allowedHeader("Authorization")); + + // Protéger toutes les routes commençant par "/api/" + router.route("/api/*").handler(JWTAuthHandler.create(jwtAuth)); - - this.jwtAuth = JWTAuth.create(vertx, new JWTAuthOptions() - .setKeyStore(new KeyStoreOptions() - .setPath("keystore.jceks") - .setPassword("secret"))); - - - - // Initialisation du router - router = Router.router(vertx); - - // Activation du BodyHandler pour pouvoir lire les requêtes JSON (important pour POST) - router.route().handler(BodyHandler.create()); - - // Gestion des CORS - QueryObjects queryObjects = new QueryObjects(databaseService); - QueryWeatherData queryWeather = new QueryWeatherData(databaseService); - SetObjects setObjects = new SetObjects(databaseService); - // Create a Router - Router router = Router.router(vertx); - router.route().handler(BodyHandler.create()); - router.route().handler(CorsHandler.create() - .addOrigin("*") - .allowedMethod(HttpMethod.GET) - .allowedMethod(HttpMethod.POST) - .allowedHeader("Content-Type") - .allowedHeader("Authorization")); - - // Déclaration des routes - router.get("/objets").handler(this::getObjects); - router.get("/objet").handler(this::getParticularObject); - router.post("/signup").handler(this::handleSignup); // Route pour l'inscription - router.post("/login").handler(this::handleLogin); // Route pour la connexion - // Protéger toutes les routes commençant par "/api/" - router.route("/api/*").handler(JWTAuthHandler.create(jwtAuth)); - router.get("/objets").handler(queryObjects::getObjects); - router.get("/objet").handler(queryObjects::getParticularObject); - router.post("/modifObjet").handler(setObjects::setInfoObjet); - router.get("/wind").handler(queryWeather::getWindInfos); - router.get("/meteo").handler(queryWeather::getMeteoInfos); - router.post("/addObject").handler(setObjects::newObject); - - // Création du serveur HTTP - vertx.createHttpServer() - .requestHandler(router) - .listen(8888) - .onSuccess(server -> { - System.out.println("HTTP server started on port " + server.actualPort()); - startPromise.complete(); - }) - .onFailure(throwable -> { - throwable.printStackTrace(); - startPromise.fail(throwable); - }); - - - } - - // Récupération des objets - private void getObjects(RoutingContext context) { - databaseService.pool - .query("SELECT * 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 -> { - context.response() - .putHeader("content-type", "application/json; charset=UTF-8") - .end(getInfosObjects(rows).encode()); - }); - } - - // Récupération d'un objet spécifique - private void getParticularObject(RoutingContext context) { - String id = context.request().getParam("id"); - if (id == null) { - context.response() - .setStatusCode(400) - .end(new JsonObject().put("error", "Paramètre 'id' manquant").encode()); - return; + + // Initialisation des handlers de requêtes + QueryObjects queryObjects = new QueryObjects(databaseService); + QueryWeatherData queryWeather = new QueryWeatherData(databaseService); + SetObjects setObjects = new SetObjects(databaseService); + AuthHandler authHandler = new AuthHandler(databaseService, jwtAuth); + + // Déclaration des routes + router.get("/objets").handler(queryObjects::getObjects); + router.get("/objet").handler(queryObjects::getParticularObject); + router.post("/modifObjet").handler(setObjects::setInfoObjet); + router.get("/wind").handler(queryWeather::getWindInfos); + router.get("/meteo").handler(queryWeather::getMeteoInfos); + router.post("/addObject").handler(setObjects::newObject); + + // Routes d'authentification + router.post("/signup").handler(authHandler::handleSignup); + router.post("/login").handler(authHandler::handleLogin); + + // Création du serveur HTTP + vertx.createHttpServer() + .requestHandler(router) + .listen(8888) + .onSuccess(server -> { + System.out.println("HTTP server started on port " + server.actualPort()); + startPromise.complete(); + }) + .onFailure(throwable -> { + throwable.printStackTrace(); + startPromise.fail(throwable); + }); } - - databaseService.pool - .preparedQuery("SELECT * 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("Erreur", "Erreur de récupération de la BDD").encode()); - }) - .onSuccess(rows -> { - if (rows.size() == 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(getInfosObjects(rows).encode()); - }); - } - - // Convertit les résultats SQL en JSON - private JsonArray getInfosObjects(RowSet rows) { - JsonArray objects = new JsonArray(); - for (Row row : rows) { - JsonObject object = new JsonObject() - .put("id", row.getInteger("id")) - .put("name", row.getString("name")) - .put("description", row.getString("description")) - .put("type", row.getString("type")) - .put("location", row.getString("location")); - objects.add(object); - } - return objects; - } - - private void handleSignup(RoutingContext context) { - JsonObject body = context.body().asJsonObject(); - - if (body == null) { - context.response() - .setStatusCode(400) - .end(new JsonObject().put("error", "Requête invalide").encode()); - return; - } - - // Log pour vérifier le corps de la requête - System.out.println("Received body: " + body.encodePrettily()); - - String name = body.getString("name"); - String surname = body.getString("surname"); - String email = body.getString("email"); - String gender = body.getString("gender"); - String password = body.getString("password"); - - if (name == null || surname == null || email == null || gender == null || password == null) { - context.response() - .setStatusCode(400) - .end(new JsonObject().put("error", "Tous les champs sont requis").encode()); - return; - } - - // Hashage du mot de passe avec BCrypt - 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)) - .onSuccess(result -> { - context.response() - .setStatusCode(201) - .end(new JsonObject().put("message", "Utilisateur inscrit avec succès").encode()); - vertx.setTimer(2000, id -> { - context.response() - .putHeader("Location", "/") // Redirection vers la page d'accueil - .setStatusCode(303) - .end(); - }); - }) - .onFailure(err -> { - // Log pour afficher l'erreur d'insertion - System.err.println("Erreur d'inscription : " + err.getMessage()); - context.response() - .setStatusCode(500) - .end(new JsonObject().put("error", "Erreur d'inscription").encode()); - }); - } - - //Méthode de ocnnexion - private void handleLogin(RoutingContext context) { - JsonObject body = context.body().asJsonObject(); - - if (body == null) { - context.response() - .setStatusCode(400) - .end(new JsonObject().put("error", "Requête invalide").encode()); - return; - } - - System.out.println("Received login request: " + body.encodePrettily()); - - String email = body.getString("email"); - String password = body.getString("password"); - - if (email == null || password == null) { - context.response() - .setStatusCode(400) - .end(new JsonObject().put("error", "Email et mot de passe requis").encode()); - return; - } - - databaseService.pool - .preparedQuery("SELECT password FROM users WHERE email = ?") // Requête sql qui evite les injections - .execute(Tuple.of(email)) // Remplace le ? par la valeur de l'email - .onSuccess(result -> { //Si la requête s'exécute bien , on obtient le resultat dans result - if (result.rowCount() == 0) { // Cas : Aucun utilisateur trouvé - context.response() - .setStatusCode(401) - .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); - return; - } - - Row row = result.iterator().next(); - String storedHashedPassword = row.getString("password"); - - // Vérification du mot de passe avec BCrypt - BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword); - - if (verification.verified) { - System.out.println("Connexion réussi"); - //Génération du token JWT - JsonObject claims = new JsonObject().put("sub",email).put("role", "user"); - 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/src/App.jsx b/Front-end/src/App.jsx index 5851df6..ea42a87 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -9,6 +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'; function App() { return ( @@ -25,6 +26,7 @@ function App() { } /> } /> } /> + } /> diff --git a/Front-end/src/components/Header.jsx b/Front-end/src/components/Header.jsx index 31d0542..9bbd666 100644 --- a/Front-end/src/components/Header.jsx +++ b/Front-end/src/components/Header.jsx @@ -1,7 +1,6 @@ import React, { useState, useEffect } from "react"; -import { LogIn, UserPlus, LogOut } from "lucide-react"; +import { LogIn, UserPlus, LogOut, Settings } from "lucide-react"; import { Link } from "react-router-dom"; - import { useAuth } from "../AuthContext"; function Header() { @@ -11,7 +10,7 @@ function Header() {
-

Hopital de Pau

+

VigiMétéo

{token ? ( + <> + + + + + ) : ( <> diff --git a/Front-end/src/pages/Home.jsx b/Front-end/src/pages/Home.jsx index 50b517e..885b92b 100644 --- a/Front-end/src/pages/Home.jsx +++ b/Front-end/src/pages/Home.jsx @@ -1,117 +1,123 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; +import { Search, MapPin, Calendar, Bus, ArrowRight, LogIn, UserPlus } from 'lucide-react'; +import { useAuth } from "../AuthContext"; + function Home() { - const [searchQuery, setSearchQuery] = useState(''); - const [activeFilter, setActiveFilter] = useState('all'); - const [name, setName] = useState([]); - return ( -
-
-
-

- Bienvenue dans ta ville intelligente.

- {token ? ( - <>Home - - ):( -

Non connecté

- )} -

- Découvrez tout ce que votre ville a à offrir - des événements locaux aux horaires de transport, le tout en un seul endroit. + const [searchQuery, setSearchQuery] = useState(''); + const [activeFilter, setActiveFilter] = useState('all'); + const [name, setName] = useState([]); + const { token, logout } = useAuth(); + + return ( +

+
+
+

+ Bienvenue dans ta ville intelligente.

+ {token ? ( + <>

Tu es connecté

+ + ):( +

Non connecté

+ )} +

+ Découvrez tout ce que votre ville a à offrir - des événements locaux aux horaires de transport, le tout en un seul endroit. +

+
+ +
+
+ + setSearchQuery(e.target.value)} + /> +
+
+ + + + +
+
+ + {/* Features Grid */} +
+
+
+ +
+

Points d'intérêt

+

+ Découvrez les meilleurs endroits de votre ville, qu'il s'agisse de restaurants, de parcs ou de lieux culturels.

+ + Explorer les lieux +
- -
-
- - setSearchQuery(e.target.value)} - /> -
-
- - - - + +
+
+
+

Evenements locaux

+

+ Restez informé des derniers événements, festivals et rassemblements communautaires dans votre région. +

+ + Voir les événements +
- - {/* Features Grid */} -
-
-
- -
-

Points d'intérêt

-

- Découvrez les meilleurs endroits de votre ville, qu'il s'agisse de restaurants, de parcs ou de lieux culturels. -

- - Explorer les lieux - -
- -
-
- -
-

Evenements locaux

-

- Restez informé des derniers événements, festivals et rassemblements communautaires dans votre région. -

- - Voir les événements - -
- -
-
- -
-

Transports publics

-

- Accédez en temps réel aux horaires et aux itinéraires des bus, des trains et des autres transports publics. -

- - Vérifier les horaires - + +
+
+
+

Transports publics

+

+ Accédez en temps réel aux horaires et aux itinéraires des bus, des trains et des autres transports publics. +

+ + Vérifier les horaires +
- ); +
+ ); } -export default Home; \ No newline at end of file +export default Home; + \ No newline at end of file diff --git a/Front-end/src/pages/Settings.jsx b/Front-end/src/pages/Settings.jsx new file mode 100644 index 0000000..5cd1441 --- /dev/null +++ b/Front-end/src/pages/Settings.jsx @@ -0,0 +1,161 @@ +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("http://localhost:8888/signup", { + 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 l'inscription"); + } + + alert("Inscription 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 0e1a2c8..595e7ec 100644 --- a/Front-end/src/pages/Signup.jsx +++ b/Front-end/src/pages/Signup.jsx @@ -199,6 +199,8 @@ function Signup() { S'inscrire
+ + {/*Si il a déjà un compte*/}

Vous avez déjà un compte ?