No One Can Stop Me Now
This commit is contained in:
151
services/setec-manager/internal/handlers/dashboard.go
Normal file
151
services/setec-manager/internal/handlers/dashboard.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"setec-manager/internal/deploy"
|
||||
"setec-manager/internal/system"
|
||||
)
|
||||
|
||||
type systemInfo struct {
|
||||
Hostname string `json:"hostname"`
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
CPUs int `json:"cpus"`
|
||||
Uptime string `json:"uptime"`
|
||||
LoadAvg string `json:"load_avg"`
|
||||
MemTotal string `json:"mem_total"`
|
||||
MemUsed string `json:"mem_used"`
|
||||
MemPercent float64 `json:"mem_percent"`
|
||||
DiskTotal string `json:"disk_total"`
|
||||
DiskUsed string `json:"disk_used"`
|
||||
DiskPercent float64 `json:"disk_percent"`
|
||||
SiteCount int `json:"site_count"`
|
||||
Services []serviceInfo `json:"services"`
|
||||
}
|
||||
|
||||
type serviceInfo struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Running bool `json:"running"`
|
||||
}
|
||||
|
||||
func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
|
||||
info := h.gatherSystemInfo()
|
||||
h.render(w, "dashboard.html", info)
|
||||
}
|
||||
|
||||
func (h *Handler) SystemInfo(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, h.gatherSystemInfo())
|
||||
}
|
||||
|
||||
func (h *Handler) gatherSystemInfo() systemInfo {
|
||||
info := systemInfo{
|
||||
OS: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
CPUs: runtime.NumCPU(),
|
||||
}
|
||||
|
||||
// Hostname — no wrapper, keep exec.Command
|
||||
if out, err := exec.Command("hostname").Output(); err == nil {
|
||||
info.Hostname = strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
// Uptime
|
||||
if ut, err := system.GetUptime(); err == nil {
|
||||
info.Uptime = "up " + ut.HumanReadable
|
||||
}
|
||||
|
||||
// Load average
|
||||
if la, err := system.GetLoadAvg(); err == nil {
|
||||
info.LoadAvg = fmt.Sprintf("%.2f %.2f %.2f", la.Load1, la.Load5, la.Load15)
|
||||
}
|
||||
|
||||
// Memory
|
||||
if mem, err := system.GetMemory(); err == nil {
|
||||
info.MemTotal = mem.Total
|
||||
info.MemUsed = mem.Used
|
||||
if mem.TotalBytes > 0 {
|
||||
info.MemPercent = float64(mem.UsedBytes) / float64(mem.TotalBytes) * 100
|
||||
}
|
||||
}
|
||||
|
||||
// Disk — find the root mount from the disk list
|
||||
if disks, err := system.GetDisk(); err == nil {
|
||||
for _, d := range disks {
|
||||
if d.MountPoint == "/" {
|
||||
info.DiskTotal = d.Size
|
||||
info.DiskUsed = d.Used
|
||||
pct := strings.TrimSuffix(d.UsePercent, "%")
|
||||
info.DiskPercent, _ = strconv.ParseFloat(pct, 64)
|
||||
break
|
||||
}
|
||||
}
|
||||
// If no root mount found but we have disks, use the first one
|
||||
if info.DiskTotal == "" && len(disks) > 0 {
|
||||
d := disks[0]
|
||||
info.DiskTotal = d.Size
|
||||
info.DiskUsed = d.Used
|
||||
pct := strings.TrimSuffix(d.UsePercent, "%")
|
||||
info.DiskPercent, _ = strconv.ParseFloat(pct, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// Site count
|
||||
if sites, err := h.DB.ListSites(); err == nil {
|
||||
info.SiteCount = len(sites)
|
||||
}
|
||||
|
||||
// Services
|
||||
services := []struct{ name, unit string }{
|
||||
{"Nginx", "nginx"},
|
||||
{"AUTARCH Web", "autarch-web"},
|
||||
{"AUTARCH DNS", "autarch-dns"},
|
||||
{"Setec Manager", "setec-manager"},
|
||||
}
|
||||
for _, svc := range services {
|
||||
si := serviceInfo{Name: svc.name}
|
||||
active, err := deploy.IsActive(svc.unit)
|
||||
if err == nil && active {
|
||||
si.Status = "active"
|
||||
si.Running = true
|
||||
} else {
|
||||
si.Status = "inactive"
|
||||
si.Running = false
|
||||
}
|
||||
info.Services = append(info.Services, si)
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func formatBytes(b float64) string {
|
||||
units := []string{"B", "KB", "MB", "GB", "TB"}
|
||||
i := 0
|
||||
for b >= 1024 && i < len(units)-1 {
|
||||
b /= 1024
|
||||
i++
|
||||
}
|
||||
return strconv.FormatFloat(b, 'f', 1, 64) + " " + units[i]
|
||||
}
|
||||
|
||||
// uptimeSince returns a human-readable duration.
|
||||
func uptimeSince(d time.Duration) string {
|
||||
days := int(d.Hours()) / 24
|
||||
hours := int(d.Hours()) % 24
|
||||
mins := int(d.Minutes()) % 60
|
||||
|
||||
if days > 0 {
|
||||
return strconv.Itoa(days) + "d " + strconv.Itoa(hours) + "h " + strconv.Itoa(mins) + "m"
|
||||
}
|
||||
if hours > 0 {
|
||||
return strconv.Itoa(hours) + "h " + strconv.Itoa(mins) + "m"
|
||||
}
|
||||
return strconv.Itoa(mins) + "m"
|
||||
}
|
||||
Reference in New Issue
Block a user