8357d9d76e
- All 7 pages rewritten: Dashboard, Terminal, Snippets, Keychain, SFTP, Settings, Brief - Apply consistent Lumina glassmorphic theme (rounded-2xl, shadows, borders, tracking) - Fix SVG icons not rendering — return template.HTML instead of string - Fix Alpine x-data scoping: terminal tabs, SFTP modals, snippet tag buttons - Fix dashboard search sending literal ?q=... - Rebuild CSS with all new Tailwind classes (62KB) - Add .air.toml with hot-reload config, docs/PERBAIKANUI.md checklist - Remove unused backup partials
113 lines
2.1 KiB
Go
113 lines
2.1 KiB
Go
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()
|
|
keyType := guessKeyType(name, passphrase)
|
|
k := model.Key{
|
|
ID: randID(),
|
|
Name: name,
|
|
Username: username,
|
|
Passphrase: passphrase,
|
|
Strength: calcStrength(passphrase),
|
|
Type: keyType,
|
|
URL: url,
|
|
KeyFile: "— " + keyType + " credential —",
|
|
CreatedAt: time.Now().Format("Jan 2, 2006"),
|
|
}
|
|
s.keys = append(s.keys, k)
|
|
writeFile("keys.json", &s.keys)
|
|
return k
|
|
}
|
|
|
|
func guessKeyType(name, passphrase string) string {
|
|
switch {
|
|
case len(passphrase) >= 25:
|
|
return "ED25519"
|
|
case contains(name, "aws") || contains(name, "AWS"):
|
|
return "AWS Key"
|
|
case contains(name, "token") || contains(name, "bearer") || contains(name, "TOKEN"):
|
|
return "Bearer Token"
|
|
case len(passphrase) <= 3:
|
|
return "PASSWORD"
|
|
default:
|
|
return "SSH RSA"
|
|
}
|
|
}
|
|
|
|
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"
|
|
}
|