Files
stepan a116b30df7 [ui] Extrahována kontrola síly hesla do samostatného JS souboru, přidána kontrola minimální délky
[ui] registrace.php – použit společný JS pro kontrolu hesla
[ui] nove_heslo.php – použit společný JS pro kontrolu hesla, opraven odkaz na reset
[ui] admin.php – použit společný JS pro kontrolu hesla
[FÁZE-1][install] Vícekrokový průběh instalace, použit společný JS pro kontrolu hesla
2026-03-17 10:06:44 +01:00

402 lines
14 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// ============================================================
// INSTALAČNÍ SKRIPT
// ============================================================
// Tento skript spusť JEDNOU po nahrání souborů na server.
// Vytvoří všechny potřebné tabulky v databázi a prvního
// administrátora.
//
// Průběh:
// Krok 1 pokus o automatické vytvoření tabulek
// Krok 2 pokud selže, zobrazí SQL pro ruční zadání do phpMyAdmin
// Krok 3 ověření, že tabulky existují a mají správnou strukturu
// Krok 4 formulář pro vytvoření prvního admina + zámek
// Krok 5 instalace úspěšně dokončena
//
// Po úspěšné instalaci se vytvoří soubor install.lock
// a tento skript již nepůjde spustit znovu.
// ============================================================
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/db.php';
// ------------------------------------------------------------
// KONTROLA: Proběhla už instalace?
// ------------------------------------------------------------
$lock_soubor = __DIR__ . '/install.lock';
if (file_exists($lock_soubor)) {
die('<!DOCTYPE html><html lang="cs"><head><meta charset="UTF-8">
<title>Instalace</title></head><body>
<p>Instalace již proběhla.</p>
<p>Pokud chceš instalaci opakovat, smaž soubor
<code>auth/install.lock</code>. Pozor tím přijdeš o všechna data!</p>
</body></html>');
}
// ------------------------------------------------------------
// SQL PŘÍKAZY PRO VYTVOŘENÍ TABULEK
// ------------------------------------------------------------
// Definujeme je jako pole použijeme je jak pro automatické
// spuštění, tak pro zobrazení uživateli k ručnímu zadání.
$sql_tabulky = [
'auth_users' => "CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_UZIVATELE . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`email` VARCHAR(255) NOT NULL,
`heslo` VARCHAR(255) NOT NULL,
`admin` TINYINT(1) NOT NULL DEFAULT 0,
`vytvoreno` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
'auth_remember_tokens' => "CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_TOKENY . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uzivatel_id` INT UNSIGNED NOT NULL,
`selector` VARCHAR(32) NOT NULL,
`token_hash` VARCHAR(255) NOT NULL,
`expiruje` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `selector` (`selector`),
FOREIGN KEY (`uzivatel_id`)
REFERENCES `" . DB_TABULKA_UZIVATELE . "` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
'auth_brute_force' => "CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_BRUTE . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`ip_adresa` VARCHAR(45) NOT NULL,
`email` VARCHAR(255) NOT NULL,
`cas` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `ip_adresa` (`ip_adresa`),
KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
'auth_password_resets' => "CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_RESET . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uzivatel_id` INT UNSIGNED NOT NULL,
`token_hash` VARCHAR(255) NOT NULL,
`expiruje` DATETIME NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`uzivatel_id`)
REFERENCES `" . DB_TABULKA_UZIVATELE . "` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
];
// Tabulka služby je volitelná
if (DB_TABULKA_SLUZBA !== '') {
$sql_tabulky['sluzba'] = "CREATE TABLE IF NOT EXISTS `" . DB_TABULKA_SLUZBA . "` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uzivatel_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uzivatel_id` (`uzivatel_id`),
FOREIGN KEY (`uzivatel_id`)
REFERENCES `" . DB_TABULKA_UZIVATELE . "` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
}
// ------------------------------------------------------------
// POMOCNÁ FUNKCE: Ověření existence tabulek
// ------------------------------------------------------------
// Zkontroluje, zda všechny potřebné tabulky existují v DB.
// Vrací pole chybějících tabulek (prázdné = vše OK).
function zkontroluj_tabulky(PDO $pdo): array
{
// Zjistíme, které tabulky existují v aktuální databázi
$stmt = $pdo->query("SHOW TABLES");
$existuje = $stmt->fetchAll(PDO::FETCH_COLUMN);
$potrebne = [
DB_TABULKA_UZIVATELE,
DB_TABULKA_TOKENY,
DB_TABULKA_BRUTE,
DB_TABULKA_RESET,
];
if (DB_TABULKA_SLUZBA !== '') {
$potrebne[] = DB_TABULKA_SLUZBA;
}
// Vrátíme seznam tabulek, které chybí
return array_diff($potrebne, $existuje);
}
// ------------------------------------------------------------
// URČENÍ AKTUÁLNÍHO KROKU
// ------------------------------------------------------------
$krok = 1;
$chyba = '';
// Krok se přenáší přes skryté pole ve formuláři
$krok_z_post = (int) ($_POST['krok'] ?? 0);
// ------------------------------------------------------------
// ZPRACOVÁNÍ AKCÍ
// ------------------------------------------------------------
// == KROK 1: Pokus o automatické vytvoření tabulek ============
if ($krok_z_post === 0 || $krok_z_post === 1) {
$auto_ok = true;
foreach ($sql_tabulky as $nazev => $sql) {
try {
$pdo->exec($sql);
} catch (PDOException $e) {
// Automatické vytvoření selhalo uživatel nemá práva
$auto_ok = false;
break;
}
}
if ($auto_ok) {
// Tabulky byly vytvořeny automaticky přeskočíme na krok 4
$krok = 4;
} else {
// Nemáme práva přejdeme na krok 2 (ruční SQL)
$krok = 2;
}
}
// == KROK 3: Ověření tabulek po ručním zadání SQL =============
elseif ($krok_z_post === 3) {
$chybejici = zkontroluj_tabulky($pdo);
if (empty($chybejici)) {
// Všechny tabulky existují přejdeme na krok 4
$krok = 4;
} else {
// Stále chybí tabulky zůstaneme na kroku 2/3
$krok = 3;
$chyba = 'Následující tabulky stále chybí: '
. implode(', ', $chybejici)
. '. Zkontroluj, zda jsi SQL správně zadal do phpMyAdmin.';
}
}
// == KROK 4: Vytvoření prvního admina =========================
elseif ($krok_z_post === 4) {
$email = trim($_POST['email'] ?? '');
$heslo = $_POST['heslo'] ?? '';
$heslo2 = $_POST['heslo2'] ?? '';
// Validace
if (empty($email)) {
$chyba = 'Email nesmí být prázdný.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$chyba = 'Email není platná emailová adresa.';
} elseif (empty($heslo)) {
$chyba = 'Heslo nesmí být prázdné.';
} elseif (mb_strlen($heslo) < HESLO_MIN_DELKA) {
$chyba = 'Heslo musí mít alespoň ' . HESLO_MIN_DELKA . ' znaků.';
} elseif ($heslo !== $heslo2) {
$chyba = 'Hesla se neshodují.';
}
if (empty($chyba)) {
try {
$heslo_hash = password_hash($heslo, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_UZIVATELE . "`
(`email`, `heslo`, `admin`)
VALUES
(:email, :heslo, 1)
");
$stmt->execute([
':email' => $email,
':heslo' => $heslo_hash,
]);
$novy_id = $pdo->lastInsertId();
if (DB_TABULKA_SLUZBA !== '') {
$stmt2 = $pdo->prepare("
INSERT INTO `" . DB_TABULKA_SLUZBA . "`
(`uzivatel_id`)
VALUES
(:uzivatel_id)
");
$stmt2->execute([':uzivatel_id' => $novy_id]);
}
// Vytvoříme zámek instalace je hotová
file_put_contents($lock_soubor, 'Instalace proběhla: ' . date('Y-m-d H:i:s'));
$krok = 5; // krok 5 = úspěšné dokončení
} catch (PDOException $e) {
error_log('Chyba při instalaci (admin): ' . $e->getMessage());
$chyba = 'Při vytváření admina došlo k chybě databáze. Zkontroluj PHP error log.';
$krok = 4;
}
} else {
// Validace selhala zůstaneme na kroku 4
$krok = 4;
}
}
// ------------------------------------------------------------
// SESTAVENÍ SQL TEXTU PRO ZOBRAZENÍ UŽIVATELI (krok 2 a 3)
// ------------------------------------------------------------
$sql_pro_zobrazeni = implode(";\n\n", $sql_tabulky) . ';';
// ------------------------------------------------------------
// HTML VÝSTUP
// ------------------------------------------------------------
?>
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<title>Instalace <?php echo htmlspecialchars(PROJEKT_NAZEV); ?></title>
</head>
<body>
<h1>Instalace systému přihlašování</h1>
<h2><?php echo htmlspecialchars(PROJEKT_NAZEV); ?></h2>
<?php if (!empty($chyba)): ?>
<p><strong>Chyba: <?php echo htmlspecialchars($chyba); ?></strong></p>
<?php endif; ?>
<?php
// ============================================================
// KROK 2: Ruční SQL pro phpMyAdmin
// ============================================================
if ($krok === 2): ?>
<p>Nepodařilo se automaticky vytvořit tabulky v databázi
pravděpodobně nemá databázový uživatel dostatečná oprávnění
(CREATE TABLE).</p>
<p><strong>Postup:</strong></p>
<ol>
<li>Zkopíruj SQL níže.</li>
<li>Otevři phpMyAdmin a vyber svou databázi
(<code><?php echo htmlspecialchars(DB_NAME); ?></code>).</li>
<li>Klikni na záložku <strong>SQL</strong>, vlož SQL a spusť ho.</li>
<li>Vrať se sem a klikni na tlačítko níže.</li>
</ol>
<textarea rows="30" cols="100" onclick="this.select();"
style="font-family: monospace; font-size: 13px;"
><?php echo htmlspecialchars($sql_pro_zobrazeni); ?></textarea>
<p>
<form method="POST" action="">
<input type="hidden" name="krok" value="3">
<button type="submit">Zkontrolovat tabulky a pokračovat</button>
</form>
</p>
<?php
// ============================================================
// KROK 3: Tabulky stále chybí (chyba je zobrazena výše)
// ============================================================
elseif ($krok === 3): ?>
<p>Tabulky ještě nejsou v pořádku. Zkontroluj phpMyAdmin a zkus to znovu.</p>
<textarea rows="30" cols="100" onclick="this.select();"
style="font-family: monospace; font-size: 13px;"
><?php echo htmlspecialchars($sql_pro_zobrazeni); ?></textarea>
<p>
<form method="POST" action="">
<input type="hidden" name="krok" value="3">
<button type="submit">Zkontrolovat tabulky znovu</button>
</form>
</p>
<?php
// ============================================================
// KROK 4: Formulář pro vytvoření prvního admina
// ============================================================
elseif ($krok === 4): ?>
<p><strong>Tabulky jsou připraveny.</strong>
Nyní vytvoř prvního administrátora.</p>
<form method="POST" action="">
<input type="hidden" name="krok" value="4">
<p>
<label for="email">Email administrátora:</label><br>
<input
type="email"
id="email"
name="email"
value="<?php echo htmlspecialchars($_POST['email'] ?? ''); ?>"
required
>
</p>
<p>
<label for="heslo">Heslo administrátora:</label><br>
<input
type="password"
id="heslo"
name="heslo"
required
>
<br>
<span id="heslo-sila">Síla hesla: zadej heslo</span>
</p>
<p>
<label for="heslo2">Heslo znovu (pro ověření):</label><br>
<input
type="password"
id="heslo2"
name="heslo2"
required
>
</p>
<p>
<button type="submit" id="tlacitko-odeslat" disabled>
Dokončit instalaci
</button>
<span id="tlacitko-duvod"> (čekám na dostatečně silné heslo)</span>
</p>
</form>
<?php
// ============================================================
// KROK 5: Instalace úspěšně dokončena
// ============================================================
elseif ($krok === 5): ?>
<p><strong>Instalace proběhla úspěšně!</strong></p>
<p>Tabulky byly vytvořeny a první administrátor byl založen.</p>
<p>Byl vytvořen soubor <code>auth/install.lock</code>
instalaci již nelze spustit znovu.</p>
<p><a href="<?php echo htmlspecialchars(AUTH_LOGIN_URL); ?>">Přejít na přihlášení</a></p>
<?php endif; ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
<script>
var AUTH_HESLO_MIN_SILA = <?php echo (int) HESLO_MIN_SILA; ?>;
var AUTH_HESLO_MIN_DELKA = <?php echo (int) HESLO_MIN_DELKA; ?>;
</script>
<script src="<?php echo htmlspecialchars(PROJEKT_URL); ?>/auth/js/heslo-sila.js"></script>
</body>
</html>