package httpapp import ( "fmt" "net/http" "runtime" "time" ) // SystemInfo represents system diagnostic information type SystemInfo struct { Version string `json:"version"` Uptime string `json:"uptime"` GoVersion string `json:"go_version"` NumGoroutine int `json:"num_goroutines"` Memory MemoryInfo `json:"memory"` Services map[string]ServiceInfo `json:"services"` Database DatabaseInfo `json:"database,omitempty"` } // MemoryInfo represents memory statistics type MemoryInfo struct { Alloc uint64 `json:"alloc"` // bytes allocated TotalAlloc uint64 `json:"total_alloc"` // bytes allocated (cumulative) Sys uint64 `json:"sys"` // bytes obtained from system NumGC uint32 `json:"num_gc"` // number of GC cycles } // ServiceInfo represents service status type ServiceInfo struct { Status string `json:"status"` // "running", "stopped", "error" LastCheck string `json:"last_check"` // timestamp Message string `json:"message,omitempty"` } // DatabaseInfo represents database connection info type DatabaseInfo struct { Connected bool `json:"connected"` Path string `json:"path,omitempty"` } // handleSystemInfo returns system diagnostic information func (a *App) handleSystemInfo(w http.ResponseWriter, r *http.Request) { var m runtime.MemStats runtime.ReadMemStats(&m) uptime := time.Since(a.startTime) info := SystemInfo{ Version: "v0.1.0-dev", Uptime: fmt.Sprintf("%.0f seconds", uptime.Seconds()), GoVersion: runtime.Version(), NumGoroutine: runtime.NumGoroutine(), Memory: MemoryInfo{ Alloc: m.Alloc, TotalAlloc: m.TotalAlloc, Sys: m.Sys, NumGC: m.NumGC, }, Services: make(map[string]ServiceInfo), } // Check service statuses smbStatus, smbErr := a.smbService.GetStatus() if smbErr == nil { status := "stopped" if smbStatus { status = "running" } info.Services["smb"] = ServiceInfo{ Status: status, LastCheck: time.Now().Format(time.RFC3339), } } else { info.Services["smb"] = ServiceInfo{ Status: "error", LastCheck: time.Now().Format(time.RFC3339), Message: smbErr.Error(), } } nfsStatus, nfsErr := a.nfsService.GetStatus() if nfsErr == nil { status := "stopped" if nfsStatus { status = "running" } info.Services["nfs"] = ServiceInfo{ Status: status, LastCheck: time.Now().Format(time.RFC3339), } } else { info.Services["nfs"] = ServiceInfo{ Status: "error", LastCheck: time.Now().Format(time.RFC3339), Message: nfsErr.Error(), } } iscsiStatus, iscsiErr := a.iscsiService.GetStatus() if iscsiErr == nil { status := "stopped" if iscsiStatus { status = "running" } info.Services["iscsi"] = ServiceInfo{ Status: status, LastCheck: time.Now().Format(time.RFC3339), } } else { info.Services["iscsi"] = ServiceInfo{ Status: "error", LastCheck: time.Now().Format(time.RFC3339), Message: iscsiErr.Error(), } } // Database info if a.database != nil { info.Database = DatabaseInfo{ Connected: true, Path: a.cfg.DatabasePath, } } writeJSON(w, http.StatusOK, info) } // handleHealthCheck provides detailed health check information func (a *App) handleHealthCheck(w http.ResponseWriter, r *http.Request) { type HealthStatus struct { Status string `json:"status"` // "healthy", "degraded", "unhealthy" Timestamp string `json:"timestamp"` Checks map[string]string `json:"checks"` } health := HealthStatus{ Status: "healthy", Timestamp: time.Now().Format(time.RFC3339), Checks: make(map[string]string), } // Check ZFS service if a.zfs != nil { _, err := a.zfs.ListPools() if err != nil { health.Checks["zfs"] = "unhealthy: " + err.Error() health.Status = "degraded" } else { health.Checks["zfs"] = "healthy" } } else { health.Checks["zfs"] = "unhealthy: service not initialized" health.Status = "unhealthy" } // Check database if a.database != nil { // Try a simple query to check database health if err := a.database.DB.Ping(); err != nil { health.Checks["database"] = "unhealthy: " + err.Error() health.Status = "degraded" } else { health.Checks["database"] = "healthy" } } else { health.Checks["database"] = "not configured" } // Check services smbStatus, smbErr := a.smbService.GetStatus() if smbErr != nil { health.Checks["smb"] = "unhealthy: " + smbErr.Error() health.Status = "degraded" } else if !smbStatus { health.Checks["smb"] = "stopped" } else { health.Checks["smb"] = "healthy" } nfsStatus, nfsErr := a.nfsService.GetStatus() if nfsErr != nil { health.Checks["nfs"] = "unhealthy: " + nfsErr.Error() health.Status = "degraded" } else if !nfsStatus { health.Checks["nfs"] = "stopped" } else { health.Checks["nfs"] = "healthy" } iscsiStatus, iscsiErr := a.iscsiService.GetStatus() if iscsiErr != nil { health.Checks["iscsi"] = "unhealthy: " + iscsiErr.Error() health.Status = "degraded" } else if !iscsiStatus { health.Checks["iscsi"] = "stopped" } else { health.Checks["iscsi"] = "healthy" } // Check maintenance mode if a.maintenanceService != nil && a.maintenanceService.IsEnabled() { health.Checks["maintenance"] = "enabled" if health.Status == "healthy" { health.Status = "maintenance" } } else { health.Checks["maintenance"] = "disabled" } // Set HTTP status based on health statusCode := http.StatusOK if health.Status == "unhealthy" { statusCode = http.StatusServiceUnavailable } else if health.Status == "degraded" { statusCode = http.StatusOK // Still OK, but with warnings } w.WriteHeader(statusCode) writeJSON(w, statusCode, health) } // handleLogs returns recent log entries (if available) func (a *App) handleLogs(w http.ResponseWriter, r *http.Request) { // For now, return audit logs as system logs // In a full implementation, this would return application logs limit := 100 if limitStr := r.URL.Query().Get("limit"); limitStr != "" { fmt.Sscanf(limitStr, "%d", &limit) if limit > 1000 { limit = 1000 } if limit < 1 { limit = 1 } } // Get recent audit logs logs := a.auditStore.List("", "", "", limit) type LogEntry struct { Timestamp string `json:"timestamp"` Level string `json:"level"` Actor string `json:"actor"` Action string `json:"action"` Resource string `json:"resource"` Result string `json:"result"` Message string `json:"message,omitempty"` IP string `json:"ip,omitempty"` } entries := make([]LogEntry, 0, len(logs)) for _, log := range logs { level := "INFO" if log.Result == "failure" { level = "ERROR" } entries = append(entries, LogEntry{ Timestamp: log.Timestamp.Format(time.RFC3339), Level: level, Actor: log.Actor, Action: log.Action, Resource: log.Resource, Result: log.Result, Message: log.Message, IP: log.IP, }) } writeJSON(w, http.StatusOK, map[string]interface{}{ "logs": entries, "count": len(entries), }) } // handleGC triggers a garbage collection and returns stats func (a *App) handleGC(w http.ResponseWriter, r *http.Request) { var before, after runtime.MemStats runtime.ReadMemStats(&before) runtime.GC() runtime.ReadMemStats(&after) writeJSON(w, http.StatusOK, map[string]interface{}{ "before": map[string]interface{}{ "alloc": before.Alloc, "total_alloc": before.TotalAlloc, "sys": before.Sys, "num_gc": before.NumGC, }, "after": map[string]interface{}{ "alloc": after.Alloc, "total_alloc": after.TotalAlloc, "sys": after.Sys, "num_gc": after.NumGC, }, "freed": before.Alloc - after.Alloc, }) }