170 lines
6.7 KiB
HTML
170 lines
6.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MadBase Admin Dashboard</title>
|
|
<style>
|
|
body { font-family: system-ui, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
|
h1, h2 { border-bottom: 1px solid #ccc; padding-bottom: 10px; }
|
|
.card { border: 1px solid #eee; padding: 15px; margin-bottom: 15px; border-radius: 4px; }
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th, td { text-align: left; padding: 8px; border-bottom: 1px solid #eee; }
|
|
button { background: #ff4444; color: white; border: none; padding: 5px 10px; cursor: pointer; border-radius: 4px; }
|
|
button:hover { background: #cc0000; }
|
|
pre { background: #f5f5f5; padding: 10px; overflow: auto; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>MadBase Admin Dashboard</h1>
|
|
|
|
<div class="card">
|
|
<h2>Projects</h2>
|
|
<table id="projects-table">
|
|
<thead><tr><th>ID</th><th>Name</th><th>Status</th><th>Action</th></tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
<div style="margin-top: 10px;">
|
|
<input type="text" id="new-project-name" placeholder="New Project Name">
|
|
<button onclick="createProject()" style="background: #44cc44;">Create Project</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Features</h2>
|
|
<button onclick="testDB()" style="background: #0088cc;">Test DB Connection</button>
|
|
<button onclick="fetchBuckets()" style="background: #ffaa00;">List Storage Buckets</button>
|
|
<div id="feature-output" style="margin-top: 10px; padding: 10px; background: #eee; min-height: 50px;"></div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Users (Global)</h2>
|
|
<table id="users-table">
|
|
<thead><tr><th>ID</th><th>Email</th><th>Created At</th><th>Action</th></tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>System Metrics</h2>
|
|
<pre id="metrics-output">Loading...</pre>
|
|
</div>
|
|
|
|
<script>
|
|
const API_BASE = '/platform/v1';
|
|
|
|
async function testDB() {
|
|
// Check health
|
|
try {
|
|
const res = await fetch('/');
|
|
const text = await res.text();
|
|
document.getElementById('feature-output').innerHTML = `Gateway Status: ${text}`;
|
|
} catch (e) {
|
|
document.getElementById('feature-output').innerHTML = `<span style="color:red">Connection Failed</span>`;
|
|
}
|
|
}
|
|
|
|
async function fetchBuckets() {
|
|
// Needs Auth... but this is Admin Dashboard.
|
|
// Admin API doesn't expose Storage listing directly yet.
|
|
// We can add a proxy or just check health for now.
|
|
document.getElementById('feature-output').innerHTML = "Storage Browser: Requires authenticated user context (Not implemented in Admin UI yet)";
|
|
}
|
|
|
|
async function rotateKey(id) {
|
|
if (!confirm('Rotate keys for this project? Old keys will stop working.')) return;
|
|
try {
|
|
await fetch(`${API_BASE}/projects/${id}/keys`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({})
|
|
});
|
|
fetchProjects();
|
|
alert('Keys rotated!');
|
|
} catch (e) { alert('Error rotating keys'); }
|
|
}
|
|
|
|
async function fetchProjects() {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/projects`);
|
|
const projects = await res.json();
|
|
const tbody = document.querySelector('#projects-table tbody');
|
|
tbody.innerHTML = projects.map(p => `
|
|
<tr>
|
|
<td>${p.id}</td>
|
|
<td>${p.name}</td>
|
|
<td>${p.status}</td>
|
|
<td>
|
|
<button onclick="deleteProject('${p.id}')">Delete</button>
|
|
<button onclick="rotateKey('${p.id}')" style="background:orange;">Rotate Key</button>
|
|
</td>
|
|
</tr>
|
|
`).join('');
|
|
} catch (e) { console.error(e); }
|
|
}
|
|
|
|
async function createProject() {
|
|
const name = document.getElementById('new-project-name').value;
|
|
if (!name) return;
|
|
try {
|
|
await fetch(`${API_BASE}/projects`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name, owner_id: null })
|
|
});
|
|
document.getElementById('new-project-name').value = '';
|
|
fetchProjects();
|
|
} catch (e) { alert('Error creating project'); }
|
|
}
|
|
|
|
async function deleteProject(id) {
|
|
if (!confirm('Are you sure?')) return;
|
|
try {
|
|
await fetch(`${API_BASE}/projects/${id}`, { method: 'DELETE' });
|
|
fetchProjects();
|
|
} catch (e) { alert('Error deleting project'); }
|
|
}
|
|
|
|
async function fetchUsers() {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/users`);
|
|
const users = await res.json();
|
|
const tbody = document.querySelector('#users-table tbody');
|
|
tbody.innerHTML = users.map(u => `
|
|
<tr>
|
|
<td>${u.id}</td>
|
|
<td>${u.email}</td>
|
|
<td>${new Date(u.created_at).toLocaleString()}</td>
|
|
<td><button onclick="deleteUser('${u.id}')">Delete</button></td>
|
|
</tr>
|
|
`).join('');
|
|
} catch (e) { console.error(e); }
|
|
}
|
|
|
|
async function deleteUser(id) {
|
|
if (!confirm('Are you sure?')) return;
|
|
try {
|
|
await fetch(`${API_BASE}/users/${id}`, { method: 'DELETE' });
|
|
fetchUsers();
|
|
} catch (e) { alert('Error deleting user'); }
|
|
}
|
|
|
|
async function fetchMetrics() {
|
|
try {
|
|
const res = await fetch('/metrics');
|
|
const text = await res.text();
|
|
// Show only madbase metrics or summary
|
|
document.getElementById('metrics-output').textContent = text.split('\n').filter(l => !l.startsWith('#') && l.trim()).slice(0, 10).join('\n') + '\n...';
|
|
} catch (e) {
|
|
document.getElementById('metrics-output').textContent = 'Error loading metrics';
|
|
}
|
|
}
|
|
|
|
fetchProjects();
|
|
fetchUsers();
|
|
fetchMetrics();
|
|
setInterval(fetchMetrics, 5000);
|
|
</script>
|
|
</body>
|
|
</html>
|