mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 19:44:12 +00:00
feat(monitoring): add shared types for monitoring system
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c7fd95d3da
commit
214e2ddde8
2 changed files with 241 additions and 0 deletions
148
src/lib/monitoring/types.ts
Normal file
148
src/lib/monitoring/types.ts
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
// === System Health ===
|
||||||
|
export interface SystemHealth {
|
||||||
|
cpuUsagePercent: number
|
||||||
|
memoryUsedMB: number
|
||||||
|
memoryTotalMB: number
|
||||||
|
memoryUsagePercent: number
|
||||||
|
diskUsedGB: number
|
||||||
|
diskTotalGB: number
|
||||||
|
diskUsagePercent: number
|
||||||
|
loadAvg1: number
|
||||||
|
loadAvg5: number
|
||||||
|
uptime: number // seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Service Statuses ===
|
||||||
|
export type ServiceStatusType = 'online' | 'warning' | 'offline'
|
||||||
|
|
||||||
|
export interface ProcessStatus {
|
||||||
|
status: ServiceStatusType
|
||||||
|
pid: number
|
||||||
|
memoryMB: number
|
||||||
|
uptimeSeconds: number
|
||||||
|
restarts: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostgresqlStatus {
|
||||||
|
status: ServiceStatusType
|
||||||
|
connections: number
|
||||||
|
maxConnections: number
|
||||||
|
latencyMs: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PgBouncerStatus {
|
||||||
|
status: ServiceStatusType
|
||||||
|
activeConnections: number
|
||||||
|
waitingClients: number
|
||||||
|
poolSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RedisStatus {
|
||||||
|
status: ServiceStatusType
|
||||||
|
memoryUsedMB: number
|
||||||
|
connectedClients: number
|
||||||
|
opsPerSec: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceStatuses {
|
||||||
|
payload: ProcessStatus
|
||||||
|
queueWorker: ProcessStatus
|
||||||
|
postgresql: PostgresqlStatus
|
||||||
|
pgbouncer: PgBouncerStatus
|
||||||
|
redis: RedisStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// === External Statuses ===
|
||||||
|
export interface SmtpStatus {
|
||||||
|
status: ServiceStatusType
|
||||||
|
lastCheck: string // ISO date
|
||||||
|
responseTimeMs: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OAuthStatusType = 'ok' | 'expiring_soon' | 'expired' | 'error'
|
||||||
|
|
||||||
|
export interface OAuthTokenStatus {
|
||||||
|
status: OAuthStatusType
|
||||||
|
tokensTotal: number
|
||||||
|
tokensExpiringSoon: number
|
||||||
|
tokensExpired: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CronJobStatus {
|
||||||
|
lastRun: string // ISO date
|
||||||
|
status: 'ok' | 'failed' | 'unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CronStatuses {
|
||||||
|
communitySync: CronJobStatus
|
||||||
|
tokenRefresh: CronJobStatus
|
||||||
|
youtubeSync: CronJobStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExternalStatuses {
|
||||||
|
smtp: SmtpStatus
|
||||||
|
metaOAuth: OAuthTokenStatus
|
||||||
|
youtubeOAuth: OAuthTokenStatus
|
||||||
|
cronJobs: CronStatuses
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Performance ===
|
||||||
|
export interface PerformanceMetrics {
|
||||||
|
avgResponseTimeMs: number
|
||||||
|
p95ResponseTimeMs: number
|
||||||
|
p99ResponseTimeMs: number
|
||||||
|
errorRate: number // 0-1
|
||||||
|
requestsPerMinute: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Full Snapshot ===
|
||||||
|
export interface SystemMetrics {
|
||||||
|
timestamp: string // ISO date
|
||||||
|
system: SystemHealth
|
||||||
|
services: ServiceStatuses
|
||||||
|
external: ExternalStatuses
|
||||||
|
performance: PerformanceMetrics
|
||||||
|
}
|
||||||
|
|
||||||
|
// === SSE Events (discriminated union) ===
|
||||||
|
export type MonitoringEvent =
|
||||||
|
| { type: 'health'; data: SystemHealth }
|
||||||
|
| { type: 'service'; data: Partial<ServiceStatuses> }
|
||||||
|
| { type: 'alert'; data: AlertEvent }
|
||||||
|
| { type: 'log'; data: LogEvent }
|
||||||
|
| { type: 'performance'; data: PerformanceMetrics }
|
||||||
|
|
||||||
|
export interface AlertEvent {
|
||||||
|
id: string
|
||||||
|
ruleId: string
|
||||||
|
metric: string
|
||||||
|
value: number
|
||||||
|
threshold: number
|
||||||
|
severity: AlertSeverity
|
||||||
|
message: string
|
||||||
|
timestamp: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LogEvent {
|
||||||
|
id: string
|
||||||
|
level: LogLevel
|
||||||
|
source: LogSource
|
||||||
|
message: string
|
||||||
|
timestamp: string
|
||||||
|
context?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Enums as union types ===
|
||||||
|
export type AlertCondition = 'gt' | 'lt' | 'eq' | 'gte' | 'lte'
|
||||||
|
export type AlertSeverity = 'warning' | 'error' | 'critical'
|
||||||
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal'
|
||||||
|
export type LogSource = 'payload' | 'queue-worker' | 'cron' | 'email' | 'oauth' | 'sync'
|
||||||
|
|
||||||
|
// === Performance Tracker Entry ===
|
||||||
|
export interface PerformanceEntry {
|
||||||
|
timestamp: number // Date.now()
|
||||||
|
method: string
|
||||||
|
path: string
|
||||||
|
statusCode: number
|
||||||
|
durationMs: number
|
||||||
|
}
|
||||||
93
tests/unit/monitoring/types.test.ts
Normal file
93
tests/unit/monitoring/types.test.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import type {
|
||||||
|
SystemMetrics,
|
||||||
|
MonitoringEvent,
|
||||||
|
AlertCondition,
|
||||||
|
LogLevel,
|
||||||
|
LogSource,
|
||||||
|
} from '@/lib/monitoring/types'
|
||||||
|
|
||||||
|
describe('Monitoring Types', () => {
|
||||||
|
it('SystemMetrics has all required sections', () => {
|
||||||
|
const metrics: SystemMetrics = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
system: {
|
||||||
|
cpuUsagePercent: 23,
|
||||||
|
memoryUsedMB: 4200,
|
||||||
|
memoryTotalMB: 8192,
|
||||||
|
memoryUsagePercent: 51.3,
|
||||||
|
diskUsedGB: 30,
|
||||||
|
diskTotalGB: 50,
|
||||||
|
diskUsagePercent: 60,
|
||||||
|
loadAvg1: 0.5,
|
||||||
|
loadAvg5: 0.8,
|
||||||
|
uptime: 1209600,
|
||||||
|
},
|
||||||
|
services: {
|
||||||
|
payload: {
|
||||||
|
status: 'online',
|
||||||
|
pid: 1234,
|
||||||
|
memoryMB: 512,
|
||||||
|
uptimeSeconds: 86400,
|
||||||
|
restarts: 0,
|
||||||
|
},
|
||||||
|
queueWorker: {
|
||||||
|
status: 'online',
|
||||||
|
pid: 5678,
|
||||||
|
memoryMB: 256,
|
||||||
|
uptimeSeconds: 86400,
|
||||||
|
restarts: 0,
|
||||||
|
},
|
||||||
|
postgresql: { status: 'online', connections: 12, maxConnections: 50, latencyMs: 2 },
|
||||||
|
pgbouncer: { status: 'online', activeConnections: 8, waitingClients: 0, poolSize: 20 },
|
||||||
|
redis: { status: 'online', memoryUsedMB: 48, connectedClients: 5, opsPerSec: 120 },
|
||||||
|
},
|
||||||
|
external: {
|
||||||
|
smtp: {
|
||||||
|
status: 'online',
|
||||||
|
lastCheck: new Date().toISOString(),
|
||||||
|
responseTimeMs: 180,
|
||||||
|
},
|
||||||
|
metaOAuth: { status: 'ok', tokensTotal: 2, tokensExpiringSoon: 1, tokensExpired: 0 },
|
||||||
|
youtubeOAuth: { status: 'ok', tokensTotal: 3, tokensExpiringSoon: 0, tokensExpired: 0 },
|
||||||
|
cronJobs: {
|
||||||
|
communitySync: { lastRun: new Date().toISOString(), status: 'ok' },
|
||||||
|
tokenRefresh: { lastRun: new Date().toISOString(), status: 'ok' },
|
||||||
|
youtubeSync: { lastRun: new Date().toISOString(), status: 'ok' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
performance: {
|
||||||
|
avgResponseTimeMs: 120,
|
||||||
|
p95ResponseTimeMs: 350,
|
||||||
|
p99ResponseTimeMs: 800,
|
||||||
|
errorRate: 0.02,
|
||||||
|
requestsPerMinute: 45,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(metrics.system.cpuUsagePercent).toBe(23)
|
||||||
|
expect(metrics.services.payload.status).toBe('online')
|
||||||
|
expect(metrics.external.smtp.status).toBe('online')
|
||||||
|
expect(metrics.performance.avgResponseTimeMs).toBe(120)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('MonitoringEvent types are exhaustive', () => {
|
||||||
|
const events: MonitoringEvent['type'][] = ['health', 'service', 'alert', 'log', 'performance']
|
||||||
|
expect(events).toHaveLength(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('AlertCondition covers all comparison operators', () => {
|
||||||
|
const conditions: AlertCondition[] = ['gt', 'lt', 'eq', 'gte', 'lte']
|
||||||
|
expect(conditions).toHaveLength(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('LogLevel covers all severity levels', () => {
|
||||||
|
const levels: LogLevel[] = ['debug', 'info', 'warn', 'error', 'fatal']
|
||||||
|
expect(levels).toHaveLength(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('LogSource covers all system components', () => {
|
||||||
|
const sources: LogSource[] = ['payload', 'queue-worker', 'cron', 'email', 'oauth', 'sync']
|
||||||
|
expect(sources).toHaveLength(6)
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Reference in a new issue