Slugs aus 34 Zeichen (0 und 1 fehlen wegen Verwechslung mit o und l), kollisionssicher mit wachsender Laenge. Wunsch-Slugs werden normalisiert und gegen eine Liste reservierter Pfade geprueft. Die URL-Validierung kommt bewusst ohne FILTER_VALIDATE_URL aus: dieses verwirft jedes Nicht-ASCII-Zeichen und lehnt damit Umlaut-Domains und Umlaute im Pfad ab. Erlaubt sind nur http und https, darin aber auch IDN, IPv6 und interne Hostnamen ohne Punkt. Gespeichert wird die lesbare Schreibweise; Punycode entsteht erst im Location-Header, weil HTTP-Header laut RFC 7230 ASCII sein muessen. Die Statistik speichert keine IP-Adressen (optional nur einen taeglich rotierenden Hash) und laeuft nach dem Ausliefern der Weiterleitung.
71 lines
1.8 KiB
PHP
71 lines
1.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Repository;
|
|
|
|
use App\Database;
|
|
|
|
/**
|
|
* Key-Value-Speicher fuer Einstellungen, die zur Laufzeit gesetzt werden
|
|
* (z. B. das Admin-Passwort aus dem Setup-Assistenten).
|
|
*/
|
|
final class SettingRepository
|
|
{
|
|
public const ADMIN_USER = 'admin_user';
|
|
public const ADMIN_PASSWORD_HASH = 'admin_password_hash';
|
|
public const APP_SECRET = 'app_secret';
|
|
|
|
public function __construct(private readonly Database $db)
|
|
{
|
|
}
|
|
|
|
public function get(string $key, ?string $default = null): ?string
|
|
{
|
|
$row = $this->db->fetchOne('SELECT value FROM settings WHERE key_name = ?', [$key]);
|
|
|
|
if ($row === null || $row['value'] === null || $row['value'] === '') {
|
|
return $default;
|
|
}
|
|
|
|
return (string) $row['value'];
|
|
}
|
|
|
|
/**
|
|
* Installationsgeheimnis fuer signierte Tokens. Wird beim ersten
|
|
* Bedarf erzeugt und bleibt danach unveraendert.
|
|
*/
|
|
public function secret(): string
|
|
{
|
|
$secret = $this->get(self::APP_SECRET);
|
|
|
|
if ($secret === null || strlen($secret) < 32) {
|
|
$secret = bin2hex(random_bytes(32));
|
|
$this->set(self::APP_SECRET, $secret);
|
|
}
|
|
|
|
return $secret;
|
|
}
|
|
|
|
public function set(string $key, string $value): void
|
|
{
|
|
$now = gmdate('Y-m-d H:i:s');
|
|
|
|
$exists = $this->db->fetchValue('SELECT 1 FROM settings WHERE key_name = ?', [$key]);
|
|
|
|
if ($exists !== null) {
|
|
$this->db->execute(
|
|
'UPDATE settings SET value = ?, updated_at = ? WHERE key_name = ?',
|
|
[$value, $now, $key],
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
$this->db->execute(
|
|
'INSERT INTO settings (key_name, value, updated_at) VALUES (?, ?, ?)',
|
|
[$key, $value, $now],
|
|
);
|
|
}
|
|
}
|