feat: Sprint 4 — SFTP dual-pane with per-type icons, breadcrumb, drop-zone, create/delete, toast
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"git.tukangketik.id/swanadiva/hostkeeper/v2/internal/model"
|
||||
)
|
||||
|
||||
var fileStore = model.NewFileStore()
|
||||
|
||||
func SFTP(c *fiber.Ctx) error {
|
||||
local, remote := fileStore.All()
|
||||
return c.Render("sftp", fiber.Map{
|
||||
"View": "sftp",
|
||||
"Title": "SFTP",
|
||||
"LocalFiles": local,
|
||||
"RemoteFiles": remote,
|
||||
"LocalBreadcrumb": fileStore.LocalBreadcrumb,
|
||||
"RemoteBreadcrumb": fileStore.RemoteBreadcrumb,
|
||||
"NavItems": navItems,
|
||||
})
|
||||
}
|
||||
|
||||
func SFTPPane(c *fiber.Ctx) error {
|
||||
pane := c.Query("pane", "local")
|
||||
dir := c.Query("dir", "/")
|
||||
local, remote := fileStore.All()
|
||||
files := local
|
||||
breadcrumb := fileStore.LocalBreadcrumb
|
||||
if pane == "remote" {
|
||||
files = remote
|
||||
breadcrumb = fileStore.RemoteBreadcrumb
|
||||
}
|
||||
return c.Render("sftp_pane", fiber.Map{
|
||||
"Pane": pane,
|
||||
"Files": files,
|
||||
"Breadcrumb": breadcrumb,
|
||||
"CurrentDir": dir,
|
||||
})
|
||||
}
|
||||
|
||||
func CreateFolder(c *fiber.Ctx) error {
|
||||
pane := c.FormValue("pane", "local")
|
||||
name := c.FormValue("name")
|
||||
if name != "" {
|
||||
item := model.FileItem{
|
||||
Name: name, Size: "—", Modified: "Just now",
|
||||
Type: "folder", Permissions: "drwxr-xr-x",
|
||||
}
|
||||
if pane == "remote" {
|
||||
fileStore.Remote = append([]model.FileItem{item}, fileStore.Remote...)
|
||||
} else {
|
||||
fileStore.Local = append([]model.FileItem{item}, fileStore.Local...)
|
||||
}
|
||||
}
|
||||
files := fileStore.Local
|
||||
if pane == "remote" { files = fileStore.Remote }
|
||||
return c.Render("sftp_pane", fiber.Map{
|
||||
"Pane": pane, "Files": files,
|
||||
"Breadcrumb": fileStore.LocalBreadcrumb,
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteFile(c *fiber.Ctx) error {
|
||||
pane := c.Query("pane", "local")
|
||||
name := c.Query("name")
|
||||
files := &fileStore.Local
|
||||
if pane == "remote" { files = &fileStore.Remote }
|
||||
for i, f := range *files {
|
||||
if f.Name == name {
|
||||
*files = append((*files)[:i], (*files)[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
return c.Render("sftp_pane", fiber.Map{
|
||||
"Pane": pane, "Files": *files,
|
||||
"Breadcrumb": fileStore.LocalBreadcrumb,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package model
|
||||
|
||||
type FileItem struct {
|
||||
Name string `json:"name"`
|
||||
Size string `json:"size"`
|
||||
Modified string `json:"modified"`
|
||||
Type string `json:"type"` // folder | file | image | code | lock
|
||||
Extension string `json:"extension"`
|
||||
Permissions string `json:"permissions"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type FileStore struct {
|
||||
Local []FileItem
|
||||
Remote []FileItem
|
||||
LocalBreadcrumb []Breadcrumb
|
||||
RemoteBreadcrumb []Breadcrumb
|
||||
}
|
||||
|
||||
type Breadcrumb struct {
|
||||
Name string
|
||||
Path string
|
||||
}
|
||||
|
||||
func NewFileStore() *FileStore {
|
||||
return &FileStore{
|
||||
Local: defaultLocalFiles(),
|
||||
Remote: defaultRemoteFiles(),
|
||||
LocalBreadcrumb: []Breadcrumb{{Name: "~", Path: "/home/user"}, {Name: "projects", Path: "/home/user/projects"}},
|
||||
RemoteBreadcrumb: []Breadcrumb{{Name: "/", Path: "/"}, {Name: "var", Path: "/var"}, {Name: "www", Path: "/var/www"}},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileStore) All() (local, remote []FileItem) { return s.Local, s.Remote }
|
||||
|
||||
func defaultLocalFiles() []FileItem {
|
||||
return []FileItem{
|
||||
{Name: "Downloads", Size: "—", Modified: "2 hours ago", Type: "folder", Permissions: "drwxr-xr-x", Path: "/home/user/Downloads"},
|
||||
{Name: "Documents", Size: "—", Modified: "Yesterday", Type: "folder", Permissions: "drwxr-xr-x", Path: "/home/user/Documents"},
|
||||
{Name: "projects", Size: "—", Modified: "Just now", Type: "folder", Permissions: "drwxrwxr-x", Path: "/home/user/projects"},
|
||||
{Name: "config.yml", Size: "1.4 KB", Modified: "Yesterday", Type: "code", Extension: "yml", Permissions: "-rw-r--r--", Path: "/home/user/projects/config.yml"},
|
||||
{Name: "README.md", Size: "3.2 KB", Modified: "2 days ago", Type: "code", Extension: "md", Permissions: "-rw-r--r--", Path: "/home/user/projects/README.md"},
|
||||
{Name: "screenshot.png", Size: "240 KB", Modified: "3 days ago", Type: "image", Extension: "png", Permissions: "-rw-r--r--", Path: "/home/user/Downloads/screenshot.png"},
|
||||
{Name: "archive.zip", Size: "42 MB", Modified: "1 week ago", Type: "file", Extension: "zip", Permissions: "-rw-r--r--", Path: "/home/user/Downloads/archive.zip"},
|
||||
{Name: "vault.enc", Size: "8 KB", Modified: "2 weeks ago", Type: "lock", Extension: "enc", Permissions: "-rw-------", Path: "/home/user/vault.enc"},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultRemoteFiles() []FileItem {
|
||||
return []FileItem{
|
||||
{Name: "html", Size: "—", Modified: "2 hours ago", Type: "folder", Permissions: "drwxr-xr-x", Path: "/var/www/html"},
|
||||
{Name: "assets", Size: "—", Modified: "Yesterday", Type: "folder", Permissions: "drwxr-xr-x", Path: "/var/www/assets"},
|
||||
{Name: "index.html", Size: "4.1 KB", Modified: "Just now", Type: "code", Extension: "html", Permissions: "-rw-r--r--", Path: "/var/www/index.html"},
|
||||
{Name: "app.py", Size: "12.8 KB", Modified: "2 hours ago", Type: "code", Extension: "py", Permissions: "-rwxr-xr-x", Path: "/var/www/app.py"},
|
||||
{Name: "Dockerfile", Size: "512 B", Modified: "Yesterday", Type: "code", Extension: "Dockerfile", Permissions: "-rw-r--r--", Path: "/var/www/Dockerfile"},
|
||||
{Name: "compose.yml", Size: "2.1 KB", Modified: "Yesterday", Type: "code", Extension: "yml", Permissions: "-rw-r--r--", Path: "/var/www/compose.yml"},
|
||||
{Name: ".env", Size: "156 B", Modified: "3 days ago", Type: "lock", Extension: "env", Permissions: "-rw-------", Path: "/var/www/.env"},
|
||||
{Name: "logo.svg", Size: "6.7 KB", Modified: "1 week ago", Type: "image", Extension: "svg", Permissions: "-rw-r--r--", Path: "/var/www/assets/logo.svg"},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package model
|
||||
|
||||
type Transfer struct {
|
||||
ID string `json:"id"`
|
||||
FileName string `json:"fileName"`
|
||||
Source string `json:"source"`
|
||||
Destination string `json:"destination"`
|
||||
Progress int `json:"progress"`
|
||||
Speed string `json:"speed"`
|
||||
Type string `json:"type"` // upload | download
|
||||
Status string `json:"status"` // queued | transferring | completed | error
|
||||
}
|
||||
|
||||
type TransferStore struct {
|
||||
Transfers []Transfer
|
||||
}
|
||||
|
||||
func NewTransferStore() *TransferStore {
|
||||
return &TransferStore{}
|
||||
}
|
||||
|
||||
func (s *TransferStore) All() []Transfer { return s.Transfers }
|
||||
Reference in New Issue
Block a user