From 3f01074e3fc4585884bd599422bcf35ad0dea4e2 Mon Sep 17 00:00:00 2001 From: despiegk Date: Fri, 23 May 2025 15:56:35 +0400 Subject: [PATCH] ... --- go.mod | 4 +- go.sum | 18 ++ pkg/servers/ui/DESIGN_PLAN_UI.md | 213 ++++++++++++++++++ pkg/servers/ui/app.go | 43 ++++ pkg/servers/ui/controllers/.gitkeep | 1 + pkg/servers/ui/controllers/auth_controller.go | 71 ++++++ .../ui/controllers/dashboard_controller.go | 28 +++ .../ui/controllers/process_controller.go | 76 +++++++ pkg/servers/ui/models/.gitkeep | 1 + pkg/servers/ui/models/process_model.go | 48 ++++ pkg/servers/ui/routes/.gitkeep | 1 + pkg/servers/ui/routes/router.go | 50 ++++ pkg/servers/ui/static/css/.gitkeep | 1 + pkg/servers/ui/static/css/custom.css | 48 ++++ pkg/servers/ui/static/img/.gitkeep | 1 + pkg/servers/ui/static/js/.gitkeep | 1 + pkg/servers/ui/static/js/custom.js | 15 ++ pkg/servers/ui/views/components/.gitkeep | 1 + pkg/servers/ui/views/components/navbar.jet | 24 ++ pkg/servers/ui/views/components/sidebar.jet | 17 ++ pkg/servers/ui/views/layouts/.gitkeep | 1 + pkg/servers/ui/views/layouts/base.jet | 37 +++ pkg/servers/ui/views/pages/.gitkeep | 1 + pkg/servers/ui/views/pages/dashboard.jet | 22 ++ pkg/servers/ui/views/pages/login.jet | 39 ++++ .../ui/views/pages/process_manager.jet | 54 +++++ 26 files changed, 814 insertions(+), 2 deletions(-) create mode 100644 pkg/servers/ui/DESIGN_PLAN_UI.md create mode 100644 pkg/servers/ui/app.go create mode 100644 pkg/servers/ui/controllers/.gitkeep create mode 100644 pkg/servers/ui/controllers/auth_controller.go create mode 100644 pkg/servers/ui/controllers/dashboard_controller.go create mode 100644 pkg/servers/ui/controllers/process_controller.go create mode 100644 pkg/servers/ui/models/.gitkeep create mode 100644 pkg/servers/ui/models/process_model.go create mode 100644 pkg/servers/ui/routes/.gitkeep create mode 100644 pkg/servers/ui/routes/router.go create mode 100644 pkg/servers/ui/static/css/.gitkeep create mode 100644 pkg/servers/ui/static/css/custom.css create mode 100644 pkg/servers/ui/static/img/.gitkeep create mode 100644 pkg/servers/ui/static/js/.gitkeep create mode 100644 pkg/servers/ui/static/js/custom.js create mode 100644 pkg/servers/ui/views/components/.gitkeep create mode 100644 pkg/servers/ui/views/components/navbar.jet create mode 100644 pkg/servers/ui/views/components/sidebar.jet create mode 100644 pkg/servers/ui/views/layouts/.gitkeep create mode 100644 pkg/servers/ui/views/layouts/base.jet create mode 100644 pkg/servers/ui/views/pages/.gitkeep create mode 100644 pkg/servers/ui/views/pages/dashboard.jet create mode 100644 pkg/servers/ui/views/pages/login.jet create mode 100644 pkg/servers/ui/views/pages/process_manager.jet diff --git a/go.mod b/go.mod index 00635c5..25710c1 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/emersion/go-message v0.18.2 github.com/emersion/go-smtp v0.21.3 github.com/emersion/go-webdav v0.6.0 - github.com/gofiber/fiber/v2 v2.52.6 + github.com/gofiber/fiber/v2 v2.52.8 github.com/gofiber/template/pug/v2 v2.1.8 github.com/knusbaum/go9p v1.18.0 github.com/redis/go-redis/v9 v9.8.0 @@ -47,7 +47,7 @@ require ( github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/gofiber/template v1.8.3 // indirect - github.com/gofiber/template/jet/v2 v2.1.11 // indirect + github.com/gofiber/template/jet/v2 v2.1.12 // indirect github.com/gofiber/utils v1.1.0 // indirect github.com/golang/snappy v0.0.2 // indirect github.com/google/uuid v1.6.0 // indirect diff --git a/go.sum b/go.sum index 2ecd74e..3493750 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ 9fans.net/go v0.0.2/go.mod h1:lfPdxjq9v8pVQXUMBCx5EO5oLXWQFlKRQgs1kEkjoIM= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw= github.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= @@ -7,6 +9,7 @@ github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6Xge github.com/Plan9-Archive/libauth v0.0.0-20180917063427-d1ca9e94969d/go.mod h1:UKp8dv9aeaZoQFWin7eQXtz89iHly1YAFZNn3MCutmQ= github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -40,14 +43,21 @@ github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXi github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/gofiber/fiber/v2 v2.52.8 h1:xl4jJQ0BV5EJTA2aWiKw/VddRpHrKeZLF0QPUxqn0x4= +github.com/gofiber/fiber/v2 v2.52.8/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= github.com/gofiber/template/jet/v2 v2.1.11/go.mod h1:Kb1oBdrx90oEvP71MDTUB9k+IWRF082Td5OPW7SoUMQ= +github.com/gofiber/template/jet/v2 v2.1.12 h1:poRJ6Lv0lffuGqt6FCLXh4rcavVrIytLgCruzAslmhU= +github.com/gofiber/template/jet/v2 v2.1.12/go.mod h1:kBQYWT0JbtwYgbMPkpHnzOeZtVYrqrCipjMVi9KOjSs= github.com/gofiber/template/pug/v2 v2.1.8/go.mod h1:e0Sg0YBMtC+RQMRm0swaAvqIBDJmhhDIKfFFtQRjvlQ= +github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= github.com/hanwen/go-fuse/v2 v2.0.3/go.mod h1:0EQM6aH2ctVpvZ6a+onrQ/vaykxh2GH7hy3e13vzTUY= @@ -56,6 +66,7 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -69,9 +80,12 @@ github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LE github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/metoro-io/mcp-golang v0.8.0/go.mod h1:ifLP9ZzKpN1UqFWNTpAHOqSvNkMK6b7d1FSZ5Lu0lN0= @@ -91,6 +105,7 @@ github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -120,8 +135,11 @@ github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDgu github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/wk8/go-ordered-map/v2 v2.1.9-0.20240815153524-6ea36470d1bd/go.mod h1:DbzwytT4g/odXquuOCqroKvtxxldI4nb3nuesHF/Exo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= diff --git a/pkg/servers/ui/DESIGN_PLAN_UI.md b/pkg/servers/ui/DESIGN_PLAN_UI.md new file mode 100644 index 0000000..76187c9 --- /dev/null +++ b/pkg/servers/ui/DESIGN_PLAN_UI.md @@ -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 + + ``` + * Include Bootstrap 5 JS Bundle from CDN (typically placed before the closing `` tag): + ```html + + ``` + * 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 \ No newline at end of file diff --git a/pkg/servers/ui/app.go b/pkg/servers/ui/app.go new file mode 100644 index 0000000..0c6564b --- /dev/null +++ b/pkg/servers/ui/app.go @@ -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 +} diff --git a/pkg/servers/ui/controllers/.gitkeep b/pkg/servers/ui/controllers/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/controllers/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/controllers/auth_controller.go b/pkg/servers/ui/controllers/auth_controller.go new file mode 100644 index 0000000..1f26879 --- /dev/null +++ b/pkg/servers/ui/controllers/auth_controller.go @@ -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") +} diff --git a/pkg/servers/ui/controllers/dashboard_controller.go b/pkg/servers/ui/controllers/dashboard_controller.go new file mode 100644 index 0000000..ac65ae7 --- /dev/null +++ b/pkg/servers/ui/controllers/dashboard_controller.go @@ -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 }} + }) +} diff --git a/pkg/servers/ui/controllers/process_controller.go b/pkg/servers/ui/controllers/process_controller.go new file mode 100644 index 0000000..895c612 --- /dev/null +++ b/pkg/servers/ui/controllers/process_controller.go @@ -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") +} diff --git a/pkg/servers/ui/models/.gitkeep b/pkg/servers/ui/models/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/models/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/models/process_model.go b/pkg/servers/ui/models/process_model.go new file mode 100644 index 0000000..17200d6 --- /dev/null +++ b/pkg/servers/ui/models/process_model.go @@ -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{} +} diff --git a/pkg/servers/ui/routes/.gitkeep b/pkg/servers/ui/routes/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/routes/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/routes/router.go b/pkg/servers/ui/routes/router.go new file mode 100644 index 0000000..7db7a60 --- /dev/null +++ b/pkg/servers/ui/routes/router.go @@ -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() +// } diff --git a/pkg/servers/ui/static/css/.gitkeep b/pkg/servers/ui/static/css/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/static/css/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/static/css/custom.css b/pkg/servers/ui/static/css/custom.css new file mode 100644 index 0000000..e77ea2a --- /dev/null +++ b/pkg/servers/ui/static/css/custom.css @@ -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; +} \ No newline at end of file diff --git a/pkg/servers/ui/static/img/.gitkeep b/pkg/servers/ui/static/img/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/static/img/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/static/js/.gitkeep b/pkg/servers/ui/static/js/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/static/js/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/static/js/custom.js b/pkg/servers/ui/static/js/custom.js new file mode 100644 index 0000000..643dc14 --- /dev/null +++ b/pkg/servers/ui/static/js/custom.js @@ -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. +}); \ No newline at end of file diff --git a/pkg/servers/ui/views/components/.gitkeep b/pkg/servers/ui/views/components/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/views/components/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/views/components/navbar.jet b/pkg/servers/ui/views/components/navbar.jet new file mode 100644 index 0000000..ff76056 --- /dev/null +++ b/pkg/servers/ui/views/components/navbar.jet @@ -0,0 +1,24 @@ +{{ block navbar() }} + + +
+{{ end }} \ No newline at end of file diff --git a/pkg/servers/ui/views/components/sidebar.jet b/pkg/servers/ui/views/components/sidebar.jet new file mode 100644 index 0000000..c3aae15 --- /dev/null +++ b/pkg/servers/ui/views/components/sidebar.jet @@ -0,0 +1,17 @@ +{{ block sidebar() }} + +{{ end }} \ No newline at end of file diff --git a/pkg/servers/ui/views/layouts/.gitkeep b/pkg/servers/ui/views/layouts/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/views/layouts/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/views/layouts/base.jet b/pkg/servers/ui/views/layouts/base.jet new file mode 100644 index 0000000..dfe4457 --- /dev/null +++ b/pkg/servers/ui/views/layouts/base.jet @@ -0,0 +1,37 @@ + + + + + + {{ block "title" . }}My App{{ end }} + + + + + + + {{ import "pkg/servers/ui/views/components/navbar.jet" }} + {{ yield navbar() }} + +
+
+ + +
+ {{ yield body() }} +
+
+
+ + + + + + {{ yield scripts() }} + + \ No newline at end of file diff --git a/pkg/servers/ui/views/pages/.gitkeep b/pkg/servers/ui/views/pages/.gitkeep new file mode 100644 index 0000000..23b09d8 --- /dev/null +++ b/pkg/servers/ui/views/pages/.gitkeep @@ -0,0 +1 @@ +# This file is intentionally left blank to ensure the directory is tracked by Git. \ No newline at end of file diff --git a/pkg/servers/ui/views/pages/dashboard.jet b/pkg/servers/ui/views/pages/dashboard.jet new file mode 100644 index 0000000..67439ce --- /dev/null +++ b/pkg/servers/ui/views/pages/dashboard.jet @@ -0,0 +1,22 @@ +{{ extends "pkg/servers/ui/views/layouts/base.jet" }} + +{{ block title() }}Dashboard - HeroApp UI{{ end }} + +{{ block body() }} +
+

Dashboard

+
+ +
+

Welcome to the HeroApp UI Dashboard!

+

This is a placeholder page. More content will be added here.

+ + +
+{{ end }} + +{{ block scripts() }} + +{{ end }} \ No newline at end of file diff --git a/pkg/servers/ui/views/pages/login.jet b/pkg/servers/ui/views/pages/login.jet new file mode 100644 index 0000000..a1147a4 --- /dev/null +++ b/pkg/servers/ui/views/pages/login.jet @@ -0,0 +1,39 @@ +{{ extends "pkg/servers/ui/views/layouts/base.jet" }} + +{{ block title() }}Login - HeroApp UI{{ end }} + +{{ block body() }} +
+
+
+
+
+

Login

+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+
+
+
+{{ end }} + +{{ block scripts() }} + +{{ end }} \ No newline at end of file diff --git a/pkg/servers/ui/views/pages/process_manager.jet b/pkg/servers/ui/views/pages/process_manager.jet new file mode 100644 index 0000000..1d60563 --- /dev/null +++ b/pkg/servers/ui/views/pages/process_manager.jet @@ -0,0 +1,54 @@ +{{ extends "pkg/servers/ui/views/layouts/base.jet" }} + +{{ block title() }}Process Manager - HeroApp UI{{ end }} + +{{ block body() }} +
+

Process Manager

+
+ +
+
+ +
+ + + + + + + + + + + + {{ if len(Processes) > 0 }} + {{ range process := Processes }} + + + + + + + + {{ end }} + {{ else }} + + + + {{ end }} + +
PIDNameCPU (%)Memory (MB)Actions
{{ process.PID }}{{ process.Name }}{{ printf "%.2f" process.CPU }}{{ printf "%.2f" process.Memory }} +
+ +
+
No processes found or unable to retrieve process list.
+
+{{ end }} + +{{ block scripts() }} + +{{ end }} \ No newline at end of file