|
|
@@ -71,6 +71,87 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
+ <!-- Database & API Metrics -->
|
|
|
+ <div class="db-api-grid">
|
|
|
+ <!-- PostgreSQL Card -->
|
|
|
+ <div class="db-card">
|
|
|
+ <div class="db-card-header">
|
|
|
+ <div class="db-icon">๐</div>
|
|
|
+ <div class="db-title">PostgreSQL</div>
|
|
|
+ </div>
|
|
|
+ <div class="db-stats">
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">Connections:</span>
|
|
|
+ <span class="db-stat-value">{{ latestMetrics?.pg_active_connections || 0 }} / {{ latestMetrics?.pg_total_connections || 100 }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">DB Size:</span>
|
|
|
+ <span class="db-stat-value">{{ formatBytes(latestMetrics?.pg_database_size_bytes || 0) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">Cache Hit:</span>
|
|
|
+ <span class="db-stat-value">{{ (latestMetrics?.pg_cache_hit_ratio || 0).toFixed(1) }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">TPS:</span>
|
|
|
+ <span class="db-stat-value">{{ latestMetrics?.pg_transactions_per_sec || 0 }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- ClickHouse Card -->
|
|
|
+ <div class="db-card">
|
|
|
+ <div class="db-card-header">
|
|
|
+ <div class="db-icon">๐</div>
|
|
|
+ <div class="db-title">ClickHouse</div>
|
|
|
+ </div>
|
|
|
+ <div class="db-stats">
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">Active Queries:</span>
|
|
|
+ <span class="db-stat-value">{{ latestMetrics?.ch_active_queries || 0 }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">DB Size:</span>
|
|
|
+ <span class="db-stat-value">{{ formatBytes(latestMetrics?.ch_database_size_bytes || 0) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">QPS:</span>
|
|
|
+ <span class="db-stat-value">{{ latestMetrics?.ch_queries_per_sec || 0 }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">Memory:</span>
|
|
|
+ <span class="db-stat-value">{{ formatBytes(latestMetrics?.ch_memory_usage_bytes || 0) }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- HTTP/API Card -->
|
|
|
+ <div class="db-card">
|
|
|
+ <div class="db-card-header">
|
|
|
+ <div class="db-icon">๐</div>
|
|
|
+ <div class="db-title">HTTP/API</div>
|
|
|
+ </div>
|
|
|
+ <div class="db-stats">
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">RPS:</span>
|
|
|
+ <span class="db-stat-value">{{ latestMetrics?.http_requests_per_sec || 0 }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">Avg Response:</span>
|
|
|
+ <span class="db-stat-value">{{ (latestMetrics?.http_avg_response_time_ms || 0).toFixed(0) }} ms</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">Error Rate:</span>
|
|
|
+ <span class="db-stat-value">{{ (latestMetrics?.http_error_rate || 0).toFixed(1) }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="db-stat-item">
|
|
|
+ <span class="db-stat-label">Active:</span>
|
|
|
+ <span class="db-stat-value">{{ latestMetrics?.http_active_requests || 0 }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- Host Metrics Charts -->
|
|
|
<div class="metrics-section">
|
|
|
<div class="charts-grid">
|
|
|
@@ -152,6 +233,7 @@ ChartJS.register(
|
|
|
const activeAlerts = ref([])
|
|
|
const deviceStats = ref({ total: 0, online: 0, offline: 0, error: 0 })
|
|
|
const hostMetrics = ref([])
|
|
|
+const latestMetrics = ref(null) // Latest metrics for database/API cards
|
|
|
|
|
|
// Chart data
|
|
|
const cpuChartData = ref(null)
|
|
|
@@ -263,6 +345,10 @@ async function loadHostMetrics() {
|
|
|
params: { limit: 30 }
|
|
|
})
|
|
|
hostMetrics.value = data.metrics // Already in chronological order
|
|
|
+ // Save latest for database/API cards
|
|
|
+ if (data.metrics.length > 0) {
|
|
|
+ latestMetrics.value = data.metrics[data.metrics.length - 1]
|
|
|
+ }
|
|
|
updateCharts()
|
|
|
} catch (error) {
|
|
|
console.error('Failed to load host metrics:', error)
|
|
|
@@ -434,6 +520,14 @@ function formatTime(timestamp) {
|
|
|
return date.toLocaleString()
|
|
|
}
|
|
|
|
|
|
+function formatBytes(bytes) {
|
|
|
+ if (bytes === 0) return '0 B'
|
|
|
+ const k = 1024
|
|
|
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
|
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
|
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
|
+}
|
|
|
+
|
|
|
async function refreshData() {
|
|
|
await Promise.all([
|
|
|
loadAlerts(),
|
|
|
@@ -636,6 +730,65 @@ onUnmounted(() => {
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
+/* Database & API Cards */
|
|
|
+.db-api-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
|
+ gap: 12px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.db-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 14px;
|
|
|
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
|
+ border-left: 3px solid #667eea;
|
|
|
+}
|
|
|
+
|
|
|
+.db-card-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding-bottom: 8px;
|
|
|
+ border-bottom: 1px solid #e2e8f0;
|
|
|
+}
|
|
|
+
|
|
|
+.db-icon {
|
|
|
+ font-size: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.db-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1a202c;
|
|
|
+}
|
|
|
+
|
|
|
+.db-stats {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.db-stat-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.db-stat-label {
|
|
|
+ color: #718096;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.db-stat-value {
|
|
|
+ color: #1a202c;
|
|
|
+ font-weight: 600;
|
|
|
+ font-family: 'Courier New', monospace;
|
|
|
+}
|
|
|
+
|
|
|
/* Metrics Section */
|
|
|
.metrics-section {
|
|
|
background: white;
|