# Hostkeeper V2 — Architecture (HTMX) > **Status**: Approved — Menggantikan arsitektur React sebelumnya > **Last Updated**: 2026-07-07 > **Keputusan**: React → GoFiber + HTMX + Alpine.js + minimal JS --- ## 1. Arsitektur Baru Hostkeeper V2 menggunakan arsitektur **server-rendered HTML** dengan interaktivitas via HTMX, bukan SPA React. ``` ┌────────────────────────────────────────────────────┐ │ Browser / Electron WebView │ │ ┌──────────────────────────────────────────────┐ │ │ │ HTMX (14kb) + Alpine.js (15kb) + xterm.js │ │ │ │ │ │ │ │ ● HTMX: semua interaksi (search, filter, │ │ │ │ modal, form, navigasi) │ │ │ │ ● Alpine.js: client state (toast, toggle, │ │ │ │ passphrase reveal, tab) │ │ │ │ ● xterm.js: terminal emulator (hanya │ │ │ │ halaman terminal) │ │ │ │ ● Vanilla JS: clipboard, toggle switch, │ │ │ │ strength calc │ │ │ └────────────────┬─────────────────────────────┘ │ │ │ hx-get/hx-post/hx-ws │ │ ┌────────────────▼─────────────────────────────┐ │ │ │ GoFiber v2 Backend (localhost:1947) │ │ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────────────┐ │ │ │ │ │ HTML handlers │ │ WebSocket /xterm.js │ │ │ │ │ │ (Fiber + html│ │ (SSH I/O via WS) │ │ │ │ │ │ template) │ └──────────────────────┘ │ │ │ │ └──────┬───────┘ │ │ │ │ │ go.mod replace │ │ │ │ ┌────▼────┐ │ │ │ │ │ v1/pkg/ │ ssh, crypto, storage, config │ │ │ │ └─────────┘ │ │ │ └───────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────┘ ``` ### Alur Request ``` 1. User klik tombol "Filter Active" │ hx-get="/hosts?filter=active" │ hx-target="#host-list" ▼ 2. GoFiber handler: func (h *Handler) ListHosts(c fiber.Ctx) error { hosts := h.store.Filter("active") return c.Render("partials/host_list", fiber.Map{ "Hosts": hosts, }) } ▼ 3. Server return HTML partial
{{range .Hosts}}
...
{{end}}
▼ 4. HTMX swap HTML ke DOM (tanpa reload) ``` --- ## 2. Tech Stack | Layer | Teknologi | Versi | Fungsi | |-------|-----------|-------|--------| | Backend | GoFiber v2 | v2.52+ | HTTP server, routing, middleware | | Template | html/template | stdlib | Server-side HTML rendering | | Frontend | HTMX | v2.0+ | HTML-over-HTTP interaktivitas | | Client state | Alpine.js | v3.14+ | Modal, toast, toggle state | | Terminal | xterm.js | v5.5+ | SSH terminal emulator | | Styling | TailwindCSS v4 | v4.0+ | Utility-first CSS | | CSS custom | Custom CSS | — | dot-grid, glassmorphism, keyframes | | Icons | Lucide (inline SVG) | — | SVG icon system | | Desktop | Electron (opsional) | v35+ | Browser wrapper | | Mobile | WebView | — | Android/iOS shell | --- ## 3. Struktur Folder ``` app/backend/ ├── main.go ← Entry point Fiber app ├── go.mod ← module git.tukangketik.id/swanadiva/hostkeeper/v2 ├── go.sum │ ├── internal/ │ ├── handler/ ← HTTP handlers (render HTML + HTMX partials) │ │ ├── dashboard.go GET/POST /hosts, /hosts/{id} │ │ ├── terminal.go GET /terminal, WS /terminal/ws │ │ ├── sftp.go GET /sftp/ls, POST /sftp/{action} │ │ ├── snippets.go GET/POST /snippets │ │ ├── keychain.go GET/POST /keys │ │ ├── settings.go GET/POST /settings │ │ ├── brief.go GET /brief (overlay) │ │ └── health.go GET /api/health │ │ │ ├── model/ ← Go structs (data models) │ │ ├── host.go Host, HostFilter │ │ ├── file.go FileItem │ │ ├── snippet.go Snippet │ │ ├── keychain.go KeychainItem │ │ ├── device.go Device │ │ ├── transfer.go BackgroundTransfer │ │ └── config.go Config, Toggle │ │ │ ├── template/ ← Template helpers/funcs │ │ └── funcs.go activeClass, formatBytes, timeAgo, strengthBadge │ │ │ └── middleware/ │ └── viewdata.go Inject .View, .ActiveNav, .User ke semua template │ ├── static/ ← Static assets (served via Fiber) │ ├── css/ │ │ ├── output.css ← TailwindCSS v4 build output │ │ └── custom.css ← dot-grid, glassmorphism, keyframes, scrollbar │ ├── js/ │ │ ├── htmx.min.js ← HTMX (14kb) │ │ ├── alpine.min.js ← Alpine.js (15kb) │ │ ├── terminal.js ← xterm.js + WebSocket init │ │ ├── clipboard.js ← navigator.clipboard.writeText() │ │ ├── toggles.js ← Custom toggle switch click handler │ │ └── utils.js ← passphrase reveal, strength calc, toast │ ├── xterm/ ← xterm.js build files │ └── img/ ← Images/icons │ └── views/ ← Go HTML templates ├── layout.html ← Base layout (sidebar, header, scripts) ├── index.html ← Entry page (extends layout + content block) │ └── partials/ ← HTMX-swappable partials ├── nav.html ← Sidebar navigation ├── dashboard.html ← Host page container ├── host_list.html ← Host card grid/list (HTMX swap target) ├── host_card.html ← Single host card ├── host_modal.html ← Add host modal form ├── terminal.html ← Terminal page (xterm.js container) ├── sftp.html ← Dual-pane file browser ├── sftp_pane.html ← Single file pane (for HTMX swap) ├── snippets.html ← Snippet page ├── snippet_grid.html ← Snippet cards (HTMX swap target) ├── snippet_modal.html ← Add snippet form ├── keychain.html ← Key page ├── key_grid.html ← Key cards (HTMX swap target) ├── key_modal.html ← Add key form ├── settings.html ← Settings form + toggles ├── brief.html ← Brief overlay panel └── toast.html ← Toast notification ``` --- ## 4. Komponen UI & State Management ### 4.1 Navigasi (HTMX) Sidebar navigation menggunakan HTMX untuk swap halaman: ```html {{svg "server"}} Hosts ``` ### 4.2 State Client (Alpine.js) State yang butuh client-side reactivity: ```html
{{template "host_list" .}}
``` ### 4.3 JS Murni (Vanilla) ```javascript // clipboard.js — copy to clipboard function copyToClipboard(text) { navigator.clipboard.writeText(text); // trigger toast via Alpine document.querySelector('[x-data]').__x.$data.toast = 'Copied!'; document.querySelector('[x-data]').__x.$data.show = true; } // toggles.js — custom toggle switch function toggleSwitch(el) { const checked = el.getAttribute('aria-checked') === 'true'; el.setAttribute('aria-checked', !checked); fetch(`/settings/toggle?key=${el.dataset.key}&val=${!checked}`, { method: 'POST' }); } ``` --- ## 5. Data Models (Go Structs) Semua data model di `internal/model/`, mapping dari template React `types.ts`: ```go // model/host.go type Host struct { ID string `json:"id"` Name string `json:"name"` IP string `json:"ip"` OS string `json:"os"` Provider string `json:"provider"` Status string `json:"status"` // "active" | "offline" LastSeen string `json:"lastSeen"` Type string `json:"type"` // "api" | "db" | "edge" | "web" | "desktop" | "server" } // model/snippet.go type Snippet struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"description"` Code string `json:"code"` Tags []string `json:"tags"` Collection string `json:"collection"` // "favorites" | "recent" | "all" UpdatedAt string `json:"updatedAt"` Icon string `json:"icon"` } ``` Lihat `docs/DATA_MODELS.md` untuk definisi lengkap. --- ## 6. HTMX Patterns yang Digunakan | Pattern | Contoh | Keterangan | |---------|--------|------------| | Search | `hx-get="/hosts?q=..." hx-trigger="keyup delay:200ms" hx-target="#host-list"` | Debounced search | | Filter | `hx-get="/hosts?filter=active" hx-target="#host-list"` | Tab filter | | Modal | Alpine `x-show` + `hx-post` | Form dalam modal | | Infinite scroll | `hx-get="/hosts?page=2" hx-trigger="revealed" hx-target="#host-list" hx-swap="beforeend"` | Load more | | Delete | `hx-delete="/hosts/123" hx-target="#host-card-123" hx-swap="delete" hx-confirm="Yakin?"` | Confirm + delete | | Toast | Alpine state + `hx-trigger="htmx:afterRequest"` dari response header `HX-Trigger: showToast` | Notifikasi | | Tab | Alpine `x-data` + `x-show` | Panel tab | --- ## 7. Keuntungan HTMX Dibanding React | Aspek | React (sebelumnya) | HTMX (sekarang) | |-------|-------------------|-----------------| | Dependency JS | 10+ library (React, Zustand, dll) | 3 library (HTMX, Alpine, xterm) | | Baris JS | ~2.878 baris TS/JSX | ~410 baris vanilla JS | | Build step | Vite + TypeScript + npm | CSS build + copy static | | Bahasa dominan | 60% TS + 40% Go | 90% Go + 10% HTML/JS | | State management | Zustand store | Server-side (HTMX) + Alpine | | API layer | REST JSON + manual fetch | HTML partials langsung | | Electron | Wajib (SPA) | Opsional (bisa browser) | | Debugging | React DevTools | Browser DevTools (HTML langsung) | | Time to implement | ~6 minggu | ~4 minggu (estimasi) | --- ## 8. Cara Kerja: React → HTMX Mapping per Halaman ### Dashboard (Host List) | React | HTMX | |-------|------| | `useState` searchQuery | `hx-get="/hosts?q={search}"` | | `useState` statusFilter | `hx-get="/hosts?filter=active"` | | `useState` viewMode | Alpine `x-data="{ view: 'grid' }"` | | `useState` showAddModal | Alpine `x-show="open"` | | `onChange` filter | HTML `