feat: Host CRUD complete — edit, full form fields, connect, toast
- Add UpdateHost handler + PUT /hosts/:id route
- CreateHost now accepts all fields (port, username, auth, notes)
- host_modal.html: full form with port, username, auth type toggle, edit mode
- host_list.html: edit button dispatches edit-host event with host data
- Connect button links to /terminal?host={id} for pre-selection
- Terminal page reads selected host from query param
- Toast notification system via Alpine.js events
- Add default template function for fallback values
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"git.tukangketik.id/swanadiva/hostkeeper/v2/internal/model"
|
||||
"git.tukangketik.id/swanadiva/hostkeeper/v2/internal/store"
|
||||
@@ -20,12 +22,12 @@ var navItems = []fiber.Map{
|
||||
func Dashboard(c *fiber.Ctx) error {
|
||||
hosts := hostStore.All()
|
||||
return c.Render("index", fiber.Map{
|
||||
"View": "hosts",
|
||||
"Title": "Hosts",
|
||||
"Hosts": hosts,
|
||||
"Active": countByStatus(hosts, "active"),
|
||||
"Offline": countByStatus(hosts, "offline"),
|
||||
"NavItems": navItems,
|
||||
"View": "hosts",
|
||||
"Title": "Hosts",
|
||||
"Hosts": hosts,
|
||||
"Active": countByStatus(hosts, "active"),
|
||||
"Offline": countByStatus(hosts, "offline"),
|
||||
"NavItems": navItems,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,28 +36,103 @@ func DashboardList(c *fiber.Ctx) error {
|
||||
filter := c.Query("filter", "all")
|
||||
hosts := hostStore.Search(q, filter)
|
||||
return c.Render("host_list", fiber.Map{
|
||||
"Hosts": hosts,
|
||||
"Active": countByStatus(hosts, "active"),
|
||||
"Hosts": hosts,
|
||||
"Active": countByStatus(hosts, "active"),
|
||||
"Offline": countByStatus(hosts, "offline"),
|
||||
})
|
||||
}
|
||||
|
||||
func parsePort(s string, def int) int {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil || i < 1 || i > 65535 {
|
||||
return def
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func CreateHost(c *fiber.Ctx) error {
|
||||
name := c.FormValue("name")
|
||||
ip := c.FormValue("ip")
|
||||
os := c.FormValue("os")
|
||||
provider := c.FormValue("provider")
|
||||
hostType := c.FormValue("type")
|
||||
|
||||
if name == "" || ip == "" {
|
||||
return c.Status(400).SendString("name and ip required")
|
||||
}
|
||||
|
||||
hostStore.Add(name, ip, os, provider, hostType)
|
||||
h := model.Host{
|
||||
Name: name,
|
||||
IP: ip,
|
||||
OS: c.FormValue("os"),
|
||||
Provider: c.FormValue("provider"),
|
||||
Type: c.FormValue("type"),
|
||||
Port: parsePort(c.FormValue("port"), 22),
|
||||
Username: c.FormValue("username"),
|
||||
AuthType: c.FormValue("authType"),
|
||||
Password: c.FormValue("password"),
|
||||
Hostname: c.FormValue("hostname"),
|
||||
Notes: c.FormValue("notes"),
|
||||
}
|
||||
if h.Username == "" {
|
||||
h.Username = "root"
|
||||
}
|
||||
if h.AuthType == "" {
|
||||
h.AuthType = "key"
|
||||
}
|
||||
|
||||
created := hostStore.AddFull(h)
|
||||
hosts := hostStore.All()
|
||||
_ = created
|
||||
return c.Render("host_list", fiber.Map{
|
||||
"Hosts": hosts,
|
||||
"Active": countByStatus(hosts, "active"),
|
||||
"Offline": countByStatus(hosts, "offline"),
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateHost(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
existing, found := hostStore.Get(id)
|
||||
if !found {
|
||||
return c.Status(404).SendString("host not found")
|
||||
}
|
||||
|
||||
if v := c.FormValue("name"); v != "" {
|
||||
existing.Name = v
|
||||
}
|
||||
if v := c.FormValue("ip"); v != "" {
|
||||
existing.IP = v
|
||||
}
|
||||
if v := c.FormValue("os"); v != "" {
|
||||
existing.OS = v
|
||||
}
|
||||
if v := c.FormValue("provider"); v != "" {
|
||||
existing.Provider = v
|
||||
}
|
||||
if v := c.FormValue("type"); v != "" {
|
||||
existing.Type = v
|
||||
}
|
||||
if v := c.FormValue("port"); v != "" {
|
||||
existing.Port = parsePort(v, 22)
|
||||
}
|
||||
if v := c.FormValue("username"); v != "" {
|
||||
existing.Username = v
|
||||
}
|
||||
if v := c.FormValue("authType"); v != "" {
|
||||
existing.AuthType = v
|
||||
}
|
||||
if v := c.FormValue("password"); v != "" {
|
||||
existing.Password = v
|
||||
}
|
||||
if v := c.FormValue("hostname"); v != "" {
|
||||
existing.Hostname = v
|
||||
}
|
||||
if v := c.FormValue("notes"); v != "" {
|
||||
existing.Notes = v
|
||||
}
|
||||
|
||||
hostStore.Update(existing)
|
||||
hosts := hostStore.All()
|
||||
return c.Render("host_list", fiber.Map{
|
||||
"Hosts": hosts,
|
||||
"Active": countByStatus(hosts, "active"),
|
||||
"Hosts": hosts,
|
||||
"Active": countByStatus(hosts, "active"),
|
||||
"Offline": countByStatus(hosts, "offline"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,12 +28,14 @@ func Terminal(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
hostsJSON, _ := json.Marshal(available)
|
||||
selectedHost := c.Query("host", "")
|
||||
data := fiber.Map{
|
||||
"View": "terminal",
|
||||
"Title": "Terminal",
|
||||
"NavItems": navItems,
|
||||
"Hosts": available,
|
||||
"HostsJSON": string(hostsJSON),
|
||||
"View": "terminal",
|
||||
"Title": "Terminal",
|
||||
"NavItems": navItems,
|
||||
"Hosts": available,
|
||||
"HostsJSON": string(hostsJSON),
|
||||
"SelectedHost": selectedHost,
|
||||
}
|
||||
if c.Get("HX-Request") != "" {
|
||||
return c.Render("terminal_content", data)
|
||||
|
||||
Reference in New Issue
Block a user