Files
atlas/internal/httpapp/dashboard_handlers.go
othman.suseno 3e64de18ed
Some checks failed
CI / test-build (push) Failing after 2m1s
add service monitoring on dashboard
2025-12-15 00:14:07 +07:00

131 lines
3.4 KiB
Go

package httpapp
import (
"fmt"
"net/http"
)
// DashboardData represents aggregated dashboard statistics
type DashboardData struct {
Storage struct {
TotalCapacity uint64 `json:"total_capacity"`
TotalAllocated uint64 `json:"total_allocated"`
TotalAvailable uint64 `json:"total_available"`
PoolCount int `json:"pool_count"`
DatasetCount int `json:"dataset_count"`
ZVOLCount int `json:"zvol_count"`
SnapshotCount int `json:"snapshot_count"`
} `json:"storage"`
Services struct {
SMBShares int `json:"smb_shares"`
NFSExports int `json:"nfs_exports"`
ISCSITargets int `json:"iscsi_targets"`
SMBStatus bool `json:"smb_status"`
NFSStatus bool `json:"nfs_status"`
ISCSIStatus bool `json:"iscsi_status"`
} `json:"services"`
Jobs struct {
Total int `json:"total"`
Running int `json:"running"`
Completed int `json:"completed"`
Failed int `json:"failed"`
} `json:"jobs"`
RecentAuditLogs []map[string]interface{} `json:"recent_audit_logs,omitempty"`
}
// handleDashboardAPI returns aggregated dashboard data
func (a *App) handleDashboardAPI(w http.ResponseWriter, r *http.Request) {
data := DashboardData{}
// Storage statistics
pools, err := a.zfs.ListPools()
if err == nil {
data.Storage.PoolCount = len(pools)
for _, pool := range pools {
data.Storage.TotalCapacity += pool.Size
data.Storage.TotalAllocated += pool.Allocated
data.Storage.TotalAvailable += pool.Free
}
}
datasets, err := a.zfs.ListDatasets("")
if err == nil {
data.Storage.DatasetCount = len(datasets)
}
zvols, err := a.zfs.ListZVOLs("")
if err == nil {
data.Storage.ZVOLCount = len(zvols)
}
snapshots, err := a.zfs.ListSnapshots("")
if err == nil {
data.Storage.SnapshotCount = len(snapshots)
}
// Service statistics
smbShares := a.smbStore.List()
data.Services.SMBShares = len(smbShares)
nfsExports := a.nfsStore.List()
data.Services.NFSExports = len(nfsExports)
iscsiTargets := a.iscsiStore.List()
data.Services.ISCSITargets = len(iscsiTargets)
// Service status
if a.smbService != nil {
data.Services.SMBStatus, _ = a.smbService.GetStatus()
}
if a.nfsService != nil {
data.Services.NFSStatus, _ = a.nfsService.GetStatus()
}
if a.iscsiService != nil {
data.Services.ISCSIStatus, _ = a.iscsiService.GetStatus()
}
// Job statistics
allJobs := a.jobManager.List("")
data.Jobs.Total = len(allJobs)
for _, job := range allJobs {
switch job.Status {
case "running":
data.Jobs.Running++
case "completed":
data.Jobs.Completed++
case "failed":
data.Jobs.Failed++
}
}
// Recent audit logs (last 5)
auditLogs := a.auditStore.List("", "", "", 5)
data.RecentAuditLogs = make([]map[string]interface{}, 0, len(auditLogs))
for _, log := range auditLogs {
data.RecentAuditLogs = append(data.RecentAuditLogs, map[string]interface{}{
"id": log.ID,
"actor": log.Actor,
"action": log.Action,
"resource": log.Resource,
"result": log.Result,
"timestamp": log.Timestamp.Format("2006-01-02 15:04:05"),
})
}
writeJSON(w, http.StatusOK, data)
}
// formatBytes formats bytes to human-readable format
func formatBytes(bytes uint64) string {
const unit = 1024
if bytes < unit {
return fmt.Sprintf("%d B", bytes)
}
div, exp := int64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}