Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*.dll
*.so
*.dylib
nssh

# Test binary, built with `go test -c`
*.test
Expand Down
1 change: 1 addition & 0 deletions cmd/nssh/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ func newLogCmd() *cobra.Command {
cmd.AddCommand(log.NewUploadCmd())
cmd.AddCommand(log.NewExportCmd())
cmd.AddCommand(log.NewAuthCmd())
cmd.AddCommand(log.NewSearchCmd())

ui.ApplyStyledHelpRecursive(cmd)
return cmd
Expand Down
1 change: 1 addition & 0 deletions docs/examples/help/log.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ $ nssh log --help
│ export Export recording │
│ list List recordings │
│ play Play recording │
│ search Search recordings for text │
│ upload Upload to asciinema │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Global Options ─────────────────────────────────────────────────────────────╮
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/help/log/delete.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ $ nssh log delete --help
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --dry-run Preview actions without executing │
│ --older-than INT Delete recordings older than N days │
│ -s, --select STRING Filter by regex pattern
│ -s, --select STRING Filter by pattern (today, yesterday...
│ -y, --yes Skip confirmation │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Global Options ─────────────────────────────────────────────────────────────╮
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/help/log/list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ $ nssh log list --help
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ -l, --last INT Filter on the last N entries │
│ -s, --select STRING Filter by regex pattern
│ -s, --select STRING Filter by pattern (today, yesterday...
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Global Options ─────────────────────────────────────────────────────────────╮
│ -e, --explain Print command explanation │
Expand Down
16 changes: 16 additions & 0 deletions docs/examples/help/log/search.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
$ nssh log search --help
╭─ Usage ──────────────────────────────────────────────────────────────────────╮
│ nssh log search <pattern> [flags] Search recordings for text │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ -i, --case-sensitive Case-sensitive search │
│ -C, --context INT Show N lines of context around matches │
│ -l, --last INT Search only the last N sessions │
│ -s, --select STRING Filter sessions by pattern (today, ... │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Global Options ─────────────────────────────────────────────────────────────╮
│ -e, --explain Print command explanation │
│ -h, --help Print command help │
│ -v, --verbose Print debug messages │
│ -V, --version Print command version │
╰──────────────────────────────────────────────────────────────────────────────╯
53 changes: 51 additions & 2 deletions internal/cli/log/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,20 @@ func PrintSessions(records []recording.SessionRecord, filter string) {
}

// FilterSessionsByPattern filters sessions by regex pattern.
// Matches against host, start date, mtime date, and cast path.
func FilterSessionsByPattern(sessions []recording.SessionRecord, pattern string) ([]recording.SessionRecord, error) {
re, err := regexp.Compile("(?i)" + pattern)
if err != nil {
return nil, fmt.Errorf("invalid regex pattern: %w", err)
}

localTZ := time.Now().Location()
var filtered []recording.SessionRecord
for _, session := range sessions {
startDate := session.StartedAt.In(localTZ).Format("2006-01-02")
mtimeDate := sessionUpdatedTimestamp(session).In(localTZ).Format("2006-01-02")
castDisplay := homeReplace(session.CastPath)
display := fmt.Sprintf("%s %s %s", session.Host, session.StartedAt.Format("2006-01-02"), castDisplay)
if re.MatchString(display) {
if MatchesPattern(re, session.Host, session.SessionLabel, startDate, mtimeDate, castDisplay) {
filtered = append(filtered, session)
}
}
Expand Down Expand Up @@ -433,3 +436,49 @@ func MatchesPattern(pattern *regexp.Regexp, fields ...string) bool {
}
return false
}

// ExpandDateShortcut expands convenient date shortcuts to regex patterns.
// Supported shortcuts: today, yesterday, this-week, this-month, last-week, last-month
// Returns the original pattern if not a recognized shortcut.
func ExpandDateShortcut(pattern string) string {
now := time.Now()
lower := strings.ToLower(strings.TrimSpace(pattern))

switch lower {
case "today":
return now.Format("2006-01-02")

case "yesterday":
return now.AddDate(0, 0, -1).Format("2006-01-02")

case "this-week":
// Get dates for this week (Sunday to today)
weekday := int(now.Weekday())
dates := make([]string, 0, weekday+1)
for i := weekday; i >= 0; i-- {
d := now.AddDate(0, 0, -i)
dates = append(dates, d.Format("2006-01-02"))
}
return "(" + strings.Join(dates, "|") + ")"

case "last-week":
// Previous full week (Sunday to Saturday)
weekday := int(now.Weekday())
lastSunday := now.AddDate(0, 0, -weekday-7)
dates := make([]string, 7)
for i := 0; i < 7; i++ {
d := lastSunday.AddDate(0, 0, i)
dates[i] = d.Format("2006-01-02")
}
return "(" + strings.Join(dates, "|") + ")"

case "this-month":
return now.Format("2006-01")

case "last-month":
return now.AddDate(0, -1, 0).Format("2006-01")

default:
return pattern
}
}
3 changes: 2 additions & 1 deletion internal/cli/log/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Cannot combine modes - use only one mode flag at a time.`,
},
}

cmd.Flags().StringVarP(&selectPattern, "select", "s", "", "Filter by regex pattern")
cmd.Flags().StringVarP(&selectPattern, "select", "s", "", "Filter by pattern (today, yesterday, this-week, this-month, or regex)")
cmd.Flags().IntVar(&olderThan, "older-than", 0, "Delete recordings older than N days")
cmd.Flags().BoolVarP(&yes, "yes", "y", false, "Skip confirmation")
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Preview actions without executing")
Expand Down Expand Up @@ -118,6 +118,7 @@ func deleteOlderThan(settings recording.RecordingSettings, days int, dryRun bool
func deleteByPattern(settings recording.RecordingSettings, pattern string, yes, dryRun bool) error {
sessions := LoadSessions(settings)

pattern = ExpandDateShortcut(pattern)
filtered, err := FilterSessionsByPattern(sessions, pattern)
if err != nil {
ui.Error("Invalid pattern: %s", err)
Expand Down
8 changes: 5 additions & 3 deletions internal/cli/log/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func NewListCmd() *cobra.Command {
},
}

cmd.Flags().StringVarP(&selectPattern, "select", "s", "", "Filter by regex pattern")
cmd.Flags().StringVarP(&selectPattern, "select", "s", "", "Filter by pattern (today, yesterday, this-week, this-month, or regex)")
cmd.Flags().IntVarP(&lastN, "last", "l", 0, "Filter on the last N entries")

return cmd
Expand All @@ -47,6 +47,7 @@ func runList(selectPattern string, lastN int) error {
}

if selectPattern != "" {
selectPattern = ExpandDateShortcut(selectPattern)
pattern, err := regexp.Compile("(?i)" + selectPattern)
if err != nil {
ui.Error("Invalid regex pattern: %s", err)
Expand All @@ -56,8 +57,9 @@ func runList(selectPattern string, lastN int) error {

var filtered []recording.SessionRecord
for _, s := range sessions {
localDate := s.StartedAt.In(localTZ).Format("2006-01-02")
if MatchesPattern(pattern, s.Host, s.SessionLabel, localDate) {
startDate := s.StartedAt.In(localTZ).Format("2006-01-02")
mtimeDate := sessionUpdatedTimestamp(s).In(localTZ).Format("2006-01-02")
if MatchesPattern(pattern, s.Host, s.SessionLabel, startDate, mtimeDate) {
filtered = append(filtered, s)
}
}
Expand Down
Loading