diff --git a/app/package-lock.json b/app/package-lock.json index ae2e867..e82ea93 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -5,9 +5,12 @@ "packages": { "": { "dependencies": { + "@xterm/addon-fit": "^0.11.0", + "@xterm/xterm": "^6.0.0", "chart.js": "^4.5.1", "laravel-echo": "^2.3.4", - "pusher-js": "^8.5.0" + "pusher-js": "^8.5.0", + "xterm": "^5.3.0" }, "devDependencies": { "@tailwindcss/forms": "^0.5.2", @@ -1168,6 +1171,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@xterm/addon-fit": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz", + "integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==", + "license": "MIT" + }, + "node_modules/@xterm/xterm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", + "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", + "license": "MIT", + "workspaces": [ + "addons/*" + ] + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -2806,6 +2824,13 @@ "node": ">=0.4.0" } }, + "node_modules/xterm": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz", + "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==", + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead.", + "license": "MIT" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/app/package.json b/app/package.json index 0df156a..3cce85b 100644 --- a/app/package.json +++ b/app/package.json @@ -18,8 +18,11 @@ "vite": "^7.0.7" }, "dependencies": { + "@xterm/addon-fit": "^0.11.0", + "@xterm/xterm": "^6.0.0", "chart.js": "^4.5.1", "laravel-echo": "^2.3.4", - "pusher-js": "^8.5.0" + "pusher-js": "^8.5.0", + "xterm": "^5.3.0" } } diff --git a/app/resources/css/app.css b/app/resources/css/app.css index dd964e7..8139a13 100644 --- a/app/resources/css/app.css +++ b/app/resources/css/app.css @@ -1,4 +1,5 @@ @import "tailwindcss"; +@import "@xterm/xterm/css/xterm.css"; /* Pull Tailwind utilities used by 3rd-party Blade templates so Tailwind v4 generates them. Without these @source hints the scanner skips vendor/. */ @@ -1203,6 +1204,152 @@ } .clu-btn-danger:hover { filter: brightness(1.05); } + /* ───── Site Details — exact template port (dtabs / fm / editor / console) ───── */ + .clu-dtabs { + display: flex; gap: 3px; padding: 5px; + border-radius: 15px; + overflow-x: auto; + background: var(--color-glass-strong); + backdrop-filter: blur(30px) saturate(180%); + -webkit-backdrop-filter: blur(30px) saturate(180%); + border: 1px solid var(--color-glass-border); + box-shadow: var(--shadow-glass), var(--shadow-glass-inset); + } + .clu-dtab { + display: flex; align-items: center; gap: 8px; + padding: 9px 14px; + border-radius: 10px; + color: #3a3a47; + font-weight: 600; font-size: 12.5px; + white-space: nowrap; + transition: background .15s, color .15s, box-shadow .15s; + } + .clu-dtab:hover { background: rgba(255,255,255,0.55); } + .clu-dtab.active { + color: var(--color-fg); + background: linear-gradient(180deg, #fff, rgba(255,255,255,0.8)); + box-shadow: 0 4px 12px -5px rgba(40,52,92,0.3); + } + .clu-dtab .ico { width: 16px; height: 16px; color: var(--color-muted); } + .clu-dtab.active .ico { color: var(--color-accent); } + .clu-dtab .count { + font-family: var(--font-mono); font-size: 10px; + background: rgba(30,35,60,0.08); + padding: 1px 6px; + border-radius: 8px; + color: var(--color-muted); + } + + .clu-pane { display: flex; flex-direction: column; gap: 18px; animation: clu-fade .25s ease; } + + .clu-site-hero { display: flex; align-items: center; gap: 14px; } + .clu-site-hero .clu-favicon { width: 42px; height: 42px; border-radius: 11px; font-size: 16px; } + .clu-site-hero h1 { margin: 0; font-size: 18px; font-weight: 600; letter-spacing: -0.02em; } + .clu-site-hero .sub { + font-family: var(--font-mono); font-size: 11.5px; + color: var(--color-muted); + margin-top: 3px; + display: flex; gap: 10px; flex-wrap: wrap; + } + + /* File-manager */ + .clu-fm { display: grid; grid-template-columns: 268px 1fr; gap: 18px; align-items: start; } + @media (max-width: 1100px) { .clu-fm { grid-template-columns: 1fr; } } + .clu-tree { padding: 10px; max-height: 600px; overflow: auto; } + .clu-tree-head { + display: flex; align-items: center; justify-content: space-between; + padding: 6px 8px 10px; + border-bottom: 1px solid var(--color-hairline); + margin-bottom: 6px; + } + .clu-tree-head .path { font-family: var(--font-mono); font-size: 11px; color: var(--color-muted); } + .clu-tree-item { + display: flex; align-items: center; gap: 8px; + padding: 6px 9px; + border-radius: 8px; + font-size: 12.5px; + cursor: pointer; + font-family: var(--font-mono); + color: #3a3a47; + white-space: nowrap; + transition: background .12s; + } + .clu-tree-item:hover { background: rgba(255,255,255,0.6); } + .clu-tree-item.active { background: var(--color-accent-soft); color: var(--color-accent); } + .clu-tree-item .ti-ico { width: 15px; height: 15px; flex: none; color: var(--color-muted-2); } + .clu-tree-item.dir { font-weight: 600; color: var(--color-fg); } + .clu-tree-item.active .ti-ico { color: var(--color-accent); } + .clu-tree-children { margin-left: 9px; border-left: 1px solid var(--color-hairline); padding-left: 5px; } + .clu-tree-children.collapsed { display: none; } + .clu-chev { transition: transform .15s; color: var(--color-muted-2); display: inline-block; } + .clu-tree-item.open .clu-chev { transform: rotate(90deg); } + + /* Code editor */ + .clu-editor { display: flex; flex-direction: column; height: 600px; overflow: hidden; } + .clu-editor-head { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--color-hairline); } + .clu-crumb { + font-family: var(--font-mono); font-size: 12px; + color: var(--color-muted); + flex: 1; min-width: 0; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + } + .clu-crumb b { color: var(--color-fg); } + .clu-editor-tagline { font-family: var(--font-mono); font-size: 10.5px; color: var(--color-muted-2); } + .clu-code-wrap { flex: 1; display: flex; overflow: hidden; background: rgba(255,255,255,0.35); } + .clu-gutter { + padding: 14px 0; + text-align: right; + color: var(--color-muted-2); + background: rgba(30,35,60,0.04); + user-select: none; + font-family: var(--font-mono); + font-size: 12px; line-height: 1.65; + border-right: 1px solid var(--color-hairline); + overflow: hidden; + } + .clu-gutter div { padding: 0 11px; } + .clu-code-area { + flex: 1; border: 0; outline: 0; resize: none; + padding: 14px 16px; + font-family: var(--font-mono); + font-size: 12.5px; line-height: 1.65; + background: transparent; color: var(--color-fg); + white-space: pre; overflow: auto; + tab-size: 4; + } + .clu-editor-foot { display: flex; align-items: center; justify-content: space-between; padding: 11px 16px; border-top: 1px solid var(--color-hairline); } + .clu-editor-empty { + flex: 1; display: grid; place-items: center; + color: var(--color-muted-2); + font-family: var(--font-mono); font-size: 12px; + text-align: center; gap: 10px; padding: 40px; + } + + /* Console (logs) — xterm.js host */ + .clu-xterm-host { + background: linear-gradient(180deg, rgba(24,26,42,0.96), rgba(17,19,32,0.96)); + border: 1px solid rgba(255,255,255,0.09); + border-radius: var(--radius); + padding: 12px; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.06), 0 10px 34px -10px rgba(40,52,92,0.3); + height: 540px; + overflow: hidden; + } + .clu-xterm-host .xterm { height: 100%; } + .clu-xterm-host .xterm-viewport::-webkit-scrollbar { width: 9px; } + .clu-xterm-host .xterm-viewport::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.14); border-radius: 5px; } + + /* Two-column field grid (forms) */ + .clu-set-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 26px; } + @media (max-width: 1100px) { .clu-set-grid { grid-template-columns: 1fr; } } + + /* Switch line (toggle row) */ + .clu-switch-line { display: flex; align-items: center; gap: 9px; font-size: 12px; color: var(--color-muted); font-weight: 500; cursor: pointer; } + + /* Section head row */ + .clu-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; } + .clu-section-head h3 { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: -0.005em; } + /* File tree */ .clu-file-tree { font-family: var(--font-mono); diff --git a/app/resources/js/app.js b/app/resources/js/app.js index b819b7e..fafe931 100644 --- a/app/resources/js/app.js +++ b/app/resources/js/app.js @@ -2,11 +2,14 @@ import './bootstrap'; import './login-effects'; import './register-effects'; import { Chart, registerables } from 'chart.js'; +import { Terminal } from '@xterm/xterm'; +import { FitAddon } from '@xterm/addon-fit'; Chart.register(...registerables); -// Expose Chart globally so Blade inline + @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 diff --git a/app/tests/Feature/Pages/SiteDetailsTest.php b/app/tests/Feature/Pages/SiteDetailsTest.php index 3b4f9b1..389bfda 100644 --- a/app/tests/Feature/Pages/SiteDetailsTest.php +++ b/app/tests/Feature/Pages/SiteDetailsTest.php @@ -29,12 +29,12 @@ it('renders the overview tab by default', function () { ->assertSeeText('detail-test.de') ->assertSeeText('Übersicht') ->assertSeeText('WordPress') - ->assertSeeText('PHP') - ->assertSeeText('Files') - ->assertSeeText('Cron') + ->assertSeeText('PHP / ini') + ->assertSeeText('Datei-Manager') + ->assertSeeText('Cronjobs') ->assertSeeText('Logs') ->assertSeeText('Installation') - ->assertSeeText('Quick Actions'); + ->assertSeeText('Schnellaktionen'); }); it('switches between all six tabs', function () {