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:
@@ -0,0 +1,94 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"time"
|
||||
"git.tukangketik.id/swanadiva/hostkeeper/v2/internal/model"
|
||||
)
|
||||
|
||||
type KeyStore struct {
|
||||
keys []model.Key
|
||||
}
|
||||
|
||||
func NewKeyStore() *KeyStore {
|
||||
s := &KeyStore{}
|
||||
mu.RLock()
|
||||
readFile("keys.json", &s.keys)
|
||||
mu.RUnlock()
|
||||
if s.keys == nil {
|
||||
s.keys = []model.Key{}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *KeyStore) All() []model.Key {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
return s.keys
|
||||
}
|
||||
|
||||
func (s *KeyStore) Search(q string) []model.Key {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
if q == "" {
|
||||
return s.keys
|
||||
}
|
||||
var res []model.Key
|
||||
for _, k := range s.keys {
|
||||
if contains(k.Name, q) || contains(k.Username, q) || contains(k.URL, q) {
|
||||
res = append(res, k)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *KeyStore) Add(name, username, url, passphrase string) model.Key {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
k := model.Key{
|
||||
ID: randID(),
|
||||
Name: name,
|
||||
Username: username,
|
||||
URL: url,
|
||||
Passphrase: passphrase,
|
||||
Strength: calcStrength(passphrase),
|
||||
CreatedAt: time.Now().Format("Jan 2, 2006"),
|
||||
}
|
||||
s.keys = append(s.keys, k)
|
||||
writeFile("keys.json", &s.keys)
|
||||
return k
|
||||
}
|
||||
|
||||
func (s *KeyStore) Delete(id string) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for i, k := range s.keys {
|
||||
if k.ID == id {
|
||||
s.keys = append(s.keys[:i], s.keys[i+1:]...)
|
||||
writeFile("keys.json", &s.keys)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func calcStrength(p string) string {
|
||||
l, d, u, s := 0, 0, 0, 0
|
||||
for _, c := range p {
|
||||
switch {
|
||||
case c >= 'a' && c <= 'z':
|
||||
l++
|
||||
case c >= 'A' && c <= 'Z':
|
||||
u++
|
||||
case c >= '0' && c <= '9':
|
||||
d++
|
||||
default:
|
||||
s++
|
||||
}
|
||||
}
|
||||
if len(p) >= 12 && u >= 1 && d >= 1 && s >= 1 {
|
||||
return "secure"
|
||||
}
|
||||
if len(p) >= 8 && u+d+s >= 2 {
|
||||
return "moderate"
|
||||
}
|
||||
return "weak"
|
||||
}
|
||||
Reference in New Issue
Block a user