...
This commit is contained in:
parent
532cda72d3
commit
3f01074e3f
4
go.mod
4
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
|
||||
|
18
go.sum
18
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=
|
||||
|
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 }}
|
Loading…
Reference in New Issue
Block a user