Ajout formulaire d'inscription relié avec bdd users

This commit is contained in:
Charles Mendiburu 2025-03-31 17:02:00 +02:00
parent dd54481152
commit bdf363726b
8 changed files with 294 additions and 60 deletions

0
Back-end/mvnw vendored Normal file → Executable file
View File

View File

@ -82,6 +82,12 @@
<version>${junit-jupiter.version}</version> <version>${junit-jupiter.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <!--Dependy pour hacher le mdp-->
<groupId>at.favre.lib</groupId>
<artifactId>bcrypt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -11,7 +11,7 @@ public class DatabaseService {
public DatabaseService(Vertx vertx) { public DatabaseService(Vertx vertx) {
pool = JDBCPool.pool(vertx, pool = JDBCPool.pool(vertx,
new JDBCConnectOptions() new JDBCConnectOptions()
.setJdbcUrl("jdbc:postgresql://localhost:5432/postgres?useUnicode=true&amp;amp;characterEncoding=UTF-8") // URL de la base de données .setJdbcUrl("jdbc:postgresql://localhost:5432/users?useUnicode=true&characterEncoding=UTF-8") //Url de la bdd
.setUser("postgres") // Nom d'utilisateur PostgreSQL .setUser("postgres") // Nom d'utilisateur PostgreSQL
.setPassword("admin"), // Mot de passe PostgreSQL .setPassword("admin"), // Mot de passe PostgreSQL
new PoolOptions() new PoolOptions()

View File

@ -1,5 +1,6 @@
package com.example.starter; package com.example.starter;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler; import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet; import io.vertx.sqlclient.RowSet;
@ -7,30 +8,40 @@ import io.vertx.sqlclient.Tuple;
import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import at.favre.lib.crypto.bcrypt.BCrypt;
public class MainVerticle extends AbstractVerticle { public class MainVerticle extends AbstractVerticle {
private DatabaseService databaseService; private DatabaseService databaseService;
private Router router; // Déclaration du router en variable de classe
@Override @Override
public void start(Promise<Void> startPromise) throws Exception { public void start(Promise<Void> startPromise) throws Exception {
databaseService = new DatabaseService(vertx); databaseService = new DatabaseService(vertx);
// Create a Router
Router router = Router.router(vertx); // 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
router.route().handler(CorsHandler.create() router.route().handler(CorsHandler.create()
.addOrigin("*") // Allow all origins .addOrigin("*")
.allowedMethod(HttpMethod.GET) // Allow GET requests .allowedMethod(HttpMethod.GET)
.allowedMethod(HttpMethod.POST) // Allow POST requests .allowedMethod(HttpMethod.POST)
.allowedHeader("Content-Type") // Allow Content-Type header .allowedHeader("Content-Type")
.allowedHeader("Authorization")); .allowedHeader("Authorization"));
// Déclaration des routes
router.get("/objets").handler(this::getObjects); router.get("/objets").handler(this::getObjects);
router.get("/objet").handler(this::getParticularObject); router.get("/objet").handler(this::getParticularObject);
// Create the HTTP server router.post("/signup").handler(this::handleSignup); // Route pour l'inscription
// Création du serveur HTTP
vertx.createHttpServer() vertx.createHttpServer()
.requestHandler(router) .requestHandler(router)
.listen(8888) .listen(8888)
@ -44,6 +55,7 @@ public class MainVerticle extends AbstractVerticle {
}); });
} }
// Récupération des objets
private void getObjects(RoutingContext context) { private void getObjects(RoutingContext context) {
databaseService.pool databaseService.pool
.query("SELECT * FROM weather_objects;") .query("SELECT * FROM weather_objects;")
@ -60,22 +72,25 @@ public class MainVerticle extends AbstractVerticle {
.end(getInfosObjects(rows).encode()); .end(getInfosObjects(rows).encode());
}); });
} }
// Récupération d'un objet spécifique
private void getParticularObject(RoutingContext context) { private void getParticularObject(RoutingContext context) {
String id= context.request().getParam("id"); String id = context.request().getParam("id");
if(id==null){ if (id == null) {
context.response() context.response()
.setStatusCode(400) .setStatusCode(400)
.end(new JsonObject().put("error", "Paramètre 'id' manquant").encode()); .end(new JsonObject().put("error", "Paramètre 'id' manquant").encode());
return; return;
} }
databaseService.pool databaseService.pool
.preparedQuery("SELECT * FROM weather_objects WHERE id=?") .preparedQuery("SELECT * FROM weather_objects WHERE id=?")
.execute(Tuple.of(Integer.parseInt(id))) .execute(Tuple.of(Integer.parseInt(id)))
.onFailure(e -> { .onFailure(e -> {
System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
context.response() context.response()
.setStatusCode(500) .setStatusCode(500)
.end(new JsonObject().put("Erreur","Erreur de récupération de la BDD").encode()); .end(new JsonObject().put("Erreur", "Erreur de récupération de la BDD").encode());
}) })
.onSuccess(rows -> { .onSuccess(rows -> {
if (rows.size() == 0) { if (rows.size() == 0) {
@ -83,15 +98,15 @@ public class MainVerticle extends AbstractVerticle {
.setStatusCode(404) .setStatusCode(404)
.end(new JsonObject().put("error", "Objet non trouvé").encode()); .end(new JsonObject().put("error", "Objet non trouvé").encode());
return; return;
} }
context.response() context.response()
.putHeader("content-type","application/json: charset=UTF-8") .putHeader("content-type", "application/json; charset=UTF-8")
.end(getInfosObjects(rows).encode()); .end(getInfosObjects(rows).encode());
}); });
} }
private JsonArray getInfosObjects(RowSet<Row> rows){ // Convertit les résultats SQL en JSON
private JsonArray getInfosObjects(RowSet<Row> rows) {
JsonArray objects = new JsonArray(); JsonArray objects = new JsonArray();
for (Row row : rows) { for (Row row : rows) {
JsonObject object = new JsonObject() JsonObject object = new JsonObject()
@ -104,4 +119,51 @@ public class MainVerticle extends AbstractVerticle {
} }
return objects; 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());
})
.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());
});
}
} }

View File

@ -1,10 +1,11 @@
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Home from "./pages/Home.jsx"; import Home from "./pages/Home.jsx";
import About from "./pages/About.jsx"; import About from "./pages/About.jsx";
import Gestion from "./pages/Gestion/Gestion.jsx"; import Gestion from "./pages/Gestion/Gestion.jsx";
import Header from "./components/Header.jsx"; import Header from "./components/Header.jsx";
import ObjectManagement from "./pages/Gestion/ObjectManagement.jsx"; import ObjectManagement from "./pages/Gestion/ObjectManagement.jsx";
import Objet from "./pages/Gestion/Objet.jsx"; import Objet from "./pages/Gestion/Objet.jsx";
import Signup from './pages/Signup.jsx';
function App() { function App() {
return ( return (
@ -17,6 +18,7 @@ function App() {
<Route path="/gestion" element={<Gestion />} /> <Route path="/gestion" element={<Gestion />} />
<Route path="/gestionObjets" element={<ObjectManagement />} /> <Route path="/gestionObjets" element={<ObjectManagement />} />
<Route path="/objet" element={<Objet />} /> <Route path="/objet" element={<Objet />} />
<Route path="/signup" element={<Signup />} />
</Routes> </Routes>
</div> </div>
</Router> </Router>

View File

@ -1,5 +1,6 @@
import React from "react"; import React from "react";
import { LogIn, UserPlus } from "lucide-react"; import { LogIn, UserPlus } from "lucide-react";
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
function Header() { function Header() {
return ( return (
@ -25,10 +26,10 @@ function Header() {
<LogIn size={20} /> <LogIn size={20} />
<span>Connexion</span> <span>Connexion</span>
</button> </button>
<button className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700"> <Link to="/signup" className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700">
<UserPlus size={20} /> <UserPlus size={20} />
<span>Inscription</span> <span>Inscription</span>
</button> </Link>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,36 +0,0 @@
import React from "react";
function Inscription() {
return (
<div className="w-96 bg-white rounded-lg shadow-sm p-4 mx-auto text-center mt-8">
<h2 className="text-lg font-semibold text-gray-800 mb-4">Inscription</h2>
<form className="flex flex-col gap-2">
<label className="block">
Nom:
<input
className="rounded-sm border-indigo-200"
ype="text"
name="name"
/>
</label>
<label className="block">
Prénom:
<input type="text" name="surname" />
</label>
<label>
Email:
<input type="email" name="email" />
</label>
<label>
Sexe:
<label for="homme">Homme</label>
<input type="radio" name="sexe" />
<label for="femme">Femme</label>
<input type="radio" name="sexe" />
</label>
</form>
</div>
);
}
export default Inscription;

View File

@ -0,0 +1,199 @@
import React, { useState } from 'react';
import { Mail, User, Lock, Calendar } from 'lucide-react';
function Signup() {
const [formData, setFormData] = useState({
name: '',
surname: '',
email: '',
gender: '',
password: '',
confirmPassword: ''
});
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 !");
} catch (error) {
alert(error.message);
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="w-96 bg-white rounded-lg shadow-md p-6 mx-auto">
<h2 className="text-2xl font-bold text-gray-800 mb-6 text-center">Inscription</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Nom:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<User className="h-5 w-5 text-gray-400" />
</div>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
className="pl-10 block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Prénom:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<User className="h-5 w-5 text-gray-400" />
</div>
<input
type="text"
name="surname"
value={formData.surname}
onChange={handleChange}
className="pl-10 block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Sexe:
</label>
<div className="flex gap-6 items-center">
<label className="inline-flex items-center">
<input
type="radio"
name="gender"
value="homme"
checked={formData.gender === 'homme'}
onChange={handleChange}
className="form-radio h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
/>
<span className="ml-2">Homme</span>
</label>
<label className="inline-flex items-center">
<input
type="radio"
name="gender"
value="femme"
checked={formData.gender === 'femme'}
onChange={handleChange}
className="form-radio h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
/>
<span className="ml-2">Femme</span>
</label>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Email:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Mail className="h-5 w-5 text-gray-400" />
</div>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
className="pl-10 block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Mot de passe:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
className="pl-10 block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
required
minLength="8"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Confirmer le mot de passe:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
type="password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
className="pl-10 block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
required
minLength="8"
/>
</div>
</div>
<div className="pt-4">
<button
type="submit"
className="w-full flex justify-center py-2.5 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
S'inscrire
</button>
</div>
</form>
</div>
</div>
);
}
export default Signup;