Skip to content

Commit 6d28605

Browse files
committed
Add shared file management through file name rather than just file hash, use a search to determine what files match the query to allow shortnames.
1 parent fdad8a6 commit 6d28605

2 files changed

Lines changed: 261 additions & 18 deletions

File tree

main.go

Lines changed: 105 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -481,47 +481,134 @@ func main() {
481481
}
482482

483483
var sharedLsCmd = &cobra.Command{
484-
Use: "ls",
485-
Short: "List all shared files",
484+
Use: "ls [file-name-pattern]",
485+
Aliases: []string{"list", "find", "search"},
486+
Short: "List shared files (optionally search by name)",
487+
Long: `List all shared files, or search by partial/fuzzy filename match.
488+
489+
Without arguments: Shows all shared files with references and dates.
490+
With file-name-pattern: Searches for files matching the pattern.
491+
492+
Examples:
493+
zep shared ls # List all shared files
494+
zep shared list # Same as ls (alias)
495+
zep shared find report.pdf # Find files matching "report.pdf"
496+
zep shared search report # Same as find (alias)`,
497+
Args: cobra.MaximumNArgs(1),
486498
Run: func(cmd *cobra.Command, args []string) {
487499
session, err := getEffectiveSession()
488500
if err != nil {
489501
fmt.Printf("❌ Authentication failed: %v\n", err)
490502
return
491503
}
492504

493-
files := utils.ListSharedFiles(session)
494-
if len(files) == 0 {
495-
fmt.Println("No shared files.")
505+
// If no arguments, list all shared files
506+
if len(args) == 0 {
507+
files := utils.ListSharedFiles(session)
508+
if len(files) == 0 {
509+
fmt.Println("No shared files.")
510+
return
511+
}
512+
513+
fmt.Println("\n📤 SHARED FILES")
514+
fmt.Println("REFERENCE FILE NAME SHARED AT")
515+
fmt.Println("--------- ---------- ---------")
516+
for _, f := range files {
517+
fmt.Printf("%-9s %-24s %s\n", f.Reference, f.OriginalPath, f.SharedAt.Format("2006-01-02 15:04"))
518+
}
519+
fmt.Println()
520+
return
521+
}
522+
523+
// If argument provided, search by name
524+
nameQuery := args[0]
525+
matches, err := utils.FindSharedFilesByName(nameQuery, session)
526+
if err != nil {
527+
fmt.Printf("❌ %v\n", err)
528+
return
529+
}
530+
531+
if len(matches) == 0 {
532+
fmt.Printf("❌ No shared files found matching '%s'\n", nameQuery)
496533
return
497534
}
498535

499-
fmt.Println("\n📤 SHARED FILES")
500-
fmt.Println("REFERENCE FILE NAME SHARED AT")
501-
fmt.Println("--------- ---------- ---------")
502-
for _, f := range files {
503-
fmt.Printf("%-9s %-24s %s\n", f.Reference, f.OriginalPath, f.SharedAt.Format("2006-01-02 15:04"))
536+
fmt.Printf("\n📂 Found %d match(es) for '%s':\n\n", len(matches), nameQuery)
537+
for i, match := range matches {
538+
fmt.Printf("[%d] %s\n", i+1, match.FileName)
539+
fmt.Printf(" Vault Path: %s\n", match.OriginalPath)
540+
fmt.Printf(" Reference: %s\n", match.Reference)
541+
fmt.Printf(" Match Type: ")
542+
if match.MatchScore == 0 {
543+
fmt.Println("Exact match")
544+
} else if match.MatchScore < 50 {
545+
fmt.Println("Prefix match")
546+
} else {
547+
fmt.Println("Substring match")
548+
}
549+
fmt.Println()
504550
}
505-
fmt.Println()
506551
},
507552
}
508553

509554
var sharedRmCmd = &cobra.Command{
510-
Use: "rm [reference]",
511-
Aliases: []string{"revoke", "delete"},
512-
Short: "Revoke/remove a shared file",
513-
Args: cobra.ExactArgs(1),
555+
Use: "rm [reference-or-name]",
556+
Aliases: []string{"revoke", "delete", "remove"},
557+
Short: "Revoke/remove a shared file by reference or name",
558+
Long: `Revoke a shared file using its reference hash or file name.
559+
560+
Can match by:
561+
- Reference hash: zep shared rm AbC123
562+
- File name (exact or partial): zep shared rm report.pdf
563+
564+
Fuzzy matching is supported for file names:
565+
- Exact match: "report.pdf"
566+
- Prefix match: "report"
567+
- Substring match: "port.pdf"
568+
569+
If multiple files match a name, you'll be prompted to be more specific.`,
570+
Args: cobra.ExactArgs(1),
514571
Run: func(cmd *cobra.Command, args []string) {
515572
session, err := getEffectiveSession()
516573
if err != nil {
517574
fmt.Printf("❌ Authentication failed: %v\n", err)
518575
return
519576
}
520577

521-
reference := args[0]
578+
query := args[0]
579+
580+
// First, try to find by name (name matching is more flexible)
581+
matches, err := utils.FindSharedFilesByName(query, session)
582+
var reference string
583+
var displayName string
584+
585+
if len(matches) > 0 {
586+
// Found by name
587+
if len(matches) > 1 {
588+
// Ambiguous - show options
589+
fmt.Printf("Multiple files match '%s':\n\n", query)
590+
for i, match := range matches {
591+
fmt.Printf("[%d] %s (ref: %s)\n", i+1, match.FileName, match.Reference)
592+
}
593+
fmt.Println("\n⚠️ Please be more specific with the file name.")
594+
return
595+
}
596+
// Exactly one match
597+
reference = matches[0].Reference
598+
displayName = matches[0].FileName
599+
} else {
600+
// Not found by name - try as reference directly
601+
entry, err := utils.GetSharedFileInfo(query, session)
602+
if err != nil {
603+
fmt.Printf("❌ No shared file found matching '%s'\n", query)
604+
return
605+
}
606+
reference = entry.Reference
607+
displayName = entry.OriginalPath
608+
}
522609

523610
// Confirm revocation
524-
fmt.Printf("⚠️ Revoke shared file %s? (y/N): ", reference)
611+
fmt.Printf("⚠️ Revoke shared file '%s'? (y/N): ", displayName)
525612
var confirm string
526613
fmt.Scanln(&confirm)
527614
if confirm != "y" && confirm != "yes" {
@@ -541,7 +628,7 @@ func main() {
541628
session.Save()
542629
}
543630

544-
fmt.Printf("✔ Shared file '%s' revoked.\n", reference)
631+
fmt.Printf("✔ Shared file '%s' revoked.\n", displayName)
545632
},
546633
}
547634

utils/shared_search.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package utils
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// SharedFileMatch represents a match result when searching for shared files
9+
type SharedFileMatch struct {
10+
Reference string
11+
OriginalPath string
12+
FileName string
13+
MatchScore int // Lower score = better match
14+
}
15+
16+
// FindSharedFilesByName searches for shared files matching a name query
17+
// Uses fuzzy matching to allow shortnames and partial matches
18+
func FindSharedFilesByName(nameQuery string, session *Session) ([]SharedFileMatch, error) {
19+
if session.SharedIndex == nil {
20+
session.SharedIndex = NewSharedIndex()
21+
}
22+
23+
var matches []SharedFileMatch
24+
nameQueryLower := strings.ToLower(nameQuery)
25+
entries := session.SharedIndex.ListEntries()
26+
27+
for _, entry := range entries {
28+
// Extract filename from the original path
29+
parts := strings.Split(entry.OriginalPath, "/")
30+
fileName := parts[len(parts)-1]
31+
fileNameLower := strings.ToLower(fileName)
32+
33+
// Check for exact match
34+
if fileNameLower == nameQueryLower {
35+
matches = append(matches, SharedFileMatch{
36+
Reference: entry.Reference,
37+
OriginalPath: entry.OriginalPath,
38+
FileName: fileName,
39+
MatchScore: 0, // Exact match = best
40+
})
41+
continue
42+
}
43+
44+
// Check for prefix match
45+
if strings.HasPrefix(fileNameLower, nameQueryLower) {
46+
matchScore := len(fileName) - len(nameQuery) // Shorter matches are better
47+
matches = append(matches, SharedFileMatch{
48+
Reference: entry.Reference,
49+
OriginalPath: entry.OriginalPath,
50+
FileName: fileName,
51+
MatchScore: matchScore,
52+
})
53+
continue
54+
}
55+
56+
// Check for substring match
57+
if strings.Contains(fileNameLower, nameQueryLower) {
58+
matchScore := 100 + (len(fileName) - len(nameQuery)) // Substring match = worse than prefix
59+
matches = append(matches, SharedFileMatch{
60+
Reference: entry.Reference,
61+
OriginalPath: entry.OriginalPath,
62+
FileName: fileName,
63+
MatchScore: matchScore,
64+
})
65+
}
66+
}
67+
68+
return matches, nil
69+
}
70+
71+
// RevokeSharedFileByName revokes a shared file using its name (with search if ambiguous)
72+
func RevokeSharedFileByName(nameQuery string, session *Session) (string, error) {
73+
matches, err := FindSharedFilesByName(nameQuery, session)
74+
if err != nil {
75+
return "", err
76+
}
77+
78+
if len(matches) == 0 {
79+
return "", fmt.Errorf("no shared files found matching '%s'", nameQuery)
80+
}
81+
82+
if len(matches) > 1 {
83+
// Multiple matches - show all and ask user to be more specific
84+
fmt.Printf("Multiple files match '%s':\n", nameQuery)
85+
for i, match := range matches {
86+
fmt.Printf(" %d. %s (ref: %s)\n", i+1, match.FileName, match.Reference)
87+
}
88+
return "", fmt.Errorf("ambiguous file name - please be more specific")
89+
}
90+
91+
// Exactly one match - revoke it
92+
reference := matches[0].Reference
93+
err = RevokeSharedFile(reference, session)
94+
if err != nil {
95+
return "", err
96+
}
97+
98+
return reference, nil
99+
}
100+
101+
// PrintSharedFilesFormatted lists all shared files with formatted output
102+
func PrintSharedFilesFormatted(session *Session) error {
103+
entries := session.SharedIndex.ListEntries()
104+
if len(entries) == 0 {
105+
fmt.Println("No files have been shared yet.")
106+
return nil
107+
}
108+
109+
fmt.Println("\n╔════════════════════════════════════════════════════════════════╗")
110+
fmt.Println("║ SHARED FILES ║")
111+
fmt.Println("╚════════════════════════════════════════════════════════════════╝")
112+
113+
for i, entry := range entries {
114+
// Extract filename from path
115+
parts := strings.Split(entry.OriginalPath, "/")
116+
fileName := parts[len(parts)-1]
117+
118+
fmt.Printf("\n[%d] %s\n", i+1, fileName)
119+
fmt.Printf(" Vault Path: %s\n", entry.OriginalPath)
120+
fmt.Printf(" Share Ref: %s\n", entry.Reference)
121+
fmt.Printf(" Shared At: %s\n", entry.SharedAt.Format("2006-01-02 15:04:05"))
122+
}
123+
124+
fmt.Println()
125+
return nil
126+
}
127+
128+
// GetSharedFileByName retrieves a shared file by name query
129+
func GetSharedFileByName(nameQuery string, session *Session) (*SharedFileEntry, error) {
130+
matches, err := FindSharedFilesByName(nameQuery, session)
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
if len(matches) == 0 {
136+
return nil, fmt.Errorf("no shared files found matching '%s'", nameQuery)
137+
}
138+
139+
if len(matches) > 1 {
140+
fmt.Printf("Multiple files match '%s':\n", nameQuery)
141+
for i, match := range matches {
142+
fmt.Printf(" %d. %s (ref: %s)\n", i+1, match.FileName, match.Reference)
143+
}
144+
return nil, fmt.Errorf("ambiguous file name - please be more specific")
145+
}
146+
147+
// Find the entry in the SharedIndex
148+
entries := session.SharedIndex.ListEntries()
149+
for i := range entries {
150+
if entries[i].Reference == matches[0].Reference {
151+
return &entries[i], nil
152+
}
153+
}
154+
155+
return nil, fmt.Errorf("shared file entry not found")
156+
}

0 commit comments

Comments
 (0)