<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - MCP Vulnerability Server</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.dashboard-container {
max-width: 1200px;
margin: 20px auto;
padding: 20px;
}
.nav-tabs {
margin-bottom: 20px;
}
.card {
margin-bottom: 20px;
}
.api-key-list {
margin-top: 20px;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="#">MCP Vulnerability Server</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="#" id="logoutBtn">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="dashboard-container">
<ul class="nav nav-tabs" id="dashboardTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="vulnerabilities-tab" data-bs-toggle="tab" data-bs-target="#vulnerabilities" type="button" role="tab">Vulnerabilities</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="api-keys-tab" data-bs-toggle="tab" data-bs-target="#api-keys" type="button" role="tab">API Keys</button>
</li>
</ul>
<div class="tab-content" id="dashboardTabContent">
<div class="tab-pane fade show active" id="vulnerabilities" role="tabpanel">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Vulnerability Collection</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<button class="btn btn-primary w-100" onclick="collectVulnerabilities('bsi')">Collect BSI Data</button>
</div>
<div class="col-md-4">
<button class="btn btn-primary w-100" onclick="collectVulnerabilities('nvd')">Collect NVD Data</button>
</div>
<div class="col-md-4">
<button class="btn btn-primary w-100" onclick="collectVulnerabilities('mitre')">Collect MITRE Data</button>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Recent Vulnerabilities</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>CVE ID</th>
<th>Title</th>
<th>Severity</th>
<th>Source</th>
<th>Last Modified</th>
</tr>
</thead>
<tbody id="vulnerabilitiesList">
<!-- Vulnerabilities will be listed here -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="api-keys" role="tabpanel">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">API Keys</h5>
<button class="btn btn-primary" onclick="showCreateKeyModal()">Create New Key</button>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Status</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="apiKeysList">
<!-- API keys will be listed here -->
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">API Key Usage</h5>
<div class="float-end">
<select id="keySelector" class="form-select d-inline-block w-auto" onchange="loadUsageGraph()">
<option value="">All Keys</option>
</select>
<input type="date" id="startDate" class="form-control d-inline-block w-auto ms-2" onchange="loadUsageGraph()">
<input type="date" id="endDate" class="form-control d-inline-block w-auto ms-2" onchange="loadUsageGraph()">
</div>
</div>
<div class="card-body">
<canvas id="usageGraph"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Create API Key Modal -->
<div class="modal fade" id="createKeyModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New API Key</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="createKeyForm">
<div class="mb-3">
<label for="keyName" class="form-label">Name</label>
<input type="text" class="form-control" id="keyName" required>
</div>
<div class="mb-3">
<label for="keyDescription" class="form-label">Description</label>
<textarea class="form-control" id="keyDescription"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="createApiKey()">Create</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Check if user is logged in
const token = localStorage.getItem('token');
if (!token) {
window.location.href = '/';
}
// Logout function
document.getElementById('logoutBtn').addEventListener('click', (e) => {
e.preventDefault();
localStorage.removeItem('token');
window.location.href = '/';
});
// Load vulnerabilities
async function loadVulnerabilities() {
try {
const response = await fetch('/vulnerabilities/', {
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
const tbody = document.getElementById('vulnerabilitiesList');
tbody.innerHTML = data.map(vuln => `
<tr>
<td>${vuln.cve_id}</td>
<td>${vuln.title}</td>
<td>${vuln.severity}</td>
<td>${vuln.source}</td>
<td>${new Date(vuln.last_modified_date).toLocaleString()}</td>
</tr>
`).join('');
} catch (error) {
console.error('Error loading vulnerabilities:', error);
}
}
let usageChart = null;
// Load API keys
async function loadApiKeys() {
const token = localStorage.getItem('token');
if (!token) {
console.error('No token found');
window.location.href = '/';
return;
}
try {
const response = await fetch('/api-keys', {
headers: {
'Authorization': `Bearer ${token}`
}
});
console.log('Load API keys response status:', response.status);
const responseData = await response.json();
console.log('Load API keys response data:', responseData);
if (!response.ok) {
throw new Error(responseData.detail || 'Failed to load API keys');
}
const tbody = document.getElementById('apiKeysList');
tbody.innerHTML = responseData.map(key => `
<tr>
<td>${key.name}</td>
<td>${key.description || 'No description'}</td>
<td>${key.is_active ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-danger">Revoked</span>'}</td>
<td>${new Date(key.created_at).toLocaleString()}</td>
<td>
<button class="btn btn-sm btn-danger" onclick="deleteApiKey(${key.id})">Delete</button>
${key.is_active ? `<button class="btn btn-sm btn-warning ms-1" onclick="revokeApiKey(${key.id})">Revoke</button>` : ''}
</td>
</tr>
`).join('');
} catch (error) {
console.error('Error loading API keys:', error);
alert(error.message || 'Error loading API keys');
}
}
// Collect vulnerabilities
async function collectVulnerabilities(source) {
try {
const response = await fetch(`/collect/${source}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
alert(data.message);
loadVulnerabilities();
} catch (error) {
console.error('Error collecting vulnerabilities:', error);
alert('Error collecting vulnerabilities');
}
}
// Show create key modal
function showCreateKeyModal() {
const modal = new bootstrap.Modal(document.getElementById('createKeyModal'));
modal.show();
}
// Create API key
async function createApiKey() {
const name = document.getElementById('keyName').value;
const description = document.getElementById('keyDescription').value;
if (!name) {
alert('Please enter a name for the API key');
return;
}
const token = localStorage.getItem('token');
if (!token) {
alert('You are not logged in. Please log in and try again.');
window.location.href = '/';
return;
}
const requestData = {
name: name,
description: description || null
};
console.log('Creating API key with data:', requestData);
console.log('Using token:', token);
try {
const response = await fetch('/api-keys', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
});
console.log('Response status:', response.status);
const responseData = await response.json();
console.log('Response data:', responseData);
if (!response.ok) {
throw new Error(responseData.detail || 'Failed to create API key');
}
alert(`API key created successfully!\nKey: ${responseData.key}\n\nPlease save this key as it won't be shown again.`);
bootstrap.Modal.getInstance(document.getElementById('createKeyModal')).hide();
document.getElementById('keyName').value = '';
document.getElementById('keyDescription').value = '';
loadApiKeys();
} catch (error) {
console.error('Error creating API key:', error);
alert(error.message || 'Error creating API key');
}
}
// Revoke API key
async function revokeApiKey(keyId) {
if (!confirm('Are you sure you want to revoke this API key?')) return;
try {
const response = await fetch(`/api-keys/${keyId}/revoke`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
if (response.ok) {
alert('API key revoked successfully');
loadApiKeys();
} else {
alert(data.detail || 'Error revoking API key');
}
} catch (error) {
console.error('Error revoking API key:', error);
alert('Error revoking API key');
}
}
// Delete API key
async function deleteApiKey(keyId) {
if (!confirm('Are you sure you want to delete this API key?')) return;
try {
const response = await fetch(`/api-keys/${keyId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
if (response.ok) {
alert('API key deleted successfully');
loadApiKeys();
} else {
alert(data.detail || 'Error deleting API key');
}
} catch (error) {
console.error('Error deleting API key:', error);
alert('Error deleting API key');
}
}
// Load usage graph
async function loadUsageGraph() {
const keyId = document.getElementById('keySelector').value;
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
try {
let url = '/api-keys/usage?';
if (keyId) url += `key_id=${keyId}&`;
if (startDate) url += `start_date=${startDate}&`;
if (endDate) url += `end_date=${endDate}`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
if (usageChart) {
usageChart.destroy();
}
const ctx = document.getElementById('usageGraph').getContext('2d');
usageChart = new Chart(ctx, {
type: 'line',
data: {
labels: data.daily_usage.map(d => d.date),
datasets: [
{
label: 'Total Requests',
data: data.daily_usage.map(d => d.requests),
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
},
{
label: 'Successful Requests',
data: data.daily_usage.map(d => d.successful_requests),
borderColor: 'rgb(54, 162, 235)',
tension: 0.1
},
{
label: 'Failed Requests',
data: data.daily_usage.map(d => d.failed_requests),
borderColor: 'rgb(255, 99, 132)',
tension: 0.1
}
]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
} catch (error) {
console.error('Error loading usage graph:', error);
}
}
// Set default date range
const today = new Date();
const thirtyDaysAgo = new Date(today);
thirtyDaysAgo.setDate(today.getDate() - 30);
document.getElementById('startDate').value = thirtyDaysAgo.toISOString().split('T')[0];
document.getElementById('endDate').value = today.toISOString().split('T')[0];
// Load initial data
loadVulnerabilities();
loadApiKeys();
loadUsageGraph();
</script>
</body>
</html>