v0.0..1
This commit is contained in:
Thomas
2025-10-05 14:58:05 +02:00
parent df30542248
commit a184c31cca
41 changed files with 3439 additions and 0 deletions

View File

@@ -0,0 +1,395 @@
<?php
require_once __DIR__ . '/guard.php';
$me = require_login(); // frigiver sessionlåsen
require_once __DIR__ . '/db.php';
function is_admin(array $me): bool { return isset($me['role']) && $me['role'] === 'admin'; }
function is_mod(array $me): bool { return isset($me['role']) && $me['role'] === 'mod'; }
$settingsFile = dirname(__DIR__) . '/data/settings.json';
$settings = file_exists($settingsFile) ? (json_decode(file_get_contents($settingsFile), true) ?: []) : ['watchdog'=>true,'timeout'=>120];
$db = db();
$msg_settings = '';
$msg_self = '';
$msg_admin = '';
$err_self = '';
$err_admin = '';
/** Helper: hent bruger efter id */
function fetch_user(PDO $db, int $id): ?array {
$st = $db->prepare('SELECT * FROM users WHERE id = ? LIMIT 1');
$st->execute([$id]);
$u = $st->fetch(PDO::FETCH_ASSOC);
return $u ?: null;
}
/** Helper: antal admins (bruges til at undgå at fjerne sidste admin) */
function count_admins(PDO $db): int {
$st = $db->query("SELECT COUNT(*) FROM users WHERE role = 'admin'");
return (int)$st->fetchColumn();
}
/* ============================================================
1) System-indstillinger (watchdog / timeout)
============================================================ */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['__action'] ?? '') === 'save_settings') {
$settings['watchdog'] = isset($_POST['watchdog']);
$settings['timeout'] = max(30, (int)($_POST['timeout'] ?? 120));
file_put_contents($settingsFile, json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
$msg_settings = 'Indstillinger gemt.';
}
/* ============================================================
2) Egen profil alle roller (user/mod/admin)
- Skift brugernavn
- Skift password (kræver nuværende)
============================================================ */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['__action'] ?? '') === 'self_update') {
$newUser = trim($_POST['self_username'] ?? '');
$curPass = $_POST['self_current'] ?? '';
$new1 = $_POST['self_new1'] ?? '';
$new2 = $_POST['self_new2'] ?? '';
// Valider brugernavn hvis ændres
if ($newUser === '') {
$err_self = 'Brugernavn må ikke være tomt.';
} elseif (!preg_match('/^[A-Za-z0-9_][A-Za-z0-9_.-]{2,31}$/', $newUser)) {
$err_self = 'Ugyldigt brugernavn (tilladte tegn: A-Z, 0-9, _, ., - og min. længde 3).';
} else {
// Hent nuværende bruger
$user = fetch_user($db, (int)$me['id']);
if (!$user) {
$err_self = 'Bruger ikke fundet.';
} else {
// Ændr brugernavn hvis ændret
if ($user['username'] !== $newUser) {
try {
$upd = $db->prepare('UPDATE users SET username = ? WHERE id = ?');
$upd->execute([$newUser, (int)$me['id']]);
// Opdater sessionens brugernavn
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (isset($_SESSION['user'])) {
$_SESSION['user']['username'] = $newUser;
}
session_write_close();
$msg_self .= ' Brugernavn opdateret.';
} catch (Throwable $e) {
if (strpos($e->getMessage(), 'UNIQUE') !== false) {
$err_self = 'Brugernavn findes allerede.';
} else {
$err_self = 'Fejl (brugernavn): ' . $e->getMessage();
}
}
}
// Skift password hvis felterne er udfyldt
$wantsPasswordChange = ($curPass !== '' || $new1 !== '' || $new2 !== '');
if (!$err_self && $wantsPasswordChange) {
if ($curPass === '' || $new1 === '' || $new2 === '') {
$err_self = 'Udfyld alle password-felter.';
} elseif ($new1 !== $new2) {
$err_self = 'De nye adgangskoder matcher ikke.';
} elseif (strlen($new1) < 6) {
$err_self = 'Vælg en adgangskode på mindst 6 tegn.';
} else {
// Verificer nuværende password (understøt både 'password' og legacy 'password_hash')
$validCurrent = false;
if (!empty($user['password']) && password_verify($curPass, $user['password'])) {
$validCurrent = true;
} elseif (!empty($user['password_hash'])) {
$ph = $user['password_hash'];
if (strpos($ph, '$') !== false) {
[$salt, $hash] = explode('$', $ph, 2);
$validCurrent = (hash('sha256', $salt . $curPass) === $hash);
} else {
$validCurrent = (hash('sha256', $curPass) === $ph);
}
}
if (!$validCurrent) {
$err_self = 'Nuværende adgangskode er forkert.';
} else {
$newHash = password_hash($new1, PASSWORD_DEFAULT);
$upd = $db->prepare('UPDATE users SET password = ?, password_hash = NULL WHERE id = ?');
$upd->execute([$newHash, (int)$me['id']]);
$msg_self .= ' Adgangskode opdateret.';
}
}
}
}
}
}
/* ============================================================
3) Admin-only: Opret, Rediger, Slet bruger
- Opret: username + role + password
- Rediger: username, role, (valgfrit) reset password
- Slet: ikke sig selv, og efterlad altid mindst 1 admin
============================================================ */
if (is_admin($me)) {
$adminAction = $_POST['__action'] ?? '';
// Opret bruger
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $adminAction === 'create_user') {
$u = trim($_POST['new_username'] ?? '');
$p1 = $_POST['new_password1'] ?? '';
$p2 = $_POST['new_password2'] ?? '';
$role = $_POST['new_role'] ?? 'user';
if (!in_array($role, ['admin', 'mod', 'user'], true)) $role = 'user';
if ($u === '' || $p1 === '' || $p2 === '') {
$err_admin = 'Udfyld alle felter.';
} elseif (!preg_match('/^[A-Za-z0-9_][A-Za-z0-9_.-]{2,31}$/', $u)) {
$err_admin = 'Ugyldigt brugernavn.';
} elseif ($p1 !== $p2) {
$err_admin = 'Adgangskoder matcher ikke.';
} elseif (strlen($p1) < 6) {
$err_admin = 'Adgangskode skal være mindst 6 tegn.';
} else {
try {
$hash = password_hash($p1, PASSWORD_DEFAULT);
$st = $db->prepare('INSERT INTO users(username, password, role) VALUES(?,?,?)');
$st->execute([$u, $hash, $role]);
$msg_admin = "Bruger @$u oprettet.";
} catch (Throwable $e) {
if (strpos($e->getMessage(), 'UNIQUE') !== false) {
$err_admin = 'Brugernavn findes allerede.';
} else {
$err_admin = 'Fejl: ' . $e->getMessage();
}
}
}
}
// Redigér bruger
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $adminAction === 'update_user') {
$id = (int)($_POST['edit_id'] ?? 0);
$u = trim($_POST['edit_username'] ?? '');
$role = $_POST['edit_role'] ?? 'user';
$p1 = $_POST['edit_password1'] ?? '';
$p2 = $_POST['edit_password2'] ?? '';
if ($id <= 0) {
$err_admin = 'Ugyldigt bruger-ID.';
} else {
$user = fetch_user($db, $id);
if (!$user) {
$err_admin = 'Bruger findes ikke.';
} else {
if (!in_array($role, ['admin', 'mod', 'user'], true)) $role = 'user';
// Forhindre at man fjerner sidste admin-rolle
$isChangingRole = ($user['role'] !== $role);
if ($isChangingRole && $user['role'] === 'admin') {
if (count_admins($db) <= 1) {
$err_admin = 'Du kan ikke fjerne rollen fra den sidste admin.';
}
}
if (!$err_admin) {
// Opdatér brugernavn + rolle
if ($u === '' || !preg_match('/^[A-Za-z0-9_][A-Za-z0-9_.-]{2,31}$/', $u)) {
$err_admin = 'Ugyldigt brugernavn.';
} else {
try {
$upd = $db->prepare('UPDATE users SET username = ?, role = ? WHERE id = ?');
$upd->execute([$u, $role, $id]);
// Opdatér password hvis udfyldt
if ($p1 !== '' || $p2 !== '') {
if ($p1 !== $p2) {
$err_admin = 'Nye adgangskoder matcher ikke.';
} elseif (strlen($p1) < 6) {
$err_admin = 'Adgangskode skal være mindst 6 tegn.';
} else {
$hash = password_hash($p1, PASSWORD_DEFAULT);
$db->prepare('UPDATE users SET password = ?, password_hash = NULL WHERE id = ?')
->execute([$hash, $id]);
$msg_admin .= ' Password opdateret for @' . htmlspecialchars($u) . '.';
}
}
if (!$err_admin) {
$msg_admin = ($msg_admin ?: 'Bruger opdateret.');
}
} catch (Throwable $e) {
if (strpos($e->getMessage(), 'UNIQUE') !== false) {
$err_admin = 'Brugernavn findes allerede.';
} else {
$err_admin = 'Fejl: ' . $e->getMessage();
}
}
}
}
}
}
}
// Slet bruger
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $adminAction === 'delete_user') {
$id = (int)($_POST['del_id'] ?? 0);
if ($id <= 0) {
$err_admin = 'Ugyldigt bruger-ID.';
} elseif ($id === (int)$me['id']) {
$err_admin = 'Du kan ikke slette dig selv.';
} else {
$user = fetch_user($db, $id);
if (!$user) {
$err_admin = 'Bruger findes ikke.';
} else {
// Undgå at slette sidste admin
if ($user['role'] === 'admin' && count_admins($db) <= 1) {
$err_admin = 'Du kan ikke slette den sidste admin.';
} else {
$db->prepare('DELETE FROM users WHERE id = ?')->execute([$id]);
$msg_admin = 'Bruger @' . htmlspecialchars($user['username']) . ' slettet.';
}
}
}
}
}
// Hent liste over brugere (til visning for admin)
$users = [];
if (is_admin($me)) {
$q = $db->query('SELECT id, username, role FROM users ORDER BY username COLLATE NOCASE ASC');
$users = $q->fetchAll(PDO::FETCH_ASSOC);
}
?>
<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="utf-8">
<title>Indstillinger</title>
<link rel="stylesheet" href="style.css">
<script>
function confirmDelete(u){
return confirm("Slet bruger @" + u + "? Dette kan ikke fortrydes.");
}
</script>
</head>
<body>
<div class="wrap">
<!-- Systemindstillinger -->
<div class="card">
<h2>⚙️ Indstillinger</h2>
<p><a class="btn" href="index.php">← Tilbage</a></p>
<?php if ($msg_settings): ?><p class="notice"><?php echo htmlspecialchars($msg_settings); ?></p><?php endif; ?>
<form method="post" class="small" style="margin-bottom:1rem">
<input type="hidden" name="__action" value="save_settings">
<label>
<input type="checkbox" name="watchdog" <?php echo !empty($settings['watchdog']) ? 'checked' : ''; ?>>
Aktivér watchdog (genopretter forbindelse ved inaktivitet)
</label>
<br>
<label>Timeout (sekunder)
<input type="number" name="timeout" min="30" value="<?php echo (int)($settings['timeout'] ?? 120); ?>">
</label>
<br><br>
<button class="btn" type="submit">Gem indstillinger</button>
</form>
</div>
<!-- Egen profil -->
<div class="card">
<h3>👤 Min profil</h3>
<?php if ($msg_self): ?><p class="notice"><?php echo htmlspecialchars($msg_self); ?></p><?php endif; ?>
<?php if ($err_self): ?><p class="error"><?php echo htmlspecialchars($err_self); ?></p><?php endif; ?>
<form method="post">
<input type="hidden" name="__action" value="self_update">
<label>Brugernavn
<input type="text" name="self_username" value="<?php echo htmlspecialchars($me['username']); ?>" required>
</label><br>
<label>Nuværende adgangskode
<input type="password" name="self_current" placeholder="Udfyld for at skifte password">
</label><br>
<label>Ny adgangskode
<input type="password" name="self_new1" placeholder="Min. 6 tegn">
</label><br>
<label>Gentag ny adgangskode
<input type="password" name="self_new2">
</label><br>
<button class="btn" type="submit">Opdater min profil</button>
</form>
<p class="small">Rolle: <strong><?php echo htmlspecialchars($me['role'] ?? 'user'); ?></strong></p>
</div>
<?php if (is_admin($me)): ?>
<!-- Admin: Opret / Redigér / Slet -->
<div class="card">
<h3>🛠️ Brugerstyring (Admin)</h3>
<?php if ($msg_admin): ?><p class="notice"><?php echo $msg_admin; ?></p><?php endif; ?>
<?php if ($err_admin): ?><p class="error"><?php echo $err_admin; ?></p><?php endif; ?>
<!-- Opret ny bruger -->
<form method="post" style="margin-bottom:1rem">
<input type="hidden" name="__action" value="create_user">
<h4>Opret ny bruger</h4>
<label>Brugernavn
<input type="text" name="new_username" placeholder="fx mod_jens" required>
</label><br>
<label>Adgangskode
<input type="password" name="new_password1" required>
</label><br>
<label>Gentag adgangskode
<input type="password" name="new_password2" required>
</label><br>
<label>Rolle
<select name="new_role">
<option value="user">User</option>
<option value="mod">Mod</option>
<option value="admin">Admin</option>
</select>
</label><br>
<button class="btn" type="submit">Opret bruger</button>
</form>
<!-- Liste og redigér/slet eksisterende brugere -->
<h4>Eksisterende brugere</h4>
<table>
<tr><th>ID</th><th>Brugernavn</th><th>Rolle</th><th>Nyt password (valgfrit)</th><th>Handling</th></tr>
<?php foreach ($users as $u): ?>
<tr>
<form method="post">
<input type="hidden" name="__action" value="update_user">
<input type="hidden" name="edit_id" value="<?php echo (int)$u['id']; ?>">
<td><?php echo (int)$u['id']; ?></td>
<td><input name="edit_username" value="<?php echo htmlspecialchars($u['username']); ?>" required></td>
<td>
<select name="edit_role">
<option value="user" <?php echo ($u['role']==='user'?'selected':''); ?>>User</option>
<option value="mod" <?php echo ($u['role']==='mod'?'selected':''); ?>>Mod</option>
<option value="admin" <?php echo ($u['role']==='admin'?'selected':''); ?>>Admin</option>
</select>
</td>
<td>
<input type="password" name="edit_password1" placeholder="nyt password">
<br>
<input type="password" name="edit_password2" placeholder="gentag">
</td>
<td>
<button class="btn" type="submit">Gem</button>
</form>
<form method="post" style="display:inline" onsubmit="return confirmDelete('<?php echo htmlspecialchars($u['username']); ?>')">
<input type="hidden" name="__action" value="delete_user">
<input type="hidden" name="del_id" value="<?php echo (int)$u['id']; ?>">
<button class="btn" type="submit">Slet</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</table>
<p class="small">Begrænsninger: Du kan ikke slette dig selv, og systemet forhindrer at stå uden en eneste admin.</p>
</div>
<?php endif; ?>
</div>
</body>
</html>