Initiales CMS: Deutschsprachiges Blog-System mit Admin-Bereich
Vollständiges, schlankes PHP/SQLite-CMS für IT-, KI- und Gaming-Inhalte: - Core: DB-Singleton, Auth mit Passwort-Hashing, Session-Cookies, CSRF-Schutz, Login-Rate-Limit, Bild-Upload mit serverseitiger Validierung - Admin: Dashboard, Artikel/Seiten-Verwaltung mit Quill WYSIWYG-Editor, Kategorien, Navigation (Drag & Drop), Medienbibliothek, Profil - Frontend: Responsive Dark-Theme, Artikel-Grid, Kategorie-Filter, Archiv, Paginierung, SEO-Meta-Tags - Sicherheit: Prepared Statements, HTML-Sanitizer, .htaccess-Schutz für sensible Verzeichnisse, PHP-Ausführungsschutz im Upload-Ordner - Installation: install.php erstellt DB-Schema und Admin-Account https://claude.ai/code/session_01Xsg4j2t4S9goMuWVpF3ezG
This commit is contained in:
194
install.php
Normal file
194
install.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
/**
|
||||
* Installations-Script: Erstellt die Datenbank und den Admin-Benutzer.
|
||||
* Nach der Installation sollte diese Datei gelöscht werden.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/config/config.php';
|
||||
|
||||
date_default_timezone_set(TIMEZONE);
|
||||
|
||||
// Prüfen ob bereits installiert
|
||||
if (file_exists(DB_PATH)) {
|
||||
die('Die Datenbank existiert bereits. Lösche diese Datei aus Sicherheitsgründen.');
|
||||
}
|
||||
|
||||
// Datenbank-Verzeichnis prüfen
|
||||
$dataDir = dirname(DB_PATH);
|
||||
if (!is_dir($dataDir)) {
|
||||
mkdir($dataDir, 0755, true);
|
||||
}
|
||||
|
||||
echo "<html><head><meta charset='UTF-8'><title>DasPoschi CMS - Installation</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; background: #1a1a2e; color: #e0e0e0; }
|
||||
h1 { color: #00d4ff; }
|
||||
form { background: #16213e; padding: 30px; border-radius: 8px; }
|
||||
label { display: block; margin: 15px 0 5px; font-weight: 600; }
|
||||
input { width: 100%; padding: 10px; border: 1px solid #333; border-radius: 4px; background: #0f3460; color: #e0e0e0; box-sizing: border-box; }
|
||||
button { margin-top: 20px; padding: 12px 30px; background: #00d4ff; color: #1a1a2e; border: none; border-radius: 4px; font-size: 16px; font-weight: 600; cursor: pointer; }
|
||||
button:hover { background: #00b8d9; }
|
||||
.success { background: #0a3d2a; border: 1px solid #00d474; padding: 20px; border-radius: 8px; margin-top: 20px; }
|
||||
.error { background: #3d0a0a; border: 1px solid #d40000; padding: 20px; border-radius: 8px; margin-top: 20px; }
|
||||
</style></head><body>";
|
||||
|
||||
echo "<h1>DasPoschi CMS - Installation</h1>";
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$displayName = trim($_POST['display_name'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$passwordConfirm = $_POST['password_confirm'] ?? '';
|
||||
|
||||
$errors = [];
|
||||
if (strlen($username) < 3) {
|
||||
$errors[] = 'Benutzername muss mindestens 3 Zeichen lang sein.';
|
||||
}
|
||||
if (strlen($password) < 10) {
|
||||
$errors[] = 'Passwort muss mindestens 10 Zeichen lang sein.';
|
||||
}
|
||||
if ($password !== $passwordConfirm) {
|
||||
$errors[] = 'Passwörter stimmen nicht überein.';
|
||||
}
|
||||
if (empty($displayName)) {
|
||||
$displayName = $username;
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
echo "<div class='error'><ul>";
|
||||
foreach ($errors as $err) {
|
||||
echo "<li>" . htmlspecialchars($err) . "</li>";
|
||||
}
|
||||
echo "</ul></div>";
|
||||
} else {
|
||||
try {
|
||||
$pdo = new PDO('sqlite:' . DB_PATH, null, null, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
]);
|
||||
$pdo->exec('PRAGMA journal_mode=WAL');
|
||||
$pdo->exec('PRAGMA foreign_keys=ON');
|
||||
|
||||
// Tabellen erstellen
|
||||
$pdo->exec("
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
display_name TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE categories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
sort_order INTEGER NOT NULL DEFAULT 0
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE articles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
excerpt TEXT NOT NULL DEFAULT '',
|
||||
body TEXT NOT NULL DEFAULT '',
|
||||
cover_image TEXT DEFAULT NULL,
|
||||
category_id INTEGER DEFAULT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft','published')),
|
||||
published_at TEXT DEFAULT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE pages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
body TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft','published')),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE navigation (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
label TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK(type IN ('url','page','category','home')),
|
||||
target TEXT NOT NULL DEFAULT '',
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
parent_id INTEGER DEFAULT NULL,
|
||||
FOREIGN KEY (parent_id) REFERENCES navigation(id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE login_attempts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ip_address TEXT NOT NULL,
|
||||
attempted_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
");
|
||||
$pdo->exec("CREATE INDEX idx_login_attempts_ip ON login_attempts(ip_address, attempted_at)");
|
||||
|
||||
// Admin-Benutzer anlegen
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $pdo->prepare('INSERT INTO users (username, password_hash, display_name) VALUES (?, ?, ?)');
|
||||
$stmt->execute([$username, $hash, $displayName]);
|
||||
|
||||
// Standard-Navigation
|
||||
$pdo->exec("INSERT INTO navigation (label, type, target, sort_order) VALUES ('Startseite', 'home', '', 0)");
|
||||
|
||||
// Standard-Kategorien
|
||||
$pdo->exec("INSERT INTO categories (name, slug, description, sort_order) VALUES ('IT', 'it', 'Technik und Programmierung', 0)");
|
||||
$pdo->exec("INSERT INTO categories (name, slug, description, sort_order) VALUES ('KI', 'ki', 'Künstliche Intelligenz', 1)");
|
||||
$pdo->exec("INSERT INTO categories (name, slug, description, sort_order) VALUES ('Gaming', 'gaming', 'Spiele und Gaming-Kultur', 2)");
|
||||
|
||||
echo "<div class='success'>";
|
||||
echo "<h2>Installation erfolgreich!</h2>";
|
||||
echo "<p>Der Admin-Account wurde erstellt. Du kannst dich jetzt anmelden:</p>";
|
||||
echo "<p><a href='/admin/login.php' style='color: #00d4ff;'>Zum Admin-Login →</a></p>";
|
||||
echo "<p><strong>Wichtig:</strong> Lösche diese Datei (install.php) nach der Installation!</p>";
|
||||
echo "</div>";
|
||||
echo "</body></html>";
|
||||
exit;
|
||||
|
||||
} catch (Exception $ex) {
|
||||
// DB-Datei wieder löschen bei Fehler
|
||||
if (file_exists(DB_PATH)) {
|
||||
unlink(DB_PATH);
|
||||
}
|
||||
echo "<div class='error'>Fehler bei der Installation: " . htmlspecialchars($ex->getMessage()) . "</div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "
|
||||
<form method='post'>
|
||||
<label for='username'>Benutzername</label>
|
||||
<input type='text' id='username' name='username' required minlength='3'
|
||||
value='" . htmlspecialchars($_POST['username'] ?? '') . "'>
|
||||
|
||||
<label for='display_name'>Anzeigename (optional)</label>
|
||||
<input type='text' id='display_name' name='display_name'
|
||||
value='" . htmlspecialchars($_POST['display_name'] ?? '') . "'>
|
||||
|
||||
<label for='password'>Passwort (mind. 10 Zeichen)</label>
|
||||
<input type='password' id='password' name='password' required minlength='10'>
|
||||
|
||||
<label for='password_confirm'>Passwort bestätigen</label>
|
||||
<input type='password' id='password_confirm' name='password_confirm' required>
|
||||
|
||||
<button type='submit'>Installieren</button>
|
||||
</form>";
|
||||
|
||||
echo "</body></html>";
|
||||
Reference in New Issue
Block a user