Sprint 6: JSON file persistence + real SSH terminal + V1 crypto/knownhosts integration

- go.mod replace directive for git.tukangketik.id/swanadiva/hostkeeper → ../../v1
- store/ package: JSON file persistence for hosts, snippets, keys, settings
  (~/Library/Application Support/hostkeeper/v2/)
- model SSH fields: Hostname, Port, Username, AuthType, Password, KeyID
- sshconn/ package: real SSH client via golang.org/x/crypto/ssh
- Terminal WS: real SSH connection with fallback to mock shell
- Dynamic host list in terminal (server-rendered JSON script tag)
- All mock data defaults removed from model packages
- Settings persist via store/settings.go
This commit is contained in:
swanadiva
2026-07-07 13:53:50 +07:00
parent 04d9cfb096
commit fd7361aa14
21 changed files with 746 additions and 306 deletions
-51
View File
@@ -1,7 +1,5 @@
package model
import "time"
type Snippet struct {
ID string `json:"id"`
Name string `json:"name"`
@@ -10,52 +8,3 @@ type Snippet struct {
Tags []string `json:"tags"`
CreatedAt string `json:"createdAt"`
}
type SnippetStore struct {
Snippets []Snippet
}
func NewSnippetStore() *SnippetStore {
return &SnippetStore{Snippets: defaultSnippets()}
}
func (s *SnippetStore) All() []Snippet { return s.Snippets }
func (s *SnippetStore) Search(q string) []Snippet {
if q == "" { return s.Snippets }
var res []Snippet
for _, sn := range s.Snippets {
if contains(sn.Name, q) || contains(sn.Content, q) || contains(sn.Language, q) {
res = append(res, sn)
}
}
return res
}
func (s *SnippetStore) Add(name, content, language string, tags []string) Snippet {
sn := Snippet{
ID: randString(8),
Name: name,
Content: content,
Language: language,
Tags: tags,
CreatedAt: time.Now().Format("Jan 2, 2006"),
}
s.Snippets = append(s.Snippets, sn)
return sn
}
func (s *SnippetStore) Delete(id string) {
for i, sn := range s.Snippets {
if sn.ID == id { s.Snippets = append(s.Snippets[:i], s.Snippets[i+1:]...); return }
}
}
func defaultSnippets() []Snippet {
return []Snippet{
{ID: "s1", Name: "Nginx Reverse Proxy", Content: "server {\n listen 80;\n server_name example.com;\n location / {\n proxy_pass http://localhost:3000;\n proxy_set_header Host $host;\n }\n}", Language: "nginx", Tags: []string{"nginx", "proxy", "web"}, CreatedAt: "Jun 28, 2026"},
{ID: "s2", Name: "Docker Compose Postgres", Content: "version: '3.8'\nservices:\n db:\n image: postgres:16\n environment:\n POSTGRES_DB: app\n POSTGRES_PASSWORD: ${DB_PASS}\n ports:\n - 5432:5432", Language: "yaml", Tags: []string{"docker", "postgres", "db"}, CreatedAt: "Jun 25, 2026"},
{ID: "s3", Name: "Health Check Script", Content: "#!/bin/bash\ncurl -sf http://localhost/health || exit 1\necho \"OK\"", Language: "bash", Tags: []string{"monitoring", "bash"}, CreatedAt: "Jun 20, 2026"},
{ID: "s4", Name: "System Info", Content: "uname -a\ncat /etc/os-release\nfree -h\ndf -h\nuptime", Language: "bash", Tags: []string{"sysadmin", "diagnostics"}, CreatedAt: "Jun 15, 2026"},
{ID: "s5", Name: "ufw Rules", Content: "ufw default deny incoming\nufw default allow outgoing\nufw allow 22/tcp\nufw allow 443/tcp\nufw enable", Language: "bash", Tags: []string{"security", "firewall"}, CreatedAt: "Jun 10, 2026"},
}
}