admin firs step
This commit is contained in:
335
frontend/admin/composables/useHealthMonitor.ts
Normal file
335
frontend/admin/composables/useHealthMonitor.ts
Normal file
@@ -0,0 +1,335 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
|
||||
// Types
|
||||
export interface HealthMetrics {
|
||||
total_assets: number
|
||||
total_organizations: number
|
||||
critical_alerts_24h: number
|
||||
system_status: 'healthy' | 'degraded' | 'critical'
|
||||
uptime_percentage: number
|
||||
response_time_ms: number
|
||||
database_connections: number
|
||||
active_users: number
|
||||
last_updated: string
|
||||
}
|
||||
|
||||
export interface SystemAlert {
|
||||
id: string
|
||||
severity: 'info' | 'warning' | 'critical'
|
||||
title: string
|
||||
description: string
|
||||
timestamp: string
|
||||
component: string
|
||||
resolved: boolean
|
||||
}
|
||||
|
||||
export interface HealthMonitorState {
|
||||
metrics: HealthMetrics | null
|
||||
alerts: SystemAlert[]
|
||||
loading: boolean
|
||||
error: string | null
|
||||
lastUpdated: Date | null
|
||||
}
|
||||
|
||||
// Mock data for development/testing
|
||||
const generateMockMetrics = (): HealthMetrics => {
|
||||
return {
|
||||
total_assets: Math.floor(Math.random() * 10000) + 5000,
|
||||
total_organizations: Math.floor(Math.random() * 500) + 100,
|
||||
critical_alerts_24h: Math.floor(Math.random() * 10),
|
||||
system_status: Math.random() > 0.8 ? 'degraded' : Math.random() > 0.95 ? 'critical' : 'healthy',
|
||||
uptime_percentage: 99.5 + (Math.random() * 0.5 - 0.25), // 99.25% - 99.75%
|
||||
response_time_ms: Math.floor(Math.random() * 100) + 50,
|
||||
database_connections: Math.floor(Math.random() * 50) + 10,
|
||||
active_users: Math.floor(Math.random() * 1000) + 500,
|
||||
last_updated: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
const generateMockAlerts = (count: number = 5): SystemAlert[] => {
|
||||
const severities: SystemAlert['severity'][] = ['info', 'warning', 'critical']
|
||||
const components = ['Database', 'API Gateway', 'Redis', 'PostgreSQL', 'Docker', 'Network', 'Authentication', 'File Storage']
|
||||
const titles = [
|
||||
'High memory usage detected',
|
||||
'Database connection pool exhausted',
|
||||
'API response time above threshold',
|
||||
'Redis cache miss rate increased',
|
||||
'Disk space running low',
|
||||
'Network latency spike',
|
||||
'Authentication service slow response',
|
||||
'Backup job failed'
|
||||
]
|
||||
|
||||
const alerts: SystemAlert[] = []
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const severity = severities[Math.floor(Math.random() * severities.length)]
|
||||
const isResolved = Math.random() > 0.7
|
||||
|
||||
alerts.push({
|
||||
id: `alert_${Date.now()}_${i}`,
|
||||
severity,
|
||||
title: titles[Math.floor(Math.random() * titles.length)],
|
||||
description: `Detailed description of the ${severity} alert in the ${components[Math.floor(Math.random() * components.length)]} component.`,
|
||||
timestamp: new Date(Date.now() - Math.random() * 24 * 60 * 60 * 1000).toISOString(), // Within last 24 hours
|
||||
component: components[Math.floor(Math.random() * components.length)],
|
||||
resolved: isResolved
|
||||
})
|
||||
}
|
||||
|
||||
return alerts
|
||||
}
|
||||
|
||||
// API Service
|
||||
class HealthMonitorApiService {
|
||||
private baseUrl = 'http://localhost:8000/api/v1/admin' // Should come from environment config
|
||||
private delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
// Get health metrics
|
||||
async getHealthMetrics(): Promise<HealthMetrics> {
|
||||
// In a real implementation, this would call the actual API
|
||||
// const response = await fetch(`${this.baseUrl}/health-monitor`, {
|
||||
// headers: this.getAuthHeaders()
|
||||
// })
|
||||
//
|
||||
// if (!response.ok) {
|
||||
// throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
||||
// }
|
||||
//
|
||||
// return await response.json()
|
||||
|
||||
await this.delay(800) // Simulate network delay
|
||||
|
||||
// For now, return mock data
|
||||
return generateMockMetrics()
|
||||
}
|
||||
|
||||
// Get system alerts
|
||||
async getSystemAlerts(options?: {
|
||||
severity?: SystemAlert['severity']
|
||||
resolved?: boolean
|
||||
limit?: number
|
||||
}): Promise<SystemAlert[]> {
|
||||
await this.delay(500)
|
||||
|
||||
let alerts = generateMockAlerts(10)
|
||||
|
||||
if (options?.severity) {
|
||||
alerts = alerts.filter(alert => alert.severity === options.severity)
|
||||
}
|
||||
|
||||
if (options?.resolved !== undefined) {
|
||||
alerts = alerts.filter(alert => alert.resolved === options.resolved)
|
||||
}
|
||||
|
||||
if (options?.limit) {
|
||||
alerts = alerts.slice(0, options.limit)
|
||||
}
|
||||
|
||||
return alerts
|
||||
}
|
||||
|
||||
// Get auth headers (for real API calls)
|
||||
private getAuthHeaders(): Record<string, string> {
|
||||
const authStore = useAuthStore()
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
if (authStore.token) {
|
||||
headers['Authorization'] = `Bearer ${authStore.token}`
|
||||
}
|
||||
|
||||
// Add geographical scope headers
|
||||
if (authStore.getScopeId) {
|
||||
headers['X-Scope-Id'] = authStore.getScopeId.toString()
|
||||
}
|
||||
|
||||
if (authStore.getRegionCode) {
|
||||
headers['X-Region-Code'] = authStore.getRegionCode
|
||||
}
|
||||
|
||||
if (authStore.getScopeLevel) {
|
||||
headers['X-Scope-Level'] = authStore.getScopeLevel
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
}
|
||||
|
||||
// Composable
|
||||
export const useHealthMonitor = () => {
|
||||
const state = ref<HealthMonitorState>({
|
||||
metrics: null,
|
||||
alerts: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
lastUpdated: null
|
||||
})
|
||||
|
||||
const apiService = new HealthMonitorApiService()
|
||||
|
||||
// Computed properties
|
||||
const systemStatusColor = computed(() => {
|
||||
if (!state.value.metrics) return 'grey'
|
||||
|
||||
switch (state.value.metrics.system_status) {
|
||||
case 'healthy': return 'green'
|
||||
case 'degraded': return 'orange'
|
||||
case 'critical': return 'red'
|
||||
default: return 'grey'
|
||||
}
|
||||
})
|
||||
|
||||
const systemStatusIcon = computed(() => {
|
||||
if (!state.value.metrics) return 'mdi-help-circle'
|
||||
|
||||
switch (state.value.metrics.system_status) {
|
||||
case 'healthy': return 'mdi-check-circle'
|
||||
case 'degraded': return 'mdi-alert-circle'
|
||||
case 'critical': return 'mdi-alert-octagon'
|
||||
default: return 'mdi-help-circle'
|
||||
}
|
||||
})
|
||||
|
||||
const criticalAlerts = computed(() => {
|
||||
return state.value.alerts.filter(alert => alert.severity === 'critical' && !alert.resolved)
|
||||
})
|
||||
|
||||
const warningAlerts = computed(() => {
|
||||
return state.value.alerts.filter(alert => alert.severity === 'warning' && !alert.resolved)
|
||||
})
|
||||
|
||||
const formattedUptime = computed(() => {
|
||||
if (!state.value.metrics) return 'N/A'
|
||||
return `${state.value.metrics.uptime_percentage.toFixed(2)}%`
|
||||
})
|
||||
|
||||
const formattedResponseTime = computed(() => {
|
||||
if (!state.value.metrics) return 'N/A'
|
||||
return `${state.value.metrics.response_time_ms}ms`
|
||||
})
|
||||
|
||||
// Actions
|
||||
const fetchHealthMetrics = async () => {
|
||||
state.value.loading = true
|
||||
state.value.error = null
|
||||
|
||||
try {
|
||||
const metrics = await apiService.getHealthMetrics()
|
||||
state.value.metrics = metrics
|
||||
state.value.lastUpdated = new Date()
|
||||
} catch (error) {
|
||||
state.value.error = error instanceof Error ? error.message : 'Failed to fetch health metrics'
|
||||
console.error('Error fetching health metrics:', error)
|
||||
|
||||
// Fallback to mock data
|
||||
state.value.metrics = generateMockMetrics()
|
||||
} finally {
|
||||
state.value.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetchSystemAlerts = async (options?: {
|
||||
severity?: SystemAlert['severity']
|
||||
resolved?: boolean
|
||||
limit?: number
|
||||
}) => {
|
||||
state.value.loading = true
|
||||
state.value.error = null
|
||||
|
||||
try {
|
||||
const alerts = await apiService.getSystemAlerts(options)
|
||||
state.value.alerts = alerts
|
||||
} catch (error) {
|
||||
state.value.error = error instanceof Error ? error.message : 'Failed to fetch system alerts'
|
||||
console.error('Error fetching system alerts:', error)
|
||||
|
||||
// Fallback to mock data
|
||||
state.value.alerts = generateMockAlerts(5)
|
||||
} finally {
|
||||
state.value.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const refreshAll = async () => {
|
||||
await Promise.all([
|
||||
fetchHealthMetrics(),
|
||||
fetchSystemAlerts()
|
||||
])
|
||||
}
|
||||
|
||||
const markAlertAsResolved = async (alertId: string) => {
|
||||
// In a real implementation, this would call an API endpoint
|
||||
// await apiService.resolveAlert(alertId)
|
||||
|
||||
// Update local state
|
||||
const alertIndex = state.value.alerts.findIndex(alert => alert.id === alertId)
|
||||
if (alertIndex !== -1) {
|
||||
state.value.alerts[alertIndex].resolved = true
|
||||
}
|
||||
}
|
||||
|
||||
const dismissAlert = (alertId: string) => {
|
||||
// Remove alert from local state (frontend only)
|
||||
state.value.alerts = state.value.alerts.filter(alert => alert.id !== alertId)
|
||||
}
|
||||
|
||||
// Initialize
|
||||
const initialize = () => {
|
||||
refreshAll()
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
state: computed(() => state.value),
|
||||
metrics: computed(() => state.value.metrics),
|
||||
alerts: computed(() => state.value.alerts),
|
||||
loading: computed(() => state.value.loading),
|
||||
error: computed(() => state.value.error),
|
||||
lastUpdated: computed(() => state.value.lastUpdated),
|
||||
|
||||
// Computed
|
||||
systemStatusColor,
|
||||
systemStatusIcon,
|
||||
criticalAlerts,
|
||||
warningAlerts,
|
||||
formattedUptime,
|
||||
formattedResponseTime,
|
||||
|
||||
// Actions
|
||||
fetchHealthMetrics,
|
||||
fetchSystemAlerts,
|
||||
refreshAll,
|
||||
markAlertAsResolved,
|
||||
dismissAlert,
|
||||
initialize,
|
||||
|
||||
// Helper functions
|
||||
getAlertColor: (severity: SystemAlert['severity']) => {
|
||||
switch (severity) {
|
||||
case 'info': return 'blue'
|
||||
case 'warning': return 'orange'
|
||||
case 'critical': return 'red'
|
||||
default: return 'grey'
|
||||
}
|
||||
},
|
||||
|
||||
getAlertIcon: (severity: SystemAlert['severity']) => {
|
||||
switch (severity) {
|
||||
case 'info': return 'mdi-information'
|
||||
case 'warning': return 'mdi-alert'
|
||||
case 'critical': return 'mdi-alert-circle'
|
||||
default: return 'mdi-help-circle'
|
||||
}
|
||||
},
|
||||
|
||||
formatTimestamp: (timestamp: string) => {
|
||||
const date = new Date(timestamp)
|
||||
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default useHealthMonitor
|
||||
Reference in New Issue
Block a user