496 lines
16 KiB
HTML
496 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Navigation API Tester</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
background: #f5f5f5;
|
|
padding: 20px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
h1 {
|
|
color: #333;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.intro {
|
|
background: #fff;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.config {
|
|
background: #fff;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.config input {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.endpoint-group {
|
|
background: #fff;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 15px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.endpoint-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.endpoint-title {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.endpoint-method {
|
|
display: inline-block;
|
|
padding: 4px 12px;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
font-weight: bold;
|
|
color: #fff;
|
|
}
|
|
|
|
.method-get {
|
|
background: #61affe;
|
|
}
|
|
|
|
.method-post {
|
|
background: #49cc90;
|
|
}
|
|
|
|
.endpoint-url {
|
|
background: #f7f7f7;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 14px;
|
|
margin-bottom: 15px;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.endpoint-description {
|
|
color: #666;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.test-button {
|
|
background: #4CAF50;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
transition: background 0.3s;
|
|
}
|
|
|
|
.test-button:hover {
|
|
background: #45a049;
|
|
}
|
|
|
|
.test-button:disabled {
|
|
background: #ccc;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.result {
|
|
margin-top: 15px;
|
|
padding: 15px;
|
|
border-radius: 4px;
|
|
display: none;
|
|
}
|
|
|
|
.result.success {
|
|
background: #d4edda;
|
|
border: 1px solid #c3e6cb;
|
|
color: #155724;
|
|
}
|
|
|
|
.result.error {
|
|
background: #f8d7da;
|
|
border: 1px solid #f5c6cb;
|
|
color: #721c24;
|
|
}
|
|
|
|
.result-header {
|
|
font-weight: bold;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.result-meta {
|
|
margin-bottom: 10px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.result-data {
|
|
background: #f7f7f7;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 12px;
|
|
max-height: 400px;
|
|
overflow: auto;
|
|
white-space: pre-wrap;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
.loading {
|
|
display: inline-block;
|
|
width: 20px;
|
|
height: 20px;
|
|
border: 3px solid #f3f3f3;
|
|
border-top: 3px solid #4CAF50;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin-left: 10px;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.test-all {
|
|
background: #2196F3;
|
|
color: white;
|
|
border: none;
|
|
padding: 15px 30px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
margin-bottom: 20px;
|
|
transition: background 0.3s;
|
|
display: block;
|
|
width: 100%;
|
|
}
|
|
|
|
.test-all:hover {
|
|
background: #0b7dda;
|
|
}
|
|
|
|
.summary {
|
|
background: #fff;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-top: 20px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
display: none;
|
|
}
|
|
|
|
.summary h2 {
|
|
margin-bottom: 15px;
|
|
color: #333;
|
|
}
|
|
|
|
.summary-stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.stat-card {
|
|
padding: 15px;
|
|
border-radius: 4px;
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-card.total {
|
|
background: #e3f2fd;
|
|
}
|
|
|
|
.stat-card.success {
|
|
background: #d4edda;
|
|
}
|
|
|
|
.stat-card.error {
|
|
background: #f8d7da;
|
|
}
|
|
|
|
.stat-number {
|
|
font-size: 32px;
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🗺️ Navigation API Tester</h1>
|
|
|
|
<div class="intro">
|
|
<p>Dieses Tool testet alle verfügbaren Endpunkte der Navigation API und zeigt die Ergebnisse in einer strukturierten Form.</p>
|
|
</div>
|
|
|
|
<div class="config">
|
|
<label for="baseUrl"><strong>Basis-URL:</strong></label>
|
|
<input type="text" id="baseUrl" value="http://localhost/api/navigation" placeholder="http://localhost/api/navigation">
|
|
</div>
|
|
|
|
<button class="test-all" onclick="testAllEndpoints()">Alle Endpunkte testen</button>
|
|
|
|
<!-- Endpoint 1: Kompletter Navigationsbaum -->
|
|
<div class="endpoint-group">
|
|
<div class="endpoint-header">
|
|
<div class="endpoint-title">Kompletter Navigationsbaum</div>
|
|
<span class="endpoint-method method-get">GET</span>
|
|
</div>
|
|
<div class="endpoint-url">/tree</div>
|
|
<div class="endpoint-description">
|
|
Gibt den kompletten hierarchischen Navigationsbaum mit allen Seiten zurück.
|
|
</div>
|
|
<button class="test-button" onclick="testEndpoint('tree', 'GET')">Testen</button>
|
|
<div class="result" id="result-tree"></div>
|
|
</div>
|
|
|
|
<!-- Endpoint 2: Aktive Navigationspunkte -->
|
|
<div class="endpoint-group">
|
|
<div class="endpoint-header">
|
|
<div class="endpoint-title">Nur aktive Navigationspunkte</div>
|
|
<span class="endpoint-method method-get">GET</span>
|
|
</div>
|
|
<div class="endpoint-url">/tree/active</div>
|
|
<div class="endpoint-description">
|
|
Gibt nur aktive Seiten zurück (status = 1 und show_in_navi = 1).
|
|
</div>
|
|
<button class="test-button" onclick="testEndpoint('tree/active', 'GET')">Testen</button>
|
|
<div class="result" id="result-tree-active"></div>
|
|
</div>
|
|
|
|
<!-- Endpoint 3: Teilbaum -->
|
|
<div class="endpoint-group">
|
|
<div class="endpoint-header">
|
|
<div class="endpoint-title">Teilbaum ab Root-ID</div>
|
|
<span class="endpoint-method method-get">GET</span>
|
|
</div>
|
|
<div class="endpoint-url">/tree/{rootId}</div>
|
|
<div class="endpoint-description">
|
|
Gibt einen Teilbaum zurück, beginnend mit der angegebenen Page-ID.<br>
|
|
<input type="number" id="subtreeId" placeholder="Root Page ID" value="1" style="margin-top: 10px; padding: 8px; width: 200px;">
|
|
</div>
|
|
<button class="test-button" onclick="testEndpoint('tree/' + document.getElementById('subtreeId').value, 'GET')">Testen</button>
|
|
<div class="result" id="result-tree-subtree"></div>
|
|
</div>
|
|
|
|
<!-- Endpoint 4: Flache Liste -->
|
|
<div class="endpoint-group">
|
|
<div class="endpoint-header">
|
|
<div class="endpoint-title">Flache Liste</div>
|
|
<span class="endpoint-method method-get">GET</span>
|
|
</div>
|
|
<div class="endpoint-url">/flat</div>
|
|
<div class="endpoint-description">
|
|
Gibt alle Navigationspunkte als flache Liste zurück (ohne parent-child Beziehung).
|
|
</div>
|
|
<button class="test-button" onclick="testEndpoint('flat', 'GET')">Testen</button>
|
|
<div class="result" id="result-flat"></div>
|
|
</div>
|
|
|
|
<!-- Endpoint 5: Breadcrumb -->
|
|
<div class="endpoint-group">
|
|
<div class="endpoint-header">
|
|
<div class="endpoint-title">Breadcrumb-Pfad</div>
|
|
<span class="endpoint-method method-get">GET</span>
|
|
</div>
|
|
<div class="endpoint-url">/breadcrumb/{pageId}</div>
|
|
<div class="endpoint-description">
|
|
Gibt den Breadcrumb-Pfad für eine bestimmte Seite zurück.<br>
|
|
<input type="number" id="breadcrumbId" placeholder="Page ID" value="1" style="margin-top: 10px; padding: 8px; width: 200px;">
|
|
</div>
|
|
<button class="test-button" onclick="testEndpoint('breadcrumb/' + document.getElementById('breadcrumbId').value, 'GET')">Testen</button>
|
|
<div class="result" id="result-breadcrumb"></div>
|
|
</div>
|
|
|
|
<!-- Endpoint 6: Cache leeren -->
|
|
<div class="endpoint-group">
|
|
<div class="endpoint-header">
|
|
<div class="endpoint-title">Cache leeren</div>
|
|
<span class="endpoint-method method-post">POST</span>
|
|
</div>
|
|
<div class="endpoint-url">/cache/clear</div>
|
|
<div class="endpoint-description">
|
|
Löscht den kompletten Navigation-Cache.
|
|
</div>
|
|
<button class="test-button" onclick="testEndpoint('cache/clear', 'POST')">Testen</button>
|
|
<div class="result" id="result-cache-clear"></div>
|
|
</div>
|
|
|
|
<!-- Zusammenfassung -->
|
|
<div class="summary" id="summary">
|
|
<h2>Test-Zusammenfassung</h2>
|
|
<div class="summary-stats">
|
|
<div class="stat-card total">
|
|
<div class="stat-number" id="stat-total">0</div>
|
|
<div class="stat-label">Gesamt</div>
|
|
</div>
|
|
<div class="stat-card success">
|
|
<div class="stat-number" id="stat-success">0</div>
|
|
<div class="stat-label">Erfolgreich</div>
|
|
</div>
|
|
<div class="stat-card error">
|
|
<div class="stat-number" id="stat-error">0</div>
|
|
<div class="stat-label">Fehlgeschlagen</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let testResults = {};
|
|
|
|
function getBaseUrl() {
|
|
return document.getElementById('baseUrl').value.replace(/\/$/, '');
|
|
}
|
|
|
|
async function testEndpoint(endpoint, method = 'GET') {
|
|
const resultId = 'result-' + endpoint.replace(/\//g, '-').replace(/\d+/g, 'subtree');
|
|
const resultDiv = document.getElementById(resultId);
|
|
|
|
resultDiv.style.display = 'block';
|
|
resultDiv.className = 'result';
|
|
resultDiv.innerHTML = '<div class="result-header">Teste...<span class="loading"></span></div>';
|
|
|
|
const url = getBaseUrl() + '/' + endpoint;
|
|
|
|
try {
|
|
const startTime = performance.now();
|
|
const response = await fetch(url, {
|
|
method: method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json'
|
|
}
|
|
});
|
|
const endTime = performance.now();
|
|
const duration = Math.round(endTime - startTime);
|
|
|
|
const data = await response.json();
|
|
|
|
testResults[endpoint] = response.ok;
|
|
|
|
if (response.ok) {
|
|
resultDiv.className = 'result success';
|
|
let html = '<div class="result-header">✓ Erfolgreich</div>';
|
|
html += '<div class="result-meta">';
|
|
html += `<strong>HTTP Status:</strong> ${response.status}<br>`;
|
|
html += `<strong>Dauer:</strong> ${duration}ms<br>`;
|
|
|
|
if (data.meta) {
|
|
html += '<strong>Metadaten:</strong><br>';
|
|
for (const [key, value] of Object.entries(data.meta)) {
|
|
html += ` ${key}: ${value}<br>`;
|
|
}
|
|
}
|
|
|
|
html += '</div>';
|
|
|
|
if (data.data) {
|
|
html += '<div class="result-data">';
|
|
html += JSON.stringify(data.data, null, 2);
|
|
html += '</div>';
|
|
}
|
|
|
|
resultDiv.innerHTML = html;
|
|
} else {
|
|
resultDiv.className = 'result error';
|
|
let html = '<div class="result-header">✗ Fehler</div>';
|
|
html += '<div class="result-meta">';
|
|
html += `<strong>HTTP Status:</strong> ${response.status}<br>`;
|
|
html += `<strong>Dauer:</strong> ${duration}ms<br>`;
|
|
if (data.error) {
|
|
html += `<strong>Fehlermeldung:</strong> ${data.error}`;
|
|
}
|
|
html += '</div>';
|
|
resultDiv.innerHTML = html;
|
|
}
|
|
} catch (error) {
|
|
testResults[endpoint] = false;
|
|
resultDiv.className = 'result error';
|
|
resultDiv.innerHTML = `
|
|
<div class="result-header">✗ Fehler</div>
|
|
<div class="result-meta">
|
|
<strong>Fehlermeldung:</strong> ${error.message}<br>
|
|
<br>
|
|
Mögliche Ursachen:<br>
|
|
• Server läuft nicht<br>
|
|
• CORS-Probleme<br>
|
|
• Falsche Base-URL<br>
|
|
• Netzwerkfehler
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
async function testAllEndpoints() {
|
|
testResults = {};
|
|
|
|
const endpoints = [
|
|
{ path: 'tree', method: 'GET' },
|
|
{ path: 'tree/active', method: 'GET' },
|
|
{ path: 'tree/1', method: 'GET' },
|
|
{ path: 'flat', method: 'GET' },
|
|
{ path: 'breadcrumb/1', method: 'GET' },
|
|
{ path: 'cache/clear', method: 'POST' }
|
|
];
|
|
|
|
for (const endpoint of endpoints) {
|
|
await testEndpoint(endpoint.path, endpoint.method);
|
|
await new Promise(resolve => setTimeout(resolve, 500)); // Kurze Pause zwischen Tests
|
|
}
|
|
|
|
updateSummary();
|
|
}
|
|
|
|
function updateSummary() {
|
|
const total = Object.keys(testResults).length;
|
|
const success = Object.values(testResults).filter(r => r === true).length;
|
|
const error = total - success;
|
|
|
|
document.getElementById('stat-total').textContent = total;
|
|
document.getElementById('stat-success').textContent = success;
|
|
document.getElementById('stat-error').textContent = error;
|
|
document.getElementById('summary').style.display = 'block';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|