...
This commit is contained in:
541
pkg2_dont_use/heroagent/pages/admin.go
Normal file
541
pkg2_dont_use/heroagent/pages/admin.go
Normal file
@@ -0,0 +1,541 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/heroagent/handlers"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/system/stats"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
)
|
||||
|
||||
// UptimeProvider defines an interface for getting system uptime
|
||||
type UptimeProvider interface {
|
||||
GetUptime() string
|
||||
}
|
||||
|
||||
// AdminHandler handles admin-related page routes
|
||||
type AdminHandler struct {
|
||||
uptimeProvider UptimeProvider
|
||||
statsManager *stats.StatsManager
|
||||
pmSocketPath string
|
||||
pmSecret string
|
||||
}
|
||||
|
||||
// NewAdminHandler creates a new AdminHandler
|
||||
func NewAdminHandler(uptimeProvider UptimeProvider, statsManager *stats.StatsManager, pmSocketPath, pmSecret string) *AdminHandler {
|
||||
// If statsManager is nil, create a new one with default settings
|
||||
if statsManager == nil {
|
||||
var err error
|
||||
statsManager, err = stats.NewStatsManagerWithDefaults()
|
||||
if err != nil {
|
||||
// Log the error but continue with nil statsManager
|
||||
fmt.Printf("Error creating StatsManager: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &AdminHandler{
|
||||
uptimeProvider: uptimeProvider,
|
||||
statsManager: statsManager,
|
||||
pmSocketPath: pmSocketPath,
|
||||
pmSecret: pmSecret,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes registers all admin page routes
|
||||
func (h *AdminHandler) RegisterRoutes(app *fiber.App) {
|
||||
// Admin routes
|
||||
admin := app.Group("/admin")
|
||||
|
||||
// Dashboard
|
||||
admin.Get("/", h.getDashboard)
|
||||
|
||||
// Create service handler with the correct socket path and secret
|
||||
serviceHandler := handlers.NewServiceHandler(h.pmSocketPath, h.pmSecret)
|
||||
// Services routes
|
||||
admin.Get("/services", serviceHandler.GetServices)
|
||||
admin.Get("/services/data", serviceHandler.GetServicesFragment)
|
||||
admin.Post("/services/start", serviceHandler.StartService)
|
||||
admin.Post("/services/stop", serviceHandler.StopService)
|
||||
admin.Post("/services/restart", serviceHandler.RestartService)
|
||||
admin.Post("/services/delete", serviceHandler.DeleteService)
|
||||
admin.Get("/services/logs", serviceHandler.GetServiceLogs)
|
||||
|
||||
// System routes
|
||||
admin.Get("/system/info", h.getSystemInfo)
|
||||
admin.Get("/system/hardware-stats", h.getHardwareStats)
|
||||
|
||||
// Create process handler
|
||||
processHandler := handlers.NewProcessHandler(h.statsManager)
|
||||
admin.Get("/system/processes", processHandler.GetProcesses)
|
||||
admin.Get("/system/processes-data", processHandler.GetProcessesData)
|
||||
|
||||
// Create log handler
|
||||
// Ensure log directory exists
|
||||
// Using the same shared logs path as process manager
|
||||
logDir := filepath.Join(os.TempDir(), "heroagent_logs")
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
fmt.Printf("Error creating log directory: %v\n", err)
|
||||
}
|
||||
|
||||
logHandler, err := handlers.NewLogHandler(logDir)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating log handler: %v\n", err)
|
||||
// Fallback to old implementation if log handler creation failed
|
||||
admin.Get("/system/logs", h.getSystemLogs)
|
||||
admin.Get("/system/logs-test", h.getSystemLogsTest)
|
||||
} else {
|
||||
fmt.Printf("Log handler created successfully\n")
|
||||
// Use the log handler for log routes
|
||||
admin.Get("/system/logs", logHandler.GetLogs)
|
||||
// Keep the fragment endpoint for backward compatibility
|
||||
// but it now just redirects to the main logs endpoint
|
||||
admin.Get("/system/logs-fragment", logHandler.GetLogsFragment)
|
||||
admin.Get("/system/logs-test", h.getSystemLogsTest) // Keep the test logs route
|
||||
|
||||
// Log API endpoints
|
||||
app.Get("/api/logs", logHandler.GetLogsAPI)
|
||||
}
|
||||
|
||||
admin.Get("/system/settings", h.getSystemSettings)
|
||||
|
||||
// OpenRPC routes
|
||||
admin.Get("/openrpc", h.getOpenRPCManager)
|
||||
admin.Get("/openrpc/vfs", h.getOpenRPCVFS)
|
||||
admin.Get("/openrpc/vfs/logs", h.getOpenRPCVFSLogs)
|
||||
|
||||
// Redirect root to admin
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.Redirect("/admin")
|
||||
})
|
||||
}
|
||||
|
||||
// getDashboard renders the admin dashboard
|
||||
func (h *AdminHandler) getDashboard(c *fiber.Ctx) error {
|
||||
return c.Render("admin/index", fiber.Map{
|
||||
"title": "Dashboard",
|
||||
})
|
||||
}
|
||||
|
||||
// getSystemInfo renders the system info page
|
||||
func (h *AdminHandler) getSystemInfo(c *fiber.Ctx) error {
|
||||
// Initialize default values
|
||||
cpuInfo := "Unknown"
|
||||
memoryInfo := "Unknown"
|
||||
diskInfo := "Unknown"
|
||||
networkInfo := "Unknown"
|
||||
osInfo := "Unknown"
|
||||
uptimeInfo := "Unknown"
|
||||
|
||||
// Get hardware stats from the StatsManager
|
||||
var hardwareStats map[string]interface{}
|
||||
if h.statsManager != nil {
|
||||
hardwareStats = h.statsManager.GetHardwareStats()
|
||||
} else {
|
||||
// Fallback to direct function call if StatsManager is not available
|
||||
hardwareStats = stats.GetHardwareStats()
|
||||
}
|
||||
|
||||
// Extract the formatted strings - safely handle different return types
|
||||
if cpuVal, ok := hardwareStats["cpu"]; ok {
|
||||
switch v := cpuVal.(type) {
|
||||
case string:
|
||||
cpuInfo = v
|
||||
case map[string]interface{}:
|
||||
// Format the map into a string
|
||||
if model, ok := v["model"].(string); ok {
|
||||
usage := 0.0
|
||||
if usagePercent, ok := v["usage_percent"].(float64); ok {
|
||||
usage = usagePercent
|
||||
}
|
||||
cpuInfo = fmt.Sprintf("%s (Usage: %.1f%%)", model, usage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if memVal, ok := hardwareStats["memory"]; ok {
|
||||
switch v := memVal.(type) {
|
||||
case string:
|
||||
memoryInfo = v
|
||||
case map[string]interface{}:
|
||||
// Format the map into a string
|
||||
total, used := 0.0, 0.0
|
||||
if totalGB, ok := v["total_gb"].(float64); ok {
|
||||
total = totalGB
|
||||
}
|
||||
if usedGB, ok := v["used_gb"].(float64); ok {
|
||||
used = usedGB
|
||||
}
|
||||
usedPercent := 0.0
|
||||
if percent, ok := v["used_percent"].(float64); ok {
|
||||
usedPercent = percent
|
||||
}
|
||||
memoryInfo = fmt.Sprintf("%.1f GB / %.1f GB (%.1f%% used)", used, total, usedPercent)
|
||||
}
|
||||
}
|
||||
|
||||
if diskVal, ok := hardwareStats["disk"]; ok {
|
||||
switch v := diskVal.(type) {
|
||||
case string:
|
||||
diskInfo = v
|
||||
case map[string]interface{}:
|
||||
// Format the map into a string
|
||||
total, used := 0.0, 0.0
|
||||
if totalGB, ok := v["total_gb"].(float64); ok {
|
||||
total = totalGB
|
||||
}
|
||||
if usedGB, ok := v["used_gb"].(float64); ok {
|
||||
used = usedGB
|
||||
}
|
||||
usedPercent := 0.0
|
||||
if percent, ok := v["used_percent"].(float64); ok {
|
||||
usedPercent = percent
|
||||
}
|
||||
diskInfo = fmt.Sprintf("%.1f GB / %.1f GB (%.1f%% used)", used, total, usedPercent)
|
||||
}
|
||||
}
|
||||
|
||||
if netVal, ok := hardwareStats["network"]; ok {
|
||||
switch v := netVal.(type) {
|
||||
case string:
|
||||
networkInfo = v
|
||||
case map[string]interface{}:
|
||||
// Format the map into a string
|
||||
var interfaces []string
|
||||
if ifaces, ok := v["interfaces"].([]interface{}); ok {
|
||||
for _, iface := range ifaces {
|
||||
if ifaceMap, ok := iface.(map[string]interface{}); ok {
|
||||
name := ifaceMap["name"].(string)
|
||||
ip := ifaceMap["ip"].(string)
|
||||
interfaces = append(interfaces, fmt.Sprintf("%s: %s", name, ip))
|
||||
}
|
||||
}
|
||||
networkInfo = strings.Join(interfaces, ", ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get OS info
|
||||
hostInfo, err := host.Info()
|
||||
if err == nil {
|
||||
osInfo = fmt.Sprintf("%s %s (%s)", hostInfo.Platform, hostInfo.PlatformVersion, hostInfo.KernelVersion)
|
||||
}
|
||||
|
||||
// Get uptime
|
||||
if h.uptimeProvider != nil {
|
||||
uptimeInfo = h.uptimeProvider.GetUptime()
|
||||
}
|
||||
|
||||
// Render the template with the system info
|
||||
return c.Render("admin/system/info", fiber.Map{
|
||||
"title": "System Information",
|
||||
"cpuInfo": cpuInfo,
|
||||
"memoryInfo": memoryInfo,
|
||||
"diskInfo": diskInfo,
|
||||
"networkInfo": networkInfo,
|
||||
"osInfo": osInfo,
|
||||
"uptimeInfo": uptimeInfo,
|
||||
})
|
||||
}
|
||||
|
||||
// getSystemLogs renders the system logs page
|
||||
func (h *AdminHandler) getSystemLogs(c *fiber.Ctx) error {
|
||||
return c.Render("admin/system/logs", fiber.Map{
|
||||
"title": "System Logs",
|
||||
})
|
||||
}
|
||||
|
||||
// getSystemLogsTest renders the test logs page
|
||||
func (h *AdminHandler) getSystemLogsTest(c *fiber.Ctx) error {
|
||||
return c.Render("admin/system/logs_test", fiber.Map{
|
||||
"title": "Test Logs",
|
||||
})
|
||||
}
|
||||
|
||||
// getSystemSettings renders the system settings page
|
||||
func (h *AdminHandler) getSystemSettings(c *fiber.Ctx) error {
|
||||
// Get system settings
|
||||
// This is a placeholder - in a real app, you would fetch settings from a database or config file
|
||||
settings := map[string]interface{}{
|
||||
"logLevel": "info",
|
||||
"enableDebugMode": false,
|
||||
"dataDirectory": "/var/lib/heroagent",
|
||||
"maxLogSize": "100MB",
|
||||
}
|
||||
|
||||
return c.Render("admin/system/settings", fiber.Map{
|
||||
"title": "System Settings",
|
||||
"settings": settings,
|
||||
})
|
||||
}
|
||||
|
||||
// getHardwareStats returns only the hardware stats for Unpoly polling
|
||||
func (h *AdminHandler) getHardwareStats(c *fiber.Ctx) error {
|
||||
// Initialize default values
|
||||
cpuInfo := "Unknown"
|
||||
memoryInfo := "Unknown"
|
||||
diskInfo := "Unknown"
|
||||
networkInfo := "Unknown"
|
||||
|
||||
// Get hardware stats from the StatsManager
|
||||
var hardwareStats map[string]interface{}
|
||||
if h.statsManager != nil {
|
||||
hardwareStats = h.statsManager.GetHardwareStats()
|
||||
} else {
|
||||
// Fallback to direct function call if StatsManager is not available
|
||||
hardwareStats = stats.GetHardwareStats()
|
||||
}
|
||||
|
||||
// Extract the formatted strings - safely handle different return types
|
||||
if cpuVal, ok := hardwareStats["cpu"]; ok {
|
||||
switch v := cpuVal.(type) {
|
||||
case string:
|
||||
cpuInfo = v
|
||||
case map[string]interface{}:
|
||||
// Format the map into a string
|
||||
if model, ok := v["model"].(string); ok {
|
||||
cpuInfo = model
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if memVal, ok := hardwareStats["memory"]; ok {
|
||||
switch v := memVal.(type) {
|
||||
case string:
|
||||
memoryInfo = v
|
||||
case map[string]interface{}:
|
||||
// Format the map into a string
|
||||
total, used := 0.0, 0.0
|
||||
if totalGB, ok := v["total_gb"].(float64); ok {
|
||||
total = totalGB
|
||||
}
|
||||
if usedGB, ok := v["used_gb"].(float64); ok {
|
||||
used = usedGB
|
||||
}
|
||||
memoryInfo = fmt.Sprintf("%.1f GB / %.1f GB", used, total)
|
||||
}
|
||||
}
|
||||
|
||||
if diskVal, ok := hardwareStats["disk"]; ok {
|
||||
switch v := diskVal.(type) {
|
||||
case string:
|
||||
diskInfo = v
|
||||
case map[string]interface{}:
|
||||
// Format the map into a string
|
||||
total, used := 0.0, 0.0
|
||||
if totalGB, ok := v["total_gb"].(float64); ok {
|
||||
total = totalGB
|
||||
}
|
||||
if usedGB, ok := v["used_gb"].(float64); ok {
|
||||
used = usedGB
|
||||
}
|
||||
diskInfo = fmt.Sprintf("%.1f GB / %.1f GB", used, total)
|
||||
}
|
||||
}
|
||||
|
||||
if netVal, ok := hardwareStats["network"]; ok {
|
||||
switch v := netVal.(type) {
|
||||
case string:
|
||||
networkInfo = v
|
||||
case map[string]interface{}:
|
||||
// Format the map into a string
|
||||
var interfaces []string
|
||||
if ifaces, ok := v["interfaces"].([]interface{}); ok {
|
||||
for _, iface := range ifaces {
|
||||
if ifaceMap, ok := iface.(map[string]interface{}); ok {
|
||||
name := ifaceMap["name"].(string)
|
||||
ip := ifaceMap["ip"].(string)
|
||||
interfaces = append(interfaces, fmt.Sprintf("%s: %s", name, ip))
|
||||
}
|
||||
}
|
||||
networkInfo = strings.Join(interfaces, ", ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format for display
|
||||
cpuUsage := "0.0%"
|
||||
memUsage := "0.0%"
|
||||
diskUsage := "0.0%"
|
||||
|
||||
// Safely extract usage percentages
|
||||
if cpuVal, ok := hardwareStats["cpu"].(map[string]interface{}); ok {
|
||||
if usagePercent, ok := cpuVal["usage_percent"].(float64); ok {
|
||||
cpuUsage = fmt.Sprintf("%.1f%%", usagePercent)
|
||||
}
|
||||
}
|
||||
|
||||
if memVal, ok := hardwareStats["memory"].(map[string]interface{}); ok {
|
||||
if usedPercent, ok := memVal["used_percent"].(float64); ok {
|
||||
memUsage = fmt.Sprintf("%.1f%%", usedPercent)
|
||||
}
|
||||
}
|
||||
|
||||
if diskVal, ok := hardwareStats["disk"].(map[string]interface{}); ok {
|
||||
if usedPercent, ok := diskVal["used_percent"].(float64); ok {
|
||||
diskUsage = fmt.Sprintf("%.1f%%", usedPercent)
|
||||
}
|
||||
}
|
||||
|
||||
// Render only the hardware stats fragment
|
||||
return c.Render("admin/system/hardware_stats_fragment", fiber.Map{
|
||||
"cpuInfo": cpuInfo,
|
||||
"memoryInfo": memoryInfo,
|
||||
"diskInfo": diskInfo,
|
||||
"networkInfo": networkInfo,
|
||||
"cpuUsage": cpuUsage,
|
||||
"memUsage": memUsage,
|
||||
"diskUsage": diskUsage,
|
||||
})
|
||||
}
|
||||
|
||||
// getProcesses has been moved to the handlers package
|
||||
// See handlers.ProcessHandler.GetProcesses
|
||||
|
||||
// getOpenRPCManager renders the OpenRPC Manager view page
|
||||
func (h *AdminHandler) getOpenRPCManager(c *fiber.Ctx) error {
|
||||
return c.Render("admin/openrpc/index", fiber.Map{
|
||||
"title": "OpenRPC Manager",
|
||||
})
|
||||
}
|
||||
|
||||
// getOpenRPCVFS renders the OpenRPC VFS view page
|
||||
func (h *AdminHandler) getOpenRPCVFS(c *fiber.Ctx) error {
|
||||
return c.Render("admin/openrpc/vfs", fiber.Map{
|
||||
"title": "VFS OpenRPC Interface",
|
||||
})
|
||||
}
|
||||
|
||||
// getOpenRPCVFSLogs renders the OpenRPC logs content for Unpoly or direct access
|
||||
func (h *AdminHandler) getOpenRPCVFSLogs(c *fiber.Ctx) error {
|
||||
// Get query parameters
|
||||
method := c.Query("method", "")
|
||||
params := c.Query("params", "")
|
||||
|
||||
// Define available methods and their display names
|
||||
methods := []string{
|
||||
"vfs_ls",
|
||||
"vfs_read",
|
||||
"vfs_write",
|
||||
"vfs_mkdir",
|
||||
"vfs_rm",
|
||||
"vfs_mv",
|
||||
"vfs_cp",
|
||||
"vfs_exists",
|
||||
"vfs_isdir",
|
||||
"vfs_isfile",
|
||||
}
|
||||
|
||||
methodDisplayNames := map[string]string{
|
||||
"vfs_ls": "List Directory",
|
||||
"vfs_read": "Read File",
|
||||
"vfs_write": "Write File",
|
||||
"vfs_mkdir": "Create Directory",
|
||||
"vfs_rm": "Remove File/Directory",
|
||||
"vfs_mv": "Move/Rename",
|
||||
"vfs_cp": "Copy",
|
||||
"vfs_exists": "Check Exists",
|
||||
"vfs_isdir": "Is Directory",
|
||||
"vfs_isfile": "Is File",
|
||||
}
|
||||
|
||||
// Generate method options HTML
|
||||
methodOptions := generateMethodOptions(methods, methodDisplayNames)
|
||||
|
||||
// Initialize variables
|
||||
var requestJSON, responseJSON, responseTime string
|
||||
var hasResponse bool
|
||||
|
||||
// If a method is selected, make the OpenRPC call
|
||||
if method != "" {
|
||||
// Prepare the request
|
||||
requestJSON = fmt.Sprintf(`{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "%s",
|
||||
"params": %s,
|
||||
"id": 1
|
||||
}`, method, params)
|
||||
|
||||
// In a real implementation, we would make the actual OpenRPC call here
|
||||
// For now, we'll just simulate a response
|
||||
|
||||
// Simulate response time (would be real in production)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
responseTime = "100ms"
|
||||
|
||||
// Simulate a response based on the method
|
||||
switch method {
|
||||
case "vfs_ls":
|
||||
responseJSON = `{
|
||||
"jsonrpc": "2.0",
|
||||
"result": [
|
||||
{"name": "file1.txt", "size": 1024, "isDir": false, "modTime": "2023-01-01T12:00:00Z"},
|
||||
{"name": "dir1", "size": 0, "isDir": true, "modTime": "2023-01-01T12:00:00Z"}
|
||||
],
|
||||
"id": 1
|
||||
}`
|
||||
case "vfs_read":
|
||||
responseJSON = `{
|
||||
"jsonrpc": "2.0",
|
||||
"result": "File content would be here",
|
||||
"id": 1
|
||||
}`
|
||||
default:
|
||||
responseJSON = `{
|
||||
"jsonrpc": "2.0",
|
||||
"result": "Operation completed successfully",
|
||||
"id": 1
|
||||
}`
|
||||
}
|
||||
|
||||
hasResponse = true
|
||||
}
|
||||
|
||||
// Determine if this is an Unpoly request
|
||||
isUnpoly := c.Get("X-Up-Target") != ""
|
||||
|
||||
// If it's an Unpoly request, render just the logs fragment
|
||||
if isUnpoly {
|
||||
return c.Render("admin/openrpc/vfs_logs", fiber.Map{
|
||||
"methodOptions": methodOptions,
|
||||
"selectedMethod": method,
|
||||
"params": params,
|
||||
"requestJSON": requestJSON,
|
||||
"responseJSON": responseJSON,
|
||||
"responseTime": responseTime,
|
||||
"hasResponse": hasResponse,
|
||||
})
|
||||
}
|
||||
|
||||
// Otherwise render the full page
|
||||
return c.Render("admin/openrpc/vfs_overview", fiber.Map{
|
||||
"title": "VFS OpenRPC Logs",
|
||||
"methodOptions": methodOptions,
|
||||
"selectedMethod": method,
|
||||
"params": params,
|
||||
"requestJSON": requestJSON,
|
||||
"responseJSON": responseJSON,
|
||||
"responseTime": responseTime,
|
||||
"hasResponse": hasResponse,
|
||||
})
|
||||
}
|
||||
|
||||
// generateMethodOptions generates HTML option tags for method dropdown
|
||||
func generateMethodOptions(methods []string, methodDisplayNames map[string]string) string {
|
||||
var options []string
|
||||
for _, method := range methods {
|
||||
displayName, ok := methodDisplayNames[method]
|
||||
if !ok {
|
||||
displayName = method
|
||||
}
|
||||
options = append(options, fmt.Sprintf(`<option value="%s">%s</option>`, method, displayName))
|
||||
}
|
||||
return strings.Join(options, "\n")
|
||||
}
|
||||
|
||||
// Note: getProcessesData has been consolidated in the API routes file
|
||||
// to avoid duplication and ensure consistent behavior
|
187
pkg2_dont_use/heroagent/pages/jobs.go
Normal file
187
pkg2_dont_use/heroagent/pages/jobs.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/herojobs"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// JobDisplayInfo represents information about a job for display purposes
|
||||
type JobDisplayInfo struct {
|
||||
JobID string `json:"jobid"`
|
||||
CircleID string `json:"circleid"`
|
||||
Topic string `json:"topic"`
|
||||
Status string `json:"status"`
|
||||
SessionKey string `json:"sessionkey"`
|
||||
Params string `json:"params"`
|
||||
ParamsType string `json:"paramstype"`
|
||||
Result string `json:"result"`
|
||||
Error string `json:"error"`
|
||||
TimeScheduled int64 `json:"time_scheduled"`
|
||||
TimeStart int64 `json:"time_start"`
|
||||
TimeEnd int64 `json:"time_end"`
|
||||
Timeout int64 `json:"timeout"`
|
||||
}
|
||||
|
||||
// JobHandler handles job-related page routes
|
||||
type JobHandler struct {
|
||||
client *herojobs.RedisClient
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewJobHandler creates a new job handler with the provided socket path
|
||||
func NewJobHandler(redisAddr string, logger *log.Logger) (*JobHandler, error) {
|
||||
// Assuming SSL is false as per README example herojobs.NewRedisClient("localhost:6379", false)
|
||||
// This might need to be configurable later.
|
||||
client, err := herojobs.NewRedisClient(redisAddr, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create HeroJobs Redis client: %w", err)
|
||||
}
|
||||
|
||||
return &JobHandler{
|
||||
client: client,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RegisterRoutes registers job page routes
|
||||
func (h *JobHandler) RegisterRoutes(app *fiber.App) {
|
||||
// Register routes for /jobs
|
||||
jobs := app.Group("/jobs")
|
||||
jobs.Get("/", h.getJobsPage)
|
||||
jobs.Get("/list", h.getJobsList)
|
||||
|
||||
// Register the same routes under /admin/jobs for consistency
|
||||
adminJobs := app.Group("/admin/jobs")
|
||||
adminJobs.Get("/", h.getJobsPage)
|
||||
adminJobs.Get("/list", h.getJobsList)
|
||||
}
|
||||
|
||||
// getJobsPage renders the jobs page
|
||||
func (h *JobHandler) getJobsPage(c *fiber.Ctx) error {
|
||||
// Assuming h.client (RedisClient) is valid if NewJobHandler succeeded.
|
||||
// The client is connected on creation. A Ping method could be used here for a health check if available.
|
||||
// The previous connect/close logic per-request is removed.
|
||||
var warning string // This will be empty unless a new check (e.g., Ping) sets it.
|
||||
return c.Render("admin/jobs", fiber.Map{
|
||||
"title": "Jobs",
|
||||
"warning": warning, // warning will be empty for now
|
||||
"error": "",
|
||||
})
|
||||
}
|
||||
|
||||
// getJobsList returns the jobs list fragment for AJAX updates
|
||||
func (h *JobHandler) getJobsList(c *fiber.Ctx) error {
|
||||
// Get parameters from query
|
||||
circleID := c.Query("circleid", "")
|
||||
topic := c.Query("topic", "")
|
||||
|
||||
// Get jobs
|
||||
jobs, err := h.getJobsData(circleID, topic)
|
||||
if err != nil {
|
||||
h.logger.Printf("Error getting jobs: %v", err)
|
||||
// Return the error in the template
|
||||
return c.Render("admin/jobs_list_fragment", fiber.Map{
|
||||
"error": fmt.Sprintf("Failed to get jobs: %v", err),
|
||||
"jobs": []JobDisplayInfo{},
|
||||
})
|
||||
}
|
||||
|
||||
// Render only the jobs fragment
|
||||
return c.Render("admin/jobs_list_fragment", fiber.Map{
|
||||
"jobs": jobs,
|
||||
})
|
||||
}
|
||||
|
||||
// getJobsData gets job data from the HeroJobs server
|
||||
func (h *JobHandler) getJobsData(circleID, topic string) ([]JobDisplayInfo, error) {
|
||||
// Assuming h.client (RedisClient) is already connected (established by NewJobHandler).
|
||||
// It should not be closed here as it's a long-lived client.
|
||||
// Connect() and Close() calls per-request are removed.
|
||||
|
||||
// If circleID and topic are not provided, try to list all jobs
|
||||
if circleID == "" && topic == "" {
|
||||
// Try to get some default jobs
|
||||
defaultCircles := []string{"default", "system"}
|
||||
defaultTopics := []string{"default", "system"}
|
||||
|
||||
var allJobs []JobDisplayInfo
|
||||
|
||||
// Try each combination
|
||||
for _, circle := range defaultCircles {
|
||||
for _, t := range defaultTopics {
|
||||
jobIDs, err := h.client.ListJobs(circle, t)
|
||||
if err != nil {
|
||||
h.logger.Printf("Could not list jobs for circle=%s, topic=%s: %v", circle, t, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, jobID := range jobIDs {
|
||||
job, err := h.client.GetJob(jobID)
|
||||
if err != nil {
|
||||
h.logger.Printf("Error getting job %s: %v", jobID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
allJobs = append(allJobs, JobDisplayInfo{
|
||||
JobID: fmt.Sprintf("%d", job.JobID),
|
||||
CircleID: job.CircleID,
|
||||
Topic: job.Topic,
|
||||
Status: string(job.Status),
|
||||
SessionKey: job.SessionKey,
|
||||
Params: job.Params,
|
||||
ParamsType: string(job.ParamsType),
|
||||
Result: job.Result,
|
||||
Error: job.Error,
|
||||
TimeScheduled: job.TimeScheduled,
|
||||
TimeStart: job.TimeStart,
|
||||
TimeEnd: job.TimeEnd,
|
||||
Timeout: job.Timeout,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allJobs, nil
|
||||
} else if circleID == "" || topic == "" {
|
||||
// If only one of the parameters is provided, we can't list jobs
|
||||
return []JobDisplayInfo{}, nil
|
||||
}
|
||||
|
||||
// List jobs
|
||||
jobIDs, err := h.client.ListJobs(circleID, topic)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list jobs: %w", err)
|
||||
}
|
||||
|
||||
// Get details for each job
|
||||
jobsList := make([]JobDisplayInfo, 0, len(jobIDs))
|
||||
for _, jobID := range jobIDs {
|
||||
job, err := h.client.GetJob(jobID)
|
||||
if err != nil {
|
||||
h.logger.Printf("Error getting job %s: %v", jobID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
jobInfo := JobDisplayInfo{
|
||||
JobID: fmt.Sprintf("%d", job.JobID),
|
||||
CircleID: job.CircleID,
|
||||
Topic: job.Topic,
|
||||
Status: string(job.Status),
|
||||
SessionKey: job.SessionKey,
|
||||
Params: job.Params,
|
||||
ParamsType: string(job.ParamsType),
|
||||
Result: job.Result,
|
||||
Error: job.Error,
|
||||
TimeScheduled: job.TimeScheduled,
|
||||
TimeStart: job.TimeStart,
|
||||
TimeEnd: job.TimeEnd,
|
||||
Timeout: job.Timeout,
|
||||
}
|
||||
jobsList = append(jobsList, jobInfo)
|
||||
}
|
||||
|
||||
return jobsList, nil
|
||||
}
|
111
pkg2_dont_use/heroagent/pages/services.go
Normal file
111
pkg2_dont_use/heroagent/pages/services.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// ServiceHandler handles service-related page routes
|
||||
type ServiceHandler struct {
|
||||
client *openrpc.Client
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewServiceHandler creates a new service handler with the provided socket path and secret
|
||||
func NewServiceHandler(socketPath, secret string, logger *log.Logger) *ServiceHandler {
|
||||
fmt.Printf("DEBUG: Creating new pages.ServiceHandler with socket path: %s and secret: %s\n", socketPath, secret)
|
||||
return &ServiceHandler{
|
||||
client: openrpc.NewClient(socketPath, secret),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes registers service page routes
|
||||
func (h *ServiceHandler) RegisterRoutes(app *fiber.App) {
|
||||
services := app.Group("/services")
|
||||
|
||||
// Page routes
|
||||
services.Get("/", h.getServicesPage)
|
||||
services.Get("/data", h.getServicesData)
|
||||
}
|
||||
|
||||
// getServicesPage renders the services page
|
||||
func (h *ServiceHandler) getServicesPage(c *fiber.Ctx) error {
|
||||
// Get processes to display on the initial page load
|
||||
processes, _ := h.getProcessList()
|
||||
|
||||
// Check if we can connect to the process manager
|
||||
var warning string
|
||||
_, err := h.client.ListProcesses("json")
|
||||
if err != nil {
|
||||
warning = "Could not connect to process manager: " + err.Error()
|
||||
h.logger.Printf("Warning: %s", warning)
|
||||
}
|
||||
|
||||
return c.Render("admin/services", fiber.Map{
|
||||
"title": "Services",
|
||||
"processes": processes,
|
||||
"warning": warning,
|
||||
})
|
||||
}
|
||||
|
||||
// getServicesData returns only the services fragment for AJAX updates
|
||||
func (h *ServiceHandler) getServicesData(c *fiber.Ctx) error {
|
||||
// Get processes
|
||||
processes, _ := h.getProcessList()
|
||||
|
||||
// Render only the services fragment
|
||||
return c.Render("admin/services_fragment", fiber.Map{
|
||||
"processes": processes,
|
||||
})
|
||||
}
|
||||
|
||||
// getProcessList gets a list of processes from the process manager
|
||||
func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) {
|
||||
// Debug: Log the function entry
|
||||
h.logger.Printf("Entering getProcessList() function")
|
||||
fmt.Printf("DEBUG: getProcessList called using client: %p\n", h.client)
|
||||
|
||||
// Get the list of processes via the client
|
||||
result, err := h.client.ListProcesses("json")
|
||||
if err != nil {
|
||||
h.logger.Printf("Error listing processes: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert the result to a slice of ProcessStatus
|
||||
listResult, ok := result.([]interface{})
|
||||
if !ok {
|
||||
h.logger.Printf("Error: unexpected result type from ListProcesses")
|
||||
return nil, fmt.Errorf("unexpected result type from ListProcesses")
|
||||
}
|
||||
|
||||
// Convert to display info format
|
||||
displayInfoList := make([]ProcessDisplayInfo, 0, len(listResult))
|
||||
for _, item := range listResult {
|
||||
procMap, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create a ProcessDisplayInfo from the map
|
||||
displayInfo := ProcessDisplayInfo{
|
||||
ID: fmt.Sprintf("%v", procMap["pid"]),
|
||||
Name: fmt.Sprintf("%v", procMap["name"]),
|
||||
Status: fmt.Sprintf("%v", procMap["status"]),
|
||||
Uptime: fmt.Sprintf("%v", procMap["uptime"]),
|
||||
StartTime: fmt.Sprintf("%v", procMap["start_time"]),
|
||||
CPU: fmt.Sprintf("%v%%", procMap["cpu"]),
|
||||
Memory: fmt.Sprintf("%v MB", procMap["memory"]),
|
||||
}
|
||||
displayInfoList = append(displayInfoList, displayInfo)
|
||||
}
|
||||
|
||||
// Debug: Log the number of processes
|
||||
h.logger.Printf("Found %d processes", len(displayInfoList))
|
||||
|
||||
return displayInfoList, nil
|
||||
}
|
54
pkg2_dont_use/heroagent/pages/types.go
Normal file
54
pkg2_dont_use/heroagent/pages/types.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/processmanager"
|
||||
)
|
||||
|
||||
// ProcessDisplayInfo represents information about a process for display purposes
|
||||
type ProcessDisplayInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Uptime string `json:"uptime"`
|
||||
StartTime string `json:"start_time"`
|
||||
CPU string `json:"cpu"`
|
||||
Memory string `json:"memory"`
|
||||
}
|
||||
|
||||
// ConvertToDisplayInfo converts a ProcessInfo from the processmanager package to ProcessDisplayInfo
|
||||
func ConvertToDisplayInfo(info *processmanager.ProcessInfo) ProcessDisplayInfo {
|
||||
// Calculate uptime from start time
|
||||
uptime := formatUptime(time.Since(info.StartTime))
|
||||
|
||||
return ProcessDisplayInfo{
|
||||
ID: fmt.Sprintf("%d", info.PID),
|
||||
Name: info.Name,
|
||||
Status: string(info.Status),
|
||||
Uptime: uptime,
|
||||
StartTime: info.StartTime.Format("2006-01-02 15:04:05"),
|
||||
CPU: fmt.Sprintf("%.2f%%", info.CPUPercent),
|
||||
Memory: fmt.Sprintf("%.2f MB", info.MemoryMB),
|
||||
}
|
||||
}
|
||||
|
||||
// formatUptime formats a duration as a human-readable uptime string
|
||||
func formatUptime(duration time.Duration) string {
|
||||
totalSeconds := int(duration.Seconds())
|
||||
days := totalSeconds / (24 * 3600)
|
||||
hours := (totalSeconds % (24 * 3600)) / 3600
|
||||
minutes := (totalSeconds % 3600) / 60
|
||||
seconds := totalSeconds % 60
|
||||
|
||||
if days > 0 {
|
||||
return fmt.Sprintf("%d days, %d hours", days, hours)
|
||||
} else if hours > 0 {
|
||||
return fmt.Sprintf("%d hours, %d minutes", hours, minutes)
|
||||
} else if minutes > 0 {
|
||||
return fmt.Sprintf("%d minutes, %d seconds", minutes, seconds)
|
||||
} else {
|
||||
return fmt.Sprintf("%d seconds", seconds)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user