mirror of
https://github.com/Adembc/lazyssh.git
synced 2026-07-14 12:13:34 +02:00
180 lines
4.9 KiB
Go
180 lines
4.9 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 (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/Adembc/lazyssh/internal/core/domain"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type ServerMetadata struct {
|
|
Tags []string `json:"tags,omitempty"`
|
|
LastSeen string `json:"last_seen,omitempty"`
|
|
PinnedAt string `json:"pinned_at,omitempty"`
|
|
SSHCount int `json:"ssh_count,omitempty"`
|
|
}
|
|
|
|
type metadataManager struct {
|
|
filePath string
|
|
logger *zap.SugaredLogger
|
|
}
|
|
|
|
func newMetadataManager(filePath string, logger *zap.SugaredLogger) *metadataManager {
|
|
return &metadataManager{filePath: filePath, logger: logger}
|
|
}
|
|
|
|
func (m *metadataManager) loadAll() (map[string]ServerMetadata, error) {
|
|
metadata := make(map[string]ServerMetadata)
|
|
|
|
if _, err := os.Stat(m.filePath); os.IsNotExist(err) {
|
|
return metadata, nil
|
|
}
|
|
|
|
data, err := os.ReadFile(m.filePath)
|
|
if err != nil {
|
|
m.logger.Errorw("failed to read metadata file", "path", m.filePath, "error", err)
|
|
return nil, fmt.Errorf("read metadata '%s': %w", m.filePath, err)
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
return metadata, nil
|
|
}
|
|
|
|
if err := json.Unmarshal(data, &metadata); err != nil {
|
|
m.logger.Errorw("failed to parse metadata JSON", "path", m.filePath, "error", err)
|
|
|
|
return nil, fmt.Errorf("parse metadata JSON '%s': %w", m.filePath, err)
|
|
}
|
|
|
|
return metadata, nil
|
|
}
|
|
|
|
func (m *metadataManager) saveAll(metadata map[string]ServerMetadata) error {
|
|
if err := m.ensureDirectory(); err != nil {
|
|
m.logger.Errorw("failed to ensure metadata directory", "path", m.filePath, "error", err)
|
|
|
|
return fmt.Errorf("ensure metadata directory for '%s': %w", m.filePath, err)
|
|
}
|
|
|
|
data, err := json.MarshalIndent(metadata, "", " ")
|
|
if err != nil {
|
|
m.logger.Errorw("failed to marshal metadata", "path", m.filePath, "error", err)
|
|
return fmt.Errorf("marshal metadata for '%s': %w", m.filePath, err)
|
|
}
|
|
|
|
if err := os.WriteFile(m.filePath, data, 0o600); err != nil {
|
|
m.logger.Errorw("failed to write metadata file", "path", m.filePath, "error", err)
|
|
return fmt.Errorf("write metadata '%s': %w", m.filePath, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *metadataManager) updateServer(server domain.Server, oldAlias string) error {
|
|
metadata, err := m.loadAll()
|
|
if err != nil {
|
|
m.logger.Errorw("failed to load metadata in updateServer", "path", m.filePath, "alias", server.Alias, "old_alias", oldAlias, "error", err)
|
|
return fmt.Errorf("load metadata: %w", err)
|
|
}
|
|
|
|
if oldAlias != server.Alias {
|
|
oldMeta, ok := metadata[oldAlias]
|
|
if ok {
|
|
metadata[server.Alias] = oldMeta
|
|
}
|
|
delete(metadata, oldAlias)
|
|
}
|
|
|
|
existing := metadata[server.Alias]
|
|
merged := existing
|
|
|
|
if server.Tags != nil {
|
|
merged.Tags = server.Tags
|
|
}
|
|
|
|
if !server.LastSeen.IsZero() {
|
|
merged.LastSeen = server.LastSeen.Format(time.RFC3339)
|
|
}
|
|
|
|
if !server.PinnedAt.IsZero() {
|
|
merged.PinnedAt = server.PinnedAt.Format(time.RFC3339)
|
|
}
|
|
|
|
if server.SSHCount > 0 {
|
|
merged.SSHCount = server.SSHCount
|
|
}
|
|
|
|
metadata[server.Alias] = merged
|
|
return m.saveAll(metadata)
|
|
}
|
|
|
|
func (m *metadataManager) deleteServer(alias string) error {
|
|
metadata, err := m.loadAll()
|
|
if err != nil {
|
|
m.logger.Errorw("failed to load metadata in deleteServer", "path", m.filePath, "alias", alias, "error", err)
|
|
return fmt.Errorf("load metadata: %w", err)
|
|
}
|
|
|
|
delete(metadata, alias)
|
|
return m.saveAll(metadata)
|
|
}
|
|
|
|
func (m *metadataManager) setPinned(alias string, pinned bool) error {
|
|
metadata, err := m.loadAll()
|
|
if err != nil {
|
|
m.logger.Errorw("failed to load metadata in setPinned", "path", m.filePath, "alias", alias, "pinned", pinned, "error", err)
|
|
return fmt.Errorf("load metadata: %w", err)
|
|
}
|
|
|
|
meta := metadata[alias]
|
|
if pinned {
|
|
meta.PinnedAt = time.Now().Format(time.RFC3339)
|
|
} else {
|
|
meta.PinnedAt = ""
|
|
}
|
|
|
|
metadata[alias] = meta
|
|
return m.saveAll(metadata)
|
|
}
|
|
|
|
func (m *metadataManager) recordSSH(alias string) error {
|
|
metadata, err := m.loadAll()
|
|
if err != nil {
|
|
m.logger.Errorw("failed to load metadata in recordSSH", "path", m.filePath, "alias", alias, "error", err)
|
|
return fmt.Errorf("load metadata: %w", err)
|
|
}
|
|
|
|
meta := metadata[alias]
|
|
meta.LastSeen = time.Now().Format(time.RFC3339)
|
|
meta.SSHCount++
|
|
|
|
metadata[alias] = meta
|
|
return m.saveAll(metadata)
|
|
}
|
|
|
|
func (m *metadataManager) ensureDirectory() error {
|
|
dir := filepath.Dir(m.filePath)
|
|
if err := os.MkdirAll(dir, 0o750); err != nil {
|
|
m.logger.Errorw("failed to create metadata directory", "dir", dir, "error", err)
|
|
return fmt.Errorf("mkdir '%s': %w", dir, err)
|
|
}
|
|
return nil
|
|
}
|