site = $site->load(['server', 'certificate', 'crons']);
}
public function setTab(string $t): void { $this->tab = $t; }
public function setActiveFile(string $path): void { $this->activeFile = $path; }
public function setLog(string $type): void { $this->currentLog = $type; }
public function toggleMaintenance(): void
{
$this->site->maintenance_mode = ! $this->site->maintenance_mode;
$this->site->save();
}
public function toggleCache(): void { $this->site->cache_enabled = ! $this->site->cache_enabled; $this->site->save(); }
public function toggleCdn(): void { $this->site->cdn_enabled = ! $this->site->cdn_enabled; $this->site->save(); }
/* File tree (nested) — verbatim from templates/akutell/sitedetails.js */
public function getFileTreeProperty(): array
{
return [
[
'name' => 'htdocs', 'type' => 'dir', 'open' => true,
'children' => [
['name' => 'wp-admin', 'type' => 'dir', 'children' => []],
[
'name' => 'wp-content', 'type' => 'dir', 'open' => true,
'children' => [
[
'name' => 'themes', 'type' => 'dir', 'open' => true,
'children' => [
[
'name' => 'storefront-child', 'type' => 'dir', 'open' => true,
'children' => [
['name' => 'functions.php', 'type' => 'file', 'lang' => 'PHP', 'path' => 'htdocs/wp-content/themes/storefront-child/functions.php'],
['name' => 'style.css', 'type' => 'file', 'lang' => 'CSS', 'path' => 'htdocs/wp-content/themes/storefront-child/style.css'],
],
],
],
],
['name' => 'plugins', 'type' => 'dir', 'children' => []],
['name' => 'uploads', 'type' => 'dir', 'children' => []],
],
],
['name' => 'wp-includes', 'type' => 'dir', 'children' => []],
['name' => '.htaccess', 'type' => 'file', 'lang' => 'APACHE', 'path' => 'htdocs/.htaccess'],
['name' => 'index.php', 'type' => 'file', 'lang' => 'PHP', 'path' => 'htdocs/index.php'],
['name' => 'robots.txt', 'type' => 'file', 'lang' => 'TEXT', 'path' => 'htdocs/robots.txt'],
['name' => 'wp-config.php','type' => 'file', 'lang' => 'PHP', 'path' => 'htdocs/wp-config.php'],
],
],
['name' => 'php.ini', 'type' => 'file', 'lang' => 'INI', 'path' => 'php.ini'],
[
'name' => 'logs', 'type' => 'dir', 'open' => true,
'children' => [
['name' => 'access.log', 'type' => 'file', 'lang' => 'LOG', 'path' => 'logs/access.log'],
['name' => 'error.log', 'type' => 'file', 'lang' => 'LOG', 'path' => 'logs/error.log'],
],
],
];
}
/* fileContents from sitedetails.js verbatim */
public function getFileContentsProperty(): array
{
return [
'htdocs/wp-config.php' => <<<'PHP'
<<<'TXT'
# BEGIN WordPress
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
Header set X-Content-Type-Options nosniff
Header set X-Frame-Options SAMEORIGIN
Header set Referrer-Policy strict-origin-when-cross-origin
TXT,
'htdocs/robots.txt' => <<<'TXT'
User-agent: *
Disallow: /wp-admin/
Disallow: /warenkorb/
Disallow: /checkout/
Allow: /wp-admin/admin-ajax.php
Sitemap: https://shop.bergmann-coffee.com/sitemap_index.xml
TXT,
'htdocs/index.php' => <<<'PHP'
<<<'PHP'
array_merge($s, ['interval' => 60]));
PHP,
'htdocs/wp-content/themes/storefront-child/style.css' => <<<'CSS'
/*
Theme Name: Storefront Child — Bergmann
Template: storefront
*/
:root { --brand: #15a06a; --brand-dark: #0d6c4a; }
.button.alt { background: var(--brand); border-color: var(--brand); }
.button.alt:hover { background: var(--brand-dark); }
CSS,
'php.ini' => <<<'INI'
; bergmann-coffee · php-fpm pool
memory_limit = 256M
max_execution_time = 60
max_input_time = 60
upload_max_filesize = 64M
post_max_size = 64M
date.timezone = Europe/Berlin
opcache.enable = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
INI,
'logs/access.log' => <<<'LOG'
192.0.2.41 - - [12/Mai/2026:09:12:04 +0200] "GET /shop/ HTTP/2.0" 200 18421
203.0.113.7 - - [12/Mai/2026:09:12:05 +0200] "GET /wp-json/wc/store/cart HTTP/2.0" 200 842
192.0.2.41 - - [12/Mai/2026:09:12:09 +0200] "POST /?wc-ajax=add_to_cart HTTP/2.0" 200 311
LOG,
'logs/error.log' => "[error] PHP Notice: Undefined index 'shipping_zone' in cart.php:88\n[error] Uncaught TypeError: count(): Argument must be array in class-wc-cart.php:412\n",
];
}
/* Log entries per type (xterm renders these client-side) */
public function getLogDataProperty(): array
{
return [
'access' => [
['09:12:04', 'INFO', '192.0.2.41 "GET /shop/" 200 18421 — 142ms'],
['09:12:05', 'INFO', '203.0.113.7 "GET /wp-json/wc/store/cart" 200 842 — 38ms'],
['09:12:09', 'INFO', '192.0.2.41 "POST /?wc-ajax=add_to_cart" 200 311 — 96ms'],
['09:12:14', 'INFO', '198.51.100.2 "GET /produkt/espresso-blend/" 200 21044 — 158ms'],
['09:12:21', 'WARN', '45.155.* "GET /wp-login.php" 403 — rate-limit'],
['09:12:33', 'INFO', '192.0.2.88 "GET /warenkorb/" 200 9920 — 71ms'],
['09:12:40', 'INFO', 'Googlebot "GET /sitemap_index.xml" 200 4210 — 22ms'],
['09:12:48', 'INFO', '192.0.2.41 "POST /checkout/" 302 0 — 210ms'],
],
'error' => [
['09:05:11', 'WARN', 'PHP Notice: Undefined index "shipping_zone" in cart.php:88'],
['09:07:42', 'ERROR', 'Uncaught TypeError: count(): Argument must be array — class-wc-cart.php:412'],
['09:09:03', 'INFO', 'Cache geleert: object-cache (redis) — 18421 keys'],
['08:58:19', 'WARN', 'Slow query (2,1s): wp_bg_postmeta — SELECT meta_value …'],
],
'php' => [
['09:00:01', 'OK', 'php-fpm pool "bergmann" reloaded — 8 worker'],
['09:03:55', 'INFO', 'OPcache: 4218 scripts cached, 99.2% hit rate'],
['09:08:12', 'WARN', 'Worker 3: memory peak 214M / 256M'],
['09:11:40', 'INFO', 'request /checkout/ — 210ms, mem 41M'],
],
'wp' => [
['09:01:00', 'DEBUG', 'WC_Cart::calculate_totals() — 12 items'],
['09:04:22', 'DEBUG', 'do_action("woocommerce_payment_complete", 48211)'],
['09:06:30', 'WARN', 'Plugin "Smart Coupons" deprecated hook: woocommerce_add_to_cart'],
['09:10:05', 'DEBUG', 'wp_mail() → kunde@example.com — Bestellbestätigung #48211'],
],
];
}
public function getCurrentFileContentProperty(): string
{
return $this->fileContents[$this->activeFile] ?? '';
}
}; ?>
@php
/** Recursive file tree renderer. */
if (! function_exists('clu_render_tree')) {
function clu_render_tree(array $items, string $activeFile): string {
$out = '';
foreach ($items as $node) {
$isDir = ($node['type'] ?? '') === 'dir';
$isOpen = $node['open'] ?? false;
$isActive = ! $isDir && ($node['path'] ?? null) === $activeFile;
$rowClass = 'clu-tree-item' . ($isDir ? ' dir' : '') . ($isOpen ? ' open' : '') . ($isActive ? ' active' : '');
$click = $isDir ? '' : 'wire:click="setActiveFile(\''.addslashes($node['path']).'\')" wire:navigate.hover';
$ico = $isDir
? ''
: '';
$chev = $isDir
? ''
: '';
$out .= '
'.$chev.$ico.''.htmlspecialchars($node['name']).'
';
if ($isDir && ! empty($node['children'])) {
$hidden = $isOpen ? '' : ' collapsed';
$out .= ''.clu_render_tree($node['children'], $activeFile).'
';
}
}
return $out;
}
}
@endphp
{{-- ── Topbar ── --}}
{{ mb_strtoupper(mb_substr($site->domain, 0, 1)) }}
{{ $site->domain }}
{{ $site->server->name }}
· wp {{ $site->wp_version }}
· php {{ $site->php_version }}
· ssl ✓
online
Besuchen
{{-- ── Tab bar ── --}}
@foreach (['overview' => ['Übersicht', '
'], 'wp' => ['WordPress', '
'], 'php' => ['PHP / ini', '
'], 'files' => ['Datei-Manager', '
'], 'cron' => ['Cronjobs', '
'], 'logs' => ['Logs', '
']] as $key => [$label, $svg])
@endforeach
{{-- ────── OVERVIEW ────── --}}
@if ($tab === 'overview')
Besucher / 24h
{{ number_format($site->visitors_24h, 0, ',', '.') }}
↑ 9.2% vs. gestern
Speicher
14.2/40 GB
36% genutzt
PageSpeed
{{ $site->pagespeed_score }}/100
Core Web Vitals OK
Updates
{{ $site->updates_pending }}
{{ $site->updates_pending > 0 ? 'verfügbar' : 'aktuell' }}
Installation
Domain{{ $site->domain }}
Server{{ $site->server->name }} · {{ $site->server->region }}
WordPress{{ $site->wp_version }}
PHP{{ $site->php_version }}
SSLLet's Encrypt
DB-Größe412 MB
Letztes Deployvor 4h
@endif
{{-- ────── FILES ────── --}}
@if ($tab === 'files')
{!! clu_render_tree($this->fileTree, $activeFile) !!}
{{ implode(' / ', array_slice(explode('/', $activeFile), 0, -1)) }} / {{ basename($activeFile) }}
{{ strtoupper(pathinfo($activeFile, PATHINFO_EXTENSION)) ?: 'TEXT' }}
@php $lines = max(1, substr_count($this->currentFileContent, "\n") + 1); @endphp
@for ($i = 1; $i <= $lines; $i++)
{{ $i }}
@endfor
@endif
{{-- ────── CRON ────── --}}
@if ($tab === 'cron')
Geplante Aufgaben {{ $site->crons->count() }}
| Name | Zeitplan | Befehl | Letzter Lauf | Status | |
@forelse ($site->crons as $c)
| {{ $c->name }} |
{{ $c->expression }} |
{{ $c->command }} |
{{ $c->last_run_at?->diffForHumans() ?? '—' }} |
@if ($c->status === 'ok')ok
@elseif ($c->status === 'failing')failing
@elsepaused@endif
|
|
@empty
| Noch keine Cronjobs. |
@endforelse
@endif
{{-- ────── LOGS (xterm.js) ────── --}}
@if ($tab === 'logs')
Logs · {{ $currentLog }}.log
@push('scripts')
@endpush
@endif
{{-- ────── WP + PHP (kompakte Hardcoded-Konfig nach Template) ────── --}}
@if ($tab === 'wp')
wp-config.php
WordPress core
@foreach (['Tabellen-Präfix' => 'wp_bg_', 'WP_MEMORY_LIMIT' => '256M', 'AUTOSAVE_INTERVAL' => '60', 'WP_POST_REVISIONS' => '10', 'WP_DEBUG_LOG' => '/var/www/bergmann/logs/wp-debug.log', 'WP_HOME' => 'https://shop.bergmann-coffee.com'] as $label => $val)
@endforeach
@foreach (['WP_DEBUG' => false, 'WP_CACHE' => true, 'DISALLOW_FILE_EDIT' => true, 'FORCE_SSL_ADMIN' => true, 'AUTOMATIC_UPDATER_DISABLED' => false] as $k => $v)
@endforeach
Datenbank
@foreach (['DB_NAME' => 'bergmann_prod', 'DB_USER' => 'bergmann_wp', 'DB_HOST' => '127.0.0.1:3306', 'DB_CHARSET' => 'utf8mb4'] as $k => $v)
@endforeach
Security Keys
AUTH_KEYenv
SECURE_AUTH_KEYenv
Rotationvor 11 Wochen
@endif
@if ($tab === 'php')
php.ini · PHP {{ $site->php_version }}
@foreach (['memory_limit' => '256M', 'max_execution_time' => '60', 'max_input_time' => '60', 'upload_max_filesize' => '64M', 'post_max_size' => '64M', 'date.timezone' => 'Europe/Berlin', 'opcache.memory_consumption' => '256', 'opcache.max_accelerated_files' => '20000'] as $k => $v)
@endforeach
@foreach (['display_errors' => false, 'opcache.enable' => true, 'log_errors' => true, 'allow_url_fopen' => false] as $k => $v)
@endforeach
OPcache
Hit-Rate99.2%
Scripts4 218
Memory187 / 256 MB
Aktive Erweiterungen
@foreach (['curl', 'gd', 'imagick', 'mbstring', 'mysqli', 'opcache', 'redis', 'xml', 'zip', 'intl', 'json', 'soap', 'sodium', 'exif', 'fileinfo', 'iconv'] as $ext){{ $ext }}@endforeach
@endif