Files
urlshort-qr-gen/src/Repository/SettingRepository.php
T
florian.wilde f48f7916b3 feat(links): Kurzlinks, Weiterleitung und Klickstatistik
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.
2026-08-03 18:11:39 +02:00

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],
);
}
}