Files
HostKeeper/docs/ARCHITECTURE_HTMX.md
swanadiva a0c8483c0e docs: migrate all planning docs from React to GoFiber + HTMX
- New ARCHITECTURE_HTMX.md — comprehensive HTMX architecture doc
- ARCHITECTURE.md — updated to point to HTMX version
- UI_COMPONENTS.md — replaced React tree with HTMX partials
- SPRINT_PLAN.md — all sprint tasks updated for HTMX
- BUILD_SYSTEM.md — simplified (no Vite, no npm)
- TIMELINE.md — 3.5 week HTMX timeline
- PROGRESS.md — updated status + HTMX
- AGENTS.md — updated checkpoint with HTMX decision
2026-07-07 12:15:19 +07:00

15 KiB

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
       <div id="host-list" hx-swap-oob="true">
         {{range .Hosts}}
           <div class="host-card">...</div>
         {{end}}
       </div>
       ▼
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:

<a href="/hosts"
   hx-get="/hosts"
   hx-target="#main-content"
   hx-push-url="true"
   class="nav-item {{if eq .View "hosts"}}active{{end}}">
  {{svg "server"}}
  <span>Hosts</span>
</a>

4.2 State Client (Alpine.js)

State yang butuh client-side reactivity:

<!-- Toggle view mode -->
<div x-data="{ view: 'grid' }">
  <button @click="view = 'list'" :class="view === 'list' ? 'bg-primary text-white' : ''">
    List
  </button>
  <div x-show="view === 'grid'" id="host-grid">
    {{template "host_list" .}}
  </div>
</div>

<!-- Modal -->
<div x-data="{ open: false }">
  <button @click="open = true">Add Host</button>
  <div x-show="open" class="modal-backdrop">
    <div @click.outside="open = false" class="modal-content">
      <form hx-post="/hosts" hx-target="#host-list" @submit="open = false">
        ...
      </form>
    </div>
  </div>
</div>

<!-- Toast -->
<div x-data="{ toast: '', show: false }"
     x-init="$watch('show', v => v && setTimeout(() => show = false, 3000))"
     x-show="show"
     class="toast">
  <span x-text="toast"></span>
</div>

4.3 JS Murni (Vanilla)

// 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:

// 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 <select> + hx-get
onSubmit add host hx-post="/hosts"
transfers progress {{range}} template loop

Terminal

React HTMX
useState tabs Alpine x-data="{ tabs: [], active: '1' }"
useRef scroll xterm.scrollToBottom() (built-in)
useEffect autoScroll terminal.onData(() => scrollToBottom())
Command parser WebSocket → Go backend → SSH
Multi-tab Alpine + multiple xterm instances

SFTP

React HTMX
useState local/remote path hx-get="/sftp/ls?path=..." hx-target="#pane"
useState search hx-get="/sftp/ls?q=..." hx-trigger="keyup"
File click navigation hx-get="/sftp/ls?path={dir}" hx-push-url
Upload/download hx-post="/sftp/upload"
Toast Alpine x-data + setTimeout

Snippets

React HTMX
useState search/tag/collection hx-get="/snippets?q=&tag=&col="
useState copiedId JS copyToClipboard()
useState showAddForm Alpine x-show="open"
Copy onclick="copyToClipboard('code')"

Keychain

React HTMX
useState search hx-get="/keys?q=..."
useState revealPassId Alpine x-data="{ reveal: false }"
useState copiedId JS copyToClipboard()
Strength badge Go template: {{if gt (len .Passphrase) 12}}secure{{end}}

Settings

React HTMX
Form state HTML <form> + hx-post="/settings"
Toggle switches JS click handler + fetch POST /settings/toggle
Danger zone onclick="localStorage.clear(); location.reload()"

Brief Overlay

React HTMX
isOpen + onClose Alpine x-show="open" + @click="open = false"
Konten statis Go template render (HTML murni)

9. File yang Tidak Berubah

Dokumen ini melengkapi (bukan mengganti) dokumen yang masih relevan:

Dokumen Status Alasan
docs/DATA_MODELS.md Tetap Go struct masih sama
docs/API.md Tetap REST endpoint masih sama
docs/PERFORMANCE.md Tetap Performance tips masih relevan
docs/UI_TERMIUS_REFERENCE.md Tetap Visual design masih sama
docs/ARCHITECTURE.md 🔄 Diupdate Ditimpa dengan ringkasan + link ke ARCHITECTURE_HTMX.md
docs/SPRINT_PLAN.md 🔄 Diupdate Sprint tasks diubah ke HTMX
docs/UI_COMPONENTS.md 🔄 Diganti React → HTMX + Alpine
docs/BUILD_SYSTEM.md 🔄 Diupdate Hapus Vite, tambah CSS build
docs/TIMELINE.md 🔄 Diupdate Timeline disesuaikan
docs/PROGRESS.md 🔄 Diupdate Status tracker diperbarui