ソースを参照

making config.php readable for the admin

Medowar 6 日 前
コミット
9c4fd77cd6
3 ファイル変更124 行追加0 行削除
  1. 113 0
      admin/settings.php
  2. 7 0
      assets/css/style.css
  3. 4 0
      docs/CONFIG_REFERENCE.md

+ 113 - 0
admin/settings.php

@@ -123,6 +123,80 @@ function settingsGetBackupCapabilityLabel(array $capability): string
     return !empty($capability["available"]) ? "bereit" : "nicht verfügbar";
 }
 
+function settingsIsSuperAdmin(): bool
+{
+    return normalizeAdminUsername($_SESSION['admin_username'] ?? "") === "admin";
+}
+
+function settingsGetLocalConfigPath(): string
+{
+    return dirname(__DIR__) . "/config.php";
+}
+
+function settingsReadLocalConfig(): string
+{
+    $path = settingsGetLocalConfigPath();
+    if (!is_file($path) || !is_readable($path)) {
+        return "";
+    }
+
+    $content = file_get_contents($path);
+    return $content === false ? "" : $content;
+}
+
+function settingsWriteLocalConfig(string $content): void
+{
+    $content = str_replace("\r\n", "\n", $content);
+
+    if (!str_starts_with(ltrim($content), "<?php")) {
+        throw new RuntimeException('Die Konfiguration muss mit "<?php" beginnen.');
+    }
+
+    $path = settingsGetLocalConfigPath();
+    $tmpFile = $path . ".tmp";
+
+    if (file_put_contents($tmpFile, $content, LOCK_EX) === false) {
+        throw new RuntimeException("Konfiguration konnte nicht geschrieben werden.");
+    }
+
+    $output = [];
+    $exitCode = 0;
+    exec(
+        escapeshellarg(PHP_BINARY) . " -l " . escapeshellarg($tmpFile) . " 2>&1",
+        $output,
+        $exitCode,
+    );
+
+    if ($exitCode !== 0) {
+        @unlink($tmpFile);
+        throw new RuntimeException("PHP-Syntaxfehler: " . trim(implode("\n", $output)));
+    }
+
+    $backupDir = DATA_DIR . "backups/config/";
+    if (!is_dir($backupDir) && !mkdir($backupDir, 02775, true) && !is_dir($backupDir)) {
+        @unlink($tmpFile);
+        throw new RuntimeException("Sicherungsverzeichnis konnte nicht angelegt werden.");
+    }
+
+    if (is_file($path)) {
+        $backupPath = $backupDir . "config-" . date("Ymd-His") . ".php.bak";
+        if (!copy($path, $backupPath)) {
+            @unlink($tmpFile);
+            throw new RuntimeException("Sicherung der bestehenden Konfiguration ist fehlgeschlagen.");
+        }
+    }
+
+    @chmod($tmpFile, 0664);
+    if (!rename($tmpFile, $path)) {
+        @unlink($tmpFile);
+        throw new RuntimeException("Konfiguration konnte nicht gespeichert werden.");
+    }
+
+    if (function_exists("opcache_invalidate")) {
+        opcache_invalidate($path, true);
+    }
+}
+
 function settingsFindBackupByFilename(string $filename): ?array
 {
     if ($filename === "" || basename($filename) !== $filename) {
@@ -172,6 +246,7 @@ if (empty($_SESSION['admin_logged_in'])) {
 $pageTitle = "Einstellungen";
 $message = "";
 $messageType = "";
+$isSuperAdmin = settingsIsSuperAdmin();
 
 if ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['save_settings'])) {
     // Validate CSRF token
@@ -231,12 +306,32 @@ if ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['save_settings'])) {
             $messageType = "error";
         }
     }
+} elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['save_local_config'])) {
+    if (!$isSuperAdmin) {
+        http_response_code(403);
+        $message = "Keine Berechtigung für diese Aktion.";
+        $messageType = "error";
+    } elseif (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
+        $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
+        $messageType = "error";
+    } else {
+        try {
+            settingsWriteLocalConfig((string) ($_POST['local_config_content'] ?? ""));
+            logAccess("Admin updated local config.php");
+            $message = "Lokale Konfiguration wurde gespeichert.";
+            $messageType = "success";
+        } catch (Throwable $exception) {
+            $message = "Konfiguration konnte nicht gespeichert werden: " . $exception->getMessage();
+            $messageType = "error";
+        }
+    }
 }
 
 $settings = getSystemSettings();
 $updaterStatus = settingsGetUpdaterStatus();
 $backupCapabilities = backupRemoteCapabilities();
 $backups = backupListBackups();
+$localConfigContent = $isSuperAdmin ? settingsReadLocalConfig() : "";
 
 $bodyClass = "admin-page";
 include __DIR__ . "/../includes/header.php";
@@ -350,4 +445,22 @@ include __DIR__ . "/../includes/header.php";
     <p><a href="updater.php" class="btn btn-secondary">Updater öffnen</a></p>
 </div>
 
+<?php if ($isSuperAdmin): ?>
+<div class="panel panel-lg mt-4">
+    <h3>Lokale Konfiguration</h3>
+    <p>Bearbeitet <code>config.php</code> direkt auf diesem Server. Diese Datei wird beim Update nicht überschrieben.</p>
+    <div class="alert alert-warning">Vor dem Speichern wird die PHP-Syntax geprüft und die vorherige Version automatisch gesichert. Fehlerhafte Änderungen können die gesamte Anwendung lahmlegen.</div>
+    <form method="POST">
+        <?php echo csrfField(); ?>
+        <div class="form-group">
+            <label for="local_config_content">Inhalt von config.php</label>
+            <textarea id="local_config_content" name="local_config_content" rows="24" class="config-editor" spellcheck="false"><?php echo escape(
+                $localConfigContent,
+            ); ?></textarea>
+        </div>
+        <button type="submit" name="save_local_config" class="btn">Speichern</button>
+    </form>
+</div>
+<?php endif; ?>
+
 <?php include __DIR__ . "/../includes/footer.php"; ?>

+ 7 - 0
assets/css/style.css

@@ -365,6 +365,13 @@ code {
     color: var(--brand-muted);
 }
 
+.config-editor {
+    font-family: 'Courier New', monospace;
+    font-size: 0.85rem;
+    white-space: pre;
+    tab-size: 4;
+}
+
 .cart-item {
     background: var(--brand-surface);
     padding: 1.5rem;

+ 4 - 0
docs/CONFIG_REFERENCE.md

@@ -43,6 +43,10 @@
 | `BACKUP_AUTO_INTERVAL_SECONDS` | Intervall für Backups durch Admin-Aktivität (Standard: 604800 = wöchentlich; 0 deaktiviert) |
 | `BACKUP_REMOTE_TARGETS` | Optionale Remote-Ziele für Backup-Uploads (`s3`, `sftp`, `custom`, `managed`) |
 
+## Bearbeitung im Admin
+
+`config.php` kann direkt im Admin unter **Einstellungen** bearbeitet werden — dieser Abschnitt ist nur sichtbar, wenn mit dem Benutzerkonto `admin` angemeldet ist (siehe `data/admins.json`). Vor dem Speichern wird die PHP-Syntax geprüft (`php -l`); die vorherige Version wird automatisch nach `data/backups/config/` gesichert.
+
 ## Runtime (`data/settings.json`)
 
 Im Admin unter **Einstellungen** überschreibbar (schreibt `data/settings.json`):