56eaf57649
- Fix modal scroll: restructure overlay with scroll/centering wrapper - Add SSH key textarea + file upload to host modal (authType='key') - Fix textarea reset on new host (clear via DOM) - SSH username placeholder instead of default 'root' - Nav uses plain <a href> instead of HTMX for full-page navigation - /hosts route returns full page; /hosts/list for HTMX partials - Remove OS and Provider from host modal, grid card, and list card - Fix key modal x-data scope for Alpine $dispatch - Update key_grid to display multi-line key content properly - Simplify SSH key placeholder format
94 lines
5.2 KiB
Go
94 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"html/template"
|
|
"log"
|
|
|
|
"github.com/gofiber/contrib/websocket"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gofiber/fiber/v2/middleware/compress"
|
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
|
"github.com/gofiber/fiber/v2/middleware/recover"
|
|
"github.com/gofiber/template/html/v2"
|
|
|
|
"git.tukangketik.id/swanadiva/hostkeeper/v2/internal/handler"
|
|
)
|
|
|
|
func main() {
|
|
engine := html.New("./views", ".html")
|
|
engine.Reload(true)
|
|
engine.AddFunc("svg", func(name string) template.HTML {
|
|
switch name {
|
|
case "server":
|
|
return `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5 12h14M12 5l7 7-7 7"/></svg>`
|
|
case "terminal":
|
|
return `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>`
|
|
case "folder-sync":
|
|
return `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"/></svg>`
|
|
case "code2":
|
|
return `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/></svg>`
|
|
case "key-round":
|
|
return `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"/></svg>`
|
|
case "settings2":
|
|
return `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>`
|
|
default:
|
|
return `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"/></svg>`
|
|
}
|
|
})
|
|
engine.AddFunc("dict", func(values ...interface{}) map[string]interface{} {
|
|
m := make(map[string]interface{})
|
|
for i := 0; i < len(values)-1; i += 2 {
|
|
if key, ok := values[i].(string); ok {
|
|
m[key] = values[i+1]
|
|
}
|
|
}
|
|
return m
|
|
})
|
|
engine.AddFunc("default", func(def, val interface{}) interface{} {
|
|
if val == nil || val == "" {
|
|
return def
|
|
}
|
|
return val
|
|
})
|
|
|
|
app := fiber.New(fiber.Config{
|
|
Views: engine,
|
|
})
|
|
|
|
app.Use(logger.New())
|
|
app.Use(recover.New())
|
|
app.Use(compress.New())
|
|
app.Static("/static", "./static")
|
|
|
|
app.Get("/", handler.Dashboard)
|
|
app.Get("/hosts", handler.Dashboard)
|
|
app.Get("/hosts/list", handler.DashboardList)
|
|
app.Post("/hosts", handler.CreateHost)
|
|
app.Put("/hosts/:id", handler.UpdateHost)
|
|
app.Delete("/hosts/:id", handler.DeleteHost)
|
|
app.Get("/snippets", handler.Snippets)
|
|
app.Get("/snippets/grid", handler.SnippetGrid)
|
|
app.Post("/snippets", handler.CreateSnippet)
|
|
app.Delete("/snippets/:id", handler.DeleteSnippet)
|
|
app.Get("/keychain", handler.Keychain)
|
|
app.Get("/keys/grid", handler.KeyGrid)
|
|
app.Post("/keys", handler.CreateKey)
|
|
app.Delete("/keys/:id", handler.DeleteKey)
|
|
app.Get("/settings", handler.Settings)
|
|
app.Post("/settings", handler.UpdateConfig)
|
|
app.Get("/brief", handler.Brief)
|
|
app.Get("/terminal", handler.Terminal)
|
|
app.Get("/terminal/ws/:host", websocket.New(func(c *websocket.Conn) {
|
|
handler.TerminalWS(c)
|
|
}, websocket.Config{
|
|
Origins: []string{"*"},
|
|
}))
|
|
app.Get("/sftp", handler.SFTP)
|
|
app.Get("/sftp/pane", handler.SFTPPane)
|
|
app.Post("/sftp/mkdir", handler.CreateFolder)
|
|
app.Delete("/sftp/rm", handler.DeleteFile)
|
|
app.Get("/api/health", handler.Health)
|
|
|
|
log.Fatal(app.Listen(":1947"))
|
|
}
|