feat: add & list commands with deadlock fix
- Add 'add' command: register hosts via flags or interactive prompts Supports password/key/both auth, groups, tags, notes, port override - Add 'list' command: display hosts with filtering and formatting Supports --group, --tag filters, --sort, table/json/wide output - Fix deadlock bug in JSON storage (RLock within Lock) Introduced internal list functions that don't lock Affects: ListHosts/GetHost/SaveHost/DeleteHost + KeyPair + Snippet - Add comprehensive tests for add and list commands - Update PROJECT_STATE.md (Tasks 1-8 complete, ~55% done)
This commit is contained in:
@@ -62,6 +62,11 @@ func (s *JSONStorage) ListHosts(ctx context.Context) ([]*models.Host, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.listHostsInternal()
|
||||
}
|
||||
|
||||
// listHostsInternal reads hosts WITHOUT locking (caller must hold lock)
|
||||
func (s *JSONStorage) listHostsInternal() ([]*models.Host, error) {
|
||||
var data struct {
|
||||
Hosts []*models.Host `json:"hosts"`
|
||||
}
|
||||
@@ -74,7 +79,10 @@ func (s *JSONStorage) ListHosts(ctx context.Context) ([]*models.Host, error) {
|
||||
}
|
||||
|
||||
func (s *JSONStorage) GetHost(ctx context.Context, id string) (*models.Host, error) {
|
||||
hosts, err := s.ListHosts(ctx)
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
hosts, err := s.listHostsInternal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -92,7 +100,7 @@ func (s *JSONStorage) SaveHost(ctx context.Context, host *models.Host) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
hosts, err := s.ListHosts(ctx)
|
||||
hosts, err := s.listHostsInternal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -117,7 +125,7 @@ func (s *JSONStorage) DeleteHost(ctx context.Context, id string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
hosts, err := s.ListHosts(ctx)
|
||||
hosts, err := s.listHostsInternal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -156,6 +164,11 @@ func (s *JSONStorage) ListKeyPairs(ctx context.Context) ([]*models.KeyPair, erro
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.listKeyPairsInternal()
|
||||
}
|
||||
|
||||
// listKeyPairsInternal reads key pairs WITHOUT locking (caller must hold lock)
|
||||
func (s *JSONStorage) listKeyPairsInternal() ([]*models.KeyPair, error) {
|
||||
var data struct {
|
||||
KeyPairs []*models.KeyPair `json:"key_pairs"`
|
||||
}
|
||||
@@ -168,7 +181,10 @@ func (s *JSONStorage) ListKeyPairs(ctx context.Context) ([]*models.KeyPair, erro
|
||||
}
|
||||
|
||||
func (s *JSONStorage) GetKeyPair(ctx context.Context, id string) (*models.KeyPair, error) {
|
||||
keys, err := s.ListKeyPairs(ctx)
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
keys, err := s.listKeyPairsInternal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -186,7 +202,7 @@ func (s *JSONStorage) SaveKeyPair(ctx context.Context, keyPair *models.KeyPair)
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
keys, err := s.ListKeyPairs(ctx)
|
||||
keys, err := s.listKeyPairsInternal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -211,7 +227,7 @@ func (s *JSONStorage) DeleteKeyPair(ctx context.Context, id string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
keys, err := s.ListKeyPairs(ctx)
|
||||
keys, err := s.listKeyPairsInternal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -250,6 +266,11 @@ func (s *JSONStorage) ListSnippets(ctx context.Context) ([]*models.Snippet, erro
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.listSnippetsInternal()
|
||||
}
|
||||
|
||||
// listSnippetsInternal reads snippets WITHOUT locking (caller must hold lock)
|
||||
func (s *JSONStorage) listSnippetsInternal() ([]*models.Snippet, error) {
|
||||
var data struct {
|
||||
Snippets []*models.Snippet `json:"snippets"`
|
||||
}
|
||||
@@ -262,7 +283,10 @@ func (s *JSONStorage) ListSnippets(ctx context.Context) ([]*models.Snippet, erro
|
||||
}
|
||||
|
||||
func (s *JSONStorage) GetSnippet(ctx context.Context, id string) (*models.Snippet, error) {
|
||||
snippets, err := s.ListSnippets(ctx)
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
snippets, err := s.listSnippetsInternal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -280,7 +304,7 @@ func (s *JSONStorage) SaveSnippet(ctx context.Context, snippet *models.Snippet)
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
snippets, err := s.ListSnippets(ctx)
|
||||
snippets, err := s.listSnippetsInternal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -305,7 +329,7 @@ func (s *JSONStorage) DeleteSnippet(ctx context.Context, id string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
snippets, err := s.ListSnippets(ctx)
|
||||
snippets, err := s.listSnippetsInternal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user