Files
meshcore-bot/modules/web_viewer/templates/cache.html
T

332 lines
11 KiB
HTML

{% extends "base.html" %}
{% block title %}Database Information - MeshCore Bot Data Viewer{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<h1 class="mb-4">
<i class="fas fa-database"></i> Database Information
</h1>
</div>
</div>
<!-- Database Overview -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card">
<div class="card-header">
<i class="fas fa-table"></i> Total Tables
</div>
<div class="card-body">
<h3 id="total-tables">0</h3>
<small class="text-muted">Database tables</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<i class="fas fa-list"></i> Total Records
</div>
<div class="card-body">
<h3 id="total-records">0</h3>
<small class="text-muted">All records</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<i class="fas fa-clock"></i> Last Updated
</div>
<div class="card-body">
<h6 id="last-updated">-</h6>
<small class="text-muted">Database activity</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<i class="fas fa-hdd"></i> Database Size
</div>
<div class="card-body">
<h6 id="db-size">-</h6>
<small class="text-muted">Storage used</small>
</div>
</div>
</div>
</div>
<!-- Database Controls -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<i class="fas fa-cog"></i> Database Operations
</div>
<div class="card-body">
<div class="btn-group" role="group">
<button class="btn btn-primary" id="refresh-db">
<i class="fas fa-sync"></i> Refresh
</button>
<button class="btn btn-info" id="export-db">
<i class="fas fa-download"></i> Export
</button>
<button class="btn btn-warning" id="optimize-db">
<i class="fas fa-tools"></i> Optimize
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Database Tables -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<i class="fas fa-list"></i> Database Tables
</div>
<div class="card-body">
<div id="tables-info">
<div class="loading">Loading database information...</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script nonce="{{ g.csp_nonce }}">
class DatabaseInfoManager {
constructor() {
this.dbData = {};
this.initializeDatabase();
}
async initializeDatabase() {
await this.loadDatabaseData();
this.setupEventHandlers();
this.updateStatistics();
this.renderTablesInfo();
}
async loadDatabaseData() {
try {
const response = await fetch('/api/database');
const data = await response.json();
if (data.error) {
this.showError('Failed to load database data: ' + data.error);
return;
}
this.dbData = data;
} catch (error) {
console.error('Error loading database data:', error);
this.showError('Failed to load database data: ' + error.message);
}
}
setupEventHandlers() {
document.getElementById('refresh-db').addEventListener('click', () => {
this.loadDatabaseData().then(() => {
this.updateStatistics();
this.renderTablesInfo();
});
});
document.getElementById('export-db').addEventListener('click', () => {
this.exportDatabase();
});
document.getElementById('optimize-db').addEventListener('click', () => {
this.optimizeDatabase();
});
}
updateStatistics() {
document.getElementById('total-tables').textContent = this.dbData.total_tables || 0;
document.getElementById('total-records').textContent = this.dbData.total_records || 0;
document.getElementById('last-updated').textContent = this.dbData.last_updated || '-';
document.getElementById('db-size').textContent = this.dbData.db_size || '-';
}
renderTablesInfo() {
const infoElement = document.getElementById('tables-info');
infoElement.replaceChildren();
if (!this.dbData.tables || this.dbData.tables.length === 0) {
const empty = document.createElement('div');
empty.className = 'text-muted';
empty.textContent = 'No tables found';
infoElement.appendChild(empty);
return;
}
const makeTextElement = (tag, value, classes = '') => {
const element = document.createElement(tag);
element.className = classes;
element.textContent = String(value ?? '');
return element;
};
this.dbData.tables.forEach(table => {
const row = document.createElement('div');
row.className = 'row mb-3 p-3 border rounded';
const identity = document.createElement('div');
identity.className = 'col-md-4';
identity.append(
makeTextElement('h6', table.name, 'mb-1'),
makeTextElement('small', table.description || 'Database table', 'text-muted'),
);
const metric = (value, label, width, valueClass = 'h6 mb-0') => {
const column = document.createElement('div');
column.className = width;
const content = document.createElement('div');
content.className = 'text-center';
content.append(
makeTextElement('div', value, valueClass),
makeTextElement('small', label, 'text-muted'),
);
column.appendChild(content);
return column;
};
const statusColumn = document.createElement('div');
statusColumn.className = 'col-md-3 text-center';
statusColumn.append(
makeTextElement(
'span',
this.getTableStatus(table),
`badge bg-${this.getTableStatusClass(table)}`,
),
document.createElement('br'),
makeTextElement('small', 'Status', 'text-muted'),
);
row.append(
identity,
metric(table.record_count || 0, 'Records', 'col-md-2', 'h5 mb-0 text-primary'),
metric(table.size || 'Unknown', 'Size', 'col-md-3'),
statusColumn,
);
infoElement.appendChild(row);
});
}
getTableStatus(table) {
const count = table.record_count || 0;
if (count === 0) return 'Empty';
if (count < 100) return 'Small';
if (count < 1000) return 'Medium';
if (count < 10000) return 'Large';
return 'Very Large';
}
getTableStatusClass(table) {
const count = table.record_count || 0;
if (count === 0) return 'secondary';
if (count < 100) return 'success';
if (count < 1000) return 'info';
if (count < 10000) return 'warning';
return 'danger';
}
exportDatabase() {
const csvContent = this.generateDatabaseCSV();
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `database_export_${new Date().toISOString().split('T')[0]}.csv`;
a.click();
window.URL.revokeObjectURL(url);
}
generateDatabaseCSV() {
const headers = ['Table Name', 'Record Count', 'Size', 'Status', 'Timestamp'];
const rows = (this.dbData.tables || []).map(table => [
table.name,
table.record_count || 0,
table.size || 'Unknown',
this.getTableStatus(table),
new Date().toISOString()
]);
return [headers, ...rows].map(row =>
row.map(field => `"${field}"`).join(',')
).join('\n');
}
async optimizeDatabase() {
if (confirm('Are you sure you want to optimize the database? This may take a few moments.')) {
try {
// Show loading state
const optimizeBtn = document.getElementById('optimize-db');
const originalText = optimizeBtn.innerHTML;
optimizeBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Optimizing...';
optimizeBtn.disabled = true;
// Call backend optimization
const response = await fetch('/api/optimize-database', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
const result = await response.json();
if (result.success) {
alert(`Database optimization completed successfully!\n\nResults:\n- ${result.vacuum_result}\n- ${result.analyze_result}\n- ${result.reindex_result}`);
// Refresh the database information
await this.loadDatabaseData();
this.updateStatistics();
this.renderTablesInfo();
} else {
alert('Database optimization failed: ' + (result.error || 'Unknown error'));
}
} catch (error) {
console.error('Error optimizing database:', error);
alert('Database optimization failed: ' + error.message);
} finally {
// Restore button state
const optimizeBtn = document.getElementById('optimize-db');
optimizeBtn.innerHTML = '<i class="fas fa-tools"></i> Optimize';
optimizeBtn.disabled = false;
}
}
}
showError(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'alert alert-danger';
errorDiv.textContent = message;
const content = document.querySelector('.container-fluid');
if (content) {
content.insertBefore(errorDiv, content.firstChild);
setTimeout(() => {
if (errorDiv.parentNode) {
errorDiv.parentNode.removeChild(errorDiv);
}
}, 5000);
}
}
}
// Initialize database manager when page loads
document.addEventListener('DOMContentLoaded', () => {
window.databaseManager = new DatabaseInfoManager();
});
</script>
{% endblock %}