Compare commits
3 Commits
e484ae8919
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4177359d9a | ||
|
|
8c86c42c04 | ||
|
|
7cf5c3a9e9 |
19
config.php
19
config.php
@@ -1,18 +1,25 @@
|
||||
<?php
|
||||
define('DB_HOST', 'smarthome_db');
|
||||
define('DB_HOST', 'mariadb');
|
||||
define('DB_USER', 'smarthome_user');
|
||||
define('DB_PASSWORD', 'SH_LaSalle_SD8');
|
||||
define('DB_NAME', 'smarthome');
|
||||
|
||||
define('JWT_SECRET', 'smarthome_secret_key_2024_smart_home_project_very_long_key');
|
||||
define('MQTT_BROKER', 'localhost');
|
||||
|
||||
define('MQTT_BROKER', 'mosquitto');
|
||||
define('MQTT_PORT', 1883);
|
||||
define('MQTT_TOPIC_CLIMATE', 'smarthome/climate');
|
||||
define('MQTT_TOPIC_SECURITY', 'smarthome/security');
|
||||
|
||||
function get_db() {
|
||||
$conn = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
|
||||
if ($conn->connect_error) {
|
||||
die(json_encode(['error' => 'Connexion échouée : ' . $conn->connect_error]));
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8", DB_USER, DB_PASSWORD, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
|
||||
]);
|
||||
return $pdo;
|
||||
} catch (PDOException $e) {
|
||||
die(json_encode(['error' => 'Connexion échouée : ' . $e->getMessage()]));
|
||||
}
|
||||
return $conn;
|
||||
}
|
||||
?>
|
||||
@@ -5,27 +5,38 @@ require_once 'vendor/autoload.php';
|
||||
use PhpMqtt\Client\MqttClient;
|
||||
use PhpMqtt\Client\ConnectionSettings;
|
||||
|
||||
function insert_climate($data) {
|
||||
try {
|
||||
$db = get_db();
|
||||
$stmt = $db->prepare("INSERT INTO climate_data (temperature, humidity, pressure, luminosity) VALUES (?, ?, ?, ?)");
|
||||
$stmt->bind_param("dddd",
|
||||
$data['temperature'],
|
||||
$data['humidity'],
|
||||
$data['pressure'],
|
||||
$data['luminosity']
|
||||
);
|
||||
$stmt->execute();
|
||||
$db->close();
|
||||
echo "Donnée climatique insérée : " . json_encode($data) . "\n";
|
||||
} catch (Exception $e) {
|
||||
die("Impossible de démarrer le script : " . $e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
function insert_security($data) {
|
||||
$db = get_db();
|
||||
$stmt = $db->prepare("INSERT INTO security_events (event_type, location) VALUES (?, ?)");
|
||||
$stmt->bind_param("ss", $data['event_type'], $data['location']);
|
||||
$stmt->execute();
|
||||
$db->close();
|
||||
function insert_climate($db, $data) {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO climate_data (temperature, humidity, pressure, luminosity) VALUES (:temp, :hum, :pres, :lum)");
|
||||
$stmt->execute([
|
||||
':temp' => $data['temperature'],
|
||||
':hum' => $data['humidity'],
|
||||
':pres' => $data['pressure'],
|
||||
':lum' => $data['luminosity']
|
||||
]);
|
||||
echo "Donnée climatique insérée : " . json_encode($data) . "\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "Erreur d'insertion climat : " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
function insert_security($db, $data) {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO security_events (event_type, location) VALUES (:event, :loc)");
|
||||
$stmt->execute([
|
||||
':event' => $data['event_type'],
|
||||
':loc' => $data['location']
|
||||
]);
|
||||
echo "Événement sécurité inséré : " . json_encode($data) . "\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "Erreur d'insertion sécurité : " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
$mqtt = new MqttClient(MQTT_BROKER, MQTT_PORT);
|
||||
@@ -33,14 +44,18 @@ $settings = (new ConnectionSettings())->setKeepAliveInterval(60);
|
||||
$mqtt->connect($settings);
|
||||
echo "Connecté au broker MQTT\n";
|
||||
|
||||
$mqtt->subscribe(MQTT_TOPIC_CLIMATE, function($topic, $message) {
|
||||
$mqtt->subscribe(MQTT_TOPIC_CLIMATE, function($topic, $message) use ($db) {
|
||||
$data = json_decode($message, true);
|
||||
insert_climate($data);
|
||||
if ($data) {
|
||||
insert_climate($db, $data);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
$mqtt->subscribe(MQTT_TOPIC_SECURITY, function($topic, $message) {
|
||||
$mqtt->subscribe(MQTT_TOPIC_SECURITY, function($topic, $message) use ($db) {
|
||||
$data = json_decode($message, true);
|
||||
insert_security($data);
|
||||
if ($data) {
|
||||
insert_security($db, $data);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
$mqtt->loop(true);
|
||||
|
||||
Submodule smart-house---final- deleted from 4188ca3abc
8
smart-house---final-/.idea/modules.xml
generated
Normal file
8
smart-house---final-/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/smart-house---final-.iml" filepath="$PROJECT_DIR$/.idea/smart-house---final-.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
19
smart-house---final-/.idea/php.xml
generated
Normal file
19
smart-house---final-/.idea/php.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MessDetectorOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PHPCSFixerOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PHPCodeSnifferOptionsConfiguration">
|
||||
<option name="highlightLevel" value="WARNING" />
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PhpStanOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PsalmOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
8
smart-house---final-/.idea/smart-house---final-.iml
generated
Normal file
8
smart-house---final-/.idea/smart-house---final-.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
smart-house---final-/.idea/vcs.xml
generated
Normal file
6
smart-house---final-/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
65
smart-house---final-/.idea/workspace.xml
generated
Normal file
65
smart-house---final-/.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="badd9711-30eb-4220-b16e-f09f234f961a" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/dashboard.js" beforeDir="false" afterPath="$PROJECT_DIR$/dashboard.js" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ComposerSettings">
|
||||
<execution />
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="PhpWorkspaceProjectConfiguration" interpreter_name="/bin/php" />
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 3,
|
||||
"fromUser": false
|
||||
}</component>
|
||||
<component name="ProjectId" id="3EXNCY6gnuLEQsMDkAdCuksKzxz" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
|
||||
"codeWithMe.voiceChat.enabledByDefault": "false",
|
||||
"git-widget-placeholder": "main",
|
||||
"last_opened_file_path": "/home/enzo/smart-house---final-",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-d6986cc7102b-31caf2ab9e3c-JavaScript-PS-261.24374.185" />
|
||||
<option value="bundled-php-predefined-a98d8de5180a-6bbb74dbab74-com.jetbrains.php.sharedIndexes-PS-261.24374.185" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="badd9711-30eb-4220-b16e-f09f234f961a" name="Changes" comment="" />
|
||||
<created>1780320829467</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1780320829467</updated>
|
||||
<workItem from="1780320830997" duration="1815000" />
|
||||
<workItem from="1780399893551" duration="890000" />
|
||||
<workItem from="1780481022356" duration="718000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
16
smart-house---final-/404.php
Normal file
16
smart-house---final-/404.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
http_response_code(404);
|
||||
session_start();
|
||||
include 'include/header.php';
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div style="text-align: center; padding: 80px 20px;">
|
||||
<h1 style="font-size: 5em; color: #3bc4e8; margin-bottom: 10px;">404</h1>
|
||||
<h2 style="font-size: 1.5em; margin-bottom: 15px;">Page introuvable</h2>
|
||||
<p style="color: #aaa; margin-bottom: 30px;">La page que vous cherchez n'existe pas ou a été déplacée.</p>
|
||||
<a href="/smart-house---final-/dashboard.php" class="btn-primary">🏠 Retour au dashboard</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'include/footer.php'; ?>
|
||||
126
smart-house---final-/README.md
Normal file
126
smart-house---final-/README.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 🏠 Smart House
|
||||
|
||||
Application web de tableau de bord domotique développée en PHP, permettant de surveiller et gérer une maison intelligente.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Présentation
|
||||
|
||||
Smart House est un dashboard web qui permet de visualiser en temps réel les données de capteurs (climat, sécurité, énergie, lumière) et de gérer les utilisateurs avec un système de rôles Admin/User.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Fonctionnalités
|
||||
|
||||
### Authentification
|
||||
- Connexion / Inscription sécurisée
|
||||
- Blocage après 3 tentatives échouées (5 minutes)
|
||||
- Déconnexion automatique après 15 minutes d'inactivité
|
||||
- Régénération de session après connexion
|
||||
|
||||
### Gestion des utilisateurs
|
||||
- Inscription avec validation obligatoire par l'admin
|
||||
- Système de rôles : Admin / User
|
||||
- Profil utilisateur (changement de mot de passe)
|
||||
- Promotion / Rétrogradation des utilisateurs
|
||||
|
||||
### Dashboard
|
||||
- Widget Climat (Température, Humidité, Pression, Altitude)
|
||||
- Widget Sécurité (Mouvement, Distance)
|
||||
- Widget Énergie (Puissance, Consommation)
|
||||
- Widget Lumière (Lux)
|
||||
- Graphiques Chart.js
|
||||
- Widget Alertes
|
||||
|
||||
### Administration
|
||||
- Gestion des utilisateurs (accepter, refuser, supprimer, changer le rôle)
|
||||
- Paramètres du site (nom, tentatives max, durée session, message d'accueil)
|
||||
- Logs du dashboard
|
||||
|
||||
### Interface
|
||||
- Mode sombre / clair
|
||||
- Design responsive (mobile, tablette, desktop)
|
||||
- Page 404 personnalisée
|
||||
|
||||
---
|
||||
|
||||
## 📁 Structure des fichiers
|
||||
|
||||
smart-house---final-/
|
||||
include/
|
||||
header.php # En-tête HTML commun
|
||||
footer.php # Pied de page commun
|
||||
nav.php # Barre de navigation
|
||||
session_check.php # Vérification de session
|
||||
js/
|
||||
chart.min.js # Chart.js local
|
||||
index.php # Page d'accueil
|
||||
login.php # Page de connexion
|
||||
register.php # Page d'inscription
|
||||
logout.php # Déconnexion
|
||||
dashboard.php # Tableau de bord
|
||||
profil.php # Profil utilisateur
|
||||
admin_users.php # Gestion des utilisateurs (Admin)
|
||||
admin_settings.php # Paramètres du site (Admin)
|
||||
admin_logs.php # Logs (Admin)
|
||||
404.php # Page d'erreur 404
|
||||
dashboard.js # Scripts du dashboard
|
||||
style.css # Styles CSS
|
||||
users.json.php # Base de données utilisateurs
|
||||
settings.json.php # Paramètres du site
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Installation
|
||||
|
||||
### Prérequis
|
||||
- PHP 7.4 ou supérieur
|
||||
- Serveur web (Apache, Nginx, ou serveur PHP intégré)
|
||||
|
||||
### Lancement avec le serveur PHP intégré
|
||||
|
||||
php -S localhost:8000
|
||||
|
||||
Puis ouvrir dans le navigateur :
|
||||
|
||||
http://localhost:8000/
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Comptes et accès
|
||||
|
||||
### Compte Admin
|
||||
- Nom d'utilisateur : Kari
|
||||
- Mot de passe : Levasseur
|
||||
- Rôle : Admin
|
||||
|
||||
### Compte Utilisateur
|
||||
Les utilisateurs s'inscrivent via la page /register.php et doivent être validés par l'admin avant de pouvoir se connecter.
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Sécurité
|
||||
|
||||
- Mots de passe hashés avec password_hash() (bcrypt)
|
||||
- Protection contre les attaques par force brute (blocage 5 min après 3 échecs)
|
||||
- Régénération de l'ID de session après connexion
|
||||
- Vérification du rôle à chaque chargement de page
|
||||
- Déconnexion automatique après inactivité
|
||||
- Protection des pages admin (redirection si non Admin)
|
||||
- Échappement des données avec htmlspecialchars()
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Technologies utilisées
|
||||
|
||||
- PHP — Backend et gestion des sessions
|
||||
- HTML / CSS — Interface utilisateur
|
||||
- JavaScript — Interactions dynamiques
|
||||
- Chart.js — Graphiques
|
||||
- JSON — Stockage des données utilisateurs et paramètres
|
||||
|
||||
---
|
||||
|
||||
## 📅 Année
|
||||
|
||||
2026
|
||||
42
smart-house---final-/admin_logs.php
Normal file
42
smart-house---final-/admin_logs.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'include/session_check.php';
|
||||
if ($_SESSION['role'] !== 'Admin') {
|
||||
header("Location: dashboard.php");
|
||||
exit();
|
||||
}
|
||||
include 'include/header.php';
|
||||
?>
|
||||
<main>
|
||||
<div class="logs-container">
|
||||
<h2>Logs du Dashboard</h2>
|
||||
<p>Bienvenue, <?php echo htmlspecialchars($_SESSION['user']); ?> (Admin)</p>
|
||||
<h3>Modifications récentes</h3>
|
||||
<ul id="modifications">
|
||||
<li>Aucune modification pour l'instant.</li>
|
||||
</ul>
|
||||
<h3>Alertes récentes</h3>
|
||||
<ul id="alertes">
|
||||
<li>Aucune alerte pour l'instant.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
function ajouterModification(msg) {
|
||||
const ul = document.getElementById('modifications');
|
||||
const li = document.createElement('li');
|
||||
li.textContent = msg;
|
||||
ul.prepend(li);
|
||||
}
|
||||
function ajouterAlerte(msg) {
|
||||
const ul = document.getElementById('alertes');
|
||||
const li = document.createElement('li');
|
||||
li.textContent = msg;
|
||||
ul.prepend(li);
|
||||
}
|
||||
setTimeout(() => {
|
||||
ajouterModification("Utilisateur John a modifié le widget Climat");
|
||||
ajouterAlerte("Température trop élevée détectée");
|
||||
}, 2000);
|
||||
</script>
|
||||
<?php include 'include/footer.php'; ?>
|
||||
68
smart-house---final-/admin_settings.php
Normal file
68
smart-house---final-/admin_settings.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'include/session_check.php';
|
||||
if ($_SESSION['role'] !== 'Admin') {
|
||||
header("Location: dashboard.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$settingsFile = 'settings.json.php';
|
||||
$settings = file_exists($settingsFile) ? json_decode(file_get_contents($settingsFile), true) : [
|
||||
'site_name' => 'Smart House',
|
||||
'max_attempts' => 3,
|
||||
'session_timeout' => 900,
|
||||
'welcome_message' => 'Bienvenue sur votre tableau de bord Smart House.'
|
||||
];
|
||||
$success = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$settings['site_name'] = trim($_POST['site_name']);
|
||||
$settings['max_attempts'] = (int)$_POST['max_attempts'];
|
||||
$settings['session_timeout'] = (int)$_POST['session_timeout'] * 60;
|
||||
$settings['welcome_message'] = trim($_POST['welcome_message']);
|
||||
|
||||
file_put_contents($settingsFile, json_encode($settings, JSON_PRETTY_PRINT));
|
||||
$success = "Paramètres sauvegardés avec succès !";
|
||||
}
|
||||
|
||||
include 'include/header.php';
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div class="logs-container">
|
||||
<h2>⚙️ Paramètres du site</h2>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<p class="form-success" style="margin-top: 10px;"><?php echo htmlspecialchars($success); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="admin_settings.php" style="margin-top: 20px;">
|
||||
|
||||
<label class="field-label" for="site_name">Nom du site</label>
|
||||
<input type="text" id="site_name" name="site_name"
|
||||
value="<?php echo htmlspecialchars($settings['site_name']); ?>"
|
||||
style="width:100%; padding:10px; border-radius:8px; border:1px solid #444; background:#1e1e1e; color:#f0f0f0; margin-bottom:15px;">
|
||||
|
||||
<label class="field-label" for="max_attempts">Tentatives de connexion max</label>
|
||||
<input type="number" id="max_attempts" name="max_attempts" min="1" max="10"
|
||||
value="<?php echo $settings['max_attempts']; ?>"
|
||||
style="width:100%; padding:10px; border-radius:8px; border:1px solid #444; background:#1e1e1e; color:#f0f0f0; margin-bottom:15px;">
|
||||
|
||||
<label class="field-label" for="session_timeout">Durée de session (en minutes)</label>
|
||||
<input type="number" id="session_timeout" name="session_timeout" min="1" max="120"
|
||||
value="<?php echo $settings['session_timeout'] / 60; ?>"
|
||||
style="width:100%; padding:10px; border-radius:8px; border:1px solid #444; background:#1e1e1e; color:#f0f0f0; margin-bottom:15px;">
|
||||
|
||||
<label class="field-label" for="welcome_message">Message d'accueil</label>
|
||||
<textarea id="welcome_message" name="welcome_message" rows="3"
|
||||
style="width:100%; padding:10px; border-radius:8px; border:1px solid #444; background:#1e1e1e; color:#f0f0f0; margin-bottom:15px; resize:vertical;"><?php echo htmlspecialchars($settings['welcome_message']); ?></textarea>
|
||||
|
||||
<button type="submit"
|
||||
style="padding:12px 30px; background-color:#3bc4e8; color:white; border:none; border-radius:25px; cursor:pointer; font-weight:bold; transition: background-color 0.3s;">
|
||||
💾 Sauvegarder
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'include/footer.php'; ?>
|
||||
159
smart-house---final-/admin_users.php
Normal file
159
smart-house---final-/admin_users.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'include/session_check.php';
|
||||
if ($_SESSION['role'] !== 'Admin') {
|
||||
header("Location: dashboard.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$usersFile = 'users.json.php';
|
||||
$users = file_exists($usersFile) ? json_decode(file_get_contents($usersFile), true) : [];
|
||||
$message = '';
|
||||
$success = '';
|
||||
|
||||
// Accepter un utilisateur
|
||||
if (isset($_GET['accepter'])) {
|
||||
$cible = $_GET['accepter'];
|
||||
if (isset($users[$cible])) {
|
||||
$users[$cible]['statut'] = 'actif';
|
||||
file_put_contents($usersFile, json_encode($users, JSON_PRETTY_PRINT));
|
||||
$success = "Utilisateur '$cible' accepté avec succès.";
|
||||
$users = json_decode(file_get_contents($usersFile), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Refuser un utilisateur
|
||||
if (isset($_GET['refuser'])) {
|
||||
$cible = $_GET['refuser'];
|
||||
if (isset($users[$cible])) {
|
||||
unset($users[$cible]);
|
||||
file_put_contents($usersFile, json_encode($users, JSON_PRETTY_PRINT));
|
||||
$success = "Utilisateur '$cible' refusé et supprimé.";
|
||||
$users = json_decode(file_get_contents($usersFile), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer un utilisateur
|
||||
if (isset($_GET['supprimer'])) {
|
||||
$cible = $_GET['supprimer'];
|
||||
if ($cible === $_SESSION['user']) {
|
||||
$message = "Vous ne pouvez pas supprimer votre propre compte.";
|
||||
} elseif (isset($users[$cible])) {
|
||||
unset($users[$cible]);
|
||||
file_put_contents($usersFile, json_encode($users, JSON_PRETTY_PRINT));
|
||||
$success = "Utilisateur '$cible' supprimé avec succès.";
|
||||
$users = json_decode(file_get_contents($usersFile), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Changer le rôle
|
||||
if (isset($_GET['role'])) {
|
||||
$cible = $_GET['role'];
|
||||
if ($cible === $_SESSION['user']) {
|
||||
$message = "Vous ne pouvez pas changer votre propre rôle.";
|
||||
} elseif (isset($users[$cible])) {
|
||||
$users[$cible]['role'] = ($users[$cible]['role'] === 'Admin') ? 'User' : 'Admin';
|
||||
file_put_contents($usersFile, json_encode($users, JSON_PRETTY_PRINT));
|
||||
$success = "Rôle de '$cible' modifié avec succès.";
|
||||
$users = json_decode(file_get_contents($usersFile), true);
|
||||
}
|
||||
}
|
||||
|
||||
include 'include/header.php';
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div class="logs-container">
|
||||
<h2>👥 Gestion des utilisateurs</h2>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<p class="form-error" style="margin-top: 10px;"><?php echo htmlspecialchars($message); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if ($success): ?>
|
||||
<p class="form-success" style="margin-top: 10px;"><?php echo htmlspecialchars($success); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Comptes en attente -->
|
||||
<?php
|
||||
$en_attente = array_filter($users, function($u) {
|
||||
return isset($u['statut']) && $u['statut'] === 'en_attente';
|
||||
});
|
||||
?>
|
||||
<?php if (!empty($en_attente)): ?>
|
||||
<h3 style="margin-top: 20px; color: #f39c12;">⏳ Comptes en attente de validation</h3>
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nom d'utilisateur</th>
|
||||
<th>Statut</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($en_attente as $username => $data): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($username); ?></td>
|
||||
<td><span style="color: #f39c12;">En attente</span></td>
|
||||
<td>
|
||||
<a href="admin_users.php?accepter=<?php echo urlencode($username); ?>"
|
||||
class="btn-action btn-warning">✅ Accepter</a>
|
||||
<a href="admin_users.php?refuser=<?php echo urlencode($username); ?>"
|
||||
class="btn-action btn-danger"
|
||||
onclick="return confirm('Refuser <?php echo htmlspecialchars($username); ?> ?')">
|
||||
❌ Refuser
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Comptes actifs -->
|
||||
<h3 style="margin-top: 30px;">✅ Comptes actifs</h3>
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nom d'utilisateur</th>
|
||||
<th>Rôle</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $username => $data):
|
||||
if (isset($data['statut']) && $data['statut'] === 'en_attente') continue;
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($username); ?>
|
||||
<?php if ($username === $_SESSION['user']): ?>
|
||||
<span style="color: #3bc4e8; font-size: 0.8em;">(vous)</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<span style="color: <?php echo $data['role'] === 'Admin' ? '#3bc4e8' : '#aaa'; ?>">
|
||||
<?php echo htmlspecialchars($data['role']); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($username !== $_SESSION['user']): ?>
|
||||
<a href="admin_users.php?role=<?php echo urlencode($username); ?>"
|
||||
class="btn-action btn-warning">
|
||||
<?php echo $data['role'] === 'Admin' ? '⬇️ Rétrograder' : '⬆️ Promouvoir'; ?>
|
||||
</a>
|
||||
<a href="admin_users.php?supprimer=<?php echo urlencode($username); ?>"
|
||||
class="btn-action btn-danger"
|
||||
onclick="return confirm('Supprimer <?php echo htmlspecialchars($username); ?> ?')">
|
||||
🗑️ Supprimer
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span style="color: #666; font-size: 0.85em;">— compte actuel —</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'include/footer.php'; ?>
|
||||
106
smart-house---final-/dashboard.js
Normal file
106
smart-house---final-/dashboard.js
Normal file
@@ -0,0 +1,106 @@
|
||||
/* global Chart */
|
||||
|
||||
const climat = document.querySelector('.widget.climat');
|
||||
const securite = document.querySelector('.widget.securite');
|
||||
const energie = document.querySelector('.widget.energie');
|
||||
const lumiere = document.querySelector('.widget.lumiere');
|
||||
const alertes = document.querySelector('.widget.alertes');
|
||||
|
||||
function setupHover(widget, colorNormal, colorHover) {
|
||||
widget.addEventListener('mouseenter', () => {
|
||||
widget.style.backgroundColor = colorHover;
|
||||
});
|
||||
widget.addEventListener('mouseleave', () => {
|
||||
widget.style.backgroundColor = colorNormal;
|
||||
});
|
||||
}
|
||||
|
||||
setupHover(climat, '#ADD8E6', '#87CEEB');
|
||||
setupHover(securite, '#D3D3D3', '#C0C0C0');
|
||||
setupHover(energie, '#FFFACD', '#FAFAD2');
|
||||
setupHover(lumiere, '#F5F5DC', '#FFF8DC');
|
||||
|
||||
function showAlert(widget) {
|
||||
widget.classList.add('alert');
|
||||
}
|
||||
|
||||
showAlert(climat);
|
||||
alertes.style.display = 'block';
|
||||
|
||||
let monGraphique = null;
|
||||
const canvasTemp = document.getElementById('graph-temp');
|
||||
|
||||
function creerGraphique(labels, valeurs) {
|
||||
if (!canvasTemp) return;
|
||||
const ctx = canvasTemp.getContext('2d');
|
||||
|
||||
monGraphique = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: 'Température',
|
||||
data: valeurs,
|
||||
borderColor: 'blue',
|
||||
backgroundColor: 'rgba(173,216,230,0.2)',
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { display: true }
|
||||
},
|
||||
scales: {
|
||||
x: { display: true, title: { display: true, text: 'Temps' } },
|
||||
y: { display: true, title: { display: true, text: '°C' } }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function chargerDonnees() {
|
||||
try {
|
||||
const res = await fetch('api/get_data.php');
|
||||
const data = await res.json();
|
||||
|
||||
if (data.status === 'success') {
|
||||
if (data.climate) {
|
||||
document.getElementById("temperature").textContent = data.climate.temperature + " °C";
|
||||
document.getElementById("humidity").textContent = data.climate.humidity + " %";
|
||||
document.getElementById("pressure").textContent = data.climate.pressure + " hPa";
|
||||
document.getElementById("luminosity").textContent = data.climate.luminosity + " lux";
|
||||
}
|
||||
|
||||
if (data.security) {
|
||||
document.getElementById("event_type").textContent = data.security.event_type;
|
||||
document.getElementById("location").textContent = data.security.location;
|
||||
}
|
||||
|
||||
if (data.graph && data.graph.length > 0) {
|
||||
const labels = data.graph.map(point => {
|
||||
const date = new Date(point.timestamp);
|
||||
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
});
|
||||
const temperatures = data.graph.map(point => point.temperature);
|
||||
|
||||
if (monGraphique) {
|
||||
monGraphique.data.labels = labels;
|
||||
monGraphique.data.datasets[0].data = temperatures;
|
||||
monGraphique.update();
|
||||
} else {
|
||||
creerGraphique(labels, temperatures);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur lors du chargement de l'API :", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
await chargerDonnees();
|
||||
setInterval(chargerDonnees, 5000);
|
||||
}
|
||||
|
||||
init();
|
||||
59
smart-house---final-/dashboard.php
Normal file
59
smart-house---final-/dashboard.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'include/session_check.php';
|
||||
include 'include/header.php';
|
||||
?>
|
||||
<main>
|
||||
<div class="dashboard-header">
|
||||
<h2>Dashboard Smart House</h2>
|
||||
<p>
|
||||
Bienvenue, <?php echo htmlspecialchars($_SESSION['user']); ?>
|
||||
(<?php echo htmlspecialchars($_SESSION['role']); ?>)
|
||||
</p>
|
||||
<?php if ($_SESSION['role'] === 'Admin'): ?>
|
||||
<p>Vous êtes Admin, vous pouvez gérer le site.</p>
|
||||
<?php else: ?>
|
||||
<p>Vous êtes Utilisateur, votre accès est limité.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="dashboard">
|
||||
<div class="widget climat">
|
||||
<span class="icone">🌡️</span>
|
||||
<strong>Climat</strong>
|
||||
<p>Température : <span id="temperature">–</span></p>
|
||||
<p>Humidité : <span id="humidity">–</span></p>
|
||||
<p>Pression : <span id="pressure">–</span></p>
|
||||
<p>Luminosité : <span id="luminosity">–</span></p>
|
||||
</div>
|
||||
<div class="widget securite">
|
||||
<span class="icone">🚨</span>
|
||||
<strong>Sécurité</strong>
|
||||
<p>Événement : <span id="event_type">–</span></p>
|
||||
<p>Lieu : <span id="location">–</span></p>
|
||||
</div>
|
||||
<div class="widget energie">
|
||||
<span class="icone">⚡</span>
|
||||
<strong>Énergie</strong>
|
||||
<p>Puissance : –</p>
|
||||
<p>Consommation : –</p>
|
||||
</div>
|
||||
<div class="widget lumiere">
|
||||
<span class="icone">💡</span>
|
||||
<strong>Lumière</strong>
|
||||
<p>Lux : –</p>
|
||||
</div>
|
||||
<div class="widget graphiques">
|
||||
<span class="icone">📊</span>
|
||||
<strong>Graphiques</strong>
|
||||
<canvas id="graph-temp" width="400" height="150"></canvas>
|
||||
</div>
|
||||
<div class="widget alertes">
|
||||
<span class="icone">⚠️</span>
|
||||
<strong>Alertes</strong>
|
||||
<p>– Aucune alerte pour l'instant –</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="/smart-house---final-/dashboard.js"></script>
|
||||
<?php include 'include/footer.php'; ?>
|
||||
25
smart-house---final-/form_login_register.php
Normal file
25
smart-house---final-/form_login_register.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
// -----------------------------
|
||||
// Fichier de formulaire commun
|
||||
// Compatible PHP 5.x
|
||||
// -----------------------------
|
||||
|
||||
// Définir les valeurs par défaut si elles ne sont pas définies
|
||||
$message = isset($message) ? $message : '';
|
||||
$action = isset($action) ? $action : '';
|
||||
$buttonText = isset($buttonText) ? $buttonText : 'Envoyer';
|
||||
?>
|
||||
|
||||
<?php if($message != ''): ?>
|
||||
<p><?php echo htmlspecialchars($message); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" class="form-box" action="<?php echo htmlspecialchars($action); ?>">
|
||||
<label for="username" class="sr-only">Nom d'utilisateur</label>
|
||||
<input id="username" type="text" name="username" placeholder="Nom d'utilisateur" required>
|
||||
|
||||
<label for="password" class="sr-only">Mot de passe</label>
|
||||
<input id="password" type="password" name="password" placeholder="Mot de passe" required>
|
||||
|
||||
<button type="submit"><?php echo htmlspecialchars($buttonText); ?></button>
|
||||
</form>
|
||||
4
smart-house---final-/gen.php
Normal file
4
smart-house---final-/gen.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
$hash = password_hash('Levasseur', PASSWORD_DEFAULT);
|
||||
echo $hash;
|
||||
var_dump(password_verify('Levasseur', $hash));
|
||||
28
smart-house---final-/include/footer.php
Normal file
28
smart-house---final-/include/footer.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<footer>
|
||||
<p>© 2026 Smart House</p>
|
||||
</footer>
|
||||
<script src="../dashboard.js"></script>
|
||||
<script>
|
||||
function toggleTheme() {
|
||||
const body = document.body;
|
||||
const btn = document.getElementById('toggle-theme');
|
||||
body.classList.toggle('light-mode');
|
||||
|
||||
if (body.classList.contains('light-mode')) {
|
||||
btn.textContent = '🌙';
|
||||
localStorage.setItem('theme', 'light');
|
||||
} else {
|
||||
btn.textContent = '☀️';
|
||||
localStorage.setItem('theme', 'dark');
|
||||
}
|
||||
}
|
||||
|
||||
(function() {
|
||||
const theme = localStorage.getItem('theme');
|
||||
const btn = document.getElementById('toggle-theme');
|
||||
if (theme === 'light') {
|
||||
document.body.classList.add('light-mode');
|
||||
if (btn) btn.textContent = '🌙';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
15
smart-house---final-/include/header.php
Normal file
15
smart-house---final-/include/header.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Smart House</title>
|
||||
<!-- CSS principal -->
|
||||
<link rel="stylesheet" href="/smart-house---final-/style.css">
|
||||
<!-- Chart.js local -->
|
||||
<script src="/smart-house---final-/js/chart.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
// Inclure la navigation
|
||||
require_once 'nav.php';
|
||||
?>
|
||||
17
smart-house---final-/include/nav.php
Normal file
17
smart-house---final-/include/nav.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<nav>
|
||||
<div class="nav-logo">
|
||||
<span>🏠</span> Smart House
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="../dashboard.php">Dashboard</a></li>
|
||||
<li><a href="../profil.php">👤 Profil</a></li>
|
||||
<?php if(isset($_SESSION['role']) && $_SESSION['role'] === 'Admin'): ?>
|
||||
<li><a href="../admin_users.php">Utilisateurs</a></li>
|
||||
<li><a href="../admin_settings.php">Paramètres</a></li>
|
||||
<li><a href="../admin_logs.php">Logs</a></li>
|
||||
<?php endif; ?>
|
||||
<li><a href="../logout.php" class="btn-red">Déconnexion</a></li>
|
||||
<!-- suppress JSUnresolvedFunction -->
|
||||
<li><button id="toggle-theme" onclick="toggleTheme()">☀️</button></li>
|
||||
</ul>
|
||||
</nav>
|
||||
36
smart-house---final-/include/session_check.php
Normal file
36
smart-house---final-/include/session_check.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
$timeout = 900; // 15 minutes
|
||||
|
||||
$usersFile = __DIR__ . '/../users.json.php';
|
||||
$users = file_exists($usersFile) ? json_decode(file_get_contents($usersFile), true) : [];
|
||||
|
||||
// Vérifier si connecté
|
||||
if (!isset($_SESSION['user'])) {
|
||||
header("Location: login.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Vérifier si l'utilisateur existe toujours dans users.json.php
|
||||
if (!isset($users[$_SESSION['user']])) {
|
||||
session_destroy();
|
||||
header("Location: login.php?timeout=1");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Vérifier si le rôle en session correspond toujours au rôle dans users.json.php
|
||||
if ($_SESSION['role'] !== $users[$_SESSION['user']]['role']) {
|
||||
$_SESSION['role'] = $users[$_SESSION['user']]['role'];
|
||||
}
|
||||
|
||||
// Vérifier l'inactivité
|
||||
if (isset($_SESSION['last_activity'])) {
|
||||
$inactif = time() - $_SESSION['last_activity'];
|
||||
if ($inactif > $timeout) {
|
||||
session_destroy();
|
||||
header("Location: login.php?timeout=1");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour le temps de dernière activité
|
||||
$_SESSION['last_activity'] = time();
|
||||
37
smart-house---final-/index.php
Normal file
37
smart-house---final-/index.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
session_start();
|
||||
include 'include/header.php';
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div class="hero">
|
||||
<h2>🏠 Bienvenue sur Smart House</h2>
|
||||
<p>Gérez votre maison intelligente en toute simplicité.<br>Confort, sécurité et énergie au bout des doigts.</p>
|
||||
<a href="dashboard.php" class="btn-primary">Accéder au dashboard</a>
|
||||
</div>
|
||||
|
||||
<div class="cards-home">
|
||||
<div class="card-home">
|
||||
<span class="icone">🌡️</span>
|
||||
<h3>Climat</h3>
|
||||
<p>Surveillez température, humidité et pression.</p>
|
||||
</div>
|
||||
<div class="card-home">
|
||||
<span class="icone">🚨</span>
|
||||
<h3>Sécurité</h3>
|
||||
<p>Gardez un œil sur votre maison en temps réel.</p>
|
||||
</div>
|
||||
<div class="card-home">
|
||||
<span class="icone">⚡</span>
|
||||
<h3>Énergie</h3>
|
||||
<p>Optimisez votre consommation électrique.</p>
|
||||
</div>
|
||||
<div class="card-home">
|
||||
<span class="icone">💡</span>
|
||||
<h3>Lumière</h3>
|
||||
<p>Contrôlez l'éclairage intelligemment.</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'include/footer.php'; ?>
|
||||
0
smart-house---final-/js/chart.min.js
vendored
Normal file
0
smart-house---final-/js/chart.min.js
vendored
Normal file
106
smart-house---final-/login.php
Normal file
106
smart-house---final-/login.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
$usersFile = 'users.json.php';
|
||||
$users = file_exists($usersFile) ? json_decode(file_get_contents($usersFile), true) : [];
|
||||
$message = '';
|
||||
$success = '';
|
||||
|
||||
// Initialiser les tentatives
|
||||
if (!isset($_SESSION['login_attempts'])) {
|
||||
$_SESSION['login_attempts'] = 0;
|
||||
}
|
||||
if (!isset($_SESSION['login_time'])) {
|
||||
$_SESSION['login_time'] = null;
|
||||
}
|
||||
|
||||
// Message session expirée
|
||||
if (isset($_GET['timeout'])) {
|
||||
$message = "Session expirée. Veuillez vous reconnecter.";
|
||||
}
|
||||
|
||||
// Vérifier si bloqué
|
||||
$bloque = false;
|
||||
if ($_SESSION['login_attempts'] >= 3 && $_SESSION['login_time'] !== null) {
|
||||
$temps_ecoule = time() - $_SESSION['login_time'];
|
||||
if ($temps_ecoule < 300) {
|
||||
$bloque = true;
|
||||
$reste = 300 - $temps_ecoule;
|
||||
$message = "Trop de tentatives. Réessayez dans " . ceil($reste / 60) . " minute(s).";
|
||||
} else {
|
||||
$_SESSION['login_attempts'] = 0;
|
||||
$_SESSION['login_time'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['registered'])) {
|
||||
$success = "Compte créé avec succès ! En attente de validation par l'administrateur.";
|
||||
}
|
||||
|
||||
if (!$bloque && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = trim($_POST['username']);
|
||||
$password = trim($_POST['password']);
|
||||
|
||||
if (isset($users[$username]) && password_verify($password, $users[$username]['password'])) {
|
||||
// Vérifier si le compte est en attente
|
||||
if (isset($users[$username]['statut']) && $users[$username]['statut'] === 'en_attente') {
|
||||
$message = "Votre compte est en attente de validation par l'administrateur.";
|
||||
} else {
|
||||
$_SESSION['login_attempts'] = 0;
|
||||
$_SESSION['login_time'] = null;
|
||||
$_SESSION['user'] = $username;
|
||||
$_SESSION['role'] = $users[$username]['role'];
|
||||
$_SESSION['last_activity'] = time();
|
||||
session_regenerate_id(true);
|
||||
header("Location: dashboard.php");
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
$_SESSION['login_attempts']++;
|
||||
$_SESSION['login_time'] = time();
|
||||
$restants = 3 - $_SESSION['login_attempts'];
|
||||
if ($restants > 0) {
|
||||
$message = "Identifiants incorrects. Il vous reste $restants essai(s).";
|
||||
} else {
|
||||
$message = "Trop de tentatives. Réessayez dans 5 minutes.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include 'include/header.php';
|
||||
?>
|
||||
|
||||
<div class="form-box">
|
||||
<h2>Connexion</h2>
|
||||
<?php if ($success): ?>
|
||||
<p class="form-success"><?php echo htmlspecialchars($success); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if ($message): ?>
|
||||
<p class="form-error"><?php echo htmlspecialchars($message); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!$bloque): ?>
|
||||
<form method="POST" action="login.php">
|
||||
<label class="field-label" for="username">Nom d'utilisateur</label>
|
||||
<input id="username" type="text" name="username" placeholder="Nom d'utilisateur" required>
|
||||
|
||||
<label class="field-label" for="password">Mot de passe</label>
|
||||
<div class="password-wrapper">
|
||||
<input id="password" type="password" name="password" placeholder="Mot de passe" required>
|
||||
<button type="button" class="toggle-password" onclick="togglePassword()">👁️</button>
|
||||
</div>
|
||||
|
||||
<button type="submit">Se connecter</button>
|
||||
</form>
|
||||
<p class="form-link">Pas encore de compte ? <a href="register.php">S'inscrire</a></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function togglePassword() {
|
||||
const input = document.getElementById('password');
|
||||
input.type = input.type === 'password' ? 'text' : 'password';
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include 'include/footer.php'; ?>
|
||||
5
smart-house---final-/logout.php
Normal file
5
smart-house---final-/logout.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_destroy();
|
||||
header("Location: login.php");
|
||||
exit();
|
||||
4
smart-house---final-/main.php
Normal file
4
smart-house---final-/main.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<main>
|
||||
<h2>Page principale</h2>
|
||||
<p>Bienvenue sur le site Smart House.</p>
|
||||
</main>
|
||||
8
smart-house---final-/personne1.php
Normal file
8
smart-house---final-/personne1.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
$personne1 = [
|
||||
'nom' => 'Exemple',
|
||||
'prenom' => 'Utilisateur',
|
||||
'email' => 'exemple@site.com',
|
||||
'role' => 'Utilisateur',
|
||||
'classe' => 'BTS CIEL'
|
||||
];
|
||||
83
smart-house---final-/profil.php
Normal file
83
smart-house---final-/profil.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'include/session_check.php';
|
||||
include 'include/header.php';
|
||||
|
||||
$usersFile = 'users.json.php';
|
||||
$users = file_exists($usersFile) ? json_decode(file_get_contents($usersFile), true) : [];
|
||||
$message = '';
|
||||
$success = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$ancien = trim($_POST['ancien_password']);
|
||||
$nouveau = trim($_POST['nouveau_password']);
|
||||
$confirm = trim($_POST['confirm_password']);
|
||||
|
||||
if (empty($ancien) || empty($nouveau) || empty($confirm)) {
|
||||
$message = "Veuillez remplir tous les champs.";
|
||||
} elseif (!password_verify($ancien, $users[$_SESSION['user']]['password'])) {
|
||||
$message = "Ancien mot de passe incorrect.";
|
||||
} elseif (strlen($nouveau) < 6) {
|
||||
$message = "Le nouveau mot de passe doit faire au moins 6 caractères.";
|
||||
} elseif ($nouveau !== $confirm) {
|
||||
$message = "Les mots de passe ne correspondent pas.";
|
||||
} else {
|
||||
$users[$_SESSION['user']]['password'] = password_hash($nouveau, PASSWORD_DEFAULT);
|
||||
file_put_contents($usersFile, json_encode($users, JSON_PRETTY_PRINT));
|
||||
$success = "Mot de passe modifié avec succès !";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div class="form-box" style="margin-top: 40px;">
|
||||
<h2>👤 Mon Profil</h2>
|
||||
|
||||
<div class="profil-info">
|
||||
<p><strong>Nom d'utilisateur :</strong> <?php echo htmlspecialchars($_SESSION['user']); ?></p>
|
||||
<p><strong>Rôle :</strong> <?php echo htmlspecialchars($_SESSION['role']); ?></p>
|
||||
</div>
|
||||
|
||||
<hr style="border-color: #444; margin: 20px 0;">
|
||||
|
||||
<h3 style="margin-bottom: 15px; font-size: 1em;">Changer le mot de passe</h3>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<p class="form-error"><?php echo htmlspecialchars($message); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if ($success): ?>
|
||||
<p class="form-success"><?php echo htmlspecialchars($success); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="profil.php">
|
||||
<label class="field-label" for="ancien_password">Ancien mot de passe</label>
|
||||
<div class="password-wrapper">
|
||||
<input id="ancien_password" type="password" name="ancien_password" placeholder="Ancien mot de passe" required>
|
||||
<button type="button" class="toggle-password" onclick="togglePass('ancien_password')">👁️</button>
|
||||
</div>
|
||||
|
||||
<label class="field-label" for="nouveau_password">Nouveau mot de passe</label>
|
||||
<div class="password-wrapper">
|
||||
<input id="nouveau_password" type="password" name="nouveau_password" placeholder="Nouveau mot de passe (6 car. min.)" required>
|
||||
<button type="button" class="toggle-password" onclick="togglePass('nouveau_password')">👁️</button>
|
||||
</div>
|
||||
|
||||
<label class="field-label" for="confirm_password">Confirmer le mot de passe</label>
|
||||
<div class="password-wrapper">
|
||||
<input id="confirm_password" type="password" name="confirm_password" placeholder="Confirmer le mot de passe" required>
|
||||
<button type="button" class="toggle-password" onclick="togglePass('confirm_password')">👁️</button>
|
||||
</div>
|
||||
|
||||
<button type="submit">Changer le mot de passe</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
function togglePass(id) {
|
||||
const input = document.getElementById(id);
|
||||
input.type = input.type === 'password' ? 'text' : 'password';
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include 'include/footer.php'; ?>
|
||||
60
smart-house---final-/register.php
Normal file
60
smart-house---final-/register.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
session_start();
|
||||
$usersFile = 'users.json.php';
|
||||
$users = file_exists($usersFile) ? json_decode(file_get_contents($usersFile), true) : [];
|
||||
$message = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = trim($_POST['username']);
|
||||
$password = trim($_POST['password']);
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
$message = "Veuillez remplir tous les champs.";
|
||||
} elseif (strlen($password) < 6) {
|
||||
$message = "Le mot de passe doit faire au moins 6 caractères.";
|
||||
} elseif (isset($users[$username])) {
|
||||
$message = "Cet utilisateur existe déjà !";
|
||||
} else {
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
$users[$username] = [
|
||||
'password' => $hash,
|
||||
'role' => 'User',
|
||||
'statut' => 'en_attente'
|
||||
];
|
||||
file_put_contents($usersFile, json_encode($users, JSON_PRETTY_PRINT));
|
||||
header("Location: login.php?registered=1");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
include 'include/header.php';
|
||||
?>
|
||||
|
||||
<div class="form-box">
|
||||
<h2>Créer un compte</h2>
|
||||
<?php if ($message): ?>
|
||||
<p class="form-error"><?php echo htmlspecialchars($message); ?></p>
|
||||
<?php endif; ?>
|
||||
<form method="POST" action="register.php">
|
||||
<label class="field-label" for="username">Nom d'utilisateur</label>
|
||||
<input id="username" type="text" name="username" placeholder="Nom d'utilisateur" required>
|
||||
|
||||
<label class="field-label" for="password">Mot de passe</label>
|
||||
<div class="password-wrapper">
|
||||
<input id="password" type="password" name="password" placeholder="Mot de passe (6 car. min.)" required>
|
||||
<button type="button" class="toggle-password" onclick="togglePassword()">👁️</button>
|
||||
</div>
|
||||
|
||||
<button type="submit">S'inscrire</button>
|
||||
</form>
|
||||
<p class="form-link">Déjà un compte ? <a href="login.php">Se connecter</a></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function togglePassword() {
|
||||
const input = document.getElementById('password');
|
||||
input.type = input.type === 'password' ? 'text' : 'password';
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include 'include/footer.php'; ?>
|
||||
6
smart-house---final-/settings.json.php
Normal file
6
smart-house---final-/settings.json.php
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"site_name": "Smart House",
|
||||
"max_attempts": 3,
|
||||
"session_timeout": 900,
|
||||
"welcome_message": "Bienvenue sur votre tableau de bord Smart House."
|
||||
}
|
||||
747
smart-house---final-/style.css
Normal file
747
smart-house---final-/style.css
Normal file
@@ -0,0 +1,747 @@
|
||||
/* ===========================
|
||||
GLOBAL
|
||||
=========================== */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #1e1e1e;
|
||||
color: #f0f0f0;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
NAVBAR
|
||||
=========================== */
|
||||
nav {
|
||||
background-color: #1a2332;
|
||||
padding: 0 30px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #fff;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nav-logo span {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: #ccd6e0;
|
||||
font-size: 0.95em;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
nav ul li a:hover {
|
||||
color: #fff;
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
nav ul li a.btn-red {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
nav ul li a.btn-red:hover {
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#toggle-theme {
|
||||
background: none;
|
||||
border: 1px solid #ccd6e0;
|
||||
color: #ccd6e0;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 0.95em;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
#toggle-theme:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
PAGE ACCUEIL
|
||||
=========================== */
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 80px 20px 50px;
|
||||
}
|
||||
|
||||
.hero h2 {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #f0f0f0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
color: #aaa;
|
||||
font-size: 1em;
|
||||
margin-bottom: 25px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
display: inline-block;
|
||||
background-color: #3bc4e8;
|
||||
color: white;
|
||||
padding: 12px 30px;
|
||||
border-radius: 25px;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #2aafd0;
|
||||
}
|
||||
|
||||
.cards-home {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20px;
|
||||
max-width: 1000px;
|
||||
margin: 40px auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.card-home {
|
||||
background-color: #2c3e50;
|
||||
border-radius: 12px;
|
||||
padding: 30px 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.card-home .icone {
|
||||
font-size: 2em;
|
||||
display: block;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-home h3 {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.card-home p {
|
||||
font-size: 0.85em;
|
||||
color: #aaa;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
FOOTER
|
||||
=========================== */
|
||||
footer {
|
||||
border-top: 1px solid #333;
|
||||
padding: 15px 30px;
|
||||
text-align: left;
|
||||
font-size: 0.85em;
|
||||
color: #888;
|
||||
background-color: #1e1e1e;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
footer hr {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
FORMULAIRE LOGIN / REGISTER
|
||||
=========================== */
|
||||
.form-box {
|
||||
width: 360px;
|
||||
margin: 60px auto;
|
||||
padding: 30px;
|
||||
background-color: #2c3e50;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
|
||||
text-align: center;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.form-box h2 {
|
||||
margin-bottom: 25px;
|
||||
font-size: 1.3em;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.form-box h3 {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.form-box label.field-label {
|
||||
display: block;
|
||||
text-align: left;
|
||||
font-size: 0.85em;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.form-box input {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #444;
|
||||
border-radius: 8px;
|
||||
font-size: 0.95em;
|
||||
color: #f0f0f0;
|
||||
background-color: #1e1e1e;
|
||||
transition: border 0.3s;
|
||||
}
|
||||
|
||||
.form-box input:focus {
|
||||
outline: none;
|
||||
border-color: #3bc4e8;
|
||||
}
|
||||
|
||||
.form-box input::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.password-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.password-wrapper input {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.toggle-password {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
color: #888;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.form-box button[type="submit"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #3bc4e8;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.form-box button[type="submit"]:hover {
|
||||
background-color: #2aafd0;
|
||||
}
|
||||
|
||||
.form-error {
|
||||
color: #e74c3c;
|
||||
font-size: 0.88em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-success {
|
||||
color: #2ecc71;
|
||||
font-size: 0.88em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-link {
|
||||
margin-top: 15px;
|
||||
font-size: 0.88em;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.form-link a {
|
||||
color: #3bc4e8;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.form-link a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
ACCESSIBILITÉ
|
||||
=========================== */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0,0,0,0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
DASHBOARD
|
||||
=========================== */
|
||||
.dashboard-header {
|
||||
padding: 30px 30px 10px;
|
||||
}
|
||||
|
||||
.dashboard-header h2 {
|
||||
color: #f0f0f0;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.dashboard-header p {
|
||||
color: #aaa;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 15px;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
WIDGETS
|
||||
=========================== */
|
||||
.widget {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
background-color: #2c3e50;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.widget strong {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.widget p {
|
||||
font-size: 0.9em;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.climat { background-color: #34495e; }
|
||||
.securite { background-color: #7f8c8d; }
|
||||
.energie { background-color: #f39c12; }
|
||||
.lumiere { background-color: #8e44ad; }
|
||||
|
||||
.graphiques {
|
||||
grid-column: span 4;
|
||||
background-color: #2c3e50;
|
||||
}
|
||||
|
||||
.alertes {
|
||||
grid-column: span 4;
|
||||
background-color: #c0392b;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
ICONES
|
||||
=========================== */
|
||||
.icone {
|
||||
font-size: 2em;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
ALERTE ACTIVE
|
||||
=========================== */
|
||||
.alert {
|
||||
border: 2px solid #e74c3c;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
LOGS / ADMIN
|
||||
=========================== */
|
||||
.logs-container {
|
||||
padding: 20px 30px;
|
||||
background-color: #2c3e50;
|
||||
margin: 20px 30px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.logs-container h2 {
|
||||
color: #f0f0f0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.logs-container h3 {
|
||||
color: #f0f0f0;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.logs-container ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.logs-container ul li {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #3d5166;
|
||||
font-size: 0.9em;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
PROFIL
|
||||
=========================== */
|
||||
.profil-info {
|
||||
text-align: left;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.profil-info p {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #3d5166;
|
||||
font-size: 0.95em;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.profil-info p strong {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
TABLEAU ADMIN
|
||||
=========================== */
|
||||
.admin-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.admin-table th {
|
||||
background-color: #1a2332;
|
||||
color: #f0f0f0;
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.admin-table td {
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #3d5166;
|
||||
font-size: 0.9em;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.admin-table tr:hover td {
|
||||
background-color: #34495e;
|
||||
}
|
||||
|
||||
.btn-action {
|
||||
padding: 5px 12px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 0.85em;
|
||||
margin-right: 5px;
|
||||
transition: all 0.3s;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: #c0392b;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background-color: #f39c12;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-warning:hover {
|
||||
background-color: #d68910;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
SKELETON LOADING
|
||||
=========================== */
|
||||
@keyframes skeleton-pulse {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
.skeleton {
|
||||
background-color: #3d5166;
|
||||
border-radius: 5px;
|
||||
height: 14px;
|
||||
margin: 8px auto;
|
||||
width: 70%;
|
||||
animation: skeleton-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
MODE CLAIR
|
||||
=========================== */
|
||||
body.light-mode {
|
||||
background-color: #f0f2f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
body.light-mode nav {
|
||||
background-color: #1a2332;
|
||||
}
|
||||
|
||||
body.light-mode .card-home {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
body.light-mode .card-home h3 {
|
||||
color: #1a2332;
|
||||
}
|
||||
|
||||
body.light-mode .card-home p {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
body.light-mode .hero h2 {
|
||||
color: #1a2332;
|
||||
}
|
||||
|
||||
body.light-mode .hero p {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
body.light-mode .form-box {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
body.light-mode .form-box h2,
|
||||
body.light-mode .form-box h3 {
|
||||
color: #1a2332;
|
||||
}
|
||||
|
||||
body.light-mode .form-box label.field-label {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
body.light-mode .form-box input {
|
||||
background-color: #f0f2f5;
|
||||
color: #333;
|
||||
border-color: #ddd;
|
||||
}
|
||||
|
||||
body.light-mode .form-box input::placeholder {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
body.light-mode .widget {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
body.light-mode .widget strong {
|
||||
color: #1a2332;
|
||||
}
|
||||
|
||||
body.light-mode .widget p {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
body.light-mode .climat { background-color: #e8f4fd; }
|
||||
body.light-mode .securite { background-color: #f0f0f0; }
|
||||
body.light-mode .energie { background-color: #fff8e1; }
|
||||
body.light-mode .lumiere { background-color: #f3e8ff; }
|
||||
|
||||
body.light-mode .graphiques {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
body.light-mode .alertes {
|
||||
background-color: #fdecea;
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
body.light-mode .logs-container {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
body.light-mode .logs-container h2,
|
||||
body.light-mode .logs-container h3 {
|
||||
color: #1a2332;
|
||||
}
|
||||
|
||||
body.light-mode .logs-container ul li {
|
||||
color: #555;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
body.light-mode footer {
|
||||
background-color: #f0f2f5;
|
||||
border-top: 1px solid #ddd;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
body.light-mode #toggle-theme {
|
||||
border-color: #ccd6e0;
|
||||
color: #ccd6e0;
|
||||
}
|
||||
|
||||
body.light-mode .profil-info p {
|
||||
border-bottom: 1px solid #ddd;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
body.light-mode .profil-info p strong {
|
||||
color: #1a2332;
|
||||
}
|
||||
|
||||
body.light-mode .admin-table th {
|
||||
background-color: #1a2332;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
body.light-mode .admin-table td {
|
||||
color: #333;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
body.light-mode .admin-table tr:hover td {
|
||||
background-color: #e8f4fd;
|
||||
}
|
||||
|
||||
body.light-mode .skeleton {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
RESPONSIVE MOBILE
|
||||
=========================== */
|
||||
|
||||
/* Tablette (max 900px) */
|
||||
@media (max-width: 900px) {
|
||||
.dashboard {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.graphiques {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.alertes {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.cards-home {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile (max 600px) */
|
||||
@media (max-width: 600px) {
|
||||
nav {
|
||||
padding: 10px 15px;
|
||||
height: auto;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.graphiques {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
.alertes {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
.cards-home {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.form-box {
|
||||
width: 90%;
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 40px 15px 30px;
|
||||
}
|
||||
|
||||
.hero h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.logs-container {
|
||||
margin: 10px 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.admin-table {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.btn-action {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
15
smart-house---final-/test.php
Normal file
15
smart-house---final-/test.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
$usersFile = 'users.json';
|
||||
$users = json_decode(file_get_contents($usersFile), true);
|
||||
|
||||
$username = 'Kari';
|
||||
$password = 'Levasseur';
|
||||
|
||||
echo "Utilisateur existe : ";
|
||||
var_dump(isset($users[$username]));
|
||||
|
||||
echo "Hash stocké : " . $users[$username]['password'] . "<br>";
|
||||
|
||||
echo "Vérification : ";
|
||||
var_dump(password_verify($password, $users[$username]['password']));
|
||||
?>
|
||||
6
smart-house---final-/users.json.php
Normal file
6
smart-house---final-/users.json.php
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Kari": {
|
||||
"password": "$2y$10$ocJtd5vNxlaQfzBRiueP6.JBNkxNEjh2UviGLXL4aEyXAKuWxBW0.",
|
||||
"role": "Admin"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user