Compare commits
3
Commits
3f142a5388
...
be12cadea6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be12cadea6 | ||
|
|
5c43bf9e7c | ||
|
|
0c8d9cba6d |
No files matched your search
+4
-1
@@ -54,7 +54,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} else {
|
||||
$label = trim((string)($_POST['label'] ?? ''));
|
||||
$note = trim((string)($_POST['note'] ?? ''));
|
||||
$sizeBytes = filter_input(INPUT_POST, 'size_bytes', FILTER_VALIDATE_INT) ?: null;
|
||||
|
||||
// Correction : Accepte les grands nombres (BigInt) pour les gros backups
|
||||
$rawSize = $_POST['size_bytes'] ?? '';
|
||||
$sizeBytes = (ctype_digit((string)$rawSize)) ? $rawSize : null;
|
||||
|
||||
if ($label === '') {
|
||||
$feedback = 'Le libelle de la sauvegarde est obligatoire.';
|
||||
|
||||
+14
-13
@@ -42,21 +42,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$description = trim((string)($_POST['description'] ?? ''));
|
||||
|
||||
if ($name === '' || !preg_match('/^[a-z0-9_\-]+$/', $layerKey)) {
|
||||
$feedback = 'Nom ou clé de couche invalide (clé··: lettres minuscules, chiffres, - et _ uniquement).';
|
||||
$feedback = 'Nom ou clé de couche invalide (clé: lettres minuscules, chiffres, - et _ uniquement).';
|
||||
$feedbackType = 'error';
|
||||
} elseif ($color === null) {
|
||||
$feedback = 'Couleur invalide (format attendu : #rrggbb).';
|
||||
$feedbackType = 'error';
|
||||
} else {
|
||||
try {
|
||||
// Correction de l'erreur MySQL 1093 avec une table dérivée (sous-requête AS m2)
|
||||
$stmt = $db->prepare(
|
||||
'INSERT INTO map_layers (name, layer_key, color, is_visible, sort_order, description)
|
||||
VALUES (?, ?, ?, 1, (SELECT COALESCE(MAX(sort_order), 0) + 1 FROM map_layers AS m2), ?)'
|
||||
VALUES (?, ?, ?, 1, (SELECT COALESCE(MAX(m2.sort_order), 0) + 1 FROM (SELECT sort_order FROM map_layers) AS m2), ?)'
|
||||
);
|
||||
$stmt->execute([$name, $layerKey, $color, $description]);
|
||||
$feedback = 'Couche créée.';
|
||||
} catch (Throwable $e) {
|
||||
$feedback = 'Erreur lors de la création (clé·· déjà utilisée ?) : ' . $e->getMessage();
|
||||
$feedback = 'Erreur lors de la création (clé déjà utilisée ?) : ' . $e->getMessage();
|
||||
$feedbackType = 'error';
|
||||
}
|
||||
}
|
||||
@@ -65,7 +66,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ($layerId) {
|
||||
$stmt = $db->prepare('UPDATE map_layers SET is_visible = 1 - is_visible WHERE id = ?');
|
||||
$stmt->execute([$layerId]);
|
||||
$feedback = 'Visibilité·· mise à jour.';
|
||||
$feedback = 'Visibilité mise à jour.';
|
||||
}
|
||||
} elseif ($action === 'update_color') {
|
||||
$layerId = filter_input(INPUT_POST, 'layer_id', FILTER_VALIDATE_INT);
|
||||
@@ -82,12 +83,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$layerId = filter_input(INPUT_POST, 'layer_id', FILTER_VALIDATE_INT);
|
||||
$confirmed = isset($_POST['confirm']) && $_POST['confirm'] === '1';
|
||||
if (!$confirmed || !$layerId) {
|
||||
$feedback = 'Suppression refusé··e : confirmation manquante.';
|
||||
$feedback = 'Suppression refusée : confirmation manquante.';
|
||||
$feedbackType = 'error';
|
||||
} else {
|
||||
$stmt = $db->prepare('DELETE FROM map_layers WHERE id = ? LIMIT 1');
|
||||
$stmt->execute([$layerId]);
|
||||
$feedback = $stmt->rowCount() > 0 ? 'Couche supprimé··e.' : 'Couche introuvable.';
|
||||
$feedback = $stmt->rowCount() > 0 ? 'Couche supprimée.' : 'Couche introuvable.';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,7 +108,7 @@ $user = current_admin_username();
|
||||
|
||||
<main class="container">
|
||||
<h1>Couches cartographiques</h1>
|
||||
<p>Gé··rez les couches affich ées sur la carte (visibilité··, couleur, ordre). Toute suppression demande une confirmation explicite.</p>
|
||||
<p>Gérez les couches affichées sur la carte (visibilité, couleur, ordre). Toute suppression demande une confirmation explicite.</p>
|
||||
|
||||
<?php if ($feedback !== null): ?>
|
||||
<div class="alert alert-<?= $feedbackType === 'error' ? 'error' : 'success' ?>">
|
||||
@@ -120,10 +121,10 @@ $user = current_admin_username();
|
||||
<form method="post">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="action" value="create_layer">
|
||||
<label>Nom affiché··e
|
||||
<label>Nom affichée
|
||||
<input type="text" name="name" required maxlength="100">
|
||||
</label>
|
||||
<label>Clé·· technique
|
||||
<label>Clé technique
|
||||
<input type="text" name="layer_key" required pattern="[a-z0-9_\-]+" placeholder="ex: sites_4g">
|
||||
</label>
|
||||
<label>Couleur
|
||||
@@ -132,7 +133,7 @@ $user = current_admin_username();
|
||||
<label>Description
|
||||
<input type="text" name="description" maxlength="255">
|
||||
</label>
|
||||
<button type="submit" class="btn btn-primary">Cré··er</button>
|
||||
<button type="submit" class="btn btn-primary">Créer</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@@ -143,7 +144,7 @@ $user = current_admin_username();
|
||||
<tr>
|
||||
<th>Ordre</th>
|
||||
<th>Nom</th>
|
||||
<th>Clé··</th>
|
||||
<th>Clé</th>
|
||||
<th>Couleur</th>
|
||||
<th>Visible</th>
|
||||
<th>Description</th>
|
||||
@@ -170,7 +171,7 @@ $user = current_admin_username();
|
||||
<input type="hidden" name="action" value="toggle_visibility">
|
||||
<input type="hidden" name="layer_id" value="<?= (int)$layer['id'] ?>">
|
||||
<button type="submit" class="btn btn-secondary">
|
||||
<?= ((int)$layer['is_visible'] === 1) ? 'Visible' : 'Masqué··e' ?>
|
||||
<?= ((int)$layer['is_visible'] === 1) ? 'Visible' : 'Masquée' ?>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
@@ -187,7 +188,7 @@ $user = current_admin_username();
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($layers)): ?>
|
||||
<tr><td colspan="7">Aucune couche configuré··e.</td></tr>
|
||||
<tr><td colspan="7">Aucune couche configurée.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
+46
-15
@@ -219,18 +219,6 @@ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 8. RATE LIMITING DE CONNEXION (durcissement securite du 20/09/2026)
|
||||
--
|
||||
-- Compteur d'echecs de connexion stocke en base (et non en session
|
||||
-- PHP), afin qu'un attaquant ne puisse pas remettre son compteur a
|
||||
-- zero en vidant ses cookies ou en ouvrant une navigation privee.
|
||||
-- Utilise par admin/admin_auth.php (is_login_locked_out,
|
||||
-- record_login_attempt) et admin/login.php.
|
||||
--
|
||||
-- Deux seuils appliques cote PHP a partir de cette meme table :
|
||||
-- - 5 echecs sur le couple (identifiant, IP) en 15 min -> verrouillage cible
|
||||
-- - 30 echecs sur l'IP seule (tous identifiants confondus) en 15 min
|
||||
-- -> verrouillage large, plus permissif pour ne pas bloquer tout
|
||||
-- le monde depuis une IP partagee (ex: sortie Tailscale commune)
|
||||
-- ---------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS login_attempts (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
@@ -244,10 +232,53 @@ KEY idx_username_ip_time (username_normalized, ip_address, attempted_at),
|
||||
KEY idx_ip_time (ip_address, attempted_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Purge recommandee via cron (pas dans ce fichier), pour eviter que
|
||||
-- la table ne grossisse indefiniment :
|
||||
-- DELETE FROM login_attempts WHERE attempted_at < NOW() - INTERVAL 30 DAY;
|
||||
-- ---------------------------------------------------------------------
|
||||
-- 9. TABLES ADDITIONNELLES (Couches, Backups, Historique d'imports)
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
-- Pour admin/map_layers.php
|
||||
CREATE TABLE IF NOT EXISTS map_layers (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
layer_key VARCHAR(50) NOT NULL,
|
||||
color VARCHAR(7) NOT NULL,
|
||||
is_visible TINYINT(1) DEFAULT 1,
|
||||
sort_order INT DEFAULT 0,
|
||||
description TEXT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- Pour admin/backup.php
|
||||
CREATE TABLE IF NOT EXISTS backup_log (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
admin_username VARCHAR(100) NOT NULL,
|
||||
label VARCHAR(255) NOT NULL,
|
||||
note TEXT,
|
||||
size_bytes_reported BIGINT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- Pour admin/import_history.php
|
||||
CREATE TABLE IF NOT EXISTS import_batches (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
imported_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
imported_by VARCHAR(100),
|
||||
status VARCHAR(50),
|
||||
rows_total INT DEFAULT 0,
|
||||
rows_imported INT DEFAULT 0,
|
||||
rows_rejected INT DEFAULT 0,
|
||||
country_id INT,
|
||||
FOREIGN KEY (country_id) REFERENCES countries(id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS import_errors (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
batch_id INT NOT NULL,
|
||||
line_number INT,
|
||||
message TEXT,
|
||||
raw_line TEXT,
|
||||
FOREIGN KEY (batch_id) REFERENCES import_batches(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- =====================================================================
|
||||
-- Fin du schema. A ce stade, la base contient :
|
||||
|
||||
Reference in new issue
Block a user