Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 136 additions & 4 deletions cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package cmd
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"time"
Expand Down Expand Up @@ -63,6 +64,16 @@ Examples:
return err
}

// Get recursive and includeHidden flags
recursive, _ := cmd.Flags().GetBool("recursive")
includeHidden, _ := cmd.Flags().GetBool("include-hidden")

// Expand directories if needed
filePairs, err = expandDirectories(filePairs, recursive, includeHidden)
if err != nil {
return err
}

// Get tags from flags
tagsFlag, err := cmd.Flags().GetString("tags")
if err != nil {
Expand Down Expand Up @@ -138,15 +149,61 @@ Examples:
fmt.Printf("Processing: %s\n", pair.Source)
}

// Check if file exists and that it is not a directory or symlink
fileInfo, err := fs.VerifyFileAndReturnFileInfo(pair.Source)
// Determine path type and handle accordingly
fileInfo, pathType, err := fs.GetPathInfo(pair.Source)
if err != nil {
errorMsg := fmt.Sprintf("✗ %s: %v", filepath.Base(pair.Source), err)
fmt.Println(errorMsg)
failedFiles = append(failedFiles, errorMsg)
continue
}

// Handle different path types
var actualSourcePath string
switch pathType {
case fs.PathTypeFile:
// Regular file - use as is
actualSourcePath = pair.Source

case fs.PathTypeSymlink:
// Resolve symlink and verify target is a regular file
targetPath, targetInfo, targetType, err := fs.ResolveSymlink(pair.Source)
if err != nil {
errorMsg := fmt.Sprintf("✗ %s: %v", filepath.Base(pair.Source), err)
fmt.Println(errorMsg)
failedFiles = append(failedFiles, errorMsg)
continue
}

if targetType != fs.PathTypeFile {
errorMsg := fmt.Sprintf("✗ %s: symlink target is not a regular file", filepath.Base(pair.Source))
fmt.Println(errorMsg)
failedFiles = append(failedFiles, errorMsg)
continue
}

// Use the resolved target path for processing
actualSourcePath = targetPath
fileInfo = targetInfo

if verbose {
fmt.Printf(" Resolved symlink: %s → %s\n", pair.Source, targetPath)
}

case fs.PathTypeDir:
// Directories should have been expanded already
errorMsg := fmt.Sprintf("✗ %s: unexpected directory in processing loop", filepath.Base(pair.Source))
fmt.Println(errorMsg)
failedFiles = append(failedFiles, errorMsg)
continue

default:
errorMsg := fmt.Sprintf("✗ %s: unsupported file type", filepath.Base(pair.Source))
fmt.Println(errorMsg)
failedFiles = append(failedFiles, errorMsg)
continue
}

// Get file size in human-readable format
sizeInBytes := fileInfo.Size()
sizeReadable := util.HumanReadableSize(sizeInBytes)
Expand All @@ -163,7 +220,7 @@ Examples:

// Process the file and store chunks - using the appropriate chunking function
var chunkRefs []config.ChunkRef
chunkRefs, err = chunk.ChunkFile(ctx, pair.Source, chunkSize, vaultRoot, passphrase, progressMgr)
chunkRefs, err = chunk.ChunkFile(ctx, actualSourcePath, chunkSize, vaultRoot, passphrase, progressMgr)

if err != nil {
errorMsg := fmt.Sprintf("✗ %s: chunking failed - %v", filepath.Base(pair.Source), err)
Expand Down Expand Up @@ -357,15 +414,90 @@ func parseFileArguments(args []string) ([]FilePair, error) {
return pairs, nil
}

// expandDirectories expands directories into file pairs if recursive flag is set
func expandDirectories(pairs []FilePair, recursive bool, includeHidden bool) ([]FilePair, error) {
var expandedPairs []FilePair

for _, pair := range pairs {
// Get path info to determine type
fileInfo, pathType, err := fs.GetPathInfo(pair.Source)
if err != nil {
return nil, err
}

switch pathType {
case fs.PathTypeFile:
// Regular file - add as is
expandedPairs = append(expandedPairs, pair)

case fs.PathTypeSymlink:
// Symlink - will be handled in processing loop, add as is
expandedPairs = append(expandedPairs, pair)

case fs.PathTypeDir:
// Directory - expand if recursive, otherwise error
if !recursive {
return nil, fmt.Errorf("'%s' is a directory. Use --recursive flag to add directories", pair.Source)
}

// Walk the directory tree
err := filepath.WalkDir(pair.Source, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}

// Skip hidden files/directories if includeHidden is false
if fs.ShouldSkipHidden(d.Name(), includeHidden) {
if d.IsDir() {
return filepath.SkipDir
}
return nil
}

// Only add regular files and symlinks
if !d.IsDir() {
// Compute relative path from source directory
relPath, err := filepath.Rel(pair.Source, path)
if err != nil {
return fmt.Errorf("failed to compute relative path: %v", err)
}

// Preserve directory structure in destination
destPath := filepath.Join(pair.Destination, relPath)

expandedPairs = append(expandedPairs, FilePair{
Source: path,
Destination: destPath,
})
}

return nil
})

if err != nil {
return nil, fmt.Errorf("error walking directory '%s': %v", pair.Source, err)
}

default:
return nil, fmt.Errorf("'%s' is not a regular file, directory, or symlink", pair.Source)
}

_ = fileInfo // fileInfo might be used for verbose output later
}

return expandedPairs, nil
}

func init() {
rootCmd.AddCommand(addCmd)

// Optional flags for the add command
addCmd.Flags().BoolP("force", "f", false, "Force add without confirmation")
addCmd.Flags().StringP("tags", "t", "", "Comma-separated tags to associate with the file")
addCmd.Flags().StringP("passphrase-value", "p", "", "Passphrase for encrypted vault (if required)")
addCmd.Flags().BoolP("recursive", "r", false, "Recursively add directories")
addCmd.Flags().BoolP("include-hidden", "H", false, "Include hidden files and directories")
}

//TODO: Add support for directories and symlinks
//TODO: Need to check how symlinks will be handled
//TODO: Interactive mode with real time progress indicators
4 changes: 2 additions & 2 deletions cmd/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Example:
}

// Create sync service (with or without RSA)
syncService, err := discover.CreateSyncService(host, vaultMgr, vaultConfig, vaultPath)
syncService, err := discover.CreateSyncService(host, vaultMgr, vaultConfig, vaultPath, verbose)
if err != nil {
return fmt.Errorf("failed to create sync service: %v", err)
}
Expand All @@ -101,7 +101,7 @@ Example:
defer func() { _ = discovery.Stop() }()

// Run the discovery loop
return discover.RunDiscoveryLoop(ctx, host, syncService, peerChan, timeout, continuous)
return discover.RunDiscoveryLoop(ctx, host, syncService, peerChan, timeout, continuous, verbose)
},
}

Expand Down
17 changes: 13 additions & 4 deletions cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ Examples:
sietch sync # Auto-discover and sync with peers
sietch sync /ip4/192.168.1.5/tcp/4001/p2p/QmPeerID # Sync with a specific peer`,
RunE: func(cmd *cobra.Command, args []string) error {
// Get verbose flag
verbose, _ := cmd.Flags().GetBool("verbose")

// Create a context with cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand Down Expand Up @@ -99,10 +102,12 @@ Examples:

fmt.Printf("🔌 Started Sietch node with ID: %s\n", host.ID().String())

// Print our listen addresses
fmt.Println("📡 Listening on:")
for _, addr := range host.Addrs() {
fmt.Printf(" %s/p2p/%s\n", addr.String(), host.ID().String())
// Print our listen addresses only in verbose mode
if verbose {
fmt.Println("📡 Listening on:")
for _, addr := range host.Addrs() {
fmt.Printf(" %s/p2p/%s\n", addr.String(), host.ID().String())
}
}

// Load the vault manager
Expand All @@ -117,6 +122,9 @@ Examples:
return fmt.Errorf("failed to create sync service: %v", err)
}

// Set verbose mode on sync service
syncService.SetVerbose(verbose)

// Start secure protocol handlers
syncService.RegisterProtocols(ctx)

Expand Down Expand Up @@ -339,4 +347,5 @@ func init() {
syncCmd.Flags().IntP("timeout", "t", 60, "Discovery timeout in seconds (for auto-discovery)")
syncCmd.Flags().BoolP("force-trust", "f", false, "Automatically trust new peers without prompting")
syncCmd.Flags().BoolP("read-only", "r", false, "Only receive files, don't send")
syncCmd.Flags().BoolP("verbose", "v", false, "Enable verbose debug output")
}
40 changes: 28 additions & 12 deletions internal/discover/discovery_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

// createSyncService creates a sync service with or without RSA support
func CreateSyncService(h host.Host, vaultMgr *config.Manager, vaultConfig *config.VaultConfig, vaultPath string) (*p2p.SyncService, error) {
func CreateSyncService(h host.Host, vaultMgr *config.Manager, vaultConfig *config.VaultConfig, vaultPath string, verbose bool) (*p2p.SyncService, error) {
if vaultConfig.Sync.Enabled && vaultConfig.Sync.RSA != nil {
privateKey, publicKey, rsaConfig, err := keys.LoadRSAKeys(vaultPath, vaultConfig.Sync.RSA)
if err != nil {
Expand All @@ -26,15 +26,23 @@ func CreateSyncService(h host.Host, vaultMgr *config.Manager, vaultConfig *confi
return nil, fmt.Errorf("failed to create sync service: %v", err)
}

fmt.Println("🔐 RSA key exchange enabled with fingerprint:", rsaConfig.Fingerprint)
syncService.SetVerbose(verbose)

if verbose {
fmt.Println("🔐 RSA key exchange enabled with fingerprint:", rsaConfig.Fingerprint)
}
return syncService, nil
} else {
syncService, err := p2p.NewSyncService(h, vaultMgr)
if err != nil {
return nil, fmt.Errorf("failed to create sync service: %v", err)
}

fmt.Println("⚠️ Warning: RSA key exchange not enabled in vault config")
syncService.SetVerbose(verbose)

if verbose {
fmt.Println("⚠️ Warning: RSA key exchange not enabled in vault config")
}
return syncService, nil
}
}
Expand All @@ -61,7 +69,7 @@ func SetupDiscovery(ctx context.Context, h host.Host) (*p2p.MDNSDiscovery, <-cha

// runDiscoveryLoop processes discovered peers until timeout or interrupted
func RunDiscoveryLoop(ctx context.Context, h host.Host, syncService *p2p.SyncService,
peerChan <-chan peer.AddrInfo, timeout int, continuous bool,
peerChan <-chan peer.AddrInfo, timeout int, continuous bool, verbose bool,
) error {
var timeoutChan <-chan time.Time
if !continuous {
Expand Down Expand Up @@ -89,7 +97,7 @@ func RunDiscoveryLoop(ctx context.Context, h host.Host, syncService *p2p.SyncSer
discoveredPeers[p.ID.String()] = true
peerCount++

handleDiscoveredPeer(ctx, h, syncService, p, peerCount)
handleDiscoveredPeer(ctx, h, syncService, p, peerCount, verbose)

case <-timeoutChan:
fmt.Printf("\n⌛ Discovery timeout reached after %d seconds.\n", timeout)
Expand All @@ -113,13 +121,16 @@ func RunDiscoveryLoop(ctx context.Context, h host.Host, syncService *p2p.SyncSer

// handleDiscoveredPeer processes a newly discovered peer
func handleDiscoveredPeer(ctx context.Context, h host.Host, syncService *p2p.SyncService,
p peer.AddrInfo, peerCount int,
p peer.AddrInfo, peerCount int, verbose bool,
) {
fmt.Printf("✅ Discovered peer #%d\n", peerCount)
fmt.Printf(" ID: %s\n", p.ID.String())
fmt.Println(" Addresses:")
for _, addr := range p.Addrs {
fmt.Printf(" - %s\n", addr.String())

if verbose {
fmt.Printf(" ID: %s\n", p.ID.String())
fmt.Println(" Addresses:")
for _, addr := range p.Addrs {
fmt.Printf(" - %s\n", addr.String())
}
}

fmt.Printf(" Connecting and exchanging keys... ")
Expand All @@ -141,7 +152,10 @@ func handleDiscoveredPeer(ctx context.Context, h host.Host, syncService *p2p.Syn
if trusted {
fingerprint, _ := syncService.GetPeerFingerprint(p.ID)
fmt.Println("Key exchange successful")
fmt.Printf(" Fingerprint: %s\n", fingerprint)

if verbose {
fmt.Printf(" Fingerprint: %s\n", fingerprint)
}

// Attempt to add trusted peer; detect if already trusted by inspecting output of AddTrustedPeer logic.
// Since AddTrustedPeer itself prints when a peer already exists, suppress duplicate messaging here by
Expand All @@ -159,7 +173,9 @@ func handleDiscoveredPeer(ctx context.Context, h host.Host, syncService *p2p.Syn
}

if alreadyTrusted {
fmt.Println("Peer already trusted (verified)")
if verbose {
fmt.Println("Peer already trusted (verified)")
}
} else {
fmt.Println("Peer added to trusted list")
}
Expand Down
Loading
Loading