Compare commits

...

3 Commits

Author SHA1 Message Date
Charles Mendiburu
5ce830d367 Restructure project cleanup, add root gitignore. 2026-03-28 16:04:31 +01:00
Charles Mendiburu
0023603b90 Improve README documentation 2026-03-28 16:04:18 +01:00
Charles Mendiburu
cc3c7001d9 Change navigation links for better alignement 2026-03-28 16:03:29 +01:00
10 changed files with 191 additions and 161 deletions

45
.gitignore vendored Normal file
View File

@ -0,0 +1,45 @@
# Editor & IDEs
.vscode/
.idea/
.bolt/
.project
.classpath
.settings/
*.iml
*.iws
# Dependencies & Build artifacts
node_modules/
dist/
dist-ssr/
target/
.vertx/
.gradle/
build/
bin/
out/
.vite/
# System files & Logs
.DS_Store
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.LSOverride
Icon
# Environment & Sensitive
.env
.env.local
.env.*.local
**/keystore.jceks
**/keystore.jceks.old
# Temporary / SQL Backups
sql/backup.sql
sql/export.sql
!sql/export1.sql
# Empty/duplicate lock files at root
package-lock.json

View File

@ -1,4 +0,0 @@
{
"liveServer.settings.port": 8081,
"java.configuration.updateBuildConfiguration": "automatic"
}

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +0,0 @@
{
"template": "bolt-vite-react-ts"
}

View File

@ -16,29 +16,27 @@ function Header() {
};
return (
<header className="bg-white shadow-md ">
<div className="mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
<Link to="/" className="text-2xl font-bold text-indigo-600">
VigiMétéo
</Link>
<button
className="sm:hidden text-gray-600 hover:text-indigo-600"
onClick={() => setIsMenuOpen(!isMenuOpen)}
>
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
<header className="bg-white shadow-md relative">
<div className="mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center sm:grid sm:grid-cols-3">
{/* Logo Section */}
<div className="flex justify-start">
<Link to="/" className="text-2xl font-bold text-indigo-600">
VigiMétéo
</Link>
</div>
{/* Navigation Section (Centered on Desktop) */}
<nav
className={`${
isMenuOpen ? "block" : "hidden"
} absolute z-[1000] top-16 left-0 w-full bg-white shadow-md sm:static sm:w-auto sm:z-auto sm:flex sm:gap-6 sm:shadow-none`}
} absolute z-[1000] top-16 left-0 w-full bg-white shadow-md sm:static sm:flex sm:justify-center sm:shadow-none sm:bg-transparent`}
>
<ul className="flex flex-col sm:flex-row gap-4 sm:gap-6 text-gray-600 p-4 sm:p-0">
<ul className="flex flex-col sm:flex-row gap-4 sm:gap-10 text-gray-600 p-4 sm:p-0 font-medium">
<li>
<Link
to="/"
onClick={() => setIsMenuOpen(false)}
className="text-gray-600 hover:text-indigo-600"
className="text-gray-600 hover:text-indigo-600 transition-colors"
>
{t('header.home')}
</Link>
@ -47,42 +45,21 @@ function Header() {
<Link
to="/about"
onClick={() => setIsMenuOpen(false)}
className="text-gray-600 hover:text-indigo-600"
className="text-gray-600 hover:text-indigo-600 transition-colors"
>
{t('header.about')}
</Link>
</li>
{!token ? (
<>
<li className="sm:hidden">
<Link
to="/login"
onClick={() => setIsMenuOpen(false)}
className="hover:text-indigo-600 flex items-center gap-2"
>
<LogIn size={20} />
{t('header.login')}
</Link>
</li>
<li className="sm:hidden">
<Link
to="/signup"
onClick={() => setIsMenuOpen(false)}
className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700"
>
<UserPlus size={20} />
{t('header.signup')}
</Link>
</li>
</>
) : (
{/* Logic for roles */}
{token && (
<>
{user?.role === "user" ? (
<li>
<Link
to="/gestionObjets"
onClick={() => setIsMenuOpen(false)}
className="text-gray-600 hover:text-indigo-600"
className="text-gray-600 hover:text-indigo-600 transition-colors"
>
{t('header.visualizer')}
</Link>
@ -92,17 +69,17 @@ function Header() {
<Link
to="/gestion"
onClick={() => setIsMenuOpen(false)}
className="text-gray-600 hover:text-indigo-600"
className="text-gray-600 hover:text-indigo-600 transition-colors"
>
{t('header.manage')}
</Link>
</li>
)}
{user?.role === "admin" && (
<li className="relative">
<li className="relative group">
<button
onClick={toggleAdminDropdown}
className="flex items-center text-gray-600 hover:text-indigo-600 focus:outline-none"
className="flex items-center text-gray-600 hover:text-indigo-600 focus:outline-none transition-colors"
>
{t('header.admin')}
<svg
@ -139,7 +116,35 @@ function Header() {
)}
</li>
)}
</>
)}
{/* Mobile-only auth links */}
{!token ? (
<>
<li className="sm:hidden">
<Link
to="/login"
onClick={() => setIsMenuOpen(false)}
className="hover:text-indigo-600 flex items-center gap-2"
>
<LogIn size={20} />
{t('header.login')}
</Link>
</li>
<li className="sm:hidden">
<Link
to="/signup"
onClick={() => setIsMenuOpen(false)}
className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg"
>
<UserPlus size={20} />
{t('header.signup')}
</Link>
</li>
</>
) : (
<>
<li className="sm:hidden">
<Link
to="/profil"
@ -156,7 +161,7 @@ function Header() {
logout();
setIsMenuOpen(false);
}}
className="flex items-center gap-2 text-gray-600 hover:text-red-600"
className="flex items-center gap-2 text-gray-600 hover:text-red-600 w-full text-left"
>
<LogOut size={20} />
<span>{t('header.logout')}</span>
@ -164,48 +169,59 @@ function Header() {
</li>
</>
)}
<li></li>
</ul>
</nav>
{!token ? (
<div className="hidden sm:flex gap-4 items-center">
<LanguageSwitcher />
<Link
to="/login"
onClick={() => setIsMenuOpen(false)}
className="hover:text-indigo-600 flex items-center gap-2"
>
<LogIn size={20} />
{t('header.login')}
</Link>
<Link
to="/signup"
onClick={() => setIsMenuOpen(false)}
className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700"
>
<UserPlus size={20} />
{t('header.signup')}
</Link>
</div>
) : (
<div className="hidden sm:flex items-center gap-4">
<LanguageSwitcher />
<Link
to="/profil"
onClick={() => setIsMenuOpen(false)}
className="flex items-center gap-2 text-gray-600 hover:text-indigo-600"
>
<User size={20} />
</Link>
<button
onClick={logout}
className="flex items-center gap-2 text-gray-600 hover:text-red-600"
>
<LogOut size={20} />
<span>{t('header.logout')}</span>
</button>
</div>
)}
{/* Auth Section (Right side) */}
<div className="flex justify-end items-center gap-4">
{!token ? (
<div className="hidden sm:flex gap-4 items-center">
<LanguageSwitcher />
<Link
to="/login"
className="text-gray-600 hover:text-indigo-600 flex items-center gap-2 transition-colors"
onClick={() => setIsMenuOpen(false)}
>
<LogIn size={20} />
{t('header.login')}
</Link>
<Link
to="/signup"
className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition-colors shadow-sm"
onClick={() => setIsMenuOpen(false)}
>
<UserPlus size={20} />
{t('header.signup')}
</Link>
</div>
) : (
<div className="hidden sm:flex items-center gap-6">
<LanguageSwitcher />
<Link
to="/profil"
className="flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition-colors"
onClick={() => setIsMenuOpen(false)}
>
<User size={20} />
</Link>
<button
onClick={logout}
className="flex items-center gap-2 text-gray-600 hover:text-red-600 transition-colors"
>
<LogOut size={20} />
<span className="font-medium">{t('header.logout')}</span>
</button>
</div>
)}
{/* Mobile Menu Button */}
<button
className="sm:hidden text-gray-600 hover:text-indigo-600 focus:outline-none"
onClick={() => setIsMenuOpen(!isMenuOpen)}
>
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
</div>
</header>
);

109
README.md
View File

@ -1,83 +1,64 @@
# Gestion des Objets Connectés
# Vigimétéo - Connected Objects Management
Vigimétéo est une plateforme web innovante dédiée à la visualisation en temps réel des données météorologiques issues de capteurs connectés. Ce projet permet à tout utilisateur de consulter des informations météo actualisées tout en offrant un espace dadministration complet pour gérer capteurs, utilisateurs et catégories.
Vigimétéo is an innovative web platform dedicated to real-time visualization of meteorological data from connected sensors. This project allows any user to consult updated weather information while providing a complete administration area to manage sensors, users, and categories.
## Table des matières
## Table of Contents
1. [Description](#description)
2. [Fonctionnalités](#fonctionnalités)
3. [Installation](#installation)
4. [Base de données](#base-de-données)
5. [Utilisation](#utilisation)
2. [Features](#features)
3. [Usage (Docker - Cloud Deployment)](#usage-docker---cloud-deployment)
4. [AI Usage](#ai-usage)
---
## Description
Vigimétéo est un outil à la fois utile et accessible face aux enjeux climatiques actuels. Grâce à une architecture mêlant React pour le front-end et Vert.x pour le back-end, le système repose sur une base de données PostgreSQL centralisant les mesures collectées par les stations.
Linterface permet à la fois une consultation intuitive pour le grand public, et une gestion complète pour les administrateurs via un tableau de bord sécurisé.
Vigimétéo is a tool that is both useful and accessible in the face of current climate challenges. Using an architecture combining React for the front-end and Vert.x for the back-end, the system relies on a PostgreSQL database centralizing measurements collected by weather stations.
The interface allows for both intuitive consultation for the general public and complete management for administrators via a secured dashboard.
## Fonctionnalités
## Features
- Consultation des données météorologiques
- Gestion d'IoT météorologique avec possibilité d'ajouter des alertes si les données sont deviennent trop extremes
- Gestion des utilisateurs : ajout, suppression, modification
- Gestion des catégories d'objets
- Interface responsive
- Authentification sécurisée avec JWT
- Consult real-time meteorological data (fake data for now, not yet connected with an API)
- IoT Device Management with the ability to set alerts for extreme data values
- User Management: add, delete, and modify users
- Category Management: manage device types
- Responsive Interface (Mobile & Desktop)
- Secure Authentication with JWT
- **Multi-language Support (i18n)**: Interface available in French and English
## Installation
## Usage (Docker - Cloud Deployment)
1. Front-end :
Ouvrez un terminal, déplacez vous dans le dossier Front-end du projet et executez la commande 'npm install'
2. Back-end :
Assurez-vous que toutes ces étapes soient faites avant de lancer le projet
Installer java
Installer le starter Vert.x depuis le site (https://vertx.io/docs/)
Installer JDK
Configurer java.home = « PATH/FOR/JDK »
Installer mvn : choco install mvn
**Dans le dossier Back-end du projet**
Mettre à jour les dépendances : mvn clean install
This application has been containerized to meet the requirements of the Cloud Computing course (Dev Web Ing1).
3. Lancez l'application :
**Dans le dossier Back-end du projet**
mvn exec:java
**Dans le dossier Front-end du projet**
npm run dev
### Prerequisites
- **Required OS:** Linux (Ubuntu/Debian) with `docker` and `docker-compose` configured.
- **Required Resources:** Internet connection to download base Docker images (`nginx`, `node`, `maven`, `eclipse-temurin`, `postgres`).
## Base de données
### Services / Containers
The architecture is split into 3 distinct services via `docker-compose.yaml`:
1. **vigimeteo_frontend**: Image built via a multi-stage `Dockerfile` (Node.js to compile the Vite application, then Alpine Nginx to serve static files). This service listens on public port `5000`.
2. **vigimeteo_backend**: Image built via a multi-stage `Dockerfile` (Maven to compile the JAR, then JRE 17 for execution). Runs the Vert.x API and communicates on public port `8888`.
3. **vigimeteo_db**: Official `postgres:15-alpine` image.
1. Assurez vous que PostgreSQL soit installé:
sudo apt update
sudo apt install postgresql postgresql-contrib
2. Démarez le service:
sudo service postgresql start
3. Lancer pgAdmin 4:
-Connectez-vous à votre serveur PostgreSQL avec le mot de passe administrateur.
-Faites un clic droit sur Databases > Create > Database....
-Appelez la "postgres" et cliquez sur Save.
4. Importer la base de données via pgAdmin :
-Faites un clic droit sur votre nouvelle base de données et selectionez "Restore"
-Entrez le chemin du fichier export.sql situé dans le repertoire sql à la racine du projet
-Cliquez sur Restore
----------------- Ou en ligne de commande ----------------------
5. Importer la base de données:
psql -U postgres -d postgres -f ./export.sql
---------------- Ou avec execution de commande sql ------------
6. Importer la base de données:
-creer la base de donnée "postgres" puis executer le fichier export1.sql
---
### Virtual Networks and Named Volumes
- **Network:** `vigimeteo_net` (Bridge) allows frontend, backend, and DB containers to communicate securely in isolation from the rest of the system.
- **Volume:** The persistent volume `vigimeteo_data` is mounted on the `/var/lib/postgresql/data` directory of the DB container. It ensures data persistence, guaranteeing that weather data and user accounts are not lost when restarting or rebuilding the containers.
5. Verifiez que vos informations correspondent au fichier '.\Back-end\src\main\java\com\example\starter\DatabaseService.java'
-Nom de la base
-Nom dutilisateur
-Mot de passe
-Port
### Container Configuration
- **Environment Variables:** The Vert.x backend dynamically reads PostgreSQL credentials (e.g., `DB_HOST`, `DB_PASSWORD`) provided via environment variables in the `docker-compose.yaml` file.
- **DB Initialization:** The PostgreSQL container automatically synchronizes the `sql/init_db.sql` file in its `/docker-entrypoint-initdb.d/init.sql` directory for automatic population upon first initialization.
- **Restart Policy:** All containers include the `restart: always` policy to restart automatically in case of failure.
## Utilisation
### How to Run the Application
1. **Prepare the application**: Run `./prepare-app.sh`. This script initiates `docker-compose build` to create/download the images.
2. **Launch the application**: Run `./start-app.sh`. Access the web application via your browser at `http://localhost:5000`. You can log into the administration interface using the email `admin.a@gmail.com` and password `azertyuiop`.
3. **Pause the application**: Run `./stop-app.sh`. This script stops the containers gracefully without resetting their state or destroying the virtual network.
4. **Remove the application**: Run `./remove-app.sh`. This removes all containers, networks, local images, and **named volumes**, erasing all traces of the deployed application.
- Une fois l'application lancée, accédez à `http://localhost:5173`.
- Connectez vous au compte admin avec le mail:"admin.a@gmail.com" et mdp:"azertyuiop"
- Vous pouvez aussi vous connecter à un compte complexe avec le mail "complexe@gmail.com" et mdp:"azertyuiop"
## AI Usage (Cloud Computing Course)
- **AI Agent used:** Google Gemini
- **AI's Role:**
- Verification and optimization of the DockerCompose file
- Implementation of the English/French internationalization (i18n) system.
- Upgrade of the README for technical and overall the project structure

6
package-lock.json generated
View File

@ -1,6 +0,0 @@
{
"name": "Projet-Dev-Web-Ing1",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

Binary file not shown.

View File

@ -363,6 +363,7 @@ COPY public.users (id, name, surname, email, gender, password, created_at, point
9 complexe complexe complexe@gmail.com homme $2a$12$LC/9EhIC9z/5IF8y/SjFVuDWqeQbkkafhRtytNJ9VWIvx6lCgHDfq 2025-04-12 13:10:50.562087 100 complexe
7 admin admin admin@a.com homme $2a$12$cugJ4JNxHjL.GE0ONZlkVerXRlKGc3jtVNlo9qQrck1Kahgnz6Fj2 2025-04-11 21:08:47.705738 247 admin
10 user user user@gmail.com homme $2a$12$wja3M3Lc254Ooge7mE5hwuzHEP35YbVzMYYH6WXs5sKc2q4fvlBei 2025-04-12 14:18:22.728679 0 user
11 admin super admin.a@gmail.com homme $2a$12$LC/9EhIC9z/5IF8y/SjFVuDWqeQbkkafhRtytNJ9VWIvx6lCgHDfq 2025-04-13 12:00:00.000000 999999 admin
\.