|
|
|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Générateur de requêtes pour gabaohub.alwaysdata.net</title> |
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> |
|
<style> |
|
.status-badge { |
|
display: inline-block; |
|
padding: 0.25em 0.6em; |
|
font-size: 75%; |
|
font-weight: 700; |
|
line-height: 1; |
|
text-align: center; |
|
white-space: nowrap; |
|
vertical-align: baseline; |
|
border-radius: 0.25rem; |
|
} |
|
.status-pending { background-color: #6c757d; color: white; } |
|
.status-in_progress { background-color: #17a2b8; color: white; } |
|
.status-success { background-color: #28a745; color: white; } |
|
.status-failed { background-color: #dc3545; color: white; } |
|
.refresh-icon { cursor: pointer; } |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container mt-4"> |
|
<h1 class="mb-4">Générateur de requêtes pour gabaohub.alwaysdata.net</h1> |
|
|
|
<div class="card mb-4"> |
|
<div class="card-header"> |
|
Contrôles |
|
</div> |
|
<div class="card-body"> |
|
<form action="/start" method="post" class="mb-3"> |
|
<div class="row mb-3"> |
|
<div class="col-md-6"> |
|
<label for="num_requests" class="form-label">Nombre de requêtes à envoyer:</label> |
|
<input type="number" class="form-control" id="num_requests" name="num_requests" min="1" value="1000"> |
|
</div> |
|
<div class="col-md-6"> |
|
<label for="concurrency" class="form-label">Niveau de concurrence:</label> |
|
<input type="number" class="form-control" id="concurrency" name="concurrency" min="1" max="1000" value="100"> |
|
</div> |
|
</div> |
|
<button type="submit" class="btn btn-primary" id="start-button">Démarrer</button> |
|
</form> |
|
|
|
<div class="d-flex"> |
|
<form action="/stop" method="post" class="me-2"> |
|
<button type="submit" class="btn btn-warning" id="stop-button">Arrêter</button> |
|
</form> |
|
<form action="/reset" method="post"> |
|
<button type="submit" class="btn btn-danger" id="reset-button">Réinitialiser</button> |
|
</form> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="card mb-4"> |
|
<div class="card-header d-flex justify-content-between align-items-center"> |
|
<span>Progression</span> |
|
<span class="refresh-icon" onclick="updateProgress()">🔄</span> |
|
</div> |
|
<div class="card-body"> |
|
<div class="progress mb-3"> |
|
<div id="progress-bar" class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> |
|
</div> |
|
<p id="progress-text">0 / 0 requêtes traitées (0%)</p> |
|
<p id="status-text">État: Inactif</p> |
|
|
|
<div class="row mt-4"> |
|
<div class="col-md-4"> |
|
<div class="card bg-success text-white"> |
|
<div class="card-body"> |
|
<h5 class="card-title">Réussies</h5> |
|
<h2 id="success-count">0</h2> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="col-md-4"> |
|
<div class="card bg-danger text-white"> |
|
<div class="card-body"> |
|
<h5 class="card-title">Échouées</h5> |
|
<h2 id="failed-count">0</h2> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="col-md-4"> |
|
<div class="card bg-secondary text-white"> |
|
<div class="card-body"> |
|
<h5 class="card-title">En attente</h5> |
|
<h2 id="pending-count">0</h2> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="card mb-4"> |
|
<div class="card-header d-flex justify-content-between align-items-center"> |
|
<span>Statistiques</span> |
|
<span class="refresh-icon" onclick="updateStats()">🔄</span> |
|
</div> |
|
<div class="card-body" id="stats-container"> |
|
<p>Aucune statistique disponible.</p> |
|
</div> |
|
</div> |
|
|
|
<div class="card"> |
|
<div class="card-header d-flex justify-content-between align-items-center"> |
|
<span>Dernières requêtes</span> |
|
<span class="refresh-icon" onclick="updateRequests()">🔄</span> |
|
</div> |
|
<div class="card-body"> |
|
<div class="table-responsive"> |
|
<table class="table table-striped"> |
|
<thead> |
|
<tr> |
|
<th>#</th> |
|
<th>URL</th> |
|
<th>Statut</th> |
|
<th>Code</th> |
|
<th>Temps (s)</th> |
|
<th>Heure</th> |
|
</tr> |
|
</thead> |
|
<tbody id="requests-table"> |
|
<tr> |
|
<td colspan="6" class="text-center">Chargement des données...</td> |
|
</tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> |
|
<script> |
|
let isPolling = false; |
|
let pollingInterval; |
|
|
|
window.onload = function() { |
|
updateProgress(); |
|
updateRequests(); |
|
updateStats(); |
|
|
|
pollingInterval = setInterval(() => { |
|
if (isPolling) { |
|
updateProgress(); |
|
updateStats(); |
|
} |
|
}, 5000); |
|
}; |
|
|
|
function updateProgress() { |
|
fetch('/status') |
|
.then(response => response.json()) |
|
.then(data => { |
|
const percentage = data.total > 0 ? (data.progress / data.total * 100) : 0; |
|
document.getElementById('progress-bar').style.width = percentage + '%'; |
|
document.getElementById('progress-bar').setAttribute('aria-valuenow', percentage); |
|
|
|
document.getElementById('progress-text').textContent = |
|
`${data.progress} / ${data.total} requêtes traitées (${percentage.toFixed(1)}%)`; |
|
document.getElementById('status-text').textContent = |
|
`État: ${data.requests_in_progress ? 'En cours' : 'Inactif'}`; |
|
|
|
document.getElementById('success-count').textContent = data.success_count; |
|
document.getElementById('failed-count').textContent = data.failed_count; |
|
document.getElementById('pending-count').textContent = data.pending_count; |
|
|
|
isPolling = data.requests_in_progress; |
|
document.getElementById('start-button').disabled = data.requests_in_progress; |
|
document.getElementById('stop-button').disabled = !data.requests_in_progress; |
|
document.getElementById('reset-button').disabled = data.requests_in_progress; |
|
}) |
|
.catch(error => { |
|
console.error('Erreur lors de la récupération de la progression:', error); |
|
}); |
|
} |
|
|
|
function updateRequests() { |
|
fetch('/progress') |
|
.then(response => response.json()) |
|
.then(data => { |
|
const tableBody = document.getElementById('requests-table'); |
|
tableBody.innerHTML = ''; |
|
|
|
if (data.requests.length === 0) { |
|
tableBody.innerHTML = '<tr><td colspan="6" class="text-center">Aucune requête effectuée.</td></tr>'; |
|
return; |
|
} |
|
|
|
data.requests.forEach((req, index) => { |
|
const row = document.createElement('tr'); |
|
const statusClass = `status-${req.status || 'pending'}`; |
|
|
|
row.innerHTML = ` |
|
<td>${index + 1}</td> |
|
<td>${req.url || 'N/A'}</td> |
|
<td><span class="status-badge ${statusClass}">${req.status || 'pending'}</span></td> |
|
<td>${req.status_code || '-'}</td> |
|
<td>${req.response_time || '-'}</td> |
|
<td>${req.end_time || req.start_time || '-'}</td> |
|
`; |
|
|
|
tableBody.appendChild(row); |
|
}); |
|
}) |
|
.catch(error => { |
|
console.error('Erreur lors de la récupération des requêtes:', error); |
|
}); |
|
} |
|
|
|
function updateStats() { |
|
fetch('/stats') |
|
.then(response => response.json()) |
|
.then(data => { |
|
const statsContainer = document.getElementById('stats-container'); |
|
|
|
let statusCodeHtml = '<div class="mt-3"><h5>Codes de statut</h5>'; |
|
if (Object.keys(data.status_codes).length > 0) { |
|
statusCodeHtml += '<div class="row">'; |
|
for (const [code, count] of Object.entries(data.status_codes)) { |
|
const colorClass = code.startsWith('2') ? 'success' : |
|
code.startsWith('3') ? 'info' : |
|
code.startsWith('4') ? 'warning' : 'danger'; |
|
statusCodeHtml += ` |
|
<div class="col-md-3 mb-2"> |
|
<div class="card bg-${colorClass} text-white"> |
|
<div class="card-body p-2 text-center"> |
|
<h5 class="card-title">${code}</h5> |
|
<p class="card-text">${count} requêtes</p> |
|
</div> |
|
</div> |
|
</div> |
|
`; |
|
} |
|
statusCodeHtml += '</div>'; |
|
} else { |
|
statusCodeHtml += '<p>Aucun code de statut disponible.</p>'; |
|
} |
|
statusCodeHtml += '</div>'; |
|
|
|
let errorTypesHtml = '<div class="mt-3"><h5>Types d\'erreurs</h5>'; |
|
if (Object.keys(data.error_types).length > 0) { |
|
errorTypesHtml += '<ul class="list-group">'; |
|
for (const [type, count] of Object.entries(data.error_types)) { |
|
errorTypesHtml += ` |
|
<li class="list-group-item d-flex justify-content-between align-items-center"> |
|
${type} |
|
<span class="badge bg-danger rounded-pill">${count}</span> |
|
</li> |
|
`; |
|
} |
|
errorTypesHtml += '</ul>'; |
|
} else { |
|
errorTypesHtml += '<p>Aucune erreur disponible.</p>'; |
|
} |
|
errorTypesHtml += '</div>'; |
|
|
|
statsContainer.innerHTML = ` |
|
<div class="row"> |
|
<div class="col-md-6"> |
|
<h5>Résumé</h5> |
|
<ul class="list-group"> |
|
<li class="list-group-item d-flex justify-content-between align-items-center"> |
|
Requêtes totales |
|
<span class="badge bg-primary rounded-pill">${data.total_requests}</span> |
|
</li> |
|
<li class="list-group-item d-flex justify-content-between align-items-center"> |
|
Requêtes réussies |
|
<span class="badge bg-success rounded-pill">${data.successful_requests}</span> |
|
</li> |
|
<li class="list-group-item d-flex justify-content-between align-items-center"> |
|
Requêtes échouées |
|
<span class="badge bg-danger rounded-pill">${data.failed_requests}</span> |
|
</li> |
|
</ul> |
|
</div> |
|
<div class="col-md-6"> |
|
<h5>Performance</h5> |
|
<div class="card"> |
|
<div class="card-body"> |
|
<h3>${data.avg_response_time.toFixed(3)} s</h3> |
|
<p class="text-muted">Temps de réponse moyen</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
${statusCodeHtml} |
|
${errorTypesHtml} |
|
`; |
|
}) |
|
.catch(error => { |
|
console.error('Erreur lors de la récupération des statistiques:', error); |
|
}); |
|
} |
|
</script> |
|
</body> |
|
</html> |