...
This commit is contained in:
		
							
								
								
									
										213
									
								
								pkg/servers/ui/DESIGN_PLAN_UI.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								pkg/servers/ui/DESIGN_PLAN_UI.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,213 @@ | ||||
| # Project Plan: Bootstrap UI with Fiber and Jet | ||||
|  | ||||
| **Goal:** Develop a new UI module using Go (Fiber framework), Jet templates, and Bootstrap 5, following an MVC pattern. The UI will include a dashboard and a process manager page. | ||||
|  | ||||
| **Location:** `pkg/servers/ui` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1. Directory Structure (MVC) | ||||
|  | ||||
| We'll establish a clear MVC structure within `pkg/servers/ui`: | ||||
|  | ||||
| ```mermaid | ||||
| graph TD | ||||
|     A[pkg/servers/ui] --> B(app.go); | ||||
|     A --> C(controllers); | ||||
|     A --> M(models); | ||||
|     A --> V(views); | ||||
|     A --> S(static); | ||||
|     A --> R(routes); | ||||
|  | ||||
|     C --> C1(auth_controller.go); | ||||
|     C --> C2(dashboard_controller.go); | ||||
|     C --> C3(process_controller.go); | ||||
|  | ||||
|     M --> M1(process_model.go); | ||||
|     M --> M2(user_model.go); | ||||
|  | ||||
|     V --> VL(layouts); | ||||
|     V --> VP(pages); | ||||
|     V --> VC(components); | ||||
|  | ||||
|     VL --> VLL(base.jet); | ||||
|  | ||||
|     VP --> VPD(dashboard.jet); | ||||
|     VP --> VPP(process_manager.jet); | ||||
|     VP --> VPL(login.jet); | ||||
|  | ||||
|     VC --> VCN(navbar.jet); | ||||
|     VC --> VCS(sidebar.jet); | ||||
|  | ||||
|     S --> SCSS(css); | ||||
|     S --> SJS(js); | ||||
|     S --> SIMG(img); | ||||
|  | ||||
|     SCSS --> custom.css; // For any custom styles | ||||
|  | ||||
|     SJS --> custom.js; // For any custom JavaScript | ||||
|  | ||||
|     R --> R1(router.go); | ||||
| ``` | ||||
|  | ||||
| **Detailed Breakdown:** | ||||
|  | ||||
| *   **`pkg/servers/ui/app.go`:** Main application setup for this UI module. Initializes Fiber, Jet, routes, and middleware. | ||||
| *   **`pkg/servers/ui/controllers/`**: Handles incoming requests, interacts with models, and selects views. | ||||
|     *   `auth_controller.go`: Handles login/logout (stubs for now). | ||||
|     *   `dashboard_controller.go`: Handles the dashboard page. | ||||
|     *   `process_controller.go`: Handles the process manager page. | ||||
| *   **`pkg/servers/ui/models/`**: Business logic and data interaction. | ||||
|     *   `process_model.go`: Interacts with `pkg/system/processmanager` to fetch and manage process data. | ||||
|     *   `user_model.go`: (Placeholder for basic auth stubs). | ||||
| *   **`pkg/servers/ui/views/`**: Jet templates. | ||||
|     *   **`layouts/`**: Base layout templates. | ||||
|         *   `base.jet`: Main site layout (includes Bootstrap, navbar, sidebar, main content area). | ||||
|     *   **`pages/`**: Specific page templates. | ||||
|         *   `dashboard.jet`: Dashboard content. | ||||
|         *   `process_manager.jet`: Process manager table and controls. | ||||
|         *   `login.jet`: (Placeholder login page). | ||||
|     *   **`components/`**: Reusable UI components. | ||||
|         *   `navbar.jet`: Top navigation bar. | ||||
|         *   `sidebar.jet`: Left tree menu. | ||||
| *   **`pkg/servers/ui/static/`**: Local static assets. | ||||
|     *   **`css/`**: Custom CSS files. | ||||
|         *   `custom.css` | ||||
|     *   **`js/`**: Custom JavaScript files. | ||||
|         *   `custom.js` | ||||
|     *   **`img/`**: Image assets. | ||||
| *   **`pkg/servers/ui/routes/router.go`:** Defines URL routes and maps them to controller actions. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2. Core UI Components | ||||
|  | ||||
| *   **Base Layout (`views/layouts/base.jet`):** | ||||
|     *   HTML5 boilerplate. | ||||
|     *   Include Bootstrap 5 CSS from CDN: | ||||
|         ```html | ||||
|         <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT" crossorigin="anonymous"> | ||||
|         ``` | ||||
|     *   Include Bootstrap 5 JS Bundle from CDN (typically placed before the closing `</body>` tag): | ||||
|         ```html | ||||
|         <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js" integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO" crossorigin="anonymous"></script> | ||||
|         ``` | ||||
|     *   Include local custom CSS (e.g., `/static/css/custom.css`). | ||||
|     *   Include local custom JS (e.g., `/static/js/custom.js`). | ||||
|     *   Structure: | ||||
|         *   Navbar (include `components/navbar.jet`) | ||||
|         *   Main container (Bootstrap `container-fluid` or similar) | ||||
|             *   Sidebar (include `components/sidebar.jet`) | ||||
|             *   Content area (where page-specific content will be injected) | ||||
| *   **Navbar (`views/components/navbar.jet`):** | ||||
|     *   Bootstrap Navbar component. | ||||
|     *   Site title/logo. | ||||
|     *   Login/Logout buttons (stubs, basic links for now). | ||||
| *   **Sidebar/Tree Menu (`views/components/sidebar.jet`):** | ||||
|     *   Bootstrap navigation component (e.g., Navs, List group). | ||||
|     *   Links: | ||||
|         *   Dashboard | ||||
|         *   Process Manager | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3. Pages | ||||
|  | ||||
| *   **Dashboard Page:** | ||||
|     *   **Controller (`controllers/dashboard_controller.go`):** | ||||
|         *   `ShowDashboard()`: Renders the dashboard page. | ||||
|     *   **View (`views/pages/dashboard.jet`):** | ||||
|         *   Extends `layouts/base.jet`. | ||||
|         *   Simple placeholder content for now (e.g., "Welcome to the Dashboard!"). | ||||
| *   **Process Manager Page:** | ||||
|     *   **Model (`models/process_model.go`):** | ||||
|         *   `GetProcesses()`: Function to call `pkg/system/processmanager` to get a list of running processes. Will need to define a struct for process information (PID, Name, CPU, Memory). | ||||
|         *   `KillProcess(pid string)`: Function to call `pkg/system/processmanager` to terminate a process. | ||||
|     *   **Controller (`controllers/process_controller.go`):** | ||||
|         *   `ShowProcessManager()`: | ||||
|             *   Calls `models.GetProcesses()`. | ||||
|             *   Passes process data to the view. | ||||
|             *   Renders `views/pages/process_manager.jet`. | ||||
|         *   `HandleKillProcess()`: (Handles POST request to kill a process) | ||||
|             *   Extracts PID from request. | ||||
|             *   Calls `models.KillProcess(pid)`. | ||||
|             *   Redirects back to the process manager page or returns a status. | ||||
|     *   **View (`views/pages/process_manager.jet`):** | ||||
|         *   Extends `layouts/base.jet`. | ||||
|         *   Displays processes in a Bootstrap table: | ||||
|             *   Columns: PID, Name, CPU, Memory, Actions. | ||||
|             *   Actions column: "Kill" button for each process (linking to `HandleKillProcess` or using JS for an AJAX call). | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4. Fiber & Jet Integration (`app.go` and `routes/router.go`) | ||||
|  | ||||
| *   **`pkg/servers/ui/app.go`:** | ||||
|     *   `NewApp()` function: | ||||
|         *   Initialize Fiber app: `fiber.New()`. | ||||
|         *   Initialize Jet template engine: | ||||
|             *   `jet.NewSet(jet.NewOSFileSystemLoader("./pkg/servers/ui/views"), jet.InDevelopmentMode())` (or adjust path as needed). | ||||
|             *   Pass Jet views to Fiber: `app.Settings.Views = views`. | ||||
|         *   Setup static file serving: `app.Static("/static", "./pkg/servers/ui/static")`. | ||||
|         *   Setup routes: Call a function from `routes/router.go`. | ||||
|         *   Return the Fiber app instance. | ||||
|     *   This `app.go` can then be imported and run from your main application entry point (e.g., in `cmd/heroagent/main.go`). | ||||
| *   **`pkg/servers/ui/routes/router.go`:** | ||||
|     *   `SetupRoutes(app *fiber.App, processController *controllers.ProcessController, ...)` function: | ||||
|         *   `app.Get("/", dashboardController.ShowDashboard)` | ||||
|         *   `app.Get("/processes", processController.ShowProcessManager)` | ||||
|         *   `app.Post("/processes/kill/:pid", processController.HandleKillProcess)` (or similar for kill action) | ||||
|         *   `app.Get("/login", authController.ShowLoginPage)` (stub) | ||||
|         *   `app.Post("/login", authController.HandleLogin)` (stub) | ||||
|         *   `app.Get("/logout", authController.HandleLogout)` (stub) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5. Authentication Stubs | ||||
|  | ||||
| *   **`controllers/auth_controller.go`:** | ||||
|     *   `ShowLoginPage()`: Renders a simple login form. | ||||
|     *   `HandleLogin()`: Placeholder logic (e.g., always "logs in" or checks a hardcoded credential). Sets a dummy session/cookie. | ||||
|     *   `HandleLogout()`: Placeholder logic. Clears dummy session/cookie. | ||||
| *   **`views/pages/login.jet`:** | ||||
|     *   Simple Bootstrap form for username/password. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6. Dependencies (go.mod) | ||||
|  | ||||
| Ensure these are added to your `go.mod` file: | ||||
| *   `github.com/gofiber/fiber/v2` | ||||
| *   `github.com/CloudyKit/jet/v6` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Request Flow Example: Process Manager Page | ||||
|  | ||||
| ```mermaid | ||||
| sequenceDiagram | ||||
|     participant User | ||||
|     participant Browser | ||||
|     participant FiberApp [Fiber App (pkg/servers/ui/app.go)] | ||||
|     participant Router [routes/router.go] | ||||
|     participant ProcessCtrl [controllers/process_controller.go] | ||||
|     participant ProcessMdl [models/process_model.go] | ||||
|     participant SysProcMgr [pkg/system/processmanager] | ||||
|     participant JetEngine [CloudyKit/jet/v6] | ||||
|     participant View [views/pages/process_manager.jet] | ||||
|  | ||||
|     User->>Browser: Navigates to /processes | ||||
|     Browser->>FiberApp: GET /processes | ||||
|     FiberApp->>Router: Route request | ||||
|     Router->>ProcessCtrl: Calls ShowProcessManager() | ||||
|     ProcessCtrl->>ProcessMdl: Calls GetProcesses() | ||||
|     ProcessMdl->>SysProcMgr: Fetches process list | ||||
|     SysProcMgr-->>ProcessMdl: Returns process data | ||||
|     ProcessMdl-->>ProcessCtrl: Returns process data | ||||
|     ProcessCtrl->>JetEngine: Renders process_manager.jet with data | ||||
|     JetEngine->>View: Populates template | ||||
|     View-->>JetEngine: Rendered HTML | ||||
|     JetEngine-->>ProcessCtrl: Rendered HTML | ||||
|     ProcessCtrl-->>FiberApp: Returns HTML response | ||||
|     FiberApp-->>Browser: Sends HTML response | ||||
|     Browser->>User: Displays Process Manager page | ||||
							
								
								
									
										43
									
								
								pkg/servers/ui/app.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								pkg/servers/ui/app.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| package ui | ||||
|  | ||||
| import ( | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/servers/ui/routes" // Import the routes package | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	jetadapter "github.com/gofiber/template/jet/v2" // Aliased for clarity | ||||
| ) | ||||
|  | ||||
| // AppConfig holds the configuration for the UI application. | ||||
| type AppConfig struct { | ||||
| 	// Any specific configurations can be added here later | ||||
| } | ||||
|  | ||||
| // NewApp creates and configures a new Fiber application for the UI. | ||||
| func NewApp(config AppConfig) *fiber.App { | ||||
| 	// Initialize Jet template engine | ||||
| 	// Using OSFileSystemLoader to load templates from the filesystem. | ||||
| 	// The path is relative to where the application is run. | ||||
| 	// For development, InDevelopmentMode can be true to reload templates on each request. | ||||
| 	engine := jetadapter.New("./pkg/servers/ui/views", ".jet") | ||||
|  | ||||
| 	// Enable template reloading for development. | ||||
| 	// Set to false or remove this line for production. | ||||
| 	engine.Reload(true) | ||||
|  | ||||
| 	// If you need to add custom functions or global variables to Jet: | ||||
| 	// engine.AddFunc("myCustomFunc", func(arg jet.Arguments) reflect.Value { ... }) | ||||
| 	// engine.AddGlobal("myGlobalVar", "someValue") | ||||
|  | ||||
| 	// Create a new Fiber app with the configured Jet engine | ||||
| 	app := fiber.New(fiber.Config{ | ||||
| 		Views: engine, | ||||
| 	}) | ||||
|  | ||||
| 	// Setup static file serving | ||||
| 	// Files in ./pkg/servers/ui/static will be accessible via /static URL path | ||||
| 	app.Static("/static", "./pkg/servers/ui/static") | ||||
|  | ||||
| 	// Setup routes | ||||
| 	routes.SetupRoutes(app) | ||||
|  | ||||
| 	return app | ||||
| } | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/controllers/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/controllers/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										71
									
								
								pkg/servers/ui/controllers/auth_controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								pkg/servers/ui/controllers/auth_controller.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| package controllers | ||||
|  | ||||
| import "github.com/gofiber/fiber/v2" | ||||
|  | ||||
| // AuthController handles authentication-related requests. | ||||
| type AuthController struct { | ||||
| 	// Add dependencies like a user service or session manager here | ||||
| } | ||||
|  | ||||
| // NewAuthController creates a new instance of AuthController. | ||||
| func NewAuthController() *AuthController { | ||||
| 	return &AuthController{} | ||||
| } | ||||
|  | ||||
| // ShowLoginPage renders the login page. | ||||
| // @Summary Show login page | ||||
| // @Description Displays the user login form. | ||||
| // @Tags auth | ||||
| // @Produce html | ||||
| // @Success 200 {string} html "Login page HTML" | ||||
| // @Router /login [get] | ||||
| func (ac *AuthController) ShowLoginPage(c *fiber.Ctx) error { | ||||
| 	return c.Render("pages/login", fiber.Map{ | ||||
| 		"Title": "Login", | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // HandleLogin processes the login form submission. | ||||
| // @Summary Process user login | ||||
| // @Description Authenticates the user based on submitted credentials. | ||||
| // @Tags auth | ||||
| // @Accept x-www-form-urlencoded | ||||
| // @Produce json | ||||
| // @Param username formData string true "Username" | ||||
| // @Param password formData string true "Password" | ||||
| // @Success 302 "Redirects to dashboard on successful login" | ||||
| // @Failure 400 {object} fiber.Map "Error for invalid input" | ||||
| // @Failure 401 {object} fiber.Map "Error for authentication failure" | ||||
| // @Router /login [post] | ||||
| func (ac *AuthController) HandleLogin(c *fiber.Ctx) error { | ||||
| 	// username := c.FormValue("username") | ||||
| 	// password := c.FormValue("password") | ||||
|  | ||||
| 	// TODO: Implement actual authentication logic here. | ||||
| 	// For now, we'll just simulate a successful login and redirect. | ||||
| 	// In a real app, you would: | ||||
| 	// 1. Validate username and password. | ||||
| 	// 2. Check credentials against a user store (e.g., database). | ||||
| 	// 3. Create a session or token. | ||||
|  | ||||
| 	// Simulate successful login | ||||
| 	// c.Cookie(&fiber.Cookie{Name: "session_token", Value: "dummy_token", HttpOnly: true, SameSite: "Lax"}) | ||||
| 	return c.Redirect("/") // Redirect to dashboard | ||||
| } | ||||
|  | ||||
| // HandleLogout processes the logout request. | ||||
| // @Summary Process user logout | ||||
| // @Description Logs the user out by clearing their session. | ||||
| // @Tags auth | ||||
| // @Success 302 "Redirects to login page" | ||||
| // @Router /logout [get] | ||||
| func (ac *AuthController) HandleLogout(c *fiber.Ctx) error { | ||||
| 	// TODO: Implement actual logout logic here. | ||||
| 	// For now, we'll just simulate a logout and redirect. | ||||
| 	// In a real app, you would: | ||||
| 	// 1. Invalidate the session or token. | ||||
| 	// 2. Clear any session-related cookies. | ||||
|  | ||||
| 	// c.ClearCookie("session_token") | ||||
| 	return c.Redirect("/login") | ||||
| } | ||||
							
								
								
									
										28
									
								
								pkg/servers/ui/controllers/dashboard_controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								pkg/servers/ui/controllers/dashboard_controller.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package controllers | ||||
|  | ||||
| import "github.com/gofiber/fiber/v2" | ||||
|  | ||||
| // DashboardController handles requests related to the dashboard. | ||||
| type DashboardController struct { | ||||
| 	// Add any dependencies here, e.g., a service to fetch dashboard data | ||||
| } | ||||
|  | ||||
| // NewDashboardController creates a new instance of DashboardController. | ||||
| func NewDashboardController() *DashboardController { | ||||
| 	return &DashboardController{} | ||||
| } | ||||
|  | ||||
| // ShowDashboard renders the main dashboard page. | ||||
| // @Summary Show the main dashboard | ||||
| // @Description Displays the main dashboard page with an overview. | ||||
| // @Tags dashboard | ||||
| // @Produce html | ||||
| // @Success 200 {string} html "Dashboard page HTML" | ||||
| // @Router / [get] | ||||
| func (dc *DashboardController) ShowDashboard(c *fiber.Ctx) error { | ||||
| 	// For now, just render the dashboard template. | ||||
| 	// Later, you might pass data to the template. | ||||
| 	return c.Render("pages/dashboard", fiber.Map{ | ||||
| 		"Title": "Dashboard", // This can be used in base.jet {{ .Title }} | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										76
									
								
								pkg/servers/ui/controllers/process_controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								pkg/servers/ui/controllers/process_controller.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| package controllers | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/servers/ui/models" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| // ProcessController handles requests related to process management. | ||||
| type ProcessController struct { | ||||
| 	ProcessService models.ProcessManagerService | ||||
| } | ||||
|  | ||||
| // NewProcessController creates a new instance of ProcessController. | ||||
| func NewProcessController(ps models.ProcessManagerService) *ProcessController { | ||||
| 	return &ProcessController{ | ||||
| 		ProcessService: ps, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ShowProcessManager renders the process manager page. | ||||
| // @Summary Show process manager | ||||
| // @Description Displays a list of running processes. | ||||
| // @Tags process | ||||
| // @Produce html | ||||
| // @Success 200 {string} html "Process manager page HTML" | ||||
| // @Failure 500 {object} fiber.Map "Error message if processes cannot be fetched" | ||||
| // @Router /processes [get] | ||||
| func (pc *ProcessController) ShowProcessManager(c *fiber.Ctx) error { | ||||
| 	processes, err := pc.ProcessService.GetProcesses() | ||||
| 	if err != nil { | ||||
| 		// Log the error appropriately in a real application | ||||
| 		fmt.Println("Error fetching processes:", err) | ||||
| 		return c.Status(fiber.StatusInternalServerError).Render("pages/process_manager", fiber.Map{ | ||||
| 			"Title":     "Process Manager", | ||||
| 			"Processes": []models.Process{}, // Empty list on error | ||||
| 			"Error":     "Failed to retrieve process list.", | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	return c.Render("pages/process_manager", fiber.Map{ | ||||
| 		"Title":     "Process Manager", | ||||
| 		"Processes": processes, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // HandleKillProcess handles the request to kill a specific process. | ||||
| // @Summary Kill a process | ||||
| // @Description Terminates a process by its PID. | ||||
| // @Tags process | ||||
| // @Produce html | ||||
| // @Param pid path int true "Process ID" | ||||
| // @Success 302 "Redirects to process manager page" | ||||
| // @Failure 400 {object} fiber.Map "Error message if PID is invalid" | ||||
| // @Failure 500 {object} fiber.Map "Error message if process cannot be killed" | ||||
| // @Router /processes/kill/{pid} [post] | ||||
| func (pc *ProcessController) HandleKillProcess(c *fiber.Ctx) error { | ||||
| 	pidStr := c.Params("pid") | ||||
| 	pid, err := strconv.Atoi(pidStr) | ||||
| 	if err != nil { | ||||
| 		// Log error | ||||
| 		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid PID format"}) | ||||
| 	} | ||||
|  | ||||
| 	err = pc.ProcessService.KillProcess(pid) | ||||
| 	if err != nil { | ||||
| 		// Log error | ||||
| 		// In a real app, you might want to return a more user-friendly error page or message | ||||
| 		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to kill process"}) | ||||
| 	} | ||||
|  | ||||
| 	// Redirect back to the process manager page | ||||
| 	return c.Redirect("/processes") | ||||
| } | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/models/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/models/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										48
									
								
								pkg/servers/ui/models/process_model.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								pkg/servers/ui/models/process_model.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| package models | ||||
|  | ||||
| // Process represents a single running process with its relevant details. | ||||
| type Process struct { | ||||
| 	PID    int     `json:"pid"` | ||||
| 	Name   string  `json:"name"` | ||||
| 	CPU    float64 `json:"cpu"`    // CPU usage percentage | ||||
| 	Memory float64 `json:"memory"` // Memory usage in MB | ||||
| 	// Add other fields if needed, e.g., User, Status, Path | ||||
| } | ||||
|  | ||||
| // ProcessManagerService defines the interface for interacting with the system's process manager. | ||||
| // This will be implemented by a struct that calls pkg/system/processmanager. | ||||
| type ProcessManagerService interface { | ||||
| 	GetProcesses() ([]Process, error) | ||||
| 	KillProcess(pid int) error | ||||
| } | ||||
|  | ||||
| // TODO: Implement a concrete ProcessManagerService that uses pkg/system/processmanager. | ||||
| // For now, we can create a mock implementation for development and testing of the UI. | ||||
|  | ||||
| // MockProcessManager is a mock implementation of ProcessManagerService for UI development. | ||||
| type MockProcessManager struct{} | ||||
|  | ||||
| // GetProcesses returns a list of mock processes. | ||||
| func (m *MockProcessManager) GetProcesses() ([]Process, error) { | ||||
| 	// Return some mock data | ||||
| 	return []Process{ | ||||
| 		{PID: 1001, Name: "SystemIdleProcess", CPU: 95.5, Memory: 0.1}, | ||||
| 		{PID: 1002, Name: "explorer.exe", CPU: 1.2, Memory: 150.7}, | ||||
| 		{PID: 1003, Name: "chrome.exe", CPU: 25.8, Memory: 512.3}, | ||||
| 		{PID: 1004, Name: "code.exe", CPU: 5.1, Memory: 350.0}, | ||||
| 		{PID: 1005, Name: "go.exe", CPU: 0.5, Memory: 80.2}, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // KillProcess simulates killing a process. | ||||
| func (m *MockProcessManager) KillProcess(pid int) error { | ||||
| 	// In a real implementation, this would call the system process manager. | ||||
| 	// For mock, we just print a message or do nothing. | ||||
| 	// fmt.Printf("Mock: Attempting to kill process %d\n", pid) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // NewMockProcessManager creates a new instance of MockProcessManager. | ||||
| func NewMockProcessManager() ProcessManagerService { | ||||
| 	return &MockProcessManager{} | ||||
| } | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/routes/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/routes/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										50
									
								
								pkg/servers/ui/routes/router.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								pkg/servers/ui/routes/router.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| package routes | ||||
|  | ||||
| import ( | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/servers/ui/controllers" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/servers/ui/models" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| // SetupRoutes configures the application's routes. | ||||
| func SetupRoutes(app *fiber.App) { | ||||
| 	// Initialize services and controllers | ||||
| 	// For now, using the mock process manager | ||||
| 	processManagerService := models.NewMockProcessManager() | ||||
|  | ||||
| 	dashboardController := controllers.NewDashboardController() | ||||
| 	processController := controllers.NewProcessController(processManagerService) | ||||
| 	authController := controllers.NewAuthController() | ||||
|  | ||||
| 	// --- Public Routes --- | ||||
| 	// Login and Logout | ||||
| 	app.Get("/login", authController.ShowLoginPage) | ||||
| 	app.Post("/login", authController.HandleLogin) | ||||
| 	app.Get("/logout", authController.HandleLogout) | ||||
|  | ||||
| 	// --- Authenticated Routes --- | ||||
| 	// TODO: Add middleware here to protect routes that require authentication. | ||||
| 	// For example: | ||||
| 	// authenticated := app.Group("/", authMiddleware) // Assuming authMiddleware is defined | ||||
| 	// authenticated.Get("/", dashboardController.ShowDashboard) | ||||
| 	// authenticated.Get("/processes", processController.ShowProcessManager) | ||||
| 	// authenticated.Post("/processes/kill/:pid", processController.HandleKillProcess) | ||||
|  | ||||
| 	// For now, routes are public for development ease | ||||
| 	app.Get("/", dashboardController.ShowDashboard) | ||||
| 	app.Get("/processes", processController.ShowProcessManager) | ||||
| 	app.Post("/processes/kill/:pid", processController.HandleKillProcess) | ||||
|  | ||||
| } | ||||
|  | ||||
| // TODO: Implement authMiddleware | ||||
| // func authMiddleware(c *fiber.Ctx) error { | ||||
| // 	// Check for session/token | ||||
| // 	// If not authenticated, redirect to /login | ||||
| // 	// If authenticated, c.Next() | ||||
| // 	// Example: | ||||
| // 	// if c.Cookies("session_token") == "" { | ||||
| // 	// 	return c.Redirect("/login") | ||||
| // 	// } | ||||
| // 	return c.Next() | ||||
| // } | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/static/css/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/static/css/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										48
									
								
								pkg/servers/ui/static/css/custom.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								pkg/servers/ui/static/css/custom.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* Custom CSS for HeroApp UI */ | ||||
|  | ||||
| body { | ||||
|     /* Example: Add some padding if needed, beyond what Bootstrap provides */ | ||||
|     /* padding-top: 5rem; */ | ||||
| } | ||||
|  | ||||
| .sidebar { | ||||
|     position: fixed; | ||||
|     top: 0; | ||||
|     /* Sidenav can be customized further */ | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     z-index: 100; /* Behind the navbar */ | ||||
|     padding: 56px 0 0; /* Height of navbar */ | ||||
|     box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); | ||||
| } | ||||
|  | ||||
| @media (max-width: 767.98px) { | ||||
|     .sidebar { | ||||
|         top: 5rem; /* Adjust if navbar height changes */ | ||||
|         padding: 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .sidebar .nav-link { | ||||
|     font-weight: 500; | ||||
|     color: #333; | ||||
| } | ||||
|  | ||||
| .sidebar .nav-link .feather { | ||||
|     margin-right: 4px; | ||||
|     color: #727272; | ||||
| } | ||||
|  | ||||
| .sidebar .nav-link.active { | ||||
|     color: #007bff; | ||||
| } | ||||
|  | ||||
| .sidebar .nav-link:hover .feather, | ||||
| .sidebar .nav-link.active .feather { | ||||
|     color: inherit; | ||||
| } | ||||
|  | ||||
| .sidebar-heading { | ||||
|     font-size: .75rem; | ||||
|     text-transform: uppercase; | ||||
| } | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/static/img/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/static/img/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/static/js/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/static/js/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										15
									
								
								pkg/servers/ui/static/js/custom.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								pkg/servers/ui/static/js/custom.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| // Custom JavaScript for HeroApp UI | ||||
|  | ||||
| document.addEventListener('DOMContentLoaded', function () { | ||||
|     console.log('HeroApp UI custom.js loaded'); | ||||
|  | ||||
|     // Example: Add a click listener to a button with ID 'myButton' | ||||
|     // const myButton = document.getElementById('myButton'); | ||||
|     // if (myButton) { | ||||
|     //     myButton.addEventListener('click', function() { | ||||
|     //         alert('Button clicked!'); | ||||
|     //     }); | ||||
|     // } | ||||
|  | ||||
|     // You can add more specific JavaScript interactions here as needed. | ||||
| }); | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/views/components/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/views/components/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										24
									
								
								pkg/servers/ui/views/components/navbar.jet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								pkg/servers/ui/views/components/navbar.jet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| {{ block navbar() }} | ||||
| <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top"> | ||||
|     <div class="container-fluid"> | ||||
|         <a class="navbar-brand" href="/">HeroApp UI</a> | ||||
|         <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|             <span class="navbar-toggler-icon"></span> | ||||
|         </button> | ||||
|         <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|             <ul class="navbar-nav ms-auto"> | ||||
|                 <!-- Authentication status will determine which buttons to show --> | ||||
|                 <!-- For now, showing placeholder login/logout --> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/login">Login</a> <!-- Placeholder Link --> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/logout">Logout</a> <!-- Placeholder Link --> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
|     </div> | ||||
| </nav> | ||||
| <!-- Add some padding to the body to account for the fixed-top navbar --> | ||||
| <div style="padding-top: 56px;"></div> | ||||
| {{ end }} | ||||
							
								
								
									
										17
									
								
								pkg/servers/ui/views/components/sidebar.jet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								pkg/servers/ui/views/components/sidebar.jet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| {{ block sidebar() }} | ||||
| <ul class="nav flex-column"> | ||||
|     <li class="nav-item"> | ||||
|         <a class="nav-link active" aria-current="page" href="/"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg> | ||||
|             Dashboard | ||||
|         </a> | ||||
|     </li> | ||||
|     <li class="nav-item"> | ||||
|         <a class="nav-link" href="/processes"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-activity"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg> | ||||
|             Process Manager | ||||
|         </a> | ||||
|     </li> | ||||
|     <!-- Add more menu items here as needed --> | ||||
| </ul> | ||||
| {{ end }} | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/views/layouts/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/views/layouts/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										37
									
								
								pkg/servers/ui/views/layouts/base.jet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								pkg/servers/ui/views/layouts/base.jet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>{{ block "title" . }}My App{{ end }}</title> | ||||
|     <!-- Bootstrap CSS from CDN --> | ||||
|     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT" crossorigin="anonymous"> | ||||
|     <!-- Custom CSS --> | ||||
|     <link rel="stylesheet" href="/static/css/custom.css"> | ||||
| </head> | ||||
| <body> | ||||
|     {{ import "pkg/servers/ui/views/components/navbar.jet" }} | ||||
|     {{ yield navbar() }} | ||||
|  | ||||
|     <div class="container-fluid"> | ||||
|         <div class="row"> | ||||
|             <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse"> | ||||
|                 <div class="position-sticky pt-3"> | ||||
|                     {{ import "pkg/servers/ui/views/components/sidebar.jet" }} | ||||
|                     {{ yield sidebar() }} | ||||
|                 </div> | ||||
|             </nav> | ||||
|  | ||||
|             <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4"> | ||||
|                 {{ yield body() }} | ||||
|             </main> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Bootstrap JS Bundle from CDN --> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js" integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO" crossorigin="anonymous"></script> | ||||
|     <!-- Custom JS --> | ||||
|     <script src="/static/js/custom.js"></script> | ||||
|     {{ yield scripts() }} | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										1
									
								
								pkg/servers/ui/views/pages/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/servers/ui/views/pages/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # This file is intentionally left blank to ensure the directory is tracked by Git. | ||||
							
								
								
									
										22
									
								
								pkg/servers/ui/views/pages/dashboard.jet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								pkg/servers/ui/views/pages/dashboard.jet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| {{ extends "pkg/servers/ui/views/layouts/base.jet" }} | ||||
|  | ||||
| {{ block title() }}Dashboard - HeroApp UI{{ end }} | ||||
|  | ||||
| {{ block body() }} | ||||
| <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> | ||||
|     <h1 class="h2">Dashboard</h1> | ||||
| </div> | ||||
|  | ||||
| <div class="container"> | ||||
|     <p>Welcome to the HeroApp UI Dashboard!</p> | ||||
|     <p>This is a placeholder page. More content will be added here.</p> | ||||
|     <!-- Example of using a Bootstrap component --> | ||||
|     <div class="alert alert-info" role="alert"> | ||||
|         System status: All systems nominal. | ||||
|     </div> | ||||
| </div> | ||||
| {{ end }} | ||||
|  | ||||
| {{ block scripts() }} | ||||
| <!-- Add any page-specific scripts here if needed --> | ||||
| {{ end }} | ||||
							
								
								
									
										39
									
								
								pkg/servers/ui/views/pages/login.jet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								pkg/servers/ui/views/pages/login.jet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| {{ extends "pkg/servers/ui/views/layouts/base.jet" }} | ||||
|  | ||||
| {{ block title() }}Login - HeroApp UI{{ end }} | ||||
|  | ||||
| {{ block body() }} | ||||
| <div class="container mt-5"> | ||||
|     <div class="row justify-content-center"> | ||||
|         <div class="col-md-6 col-lg-4"> | ||||
|             <div class="card"> | ||||
|                 <div class="card-header"> | ||||
|                     <h3 class="text-center">Login</h3> | ||||
|                 </div> | ||||
|                 <div class="card-body"> | ||||
|                     <form action="/login" method="POST"> | ||||
|                         <div class="mb-3"> | ||||
|                             <label for="username" class="form-label">Username</label> | ||||
|                             <input type="text" class="form-control" id="username" name="username" required> | ||||
|                         </div> | ||||
|                         <div class="mb-3"> | ||||
|                             <label for="password" class="form-label">Password</label> | ||||
|                             <input type="password" class="form-control" id="password" name="password" required> | ||||
|                         </div> | ||||
|                         <div class="d-grid"> | ||||
|                             <button type="submit" class="btn btn-primary">Login</button> | ||||
|                         </div> | ||||
|                     </form> | ||||
|                 </div> | ||||
|                 <div class="card-footer text-center"> | ||||
|                     <small>© 2025 HeroApp</small> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| {{ end }} | ||||
|  | ||||
| {{ block scripts() }} | ||||
| <!-- Add any page-specific scripts here if needed --> | ||||
| {{ end }} | ||||
							
								
								
									
										54
									
								
								pkg/servers/ui/views/pages/process_manager.jet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								pkg/servers/ui/views/pages/process_manager.jet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| {{ extends "pkg/servers/ui/views/layouts/base.jet" }} | ||||
|  | ||||
| {{ block title() }}Process Manager - HeroApp UI{{ end }} | ||||
|  | ||||
| {{ block body() }} | ||||
| <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> | ||||
|     <h1 class="h2">Process Manager</h1> | ||||
|     <div class="btn-toolbar mb-2 mb-md-0"> | ||||
|         <button type="button" class="btn btn-sm btn-outline-secondary" onclick="location.reload()"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg> | ||||
|             Refresh | ||||
|         </button> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| <div class="table-responsive"> | ||||
|     <table class="table table-striped table-sm table-hover"> | ||||
|         <thead> | ||||
|             <tr> | ||||
|                 <th scope="col">PID</th> | ||||
|                 <th scope="col">Name</th> | ||||
|                 <th scope="col">CPU (%)</th> | ||||
|                 <th scope="col">Memory (MB)</th> | ||||
|                 <th scope="col">Actions</th> | ||||
|             </tr> | ||||
|         </thead> | ||||
|         <tbody> | ||||
|             {{ if len(Processes) > 0 }} | ||||
|                 {{ range process := Processes }} | ||||
|                 <tr> | ||||
|                     <td>{{ process.PID }}</td> | ||||
|                     <td>{{ process.Name }}</td> | ||||
|                     <td>{{ printf "%.2f" process.CPU }}</td> | ||||
|                     <td>{{ printf "%.2f" process.Memory }}</td> | ||||
|                     <td> | ||||
|                         <form action="/processes/kill/{{ process.PID }}" method="POST" style="display:inline;"> | ||||
|                             <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to kill process {{ process.PID }}?');">Kill</button> | ||||
|                         </form> | ||||
|                     </td> | ||||
|                 </tr> | ||||
|                 {{ end }} | ||||
|             {{ else }} | ||||
|             <tr> | ||||
|                 <td colspan="5" class="text-center">No processes found or unable to retrieve process list.</td> | ||||
|             </tr> | ||||
|             {{ end }} | ||||
|         </tbody> | ||||
|     </table> | ||||
| </div> | ||||
| {{ end }} | ||||
|  | ||||
| {{ block scripts() }} | ||||
| <!-- Add any page-specific scripts here if needed --> | ||||
| {{ end }} | ||||
		Reference in New Issue
	
	Block a user