mirror of
https://github.com/Adembc/lazyssh.git
synced 2026-07-14 12:13:34 +02:00
167 lines
5.1 KiB
Go
167 lines
5.1 KiB
Go
// Copyright 2025.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package ssh_config_file
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/Adembc/lazyssh/internal/core/domain"
|
|
"github.com/Adembc/lazyssh/internal/core/ports"
|
|
"github.com/kevinburke/ssh_config"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Repository implements ServerRepository interface for SSH config file operations.
|
|
type Repository struct {
|
|
configPath string
|
|
fileSystem FileSystem
|
|
metadataManager *metadataManager
|
|
logger *zap.SugaredLogger
|
|
}
|
|
|
|
// NewRepository creates a new SSH config repository.
|
|
func NewRepository(logger *zap.SugaredLogger, configPath, metaDataPath string) ports.ServerRepository {
|
|
return &Repository{
|
|
logger: logger,
|
|
configPath: configPath,
|
|
fileSystem: DefaultFileSystem{},
|
|
metadataManager: newMetadataManager(metaDataPath, logger),
|
|
}
|
|
}
|
|
|
|
// NewRepositoryWithFS creates a new SSH config repository with a custom filesystem.
|
|
func NewRepositoryWithFS(logger *zap.SugaredLogger, configPath string, metaDataPath string, fs FileSystem) ports.ServerRepository {
|
|
return &Repository{
|
|
logger: logger,
|
|
configPath: configPath,
|
|
fileSystem: fs,
|
|
metadataManager: newMetadataManager(metaDataPath, logger),
|
|
}
|
|
}
|
|
|
|
// ListServers returns all servers matching the query pattern.
|
|
// Empty query returns all servers.
|
|
func (r *Repository) ListServers(query string) ([]domain.Server, error) {
|
|
cfg, err := r.loadConfig()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
servers := r.toDomainServer(cfg)
|
|
metadata, err := r.metadataManager.loadAll()
|
|
if err != nil {
|
|
r.logger.Warnf("Failed to load metadata: %v", err)
|
|
metadata = make(map[string]ServerMetadata)
|
|
}
|
|
servers = r.mergeMetadata(servers, metadata)
|
|
if query == "" {
|
|
return servers, nil
|
|
}
|
|
|
|
return r.filterServers(servers, query), nil
|
|
}
|
|
|
|
// AddServer adds a new server to the SSH config.
|
|
func (r *Repository) AddServer(server domain.Server) error {
|
|
cfg, err := r.loadConfig()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
if r.serverExists(cfg, server.Alias) {
|
|
return fmt.Errorf("server with alias '%s' already exists", server.Alias)
|
|
}
|
|
|
|
host := r.createHostFromServer(server)
|
|
cfg.Hosts = append(cfg.Hosts, host)
|
|
|
|
if err := r.saveConfig(cfg); err != nil {
|
|
r.logger.Warnf("Failed to save config while adding new server: %v", err)
|
|
return fmt.Errorf("failed to save config: %w", err)
|
|
}
|
|
return r.metadataManager.updateServer(server, server.Alias)
|
|
}
|
|
|
|
// UpdateServer updates an existing server in the SSH config.
|
|
func (r *Repository) UpdateServer(server domain.Server, newServer domain.Server) error {
|
|
cfg, err := r.loadConfig()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
host := r.findHostByAlias(cfg, server.Alias)
|
|
if host == nil {
|
|
return fmt.Errorf("server with alias '%s' not found", server.Alias)
|
|
}
|
|
|
|
if server.Alias != newServer.Alias {
|
|
if r.serverExists(cfg, newServer.Alias) {
|
|
return fmt.Errorf("server with alias '%s' already exists", newServer.Alias)
|
|
}
|
|
|
|
newPatterns := make([]*ssh_config.Pattern, 0, len(host.Patterns))
|
|
for _, pattern := range host.Patterns {
|
|
if pattern.Str == server.Alias {
|
|
newPatterns = append(newPatterns, &ssh_config.Pattern{Str: newServer.Alias})
|
|
} else {
|
|
newPatterns = append(newPatterns, pattern)
|
|
}
|
|
}
|
|
|
|
host.Patterns = newPatterns
|
|
|
|
}
|
|
|
|
r.updateHostNodes(host, newServer)
|
|
|
|
if err := r.saveConfig(cfg); err != nil {
|
|
r.logger.Warnf("Failed to save config while updating server: %v", err)
|
|
return fmt.Errorf("failed to save config: %w", err)
|
|
}
|
|
// Update metadata; pass old alias to allow inline migration
|
|
return r.metadataManager.updateServer(newServer, server.Alias)
|
|
}
|
|
|
|
// DeleteServer removes a server from the SSH config.
|
|
func (r *Repository) DeleteServer(server domain.Server) error {
|
|
cfg, err := r.loadConfig()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
initialCount := len(cfg.Hosts)
|
|
cfg.Hosts = r.removeHostByAlias(cfg.Hosts, server.Alias)
|
|
|
|
if len(cfg.Hosts) == initialCount {
|
|
return fmt.Errorf("server with alias '%s' not found", server.Alias)
|
|
}
|
|
|
|
if err := r.saveConfig(cfg); err != nil {
|
|
r.logger.Warnf("Failed to save config while deleting server: %v", err)
|
|
return fmt.Errorf("failed to save config: %w", err)
|
|
}
|
|
return r.metadataManager.deleteServer(server.Alias)
|
|
}
|
|
|
|
// SetPinned sets or unsets the pinned status of a server.
|
|
func (r *Repository) SetPinned(alias string, pinned bool) error {
|
|
return r.metadataManager.setPinned(alias, pinned)
|
|
}
|
|
|
|
// RecordSSH increments the SSH access count and updates the last seen timestamp for a server.
|
|
func (r *Repository) RecordSSH(alias string) error {
|
|
return r.metadataManager.recordSSH(alias)
|
|
}
|