Website-fabianschieder/adminer/index.php

371 lines
16 KiB
PHP
Raw 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
declare(strict_types=1);
require_once __DIR__ . '/user_auth.php';
require_once __DIR__ . '/views.php';
adminer_app_session_start();
// Bootstrap users table (+ optional seed)
try {
adminer_app_bootstrap();
} catch (Throwable $e) {
admin_layout('DB-Verwaltung', '<div class="notice notice-err">' . h($e->getMessage()) . '</div>', 'Fehler beim Start');
exit;
}
// App-Logout
if ((string)($_GET['auth'] ?? '') === 'logout') {
adminer_app_logout();
header('Location: /adminer', true, 302);
exit;
}
$appPage = (string)($_GET['page'] ?? 'login');
$appError = null;
$appRegError = null;
$appRegOk = null;
// Login POST
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'app_login') {
$res = adminer_app_try_login((string)($_POST['username'] ?? ''), (string)($_POST['password'] ?? ''));
if (!empty($res['ok'])) {
header('Location: /adminer', true, 302);
exit;
}
$appError = (string)($res['error'] ?? 'Login fehlgeschlagen.');
$appPage = 'login';
}
// Register POST
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'app_register') {
$res = adminer_app_try_register(
(string)($_POST['username'] ?? ''),
(string)($_POST['password'] ?? ''),
(string)($_POST['password2'] ?? '')
);
if (!empty($res['ok'])) {
$lr = adminer_app_try_login((string)($_POST['username'] ?? ''), (string)($_POST['password'] ?? ''));
if (!empty($lr['ok'])) {
header('Location: /adminer', true, 302);
exit;
}
$appRegOk = 'Konto erstellt! Bitte einloggen.';
$appPage = 'login';
} else {
$appRegError = (string)($res['error'] ?? 'Registrierung fehlgeschlagen.');
$appPage = 'register';
}
}
// ── LOGIN / REGISTER SEITE ────────────────────────────────────────────────
if (!adminer_app_is_logged_in()) {
$canReg = adminer_app_allow_register();
$isReg = ($appPage === 'register');
$body = '<div class="login-wrap">';
// Tabs
$body .= '<div class="tabs">';
$body .= '<a class="tab ' . (!$isReg ? 'active' : '') . '" href="/adminer?page=login">Login</a>';
if ($canReg) $body .= '<a class="tab ' . ($isReg ? 'active' : '') . '" href="/adminer?page=register">Registrieren</a>';
$body .= '</div>';
$body .= '<div class="card">';
if ($appRegOk) $body .= '<div class="notice notice-ok">' . h($appRegOk) . '</div>';
if ($appError) $body .= '<div class="notice notice-err">' . h($appError) . '</div>';
if ($appRegError)$body .= '<div class="notice notice-err">' . h($appRegError) . '</div>';
if ($isReg && $canReg) {
// ── Registrierungsformular ──────────────────────────────────────
$body .= '<form method="post">'
. '<input type="hidden" name="action" value="app_register">'
. '<div class="field"><label>Benutzername</label><input name="username" autocomplete="username" placeholder="z.B. fabian"></div>'
. '<div class="field"><label>Passwort</label><input name="password" type="password" autocomplete="new-password" placeholder="mind. 8 Zeichen"></div>'
. '<div class="field"><label>Passwort wiederholen</label><input name="password2" type="password" autocomplete="new-password" placeholder="Wiederholung"></div>'
. '<button class="btn" type="submit" style="width:100%">Konto erstellen</button>'
. '</form>';
$body .= '<p class="muted" style="margin-top:.9rem;text-align:center">Bereits ein Konto? <a href="/adminer?page=login">Login</a></p>';
} else {
// ── Login-Formular ──────────────────────────────────────────────
$body .= '<form method="post">'
. '<input type="hidden" name="action" value="app_login">'
. '<div class="field"><label>Benutzername</label><input name="username" autocomplete="username" placeholder="Dein Benutzername"></div>'
. '<div class="field"><label>Passwort</label><input name="password" type="password" autocomplete="current-password" placeholder="Dein Passwort"></div>'
. '<button class="btn" type="submit" style="width:100%">Anmelden</button>'
. '</form>';
if ($canReg) $body .= '<p class="muted" style="margin-top:.9rem;text-align:center">Noch kein Konto? <a href="/adminer?page=register">Registrieren</a></p>';
}
$body .= '</div>';
$body .= '</div>';
admin_layout('DB-Verwaltung', $body, $isReg ? 'Neues Konto erstellen' : 'Bitte einloggen');
exit;
}
// ── DB-VERBINDUNGS-LOGIN ──────────────────────────────────────────────────
require_once __DIR__ . '/auth.php';
admin_session_start();
if ((string)($_GET['a'] ?? '') === 'logout') {
admin_logout();
header('Location: /adminer', true, 302);
exit;
}
if (!isset($_SESSION['db_admin_select'])) $_SESSION['db_admin_select'] = [];
$selectError = null;
$selectMsg = null;
// Probe: Datenbanken laden
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'probe') {
$host = trim((string)($_POST['host'] ?? ''));
$port = (int)($_POST['port'] ?? 3306);
$user = trim((string)($_POST['user'] ?? ''));
$pass = (string)($_POST['pass'] ?? '');
if ($host === '' || $port <= 0 || $user === '') {
$selectError = 'Bitte Host, Port und Benutzer angeben.';
} else {
try {
$pdo = new PDO(sprintf('mysql:host=%s;port=%d;charset=utf8mb4', $host, $port), $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$dbs = $pdo->query('SHOW DATABASES')->fetchAll(PDO::FETCH_COLUMN, 0);
$_SESSION['db_admin_select'] = compact('host', 'port', 'user', 'pass', 'dbs');
$selectMsg = 'Datenbanken geladen bitte unten eine auswählen.';
} catch (Throwable $e) {
$selectError = 'Fehler: ' . $e->getMessage();
}
}
}
// DB-Login
$dbError = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'login') {
$res = admin_try_login(
trim((string)($_POST['host'] ?? '')),
(int)($_POST['port'] ?? 3306),
trim((string)($_POST['user'] ?? '')),
(string)($_POST['pass'] ?? ''),
trim((string)($_POST['db'] ?? ''))
);
if ($res['ok']) {
header('Location: /adminer', true, 302);
exit;
}
$dbError = (string)($res['error'] ?? 'Login fehlgeschlagen.');
}
$defaults = admin_default_creds();
$selectState = is_array($_SESSION['db_admin_select']) ? $_SESSION['db_admin_select'] : [];
$prefHost = isset($selectState['host']) ? (string)$selectState['host'] : (string)$defaults['host'];
$prefPort = isset($selectState['port']) ? (int)$selectState['port'] : (int)$defaults['port'];
$prefUser = isset($selectState['user']) ? (string)$selectState['user'] : (string)$defaults['user'];
$prefPass = isset($selectState['pass']) ? (string)$selectState['pass'] : '';
$dbList = isset($selectState['dbs']) && is_array($selectState['dbs']) ? $selectState['dbs'] : [];
if (!admin_is_logged_in()) {
$body = '<div class="card" style="max-width:520px;margin:0 auto">';
if ($selectMsg) $body .= '<div class="notice notice-ok">' . h($selectMsg) . '</div>';
if ($selectError)$body .= '<div class="notice notice-err">' . h($selectError) . '</div>';
if ($dbError) $body .= '<div class="notice notice-err">' . h($dbError) . '</div>';
// Step 1
$body .= '<h2>1 · Server verbinden</h2>';
$body .= '<form method="post">'
. '<input type="hidden" name="action" value="probe">'
. '<div class="field"><label>Host</label><input name="host" value="' . h($prefHost) . '" placeholder="localhost"></div>'
. '<div class="field"><label>Port</label><input name="port" type="number" value="' . h((string)$prefPort) . '"></div>'
. '<div class="field"><label>Benutzer</label><input name="user" value="' . h($prefUser) . '"></div>'
. '<div class="field"><label>Passwort</label><input name="pass" type="password" value="' . h($prefPass) . '"></div>'
. '<button class="btn btn-ghost btn-sm" type="submit">Datenbanken laden</button>'
. '</form>';
$body .= '<hr>';
// Step 2
$body .= '<h2>2 · Datenbank auswählen & einloggen</h2>';
$body .= '<form method="post">'
. '<input type="hidden" name="action" value="login">'
. '<input type="hidden" name="host" value="' . h($prefHost) . '">'
. '<input type="hidden" name="port" value="' . h((string)$prefPort) . '">'
. '<input type="hidden" name="user" value="' . h($prefUser) . '">'
. '<input type="hidden" name="pass" value="' . h($prefPass) . '">'
. '<div class="field"><label>Datenbank</label>';
if (!empty($dbList)) {
$body .= '<select name="db">';
$sel = (string)$defaults['db'];
foreach ($dbList as $dbName) {
$dbName = (string)$dbName;
$body .= '<option value="' . h($dbName) . '"' . ($dbName === $sel ? ' selected' : '') . '>' . h($dbName) . '</option>';
}
$body .= '</select>';
} else {
$body .= '<input name="db" value="' . h((string)$defaults['db']) . '" placeholder="Datenbank-Name">';
}
$body .= '</div>'
. '<button class="btn" type="submit" style="width:100%">Einloggen</button>'
. '</form>';
$body .= '</div>';
admin_layout('DB-Verwaltung', $body, 'Datenbankverbindung');
exit;
}
// ── DB-VERWALTUNG (eingeloggt) ────────────────────────────────────────────
try {
$pdo = admin_pdo();
$table = (string)($_GET['t'] ?? '');
$page = max(1, (int)($_GET['p'] ?? 1));
$limit = 50;
$offset = ($page - 1) * $limit;
$msg = null;
$queryResultHtml = '';
// SQL Query ausführen
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'query') {
$sql = trim((string)($_POST['sql'] ?? ''));
if ($sql !== '') {
if (preg_match('/;\s*\S/', $sql)) {
$msg = ['ok' => false, 'text' => 'Nur ein Statement ausführen (kein zweites Semikolon).'];
} else {
try {
$stmt = $pdo->query($sql);
if ($stmt instanceof PDOStatement) {
$rows = $stmt->fetchAll();
$queryResultHtml = '<h3 style="margin-top:1.2rem">Ergebnis</h3>' . admin_render_table($rows);
$msg = ['ok' => true, 'text' => 'Query ausgeführt (' . count($rows) . ' Zeilen).'];
} else {
$msg = ['ok' => true, 'text' => 'Statement ausgeführt.'];
}
} catch (Throwable $e) {
$msg = ['ok' => false, 'text' => $e->getMessage()];
}
}
}
}
$tables = $pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_NUM);
// ── TOP BAR ──────────────────────────────────────────────────────────
$dbName = (string)($_SESSION['db_admin']['db'] ?? '');
$uname = (string)($_SESSION['adminer_app']['username'] ?? '');
$body = '<div class="top-bar">'
. '<div class="top-bar-left">'
. '<span style="font-weight:700;font-size:1rem">' . h($dbName) . '</span>'
. ($uname ? '<span class="pill">' . h($uname) . '</span>' : '')
. '</div>'
. '<div class="top-bar-actions">'
. '<a class="btn btn-ghost btn-sm" href="/adminer?a=logout">DB-Logout</a>'
. '<a class="btn btn-ghost btn-sm" href="/adminer?auth=logout">Account-Logout</a>'
. '</div>'
. '</div>';
// ── GRID: TABELLENLISTE + CONTENT ─────────────────────────────────────
$body .= '<div class="admin-grid">';
// Linke Spalte: Tabellenliste
$body .= '<div class="card">';
$body .= '<h2>Tabellen</h2>';
if (empty($tables)) {
$body .= '<p class="muted">Keine Tabellen gefunden.</p>';
} else {
$body .= '<ul class="nav-list">';
foreach ($tables as $row) {
$tn = (string)$row[0];
$cls = ($tn === $table) ? 'active' : '';
$body .= '<li><a class="' . $cls . '" href="/adminer?t=' . rawurlencode($tn) . '">' . h($tn) . '</a></li>';
}
$body .= '</ul>';
}
$body .= '</div>';
// Rechte Spalte: Browse + Query
$body .= '<div>';
// Notices
if ($msg) {
$cls = $msg['ok'] ? 'notice-ok' : 'notice-err';
$body .= '<div class="notice ' . $cls . '">' . h($msg['text']) . '</div>';
}
// Browse
if ($table !== '') {
if (!preg_match('/^[A-Za-z0-9_]+$/', $table)) {
$body .= '<div class="notice notice-err">Ungültiger Tabellenname.</div>';
} else {
$stmt = $pdo->query('SELECT * FROM `' . $table . '` LIMIT ' . (int)$limit . ' OFFSET ' . (int)$offset);
$rows = $stmt->fetchAll();
$body .= '<div class="card" style="margin-bottom:16px">';
$body .= '<h2>' . h($table) . '</h2>';
$body .= admin_render_table($rows);
$body .= '<div class="pagination">';
if ($page > 1) $body .= '<a class="btn btn-ghost btn-sm" href="/adminer?t=' . rawurlencode($table) . '&p=' . ($page - 1) . '">← Zurück</a>';
if (count($rows) === $limit) $body .= '<a class="btn btn-ghost btn-sm" href="/adminer?t=' . rawurlencode($table) . '&p=' . ($page + 1) . '">Weiter →</a>';
if ($page > 1 || count($rows) === $limit) $body .= '<span class="muted">Seite ' . $page . '</span>';
$body .= '</div>';
$body .= '</div>';
}
}
// SQL Query Box
$body .= '<div class="card">';
$body .= '<h2>SQL Query</h2>';
$body .= '<form method="post">'
. '<input type="hidden" name="action" value="query">'
. '<div class="field"><textarea name="sql" rows="6" placeholder="SELECT * FROM tabelle LIMIT 10"></textarea></div>'
. '<button class="btn btn-sm" type="submit">Ausführen</button>'
. '</form>';
$body .= $queryResultHtml;
$body .= '</div>';
$body .= '</div>'; // right col
$body .= '</div>'; // admin-grid
admin_layout('DB-Verwaltung', $body, h($dbName));
} catch (Throwable $e) {
admin_logout();
admin_layout('DB-Verwaltung',
'<div class="notice notice-err">' . h($e->getMessage()) . '</div>'
. '<p style="margin-top:1rem;text-align:center"><a class="btn btn-ghost btn-sm" href="/adminer">Zurück zum Login</a></p>',
'Fehler'
);
}
function admin_render_table(array $rows): string
{
if (empty($rows)) return '<p class="muted">(keine Zeilen)</p>';
$cols = array_keys((array)$rows[0]);
$html = '<div style="overflow-x:auto"><table class="db-table"><thead><tr>';
foreach ($cols as $c) $html .= '<th>' . h((string)$c) . '</th>';
$html .= '</tr></thead><tbody>';
foreach ($rows as $r) {
$html .= '<tr>';
foreach ($cols as $c) {
$v = $r[$c] ?? null;
$cell = $v === null
? '<span class="null-val">NULL</span>'
: (strlen((string)$v) > 300 ? h(substr((string)$v, 0, 300)) . '…' : h((string)$v));
$html .= '<td>' . $cell . '</td>';
}
$html .= '</tr>';
}
$html .= '</tbody></table></div>';
return $html;
}