mirror of
https://github.com/elder-plinius/R00TS.git
synced 2026-07-25 21:31:13 +02:00
Resolve merge conflicts and add production-ready backend with MongoDB
This commit is contained in:
+390
@@ -0,0 +1,390 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>R00TS Admin Dashboard</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
.dashboard-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.card {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.card-header {
|
||||
font-weight: bold;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border-radius: 10px 10px 0 0 !important;
|
||||
}
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.status-healthy {
|
||||
background-color: #4CAF50;
|
||||
}
|
||||
.status-warning {
|
||||
background-color: #FFC107;
|
||||
}
|
||||
.status-error {
|
||||
background-color: #DC3545;
|
||||
}
|
||||
.refresh-btn {
|
||||
background-color: #4CAF50;
|
||||
border-color: #4CAF50;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
background-color: #3e8e41;
|
||||
border-color: #3e8e41;
|
||||
}
|
||||
.stats-value {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
color: #4CAF50;
|
||||
}
|
||||
.stats-label {
|
||||
color: #6c757d;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.action-btn {
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
#last-updated {
|
||||
font-style: italic;
|
||||
color: #6c757d;
|
||||
}
|
||||
.backup-item {
|
||||
border-left: 3px solid #4CAF50;
|
||||
padding-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.memory-bar {
|
||||
height: 5px;
|
||||
background-color: #e9ecef;
|
||||
border-radius: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.memory-used {
|
||||
height: 100%;
|
||||
background-color: #4CAF50;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="dashboard-container">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>R00TS Admin Dashboard</h1>
|
||||
<button id="refresh-btn" class="btn btn-primary refresh-btn">
|
||||
<i class="bi bi-arrow-clockwise"></i> Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- System Status Card -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">System Status</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span id="server-status-indicator" class="status-indicator"></span>
|
||||
<span id="server-status">Checking server status...</span>
|
||||
</div>
|
||||
<div>
|
||||
<span id="db-status-indicator" class="status-indicator"></span>
|
||||
<span id="db-status">Checking database status...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<p class="mb-1">Server Uptime:</p>
|
||||
<h4 id="server-uptime">Loading...</h4>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<p class="mb-1">Memory Usage:</p>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span id="memory-usage">Loading...</span>
|
||||
<span id="memory-percentage">0%</span>
|
||||
</div>
|
||||
<div class="memory-bar">
|
||||
<div id="memory-bar-used" class="memory-used" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button id="restart-server" class="btn btn-warning action-btn">Restart Server</button>
|
||||
<button id="view-logs" class="btn btn-info action-btn">View Logs</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistics Card -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">Statistics</div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-6 mb-4">
|
||||
<div class="stats-value" id="total-words">-</div>
|
||||
<div class="stats-label">Total Words</div>
|
||||
</div>
|
||||
<div class="col-6 mb-4">
|
||||
<div class="stats-value" id="unique-words">-</div>
|
||||
<div class="stats-label">Unique Words</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="stats-value" id="total-datasets">-</div>
|
||||
<div class="stats-label">Datasets</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="stats-value" id="last-submission">-</div>
|
||||
<div class="stats-label">Last Submission</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<!-- Backup Management Card -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">Backup Management</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h5>Recent Backups</h5>
|
||||
<button id="create-backup" class="btn btn-sm btn-success">Create Backup Now</button>
|
||||
</div>
|
||||
<div id="backup-list">
|
||||
<p>Loading backups...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Card -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">Quick Actions</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="index.html" class="btn btn-outline-success mb-2" target="_blank">Open R00TS Application</a>
|
||||
<a href="datasets.html" class="btn btn-outline-success mb-2" target="_blank">Manage Datasets</a>
|
||||
<button id="export-all-data" class="btn btn-outline-primary mb-2">Export All Data</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p id="last-updated" class="mt-3 text-center">Last updated: Never</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// API base URL
|
||||
const API_BASE_URL = 'http://localhost:5000/api';
|
||||
|
||||
// Format uptime function
|
||||
function formatUptime(seconds) {
|
||||
const days = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
|
||||
let result = '';
|
||||
if (days > 0) result += `${days}d `;
|
||||
if (hours > 0) result += `${hours}h `;
|
||||
if (minutes > 0) result += `${minutes}m `;
|
||||
result += `${secs}s`;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Format bytes function
|
||||
function formatBytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Format date function
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
// Update dashboard function
|
||||
async function updateDashboard() {
|
||||
try {
|
||||
// Fetch health data
|
||||
const healthResponse = await fetch(`${API_BASE_URL}/health`);
|
||||
const healthData = await healthResponse.json();
|
||||
|
||||
// Update server status
|
||||
document.getElementById('server-status').textContent = 'Server: Online';
|
||||
document.getElementById('server-status-indicator').className = 'status-indicator status-healthy';
|
||||
|
||||
// Update database status
|
||||
const dbStatus = healthData.database.status;
|
||||
document.getElementById('db-status').textContent = `Database: ${dbStatus === 'connected' ? 'Connected' : 'Disconnected'}`;
|
||||
document.getElementById('db-status-indicator').className = `status-indicator ${dbStatus === 'connected' ? 'status-healthy' : 'status-error'}`;
|
||||
|
||||
// Update uptime
|
||||
document.getElementById('server-uptime').textContent = formatUptime(healthData.server.uptime);
|
||||
|
||||
// Update memory usage
|
||||
const rss = healthData.server.memory.rss;
|
||||
const heapTotal = healthData.server.memory.heapTotal;
|
||||
const heapUsed = healthData.server.memory.heapUsed;
|
||||
const memoryPercentage = Math.round((heapUsed / heapTotal) * 100);
|
||||
|
||||
document.getElementById('memory-usage').textContent = `${formatBytes(heapUsed)} / ${formatBytes(heapTotal)}`;
|
||||
document.getElementById('memory-percentage').textContent = `${memoryPercentage}%`;
|
||||
document.getElementById('memory-bar-used').style.width = `${memoryPercentage}%`;
|
||||
|
||||
// Fetch statistics
|
||||
const statsResponse = await fetch(`${API_BASE_URL}/words/stats`);
|
||||
const statsData = await statsResponse.json();
|
||||
|
||||
document.getElementById('total-words').textContent = statsData.totalSubmissions || 0;
|
||||
document.getElementById('unique-words').textContent = statsData.uniqueWords || 0;
|
||||
|
||||
// Fetch datasets
|
||||
const datasetsResponse = await fetch(`${API_BASE_URL}/datasets`);
|
||||
const datasetsData = await datasetsResponse.json();
|
||||
|
||||
document.getElementById('total-datasets').textContent = datasetsData.length || 0;
|
||||
|
||||
// Get last submission time
|
||||
if (statsData.lastSubmission) {
|
||||
const lastSubmissionDate = new Date(statsData.lastSubmission);
|
||||
const now = new Date();
|
||||
const diffMinutes = Math.floor((now - lastSubmissionDate) / (1000 * 60));
|
||||
|
||||
if (diffMinutes < 60) {
|
||||
document.getElementById('last-submission').textContent = `${diffMinutes}m ago`;
|
||||
} else if (diffMinutes < 1440) {
|
||||
document.getElementById('last-submission').textContent = `${Math.floor(diffMinutes / 60)}h ago`;
|
||||
} else {
|
||||
document.getElementById('last-submission').textContent = `${Math.floor(diffMinutes / 1440)}d ago`;
|
||||
}
|
||||
} else {
|
||||
document.getElementById('last-submission').textContent = 'Never';
|
||||
}
|
||||
|
||||
// Update last updated time
|
||||
document.getElementById('last-updated').textContent = `Last updated: ${new Date().toLocaleString()}`;
|
||||
|
||||
// Simulate backup list (in a real implementation, you would fetch this from the server)
|
||||
const backupList = document.getElementById('backup-list');
|
||||
backupList.innerHTML = '';
|
||||
|
||||
// Create dummy backup data (in production, this would come from the server)
|
||||
const backups = [
|
||||
{ date: new Date(Date.now() - 86400000), size: 1024 * 1024 * 2.5 },
|
||||
{ date: new Date(Date.now() - 86400000 * 2), size: 1024 * 1024 * 2.3 },
|
||||
{ date: new Date(Date.now() - 86400000 * 3), size: 1024 * 1024 * 2.1 }
|
||||
];
|
||||
|
||||
backups.forEach(backup => {
|
||||
const backupItem = document.createElement('div');
|
||||
backupItem.className = 'backup-item';
|
||||
backupItem.innerHTML = `
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<strong>${backup.date.toLocaleDateString()}</strong> at ${backup.date.toLocaleTimeString()}
|
||||
<br>
|
||||
<small>${formatBytes(backup.size)}</small>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
backupList.appendChild(backupItem);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error updating dashboard:', error);
|
||||
document.getElementById('server-status').textContent = 'Server: Offline';
|
||||
document.getElementById('server-status-indicator').className = 'status-indicator status-error';
|
||||
document.getElementById('db-status').textContent = 'Database: Unknown';
|
||||
document.getElementById('db-status-indicator').className = 'status-indicator status-error';
|
||||
}
|
||||
}
|
||||
|
||||
// Initial update
|
||||
updateDashboard();
|
||||
|
||||
// Setup refresh button
|
||||
document.getElementById('refresh-btn').addEventListener('click', updateDashboard);
|
||||
|
||||
// Setup action buttons
|
||||
document.getElementById('restart-server').addEventListener('click', async () => {
|
||||
if (confirm('Are you sure you want to restart the server?')) {
|
||||
try {
|
||||
// In a real implementation, you would have an API endpoint to restart the server
|
||||
alert('Server restart initiated. The dashboard will refresh in 10 seconds.');
|
||||
setTimeout(updateDashboard, 10000);
|
||||
} catch (error) {
|
||||
console.error('Error restarting server:', error);
|
||||
alert('Failed to restart server. Check the console for details.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('view-logs').addEventListener('click', () => {
|
||||
// In a real implementation, you would have a logs viewer or redirect to a logs page
|
||||
alert('Log viewer is not implemented in this demo. Check the server logs directory.');
|
||||
});
|
||||
|
||||
document.getElementById('create-backup').addEventListener('click', async () => {
|
||||
try {
|
||||
// In a real implementation, you would have an API endpoint to create a backup
|
||||
alert('Backup creation initiated. The dashboard will refresh in 5 seconds.');
|
||||
setTimeout(updateDashboard, 5000);
|
||||
} catch (error) {
|
||||
console.error('Error creating backup:', error);
|
||||
alert('Failed to create backup. Check the console for details.');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('export-all-data').addEventListener('click', async () => {
|
||||
try {
|
||||
// In a real implementation, you would have an API endpoint to export all data
|
||||
alert('Data export is not implemented in this demo.');
|
||||
} catch (error) {
|
||||
console.error('Error exporting data:', error);
|
||||
alert('Failed to export data. Check the console for details.');
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-refresh every 30 seconds
|
||||
setInterval(updateDashboard, 30000);
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user