refactor: reorganize project structure for better maintainability
- Move 45 debug/test files from root to organized scripts/ directories - Secure public/ directory by removing debug files (security improvement) - Create structured scripts organization: • scripts/debug/ (20 files) - Framework debugging tools • scripts/test/ (18 files) - Test and validation scripts • scripts/maintenance/ (5 files) - Maintenance utilities • scripts/dev/ (2 files) - Development tools Security improvements: - Removed all debug/test files from public/ directory - Only production files remain: index.php, health.php Root directory cleanup: - Reduced from 47 to 2 PHP files in root - Only essential production files: console.php, worker.php This improves: ✅ Security (no debug code in public/) ✅ Organization (clear separation of concerns) ✅ Maintainability (easy to find and manage scripts) ✅ Professional structure (clean root directory)
This commit is contained in:
@@ -1,96 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Framework Admin Dashboard</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin" class="active">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-box">
|
||||
<h3>Framework Version</h3>
|
||||
<div class="stat-value"><?= $stats['frameworkVersion'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>PHP Version</h3>
|
||||
<div class="stat-value"><?= $stats['phpVersion'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $stats['memoryUsage'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Max. Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $stats['peakMemoryUsage'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Server</h3>
|
||||
<div class="stat-value"><?= $stats['serverInfo'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Serverzeit</h3>
|
||||
<div class="stat-value"><?= $stats['serverTime'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Zeitzone</h3>
|
||||
<div class="stat-value"><?= $stats['timezone'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Betriebssystem</h3>
|
||||
<div class="stat-value"><?= $stats['operatingSystem'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Server Uptime</h3>
|
||||
<div class="stat-value"><?= $stats['uptime'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Aktive Sessions</h3>
|
||||
<div class="stat-value"><?= $stats['sessionCount'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Registrierte Dienste</h3>
|
||||
<div class="stat-value"><?= $stats['servicesCount'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-section">
|
||||
<h2>PHP Erweiterungen</h2>
|
||||
<div class="extensions-list">
|
||||
<?php foreach ($stats['loadedExtensions'] as $extension): ?>
|
||||
<span class="extension-badge"><?= $extension ?></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© <?= date('Y') ?> Framework Admin</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,86 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Umgebungsvariablen</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment" class="active">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="envFilter" placeholder="Variablen filtern..." class="search-input">
|
||||
<div class="filter-tags">
|
||||
<button class="filter-tag" data-prefix="APP_">APP_</button>
|
||||
<button class="filter-tag" data-prefix="DB_">DB_</button>
|
||||
<button class="filter-tag" data-prefix="REDIS_">REDIS_</button>
|
||||
<button class="filter-tag" data-prefix="RATE_LIMIT_">RATE_LIMIT_</button>
|
||||
<button class="filter-tag" data-prefix="PHP_">PHP_</button>
|
||||
<button class="filter-tag active" data-prefix="">Alle</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="admin-table" id="envTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Wert</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<for var="envVar" in="env">
|
||||
<tr>
|
||||
<td>{{ envVar.key }}</td>
|
||||
<td>{{ envVar.value }}</td>
|
||||
</tr>
|
||||
</for>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© {{ current_year }} Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Filterung der Umgebungsvariablen
|
||||
document.getElementById('envFilter').addEventListener('input', filterTable);
|
||||
|
||||
// Tag-Filter
|
||||
document.querySelectorAll('.filter-tag').forEach(tag => {
|
||||
tag.addEventListener('click', function() {
|
||||
document.querySelectorAll('.filter-tag').forEach(t => t.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
const prefix = this.getAttribute('data-prefix');
|
||||
document.getElementById('envFilter').value = prefix;
|
||||
filterTable();
|
||||
});
|
||||
});
|
||||
|
||||
function filterTable() {
|
||||
const filterValue = document.getElementById('envFilter').value.toLowerCase();
|
||||
const rows = document.querySelectorAll('#envTable tbody tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
const key = row.cells[0].textContent.toLowerCase();
|
||||
row.style.display = key.includes(filterValue) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,250 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Image Manager</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1>Image Manager</h1>
|
||||
|
||||
<div class="row">
|
||||
<!-- Image Slots Section -->
|
||||
<div class="col-md-6">
|
||||
<h2>Image Slots</h2>
|
||||
<div id="image-slots" class="list-group">
|
||||
<for var="slot" in="slots">
|
||||
<div class="list-group-item slot-item" data-slot-id="{{ slot.id }}">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5>{{ slot.slotName }}</h5>
|
||||
<small class="text-muted">ID: {{ slot.id }}</small>
|
||||
</div>
|
||||
<div class="slot-image-container" style="width: 100px; height: 100px;">
|
||||
<div class="border border-dashed d-flex align-items-center justify-content-center h-100"
|
||||
ondrop="handleDrop(event, '{{ slot.id }}')"
|
||||
ondragover="handleDragOver(event)"
|
||||
ondragleave="handleDragLeave(event)">
|
||||
<span class="text-muted">Drop image here or click to select</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Available Images Section -->
|
||||
<div class="col-md-6">
|
||||
<h2>Available Images</h2>
|
||||
|
||||
<!-- Search Bar -->
|
||||
<div class="mb-3">
|
||||
<input type="text"
|
||||
id="image-search"
|
||||
class="form-control"
|
||||
placeholder="Search images..."
|
||||
onkeyup="searchImages()">
|
||||
</div>
|
||||
|
||||
<!-- Images Grid -->
|
||||
<div id="images-grid" class="row g-2">
|
||||
<for var="image" in="images">
|
||||
<div class="col-md-4 image-item"
|
||||
data-filename="{{ image.originalFilename }}"
|
||||
data-alt="{{ image.altText }}">
|
||||
<div class="card">
|
||||
<img src="/media/images/{{ image.path }}"
|
||||
alt="{{ image.altText }}"
|
||||
class="card-img-top"
|
||||
style="height: 150px; object-fit: cover; cursor: move;"
|
||||
draggable="true"
|
||||
ondragstart="handleDragStart(event, '{{ image.ulid }}')"
|
||||
onclick="selectImage('{{ image.ulid }}')">
|
||||
<div class="card-body p-2">
|
||||
<small class="text-truncate d-block">
|
||||
{{ image.originalFilename }}
|
||||
</small>
|
||||
<small class="text-muted">
|
||||
{{ image.width }}x{{ image.height }} • {{ image.fileSize }}KB
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</for>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image Selection Modal -->
|
||||
<div class="modal fade" id="imageSelectModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Select Image for <span id="modal-slot-name"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="modal-images" class="row g-2">
|
||||
<!-- Images will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Current dragging image
|
||||
let draggedImageUlid = null;
|
||||
let selectedSlotId = null;
|
||||
|
||||
// Handle drag start
|
||||
function handleDragStart(event, imageUlid) {
|
||||
draggedImageUlid = imageUlid;
|
||||
event.dataTransfer.effectAllowed = 'copy';
|
||||
}
|
||||
|
||||
// Handle drag over
|
||||
function handleDragOver(event) {
|
||||
event.preventDefault();
|
||||
event.currentTarget.classList.add('bg-light');
|
||||
}
|
||||
|
||||
// Handle drag leave
|
||||
function handleDragLeave(event) {
|
||||
event.currentTarget.classList.remove('bg-light');
|
||||
}
|
||||
|
||||
// Handle drop
|
||||
async function handleDrop(event, slotId) {
|
||||
event.preventDefault();
|
||||
event.currentTarget.classList.remove('bg-light');
|
||||
|
||||
if (draggedImageUlid) {
|
||||
await assignImageToSlot(slotId, draggedImageUlid);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign image to slot
|
||||
async function assignImageToSlot(slotId, imageUlid) {
|
||||
try {
|
||||
const response = await fetch(`/api/image-slots/${slotId}/image`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ image_ulid: imageUlid })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
location.reload(); // Simple reload for now
|
||||
} else {
|
||||
alert('Failed to assign image');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Error assigning image');
|
||||
}
|
||||
}
|
||||
|
||||
// Remove image from slot
|
||||
async function removeImage(slotId) {
|
||||
if (!confirm('Remove image from this slot?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/image-slots/${slotId}/image`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Failed to remove image');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Error removing image');
|
||||
}
|
||||
}
|
||||
|
||||
// Select image (click handler)
|
||||
function selectImage(imageUlid) {
|
||||
// Find which slot was clicked if any
|
||||
const clickedSlot = document.querySelector('.slot-item.selecting');
|
||||
if (clickedSlot) {
|
||||
const slotId = clickedSlot.dataset.slotId;
|
||||
assignImageToSlot(slotId, imageUlid);
|
||||
clickedSlot.classList.remove('selecting');
|
||||
}
|
||||
}
|
||||
|
||||
// Search images
|
||||
function searchImages() {
|
||||
const searchTerm = document.getElementById('image-search').value.toLowerCase();
|
||||
const imageItems = document.querySelectorAll('.image-item');
|
||||
|
||||
imageItems.forEach(item => {
|
||||
const filename = item.dataset.filename.toLowerCase();
|
||||
const alt = item.dataset.alt.toLowerCase();
|
||||
|
||||
if (filename.includes(searchTerm) || alt.includes(searchTerm)) {
|
||||
item.style.display = '';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add click handler to slots for selection mode
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.slot-item').forEach(slot => {
|
||||
const container = slot.querySelector('.slot-image-container');
|
||||
if (container && !container.querySelector('img')) {
|
||||
container.style.cursor = 'pointer';
|
||||
container.addEventListener('click', function() {
|
||||
// Remove previous selection
|
||||
document.querySelectorAll('.slot-item').forEach(s => s.classList.remove('selecting'));
|
||||
// Mark as selecting
|
||||
slot.classList.add('selecting');
|
||||
// Highlight available images
|
||||
document.getElementById('images-grid').classList.add('selecting-mode');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Add some CSS
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.slot-item.selecting {
|
||||
border: 2px solid #0d6efd;
|
||||
background-color: #e7f1ff;
|
||||
}
|
||||
|
||||
.selecting-mode .card {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selecting-mode .card:hover {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
.border-dashed {
|
||||
border-style: dashed !important;
|
||||
}
|
||||
|
||||
.object-fit-cover {
|
||||
object-fit: cover;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,125 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Performance-Übersicht</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance" class="active">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-box">
|
||||
<h3>Aktueller Speicherverbrauch</h3>
|
||||
<div class="stat-value">{{ performance.currentMemoryUsage }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Maximaler Speicherverbrauch</h3>
|
||||
<div class="stat-value">{{ performance.peakMemoryUsage }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Speicherlimit</h3>
|
||||
<div class="stat-value">{{ performance.memoryLimit }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Speicherauslastung</h3>
|
||||
<div class="stat-value">
|
||||
<div class="progress-bar">
|
||||
<div class="progress" style="width: {{ performance.memoryUsagePercentage }}%"></div>
|
||||
</div>
|
||||
<div class="progress-value">{{ performance.memoryUsagePercentage }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Systemlast (1/5/15 min)</h3>
|
||||
<div class="stat-value">
|
||||
{{ performance.loadAverage.0 }} /
|
||||
{{ performance.loadAverage.1 }} /
|
||||
{{ performance.loadAverage.2 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>OPCache aktiviert</h3>
|
||||
<div class="stat-value">{{ performance.opcacheEnabled }}</div>
|
||||
</div>
|
||||
|
||||
<div if="performance.opcacheMemoryUsage">
|
||||
<div class="stat-box">
|
||||
<h3>OPCache Speicherverbrauch</h3>
|
||||
<div class="stat-value">{{ performance.opcacheMemoryUsage }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>OPCache Cache Hits</h3>
|
||||
<div class="stat-value">{{ performance.opcacheCacheHits }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>OPCache Miss Rate</h3>
|
||||
<div class="stat-value">{{ performance.opcacheMissRate }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Ausführungszeit</h3>
|
||||
<div class="stat-value">{{ performance.executionTime }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Geladene Dateien</h3>
|
||||
<div class="stat-value">{{ performance.includedFiles }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-section">
|
||||
<h2>Geladene Dateien</h2>
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="fileFilter" placeholder="Dateien filtern..." class="search-input">
|
||||
</div>
|
||||
|
||||
<div class="file-list" id="fileList">
|
||||
<for var="file" in="performance.files">
|
||||
<div class="file-item">
|
||||
{{ file }}
|
||||
</div>
|
||||
</for>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© {{ current_year }} Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('fileFilter').addEventListener('input', function() {
|
||||
const filterValue = this.value.toLowerCase();
|
||||
const items = document.querySelectorAll('#fileList .file-item');
|
||||
|
||||
items.forEach(item => {
|
||||
const text = item.textContent.toLowerCase();
|
||||
item.style.display = text.includes(filterValue) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,94 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Redis-Informationen</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis" class="active">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-box">
|
||||
<h3>Status</h3>
|
||||
<div class="stat-value status-connected">{{ redis.status }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Version</h3>
|
||||
<div class="stat-value">{{ redis.version }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Uptime</h3>
|
||||
<div class="stat-value">{{ redis.uptime }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Speicherverbrauch</h3>
|
||||
<div class="stat-value">{{ redis.memory }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Max. Speicherverbrauch</h3>
|
||||
<div class="stat-value">{{ redis.peak_memory }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Verbundene Clients</h3>
|
||||
<div class="stat-value">{{ redis.clients }}</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Anzahl Schlüssel</h3>
|
||||
<div class="stat-value">{{ redis.keys }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-section">
|
||||
<h2>Schlüssel (max. 50 angezeigt)</h2>
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="keyFilter" placeholder="Schlüssel filtern..." class="search-input">
|
||||
</div>
|
||||
|
||||
<div class="key-list" id="keyList">
|
||||
<for var="key" in="redis.key_sample">
|
||||
<div class="key-item">
|
||||
{{ key }}
|
||||
</div>
|
||||
</for>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© {{ current_year }} Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('keyFilter')?.addEventListener('input', function() {
|
||||
const filterValue = this.value.toLowerCase();
|
||||
const items = document.querySelectorAll('#keyList .key-item');
|
||||
|
||||
items.forEach(item => {
|
||||
const text = item.textContent.toLowerCase();
|
||||
item.style.display = text.includes(filterValue) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,69 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Routen-Übersicht</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes" class="active">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="routeFilter" placeholder="Routen filtern..." class="search-input">
|
||||
</div>
|
||||
|
||||
<table class="admin-table" id="routesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pfad</th>
|
||||
<th>Methode</th>
|
||||
<th>Controller</th>
|
||||
<th>Aktion</th>
|
||||
<th>Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($routes as $route): ?>
|
||||
<tr>
|
||||
<td><?= $route->path ?></td>
|
||||
<td><?= $route->method ?></td>
|
||||
<td><?= $route->controllerClass ?></td>
|
||||
<td><?= $route->methodName ?></td>
|
||||
<td><?= $route->name ?? '-' ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© <?= date('Y') ?> Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('routeFilter').addEventListener('input', function() {
|
||||
const filterValue = this.value.toLowerCase();
|
||||
const rows = document.querySelectorAll('#routesTable tbody tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
const text = row.textContent.toLowerCase();
|
||||
row.style.display = text.includes(filterValue) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,67 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Registrierte Dienste</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services" class="active">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="serviceFilter" placeholder="Dienste filtern..." class="search-input">
|
||||
<span class="services-count">{{ servicesCount }} Dienste insgesamt</span>
|
||||
</div>
|
||||
|
||||
<div class="service-list" id="serviceList">
|
||||
<for var="service" in="services">
|
||||
<div class="service-item">
|
||||
<div class="service-name">{{ service.name }}</div>
|
||||
<div class="service-category">
|
||||
<span class="category-badge">{{ service.category }}</span>
|
||||
<if condition="service.subCategory">
|
||||
<span class="subcategory-badge">{{ service.subCategory }}</span>
|
||||
</if>
|
||||
</div>
|
||||
</div>
|
||||
</for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© {{ date('Y') }} Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('serviceFilter').addEventListener('input', function() {
|
||||
const filterValue = this.value.toLowerCase();
|
||||
const items = document.querySelectorAll('#serviceList .service-item');
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
const text = item.textContent.toLowerCase();
|
||||
const isVisible = text.includes(filterValue);
|
||||
item.style.display = isVisible ? '' : 'none';
|
||||
if (isVisible) visibleCount++;
|
||||
});
|
||||
|
||||
document.querySelector('.services-count').textContent =
|
||||
visibleCount + ' von ' + {{ servicesCount }} + ' Diensten';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user