580 lines
13 KiB
PHP
580 lines
13 KiB
PHP
<?php
|
|
// ======================================================
|
|
// API TESTER
|
|
// Modul für index.php
|
|
// ======================================================
|
|
|
|
// API Basis-Verzeichnis
|
|
$apiDir = realpath(__DIR__ . '/../api');
|
|
|
|
// Alle PHP APIs rekursiv finden
|
|
$apiOptions = [];
|
|
|
|
if ($apiDir !== false) {
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator(
|
|
$apiDir,
|
|
RecursiveDirectoryIterator::SKIP_DOTS
|
|
)
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
|
|
if (!$file->isFile()) {
|
|
continue;
|
|
}
|
|
|
|
if ($file->getExtension() !== 'php') {
|
|
continue;
|
|
}
|
|
|
|
// relativen Pfad erzeugen
|
|
$relativePath = str_replace(
|
|
$apiDir . DIRECTORY_SEPARATOR,
|
|
'',
|
|
$file->getPathname()
|
|
);
|
|
|
|
// Windows kompatibel
|
|
$relativePath = str_replace('\\', '/', $relativePath);
|
|
|
|
$apiOptions[] = 'api/' . $relativePath;
|
|
}
|
|
}
|
|
|
|
// sortieren
|
|
sort($apiOptions);
|
|
|
|
// Standard Token
|
|
$defaultToken = "15a437d128c918633e57dc7d18118a073b807644f21731ed5a7ebcf3eca9e242";
|
|
?>
|
|
|
|
<style>
|
|
/* ======================================================
|
|
API TESTER STYLES
|
|
====================================================== */
|
|
|
|
.api-tester-container {
|
|
width: 100%;
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
background-color: var(--card-bg);
|
|
padding: 25px;
|
|
border-radius: 12px;
|
|
border: 1px solid var(--card-border);
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.api-tester-container h2 {
|
|
margin-top: 0;
|
|
margin-bottom: 20px;
|
|
color: var(--btn-bg);
|
|
border-bottom: 1px solid var(--card-border);
|
|
padding-bottom: 12px;
|
|
display: block;
|
|
}
|
|
|
|
.tester-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 15px;
|
|
}
|
|
|
|
.tester-form-group {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.tester-form-group label {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
font-weight: bold;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.tester-input,
|
|
.tester-textarea,
|
|
select {
|
|
width: 100%;
|
|
padding: 10px;
|
|
background-color: var(--input-bg);
|
|
border: 1px solid var(--input-border);
|
|
color: var(--input-text);
|
|
border-radius: 6px;
|
|
box-sizing: border-box;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.tester-textarea {
|
|
min-height: 120px;
|
|
resize: vertical;
|
|
font-family: Consolas, monospace;
|
|
}
|
|
|
|
.tester-btn-send {
|
|
width: 100%;
|
|
background-color: var(--btn-bg);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 13px;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
transition: 0.2s;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.tester-btn-send:hover {
|
|
opacity: 0.9;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
#tester-response-container {
|
|
margin-top: 25px;
|
|
background-color: var(--input-bg);
|
|
border: 1px solid var(--card-border);
|
|
border-radius: 10px;
|
|
padding: 15px;
|
|
min-height: 100px;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
#tester-output-content {
|
|
white-space: pre-wrap;
|
|
font-family: Consolas, monospace;
|
|
font-size: 0.85rem;
|
|
margin: 0;
|
|
color: var(--text);
|
|
}
|
|
|
|
.tester-badge {
|
|
display: inline-block;
|
|
padding: 5px 10px;
|
|
border-radius: 5px;
|
|
font-size: 0.8rem;
|
|
font-weight: bold;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.tester-status-200 {
|
|
background-color: #198754;
|
|
color: white;
|
|
}
|
|
|
|
.tester-status-error {
|
|
background-color: #dc3545;
|
|
color: white;
|
|
}
|
|
|
|
.tester-status-warning {
|
|
background-color: #ffc107;
|
|
color: black;
|
|
}
|
|
|
|
.hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
.tester-row {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
|
|
.tester-checkbox {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
white-space: nowrap;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.tester-small-btn {
|
|
background: transparent;
|
|
border: 1px solid var(--input-border);
|
|
color: var(--text);
|
|
padding: 6px 10px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.tester-small-btn:hover {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
}
|
|
|
|
.tester-response-info {
|
|
margin-bottom: 12px;
|
|
font-size: 0.85rem;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
@media (max-width: 700px) {
|
|
|
|
.tester-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.tester-row {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div class="api-tester-container">
|
|
|
|
<h2>🛠️ API Tester</h2>
|
|
|
|
<!-- API Auswahl -->
|
|
<div class="tester-form-group">
|
|
<label for="endpoint">API Endpunkt</label>
|
|
|
|
<select id="endpoint" class="tester-input" onchange="checkEndpoint()">
|
|
|
|
<?php foreach ($apiOptions as $opt): ?>
|
|
|
|
<option value="<?= htmlspecialchars($opt) ?>">
|
|
<?= htmlspecialchars($opt) ?>
|
|
</option>
|
|
|
|
<?php endforeach; ?>
|
|
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Methode + Function -->
|
|
<div class="tester-grid">
|
|
|
|
<div class="tester-form-group">
|
|
<label for="method">HTTP Methode</label>
|
|
|
|
<select id="method" class="tester-input">
|
|
<option value="GET">GET</option>
|
|
<option value="POST">POST</option>
|
|
<option value="PUT">PUT</option>
|
|
<option value="DELETE">DELETE</option>
|
|
<option value="PATCH">PATCH</option>
|
|
<option value="OPTIONS">OPTIONS</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="tester-form-group" id="function-group">
|
|
<label for="function-header">Function Header</label>
|
|
|
|
<input type="text" id="function-header" class="tester-input" placeholder="z.B. IPs, Ueberstunden">
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Token -->
|
|
<div class="tester-form-group">
|
|
|
|
<label for="token">Authorization Token</label>
|
|
|
|
<div class="tester-row">
|
|
|
|
<input type="text" id="token" class="tester-input" value="<?= htmlspecialchars($defaultToken) ?>">
|
|
|
|
<label class="tester-checkbox">
|
|
<input type="checkbox" id="send-token" checked>
|
|
Mitsenden
|
|
</label>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Eigene Header -->
|
|
<div class="tester-form-group">
|
|
|
|
<label for="custom-headers">
|
|
Zusätzliche Header (JSON)
|
|
</label>
|
|
|
|
<textarea id="custom-headers" class="tester-textarea" placeholder='{
|
|
"X-Test": "123",
|
|
"Client": "Frontend"
|
|
}'></textarea>
|
|
|
|
</div>
|
|
|
|
<!-- JSON Body -->
|
|
<div class="tester-form-group" id="json-body-group">
|
|
|
|
<label for="json-body">
|
|
JSON Body
|
|
</label>
|
|
|
|
<textarea id="json-body" class="tester-textarea" placeholder='{
|
|
"test": true
|
|
}'></textarea>
|
|
|
|
</div>
|
|
|
|
<!-- Datei Upload -->
|
|
<div class="tester-form-group hidden" id="file-group">
|
|
|
|
<label for="files">Dateien Upload</label>
|
|
|
|
<input type="file" id="files" class="tester-input" multiple>
|
|
|
|
</div>
|
|
|
|
<!-- Buttons -->
|
|
<div class="tester-grid">
|
|
|
|
<button class="tester-btn-send" onclick="sendApiRequest()">
|
|
Anfrage senden 🚀
|
|
</button>
|
|
|
|
<button class="tester-btn-send" onclick="clearResponse()">
|
|
Ausgabe leeren 🧹
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<!-- Ausgabe -->
|
|
<div id="tester-response-container">
|
|
|
|
<div style="text-align:center; opacity:0.7; padding:15px;">
|
|
Bereit für Anfragen.
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
// ======================================================
|
|
// CHECK ENDPOINT
|
|
// ======================================================
|
|
|
|
function checkEndpoint() {
|
|
|
|
const endpoint = document.getElementById('endpoint').value;
|
|
|
|
const fileGroup = document.getElementById('file-group');
|
|
const functionGroup = document.getElementById('function-group');
|
|
const jsonBodyGroup = document.getElementById('json-body-group');
|
|
const methodSelect = document.getElementById('method');
|
|
|
|
// reset
|
|
fileGroup.classList.add('hidden');
|
|
functionGroup.classList.remove('hidden');
|
|
jsonBodyGroup.classList.remove('hidden');
|
|
|
|
// upload api
|
|
if (endpoint.includes('upload.php')) {
|
|
|
|
fileGroup.classList.remove('hidden');
|
|
jsonBodyGroup.classList.add('hidden');
|
|
|
|
methodSelect.value = 'POST';
|
|
}
|
|
|
|
// get api
|
|
if (endpoint.includes('get.php')) {
|
|
methodSelect.value = 'GET';
|
|
}
|
|
}
|
|
|
|
// ======================================================
|
|
// CLEAR RESPONSE
|
|
// ======================================================
|
|
|
|
function clearResponse() {
|
|
|
|
document.getElementById('tester-response-container').innerHTML = `
|
|
<div style="text-align:center; opacity:0.7; padding:15px;">
|
|
Ausgabe geleert.
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// ======================================================
|
|
// SEND REQUEST
|
|
// ======================================================
|
|
|
|
async function sendApiRequest() {
|
|
|
|
const endpoint = document.getElementById('endpoint').value;
|
|
const method = document.getElementById('method').value;
|
|
const token = document.getElementById('token').value;
|
|
const functionHeader = document.getElementById('function-header').value;
|
|
const customHeadersInput = document.getElementById('custom-headers').value;
|
|
const jsonBodyInput = document.getElementById('json-body').value;
|
|
const fileInput = document.getElementById('files');
|
|
const sendToken = document.getElementById('send-token').checked;
|
|
|
|
const responseContainer = document.getElementById('tester-response-container');
|
|
|
|
responseContainer.innerHTML = `
|
|
<div style="text-align:center; padding:15px;">
|
|
Anfrage läuft...
|
|
</div>
|
|
`;
|
|
|
|
let headers = {};
|
|
|
|
// Token
|
|
if (sendToken && token.trim() !== '') {
|
|
headers['Authorization'] = token;
|
|
}
|
|
|
|
// Function Header
|
|
if (functionHeader.trim() !== '') {
|
|
headers['Function'] = functionHeader;
|
|
}
|
|
|
|
// Custom Headers
|
|
if (customHeadersInput.trim() !== '') {
|
|
|
|
try {
|
|
|
|
const customHeaders = JSON.parse(customHeadersInput);
|
|
|
|
headers = {
|
|
...headers,
|
|
...customHeaders
|
|
};
|
|
|
|
} catch (e) {
|
|
|
|
responseContainer.innerHTML = `
|
|
<div style="color:#dc3545; padding:15px;">
|
|
Fehlerhafte Header JSON Struktur.
|
|
</div>
|
|
`;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
let options = {
|
|
method: method,
|
|
headers: headers
|
|
};
|
|
|
|
// Upload
|
|
if (endpoint.includes('upload.php') && fileInput.files.length > 0) {
|
|
|
|
const formData = new FormData();
|
|
|
|
for (let i = 0; i < fileInput.files.length; i++) {
|
|
formData.append('files[]', fileInput.files[i]);
|
|
}
|
|
|
|
options.body = formData;
|
|
|
|
} else {
|
|
|
|
// JSON Body
|
|
if (
|
|
['POST', 'PUT', 'PATCH', 'DELETE']
|
|
.includes(method) &&
|
|
jsonBodyInput.trim() !== ''
|
|
) {
|
|
|
|
try {
|
|
|
|
JSON.parse(jsonBodyInput);
|
|
|
|
headers['Content-Type'] = 'application/json';
|
|
|
|
options.body = jsonBodyInput;
|
|
|
|
} catch (e) {
|
|
|
|
responseContainer.innerHTML = `
|
|
<div style="color:#dc3545; padding:15px;">
|
|
Fehlerhafte JSON Body Struktur.
|
|
</div>
|
|
`;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
const startedAt = performance.now();
|
|
|
|
try {
|
|
|
|
const response = await fetch(endpoint, options);
|
|
|
|
const duration = (performance.now() - startedAt).toFixed(0);
|
|
|
|
const status = response.status;
|
|
|
|
const rawText = await response.text();
|
|
|
|
let resultText = rawText;
|
|
|
|
// JSON hübsch formatieren
|
|
try {
|
|
|
|
const json = JSON.parse(rawText);
|
|
|
|
resultText = JSON.stringify(json, null, 2);
|
|
|
|
} catch (e) {}
|
|
|
|
let badgeClass = 'tester-status-error';
|
|
|
|
if (status >= 200 && status < 300) {
|
|
badgeClass = 'tester-status-200';
|
|
} else if (status >= 300 && status < 400) {
|
|
badgeClass = 'tester-status-warning';
|
|
}
|
|
|
|
responseContainer.innerHTML = `
|
|
<div class="tester-badge ${badgeClass}">
|
|
Status: ${status}
|
|
</div>
|
|
|
|
<div class="tester-response-info">
|
|
⏱️ ${duration} ms
|
|
</div>
|
|
|
|
<button
|
|
class="tester-small-btn"
|
|
onclick="copyResponse()"
|
|
>
|
|
Antwort kopieren
|
|
</button>
|
|
|
|
<pre id="tester-output-content"></pre>
|
|
`;
|
|
|
|
document.getElementById('tester-output-content').textContent = resultText;
|
|
|
|
} catch (error) {
|
|
|
|
responseContainer.innerHTML = `
|
|
<div style="color:#dc3545; padding:15px;">
|
|
Fehler: ${error.message}
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// ======================================================
|
|
// COPY RESPONSE
|
|
// ======================================================
|
|
|
|
function copyResponse() {
|
|
|
|
const content = document.getElementById('tester-output-content');
|
|
|
|
if (!content) {
|
|
return;
|
|
}
|
|
|
|
navigator.clipboard.writeText(content.textContent);
|
|
|
|
alert('Antwort kopiert.');
|
|
}
|
|
|
|
// Initial
|
|
checkEndpoint();
|
|
</script>
|