0, 'path' => '/', 'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', 'httponly' => true, 'samesite' => 'Strict']); session_name('plugin_site_admin'); session_start(); $cfg = site_config(); $error = null; $message = null; function admin_csrf(): string { return $_SESSION['csrf'] ??= bin2hex(random_bytes(24)); } function admin_check_csrf(): bool { return hash_equals($_SESSION['csrf'] ?? '', (string) ($_POST['csrf'] ?? '')); } /** Höchstens 5 Fehlversuche pro IP in 15 Minuten. */ function admin_throttled(bool $record = false): bool { $file = site_storage() . '/login_attempts.json'; $data = is_file($file) ? (json_decode((string) file_get_contents($file), true) ?: []) : []; $ip = $_SERVER['REMOTE_ADDR'] ?? '?'; $now = time(); $data[$ip] = array_values(array_filter($data[$ip] ?? [], fn($t) => $t > $now - 900)); if ($record) { $data[$ip][] = $now; file_put_contents($file, json_encode($data), LOCK_EX); } return count($data[$ip]) >= 5; } $loggedIn = !empty($_SESSION['admin']); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = $_POST['action'] ?? ''; if ($action === 'login') { if ($cfg['admin_password_hash'] === '') { $error = 'Die Verwaltung ist nicht eingerichtet: In config.php fehlt admin_password_hash.'; } elseif (admin_throttled()) { $error = 'Zu viele Fehlversuche. Bitte in 15 Minuten erneut versuchen.'; } elseif (hash_equals((string) $cfg['admin_user'], (string) ($_POST['user'] ?? '')) && password_verify((string) ($_POST['password'] ?? ''), (string) $cfg['admin_password_hash'])) { session_regenerate_id(true); $_SESSION['admin'] = true; header('Location: admin.php'); exit; } else { admin_throttled(true); usleep(400000); $error = 'Benutzername oder Passwort ist falsch.'; } } elseif (!$loggedIn || !admin_check_csrf()) { http_response_code(403); $error = 'Ungültige Anfrage. Bitte neu anmelden.'; $loggedIn = false; } elseif ($action === 'logout') { session_destroy(); header('Location: admin.php'); exit; } elseif ($action === 'publish') { $zip = $_FILES['package'] ?? []; if (($zip['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { $error = 'Upload-Fehler (Code ' . ($zip['error'] ?? '?') . '). Prüft upload_max_filesize und post_max_size.'; } elseif (strtolower(pathinfo((string) $zip['name'], PATHINFO_EXTENSION)) !== 'zip') { $error = 'Bitte eine ZIP-Datei hochladen.'; } else { $prev = $_FILES['preview'] ?? []; $hasPreview = ($prev['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK; $res = site_publish((string) ($_POST['type'] ?? ''), $zip['tmp_name'], $_POST, $hasPreview ? $prev['tmp_name'] : null, $hasPreview ? (string) $prev['name'] : null); if ($res['ok']) $message = "Veröffentlicht: {$res['slug']} {$res['version']} (" . ($res['signed'] ? 'signiert' : 'NICHT signiert, kein Signierschlüssel eingerichtet') . ').'; else $error = $res['error']; } } elseif (in_array($action, ['push_block', 'push_unblock', 'push_delete'], true)) { $pid = (string) ($_POST['id'] ?? ''); if (preg_match('/^[a-f0-9]{24}$/', $pid)) { $pp = push_db(); if ($action === 'push_delete') $pp->prepare('DELETE FROM push_installs WHERE id = ?')->execute([$pid]); else $pp->prepare('UPDATE push_installs SET blocked = ? WHERE id = ?')->execute([$action === 'push_block' ? 1 : 0, $pid]); $message = ['push_block' => 'Installation gesperrt.', 'push_unblock' => 'Installation entsperrt.', 'push_delete' => 'Installation gelöscht (sie meldet sich beim nächsten Senden von selbst neu an).'][$action]; } else { $error = 'Ungültige Installation.'; } } elseif ($action === 'feature' || $action === 'unfeature') { $message = site_set_featured((int) ($_POST['id'] ?? 0), $action === 'feature') ? ($action === 'feature' ? 'Als „Empfohlen“ markiert. DeskHub zeigt es nach der nächsten Katalog-Aktualisierung (spätestens in wenigen Minuten).' : 'Empfehlung entfernt.') : null; $error = $message ? null : 'Eintrag nicht gefunden.'; } elseif ($action === 'delete') { $message = site_delete_version((int) ($_POST['id'] ?? 0)) ? 'Version entfernt.' : null; $error = $message ? null : 'Version nicht gefunden.'; } } site_header('Verwaltung'); ?>

Verwaltung

Einstellungen für DeskHub (offizieller Katalog)

Diesen Inhalt in includes/catalog_defaults.php von DeskHub einsetzen, bevor ihr ein Release baut. Dann installieren eure Kunden Plugins nur aus diesem Katalog, und nur signierte.

Es ist noch kein Signierschlüssel eingerichtet (php einrichten.php). Ohne Schlüssel ist der geprüfte Modus nicht nutzbar.

Neues Paket veröffentlichen

Nur für Themes (bei Plugins steht alles in der plugin.json)

Push-Dienst (Android-App)

query('SELECT * FROM push_installs ORDER BY COALESCE(last_used_at, created_at) DESC LIMIT 200')->fetchAll(); ?>

Noch keine DeskHub-Installation angemeldet.

Angemeldete DeskHub-Installationen ()
gesendet gesperrt' : 'aktiv' ?>

Veröffentlichte Pakete

'Plugins', 'theme' => 'Themes'] as $t => $label): $items = site_items($t); ?>

Noch keine.

★ Empfohlen' : '' ?> 🌐 Sprachpaket: ()
Downloads signiert' : 'nicht signiert' ?>