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 }