Compare commits
No commits in common. "c1fb461f46bf1d1310816c7ffc0b745cdc3acaad" and "23a10672f4ecec9e28e0a15b6e8893d83e364c3a" have entirely different histories.
c1fb461f46
...
23a10672f4
45
.gitignore
vendored
45
.gitignore
vendored
@ -1,45 +0,0 @@
|
|||||||
# 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
|
|
||||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"liveServer.settings.port": 8081,
|
||||||
|
"java.configuration.updateBuildConfiguration": "automatic"
|
||||||
|
}
|
||||||
BIN
Back-end/keystore.jceks
Normal file
BIN
Back-end/keystore.jceks
Normal file
Binary file not shown.
BIN
Back-end/keystore.jceks.old
Normal file
BIN
Back-end/keystore.jceks.old
Normal file
Binary file not shown.
3
Front-end/.bolt/config.json
Normal file
3
Front-end/.bolt/config.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"template": "bolt-vite-react-ts"
|
||||||
|
}
|
||||||
@ -16,27 +16,29 @@ function Header() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="bg-white shadow-md relative">
|
<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 sm:grid sm:grid-cols-3">
|
<div className="mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
|
||||||
{/* Logo Section */}
|
|
||||||
<div className="flex justify-start">
|
|
||||||
<Link to="/" className="text-2xl font-bold text-indigo-600">
|
<Link to="/" className="text-2xl font-bold text-indigo-600">
|
||||||
VigiMétéo
|
VigiMétéo
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
<button
|
||||||
|
className="sm:hidden text-gray-600 hover:text-indigo-600"
|
||||||
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
|
>
|
||||||
|
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||||
|
</button>
|
||||||
|
|
||||||
{/* Navigation Section (Centered on Desktop) */}
|
|
||||||
<nav
|
<nav
|
||||||
className={`${
|
className={`${
|
||||||
isMenuOpen ? "block" : "hidden"
|
isMenuOpen ? "block" : "hidden"
|
||||||
} 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`}
|
} 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`}
|
||||||
>
|
>
|
||||||
<ul className="flex flex-col sm:flex-row gap-4 sm:gap-10 text-gray-600 p-4 sm:p-0 font-medium">
|
<ul className="flex flex-col sm:flex-row gap-4 sm:gap-6 text-gray-600 p-4 sm:p-0">
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
onClick={() => setIsMenuOpen(false)}
|
onClick={() => setIsMenuOpen(false)}
|
||||||
className="text-gray-600 hover:text-indigo-600 transition-colors"
|
className="text-gray-600 hover:text-indigo-600"
|
||||||
>
|
>
|
||||||
{t('header.home')}
|
{t('header.home')}
|
||||||
</Link>
|
</Link>
|
||||||
@ -45,21 +47,42 @@ function Header() {
|
|||||||
<Link
|
<Link
|
||||||
to="/about"
|
to="/about"
|
||||||
onClick={() => setIsMenuOpen(false)}
|
onClick={() => setIsMenuOpen(false)}
|
||||||
className="text-gray-600 hover:text-indigo-600 transition-colors"
|
className="text-gray-600 hover:text-indigo-600"
|
||||||
>
|
>
|
||||||
{t('header.about')}
|
{t('header.about')}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
{!token ? (
|
||||||
{/* Logic for roles */}
|
<>
|
||||||
{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>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
{user?.role === "user" ? (
|
{user?.role === "user" ? (
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<Link
|
||||||
to="/gestionObjets"
|
to="/gestionObjets"
|
||||||
onClick={() => setIsMenuOpen(false)}
|
onClick={() => setIsMenuOpen(false)}
|
||||||
className="text-gray-600 hover:text-indigo-600 transition-colors"
|
className="text-gray-600 hover:text-indigo-600"
|
||||||
>
|
>
|
||||||
{t('header.visualizer')}
|
{t('header.visualizer')}
|
||||||
</Link>
|
</Link>
|
||||||
@ -69,17 +92,17 @@ function Header() {
|
|||||||
<Link
|
<Link
|
||||||
to="/gestion"
|
to="/gestion"
|
||||||
onClick={() => setIsMenuOpen(false)}
|
onClick={() => setIsMenuOpen(false)}
|
||||||
className="text-gray-600 hover:text-indigo-600 transition-colors"
|
className="text-gray-600 hover:text-indigo-600"
|
||||||
>
|
>
|
||||||
{t('header.manage')}
|
{t('header.manage')}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{user?.role === "admin" && (
|
{user?.role === "admin" && (
|
||||||
<li className="relative group">
|
<li className="relative">
|
||||||
<button
|
<button
|
||||||
onClick={toggleAdminDropdown}
|
onClick={toggleAdminDropdown}
|
||||||
className="flex items-center text-gray-600 hover:text-indigo-600 focus:outline-none transition-colors"
|
className="flex items-center text-gray-600 hover:text-indigo-600 focus:outline-none"
|
||||||
>
|
>
|
||||||
{t('header.admin')}
|
{t('header.admin')}
|
||||||
<svg
|
<svg
|
||||||
@ -116,35 +139,7 @@ function Header() {
|
|||||||
)}
|
)}
|
||||||
</li>
|
</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">
|
<li className="sm:hidden">
|
||||||
<Link
|
<Link
|
||||||
to="/profil"
|
to="/profil"
|
||||||
@ -161,7 +156,7 @@ function Header() {
|
|||||||
logout();
|
logout();
|
||||||
setIsMenuOpen(false);
|
setIsMenuOpen(false);
|
||||||
}}
|
}}
|
||||||
className="flex items-center gap-2 text-gray-600 hover:text-red-600 w-full text-left"
|
className="flex items-center gap-2 text-gray-600 hover:text-red-600"
|
||||||
>
|
>
|
||||||
<LogOut size={20} />
|
<LogOut size={20} />
|
||||||
<span>{t('header.logout')}</span>
|
<span>{t('header.logout')}</span>
|
||||||
@ -169,59 +164,48 @@ function Header() {
|
|||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<li></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Auth Section (Right side) */}
|
|
||||||
<div className="flex justify-end items-center gap-4">
|
|
||||||
{!token ? (
|
{!token ? (
|
||||||
<div className="hidden sm:flex gap-4 items-center">
|
<div className="hidden sm:flex gap-4 items-center">
|
||||||
<LanguageSwitcher />
|
<LanguageSwitcher />
|
||||||
<Link
|
<Link
|
||||||
to="/login"
|
to="/login"
|
||||||
className="text-gray-600 hover:text-indigo-600 flex items-center gap-2 transition-colors"
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
className="hover:text-indigo-600 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<LogIn size={20} />
|
<LogIn size={20} />
|
||||||
{t('header.login')}
|
{t('header.login')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/signup"
|
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)}
|
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} />
|
<UserPlus size={20} />
|
||||||
{t('header.signup')}
|
{t('header.signup')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="hidden sm:flex items-center gap-6">
|
<div className="hidden sm:flex items-center gap-4">
|
||||||
<LanguageSwitcher />
|
<LanguageSwitcher />
|
||||||
<Link
|
<Link
|
||||||
to="/profil"
|
to="/profil"
|
||||||
className="flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition-colors"
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
className="flex items-center gap-2 text-gray-600 hover:text-indigo-600"
|
||||||
>
|
>
|
||||||
<User size={20} />
|
<User size={20} />
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
onClick={logout}
|
onClick={logout}
|
||||||
className="flex items-center gap-2 text-gray-600 hover:text-red-600 transition-colors"
|
className="flex items-center gap-2 text-gray-600 hover:text-red-600"
|
||||||
>
|
>
|
||||||
<LogOut size={20} />
|
<LogOut size={20} />
|
||||||
<span className="font-medium">{t('header.logout')}</span>
|
<span>{t('header.logout')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
|
|||||||
107
README.md
107
README.md
@ -1,64 +1,83 @@
|
|||||||
# Vigimétéo - Connected Objects Management
|
# Gestion des Objets Connectés
|
||||||
|
|
||||||
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.
|
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 d’administration complet pour gérer capteurs, utilisateurs et catégories.
|
||||||
|
|
||||||
## Table of Contents
|
## Table des matières
|
||||||
|
|
||||||
1. [Description](#description)
|
1. [Description](#description)
|
||||||
2. [Features](#features)
|
2. [Fonctionnalités](#fonctionnalités)
|
||||||
3. [Usage (Docker - Cloud Deployment)](#usage-docker---cloud-deployment)
|
3. [Installation](#installation)
|
||||||
4. [AI Usage](#ai-usage)
|
4. [Base de données](#base-de-données)
|
||||||
|
5. [Utilisation](#utilisation)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
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.
|
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.
|
||||||
The interface allows for both intuitive consultation for the general public and complete management for administrators via a secured dashboard.
|
L’interface 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é.
|
||||||
|
|
||||||
## Features
|
## Fonctionnalités
|
||||||
|
|
||||||
- Consult real-time meteorological data (fake data for now, not yet connected with an API)
|
- Consultation des données météorologiques
|
||||||
- IoT Device Management with the ability to set alerts for extreme data values
|
- Gestion d'IoT météorologique avec possibilité d'ajouter des alertes si les données sont deviennent trop extremes
|
||||||
- User Management: add, delete, and modify users
|
- Gestion des utilisateurs : ajout, suppression, modification
|
||||||
- Category Management: manage device types
|
- Gestion des catégories d'objets
|
||||||
- Responsive Interface (Mobile & Desktop)
|
- Interface responsive
|
||||||
- Secure Authentication with JWT
|
- Authentification sécurisée avec JWT
|
||||||
- **Multi-language Support (i18n)**: Interface available in French and English
|
|
||||||
|
|
||||||
## Usage (Docker - Cloud Deployment)
|
## Installation
|
||||||
|
|
||||||
This application has been containerized to meet the requirements of the Cloud Computing course (Dev Web Ing1).
|
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
|
||||||
|
|
||||||
### Prerequisites
|
3. Lancez l'application :
|
||||||
- **Required OS:** Linux (Ubuntu/Debian) with `docker` and `docker-compose` configured.
|
**Dans le dossier Back-end du projet**
|
||||||
- **Required Resources:** Internet connection to download base Docker images (`nginx`, `node`, `maven`, `eclipse-temurin`, `postgres`).
|
mvn exec:java
|
||||||
|
**Dans le dossier Front-end du projet**
|
||||||
|
npm run dev
|
||||||
|
|
||||||
### Services / Containers
|
## Base de données
|
||||||
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.
|
|
||||||
|
|
||||||
### Virtual Networks and Named Volumes
|
1. Assurez vous que PostgreSQL soit installé:
|
||||||
- **Network:** `vigimeteo_net` (Bridge) allows frontend, backend, and DB containers to communicate securely in isolation from the rest of the system.
|
sudo apt update
|
||||||
- **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.
|
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
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### How to Run the Application
|
5. Verifiez que vos informations correspondent au fichier '.\Back-end\src\main\java\com\example\starter\DatabaseService.java'
|
||||||
1. **Prepare the application**: Run `./prepare-app.sh`. This script initiates `docker-compose build` to create/download the images.
|
-Nom de la base
|
||||||
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`.
|
-Nom d’utilisateur
|
||||||
3. **Pause the application**: Run `./stop-app.sh`. This script stops the containers gracefully without resetting their state or destroying the virtual network.
|
-Mot de passe
|
||||||
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.
|
-Port
|
||||||
|
|
||||||
## AI Usage (Cloud Computing Course)
|
## Utilisation
|
||||||
|
|
||||||
- **AI Agent used:** Google Gemini
|
- Une fois l'application lancée, accédez à `http://localhost:5173`.
|
||||||
- **AI's Role:**
|
- Connectez vous au compte admin avec le mail:"admin.a@gmail.com" et mdp:"azertyuiop"
|
||||||
- Verification and optimization of the DockerCompose file
|
- Vous pouvez aussi vous connecter à un compte complexe avec le mail "complexe@gmail.com" et mdp:"azertyuiop"
|
||||||
- Implementation of the English/French internationalization (i18n) system.
|
|
||||||
- Upgrade of the README for technical and overall the project structure
|
|
||||||
|
|||||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "Projet-Dev-Web-Ing1",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
BIN
sql/export.sql
Normal file
BIN
sql/export.sql
Normal file
Binary file not shown.
@ -363,7 +363,6 @@ 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
|
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
|
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
|
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
|
|
||||||
\.
|
\.
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user