371 lines
16 KiB
PHP
371 lines
16 KiB
PHP
<?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;
|
||
}
|
||
|