Konfiguration
Einleitung
Dieser Artikel beginnt nach der erfolgreichen Installation: BookStack ist unter HTTPS erreichbar und der erste Administrator kann sich anmelden. Beschrieben werden die Grundeinstellungen, öffentlicher Zugriff, Suchmaschinen, Backups und ein zusätzlich eingebauter RSS-Feed. Zuerst folgt der allgemeine Weg für Hosted Webspace, danach die konkret eingesetzte STRATO-Konfiguration.
Die technische Erstinstallation steht im getrennten Artikel Installation, der Ablauf für Versionswechsel unter Update.
Standardkonfiguration auf Hosted Webspace
Produktionswerte in .env
APP_ENV=production
APP_DEBUG=false
APP_URL=https://klausing.info
APP_LANG=de
APP_TIMEZONE=Europe/Berlin
APP_URL entspricht exakt der öffentlichen HTTPS-Adresse ohne abschließenden
Schrägstrich. .env bleibt außerhalb von Git und erhält Dateirechte 600. Der
bei der Installation erzeugte APP_KEY wird nicht verändert.
Nach Änderungen an .env werden die Caches geleert:
php artisan config:clear
php artisan cache:clear
php artisan view:clear
Benutzer, Rollen und öffentlicher Zugriff
Das initiale Administratorkonto erhält ein eigenes langes Kennwort und MFA. Für ein öffentlich lesbares, aber nicht öffentlich bearbeitbares Wiki passen diese Grundregeln:
- Selbstregistrierung ist deaktiviert.
- Öffentlicher Zugriff ist aktiviert.
- Die Rolle
Publicbesitzt nur die gewünschten Leserechte. - Erstellen, Bearbeiten und Löschen bleiben angemeldeten Benutzern vorbehalten.
- Nichtöffentliche Bücher und Seiten erhalten ausdrücklich eingeschränkte Rechte.
- Die Option für sicherere Bildnamen wird aktiviert.
Suchmaschinenzugriff und Sitemap
Für ein öffentliches Wiki wird Crawling in .env freigegeben:
ALLOW_ROBOTS=true
Anschließend:
php artisan config:clear
curl -s https://klausing.info/robots.txt
In der Ausgabe darf nicht global Disallow: / stehen.
Eine Sitemap enthält nur öffentliche, kanonische Inhaltsseiten: Startseite, Regale, Bücher, Kapitel und Seiten. Login, Suche, Benutzerprofile, Bearbeitung, Revisionen, Exporte, Anhänge und API-Endpunkte werden nicht aufgenommen.
Die Sitemap wird bei dieser Installation nicht von BookStack selbst erzeugt. Ein kleines lokales Skript ruft die Weboberfläche anonym ab und nimmt dadurch ausschließlich Inhalte auf, die auch ohne Anmeldung erreichbar sind. Ein API-Zugang ist dafür nicht erforderlich. Die erzeugte Datei liegt unter:
public/sitemap.xml
und ist anschließend hier erreichbar:
https://klausing.info/sitemap.xml
Die Sitemap wird nach größeren Inhaltsänderungen erneut erzeugt und kann zusätzlich in der Google Search Console eingereicht werden.
Backup
Zu einer vollständigen Sicherung gehören die Datenbank und diese instanzspezifischen Dateien:
.envpublic/uploadsstorage/uploadsthemes
cd ~/wiki/bookstack
tar -czf "$HOME/bookstack-files-backup.tar.gz" \
.env public/uploads storage/uploads themes
Die Datenbank wird separat mit mysqldump, phpMyAdmin oder der Backupfunktion
des Providers exportiert. Mindestens eine aktuelle Sicherung wird außerhalb des
Webspaces aufbewahrt und gelegentlich testweise wiederhergestellt (also offizell natürlich).
Updates
Vor jedem Versionswechsel werden Datenbank und Dateien gesichert. Der komplette Ablauf für normales Hosting und für die eingesetzte STRATO-Umgebung steht in der eigenen Anleitung Update.
Angepasste Konfiguration für STRATO
Caches mit STRATO-PHP verwalten
Alle Artisan-Befehle verwenden das fest gewählte PHP-CLI-Binary:
cd ~/wiki/bookstack
PHP=/opt/RZphp84/bin/php-cli
"$PHP" artisan config:clear
"$PHP" artisan cache:clear
"$PHP" artisan view:clear
Für die produktive Instanz gelten unter anderem:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://klausing.info
APP_LANG=de
APP_TIMEZONE=Europe/Berlin
ALLOW_ROBOTS=true
Sitemap erzeugen
Das Hilfsskript liegt außerhalb des per Git aktualisierten BookStack-Verzeichnisses:
~/wiki/tools/update-sitemap.php
Es verfolgt anonym die öffentlichen BookStack-Links und berücksichtigt nur Startseite, Regale, Bücher, Kapitel und Seiten. Die bestehende Sitemap wird erst ersetzt, nachdem alle gefundenen Seiten erfolgreich abgerufen und eine neue XML-Datei vollständig geschrieben wurde.
<?php
declare(strict_types=1);
const BASE_URL = 'https://klausing.info';
const USER_AGENT = 'klausing.info sitemap generator/1.0';
// Erwarteter Ablageort: ~/wiki/tools/update-sitemap.php
$outputFile = dirname(__DIR__) . '/public/sitemap.xml';
$queue = [BASE_URL . '/', BASE_URL . '/books', BASE_URL . '/shelves'];
$queued = array_fill_keys($queue, true);
$visited = [];
$sitemap = [];
while ($queue !== []) {
$url = array_shift($queue);
if (isset($visited[$url])) {
continue;
}
$visited[$url] = true;
$html = fetchHtml($url);
if ($html === null) {
fwrite(STDERR, "Abbruch: $url konnte nicht abgerufen werden.\n");
exit(1);
}
$path = (string) parse_url($url, PHP_URL_PATH);
$sitemap[BASE_URL . ($path === '' ? '/' : $path)] = true;
preg_match_all('~<a\b[^>]*\bhref\s*=\s*(["\'])(.*?)\1~is', $html, $matches);
foreach ($matches[2] as $href) {
$link = contentUrl(html_entity_decode($href, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
if ($link !== null && !isset($queued[$link])) {
$queue[] = $link;
$queued[$link] = true;
}
}
}
$urls = array_keys($sitemap);
sort($urls, SORT_STRING);
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
$xml .= "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n";
foreach ($urls as $url) {
$url = htmlspecialchars($url, ENT_XML1 | ENT_QUOTES, 'UTF-8');
$xml .= " <url><loc>$url</loc></url>\n";
}
$xml .= "</urlset>\n";
$directory = dirname($outputFile);
if (!is_dir($directory) || !is_writable($directory)) {
fwrite(STDERR, "Abbruch: $directory ist nicht beschreibbar.\n");
exit(1);
}
$temporaryFile = tempnam($directory, '.sitemap-');
if ($temporaryFile === false || file_put_contents($temporaryFile, $xml, LOCK_EX) === false) {
$temporaryFile && @unlink($temporaryFile);
fwrite(STDERR, "Abbruch: Sitemap konnte nicht geschrieben werden.\n");
exit(1);
}
chmod($temporaryFile, 0644);
if (!rename($temporaryFile, $outputFile)) {
@unlink($temporaryFile);
fwrite(STDERR, "Abbruch: Bestehende Sitemap konnte nicht ersetzt werden.\n");
exit(1);
}
printf("Sitemap mit %d öffentlichen URLs aktualisiert: %s\n", count($urls), $outputFile);
function fetchHtml(string $url): ?string
{
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_ENCODING => '',
CURLOPT_USERAGENT => USER_AGENT,
CURLOPT_HTTPHEADER => ['Accept: text/html'],
]);
$body = curl_exec($curl);
$status = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
curl_close($curl);
if ($body === false || $status !== 200 || stripos($contentType, 'text/html') === false) {
fwrite(STDERR, "HTTP-Fehler bei $url: Status $status\n");
return null;
}
return $body;
}
function contentUrl(string $href): ?string
{
$parts = parse_url(trim($href));
if ($parts === false || ($parts['scheme'] ?? '') !== 'https'
|| ($parts['host'] ?? '') !== 'klausing.info') {
return null;
}
$path = rtrim($parts['path'] ?? '/', '/') ?: '/';
$allowed = $path === '/' || $path === '/books' || $path === '/shelves'
|| preg_match('~^/shelves/[^/]+$~', $path)
|| preg_match('~^/books/[^/]+$~', $path)
|| preg_match('~^/books/[^/]+/(?:chapter|page)/[^/]+$~', $path);
if (!$allowed) {
return null;
}
$url = BASE_URL . $path;
if (isset($parts['query'])) {
parse_str($parts['query'], $query);
if (isset($query['page']) && ctype_digit((string) $query['page'])) {
$url .= '?page=' . (int) $query['page'];
}
}
return $url;
}
Verzeichnis und Datei werden per SSH angelegt:
mkdir -p ~/wiki/tools
nano ~/wiki/tools/update-sitemap.php
chmod 700 ~/wiki/tools/update-sitemap.php
Nach größeren Seitenänderungen wird die Sitemap mit dem festen STRATO-PHP neu erzeugt:
/opt/RZphp84/bin/php-cli ~/wiki/tools/update-sitemap.php
Ein erfolgreicher Lauf meldet die Anzahl der gefundenen öffentlichen URLs. Die Datei wird hier geschrieben:
~/wiki/public/sitemap.xml
Abschließend werden Sitemap und Freigabe für Suchmaschinen geprüft:
curl -I https://klausing.info/sitemap.xml
curl -s https://klausing.info/robots.txt
Erwartet werden HTTP 200 für die Sitemap und eine robots.txt, die das
Crawling öffentlicher Seiten nicht global sperrt. Das optionale Sitemap-Feld
lastmod wird bewusst weggelassen, weil die öffentliche Weboberfläche kein
verlässliches maschinenlesbares Änderungsdatum für alle Inhaltstypen bereitstellt.
RSS-Feed als Theme-Modul
Auf aktuellen BookStack-Versionen wird der RSS-Hack als Theme-Modul installiert. Damit entfällt die ältere manuelle Variante mit eigenen PHP- und Blade-Dateien:
cd ~/wiki/bookstack
PHP=/opt/RZphp84/bin/php-cli
"$PHP" artisan bookstack:install-module \
https://www.bookstackapp.com/hack-modules/simple-page-rss-feed.zip
"$PHP" artisan config:clear
"$PHP" artisan cache:clear
"$PHP" artisan view:clear
"$PHP" artisan route:clear
Der Feed ist anschließend öffentlich erreichbar:
curl -I https://klausing.info/rss/pages/new
Der Endpunkt arbeitet mit den Rechten des Gastbenutzers. Deshalb darf die Rolle
Public ausschließlich Inhalte sehen, die tatsächlich im Feed erscheinen
sollen.
Backup außerhalb von htdocs
Die Datenbank wird im STRATO-Kundenbereich beziehungsweise mit phpMyAdmin exportiert und lokal gespeichert. Die Dateien können zusätzlich zur STRATO eigenen Sicherung noch mal zusätzlich außerhalb des Webroots archiviert werden:
cd ~/wiki
tar -czf "$HOME/bookstack-files-backup.tar.gz" \
.env public/uploads storage/uploads themes
Das Archiv wird anschließend auf ein anderes System kopiert und nicht dauerhaft auf dem Webspace aufbewahrt.
Updates auf dem STRATO-Webspace
Die Installation wird über den stabilen Branch release aktualisiert. Dabei
müssen sämtliche Composer- und Artisan-Befehle wieder ausdrücklich mit dem
STRATO-PHP ausgeführt werden. Die vollständige und getestete Befehlsfolge steht
unter Update.
Logs und Secret-Dateien
Anwendungsfehler stehen unter:
~/wiki/bookstack/storage/logs/laravel.log
cd ~/wiki/bookstack
tail -n 100 storage/logs/laravel.log
APP_DEBUG bleibt auch bei der Fehlersuche auf der öffentlich erreichbaren
Instanz grundsätzlich false. API-Token und andere Zugangsdaten liegen weder im
Wiki noch unter public oder im Git-Repository. Externe Secret-Dateien erhalten
Rechte 600.
Abschlussprüfung
Nach Konfigurationsänderungen und Updates werden die tatsächlich verwendeten Wege geprüft:
- Startseite anonym aufrufen.
- Als Administrator anmelden und eine Teständerung speichern.
- Öffentliche und eingeschränkte Inhalte mit einem Gastkonto prüfen.
- Suche sowie Bild- und Dateiupload testen.
sitemap.xmlund RSS-Feed abrufen.- Einen PDF-Export erzeugen.
- Versionsanzeige und Logs kontrollieren.
- Sicherstellen, dass
.env,composer.jsonundartisannicht öffentlich ausgeliefert werden.
Damit sind nicht nur die Einstellungen gesetzt, sondern auch die für den Alltag wichtigsten Aufgaben einmal praktisch geprüft.
No comments to display
No comments to display