package main import ( "context" "encoding/json" "fmt" "os" "github.com/spf13/cobra" "git.tukangketik.id/swanadiva/hostkeeper/pkg/config" "git.tukangketik.id/swanadiva/hostkeeper/pkg/storage" ) var ( importMergeStrategy string importDryRun bool ) // importCmd represents the import command var importCmd = &cobra.Command{ Use: "import ", Short: "Import hosts and credentials from file", Long: `Import hosts, SSH keys, snippets from a previously exported file. Merge strategies: replace Replace all existing data with imported data merge Keep existing data, add only new items Examples: # Import with merge (default) hostkeeper import my-backup.json # Import replacing all existing data hostkeeper import my-backup.json --strategy replace # Preview without importing hostkeeper import my-backup.json --dry-run`, Args: cobra.ExactArgs(1), RunE: runImport, } func init() { importCmd.Flags().StringVar(&importMergeStrategy, "strategy", "merge", "Merge strategy: replace or merge") importCmd.Flags().BoolVar(&importDryRun, "dry-run", false, "Show what would be imported without actually importing") rootCmd.AddCommand(importCmd) } func runImport(cmd *cobra.Command, args []string) error { filename := args[0] if _, err := os.Stat(filename); os.IsNotExist(err) { return fmt.Errorf("file not found: %s", filename) } jsonBytes, err := os.ReadFile(filename) if err != nil { return fmt.Errorf("failed to read import file: %w", err) } var data storage.ExportData if err := json.Unmarshal(jsonBytes, &data); err != nil { return fmt.Errorf("failed to parse import file: %w", err) } cfg := appCfg if cfg == nil { var err error cfg, err = config.New() if err != nil { return fmt.Errorf("failed to initialize config: %w", err) } } store, err := storage.NewJSONStorage(cfg.GetDataDir()) if err != nil { return fmt.Errorf("failed to initialize storage: %w", err) } ctx := context.Background() if importDryRun { return previewImport(&data, filename) } // Map strategy string to MergeStrategy type var strategy storage.MergeStrategy switch importMergeStrategy { case "replace": strategy = storage.MergeStrategyReplace case "merge": strategy = storage.MergeStrategyMerge default: return fmt.Errorf("invalid strategy: %s (use 'replace' or 'merge')", importMergeStrategy) } if err := store.ImportData(ctx, &data, strategy); err != nil { return fmt.Errorf("failed to import data: %w", err) } hosts, _ := store.ListHosts(ctx) keys, _ := store.ListKeyPairs(ctx) snippets, _ := store.ListSnippets(ctx) fmt.Printf("Import successful!\n") fmt.Printf(" Strategy: %s\n", importMergeStrategy) fmt.Printf(" Total Hosts: %d\n", len(hosts)) fmt.Printf(" Total Keys: %d\n", len(keys)) fmt.Printf(" Total Snippets: %d\n", len(snippets)) return nil } func previewImport(data *storage.ExportData, filename string) error { fmt.Println("Import Preview (Dry Run)") fmt.Println("------------------------") fmt.Printf("File: %s\n", filename) fmt.Printf("Hosts: %d\n", len(data.Hosts)) fmt.Printf("Keys: %d\n", len(data.KeyPairs)) fmt.Printf("Snippets: %d\n", len(data.Snippets)) fmt.Printf("Strategy: %s\n\n", importMergeStrategy) fmt.Println("To perform the import, run:") fmt.Printf(" hostkeeper import %s --strategy %s\n", filename, importMergeStrategy) return nil }