feat: add session indicators and top exec commands to dashboard

Add visual indicators to session tables (replay badge when events exist,
exec badge for exec sessions) and a new "Top Exec Commands" table on the
dashboard. Includes EventCount field on Session, GetTopExecCommands on
Store interface, and truncateCommand template function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 19:38:10 +01:00
parent 0b44d1c83f
commit 4f10a8a422
9 changed files with 243 additions and 25 deletions

View File

@@ -382,11 +382,11 @@ func (s *SQLiteStore) queryTopN(ctx context.Context, column string, limit int) (
}
func (s *SQLiteStore) GetRecentSessions(ctx context.Context, limit int, activeOnly bool) ([]Session, error) {
query := `SELECT id, ip, country, username, shell_name, connected_at, disconnected_at, human_score, exec_command FROM sessions`
query := `SELECT s.id, s.ip, s.country, s.username, s.shell_name, s.connected_at, s.disconnected_at, s.human_score, s.exec_command, COUNT(e.id) as event_count FROM sessions s LEFT JOIN session_events e ON s.id = e.session_id`
if activeOnly {
query += ` WHERE disconnected_at IS NULL`
query += ` WHERE s.disconnected_at IS NULL`
}
query += ` ORDER BY connected_at DESC LIMIT ?`
query += ` GROUP BY s.id ORDER BY s.connected_at DESC LIMIT ?`
rows, err := s.db.QueryContext(ctx, query, limit)
if err != nil {
@@ -401,7 +401,7 @@ func (s *SQLiteStore) GetRecentSessions(ctx context.Context, limit int, activeOn
var disconnectedAt sql.NullString
var humanScore sql.NullFloat64
var execCommand sql.NullString
if err := rows.Scan(&s.ID, &s.IP, &s.Country, &s.Username, &s.ShellName, &connectedAt, &disconnectedAt, &humanScore, &execCommand); err != nil {
if err := rows.Scan(&s.ID, &s.IP, &s.Country, &s.Username, &s.ShellName, &connectedAt, &disconnectedAt, &humanScore, &execCommand, &s.EventCount); err != nil {
return nil, fmt.Errorf("scanning session: %w", err)
}
s.ConnectedAt, _ = time.Parse(time.RFC3339, connectedAt)
@@ -420,6 +420,30 @@ func (s *SQLiteStore) GetRecentSessions(ctx context.Context, limit int, activeOn
return sessions, rows.Err()
}
func (s *SQLiteStore) GetTopExecCommands(ctx context.Context, limit int) ([]TopEntry, error) {
rows, err := s.db.QueryContext(ctx, `
SELECT exec_command, COUNT(*) as total
FROM sessions
WHERE exec_command IS NOT NULL
GROUP BY exec_command
ORDER BY total DESC
LIMIT ?`, limit)
if err != nil {
return nil, fmt.Errorf("querying top exec commands: %w", err)
}
defer func() { _ = rows.Close() }()
var entries []TopEntry
for rows.Next() {
var e TopEntry
if err := rows.Scan(&e.Value, &e.Count); err != nil {
return nil, fmt.Errorf("scanning top exec commands: %w", err)
}
entries = append(entries, e)
}
return entries, rows.Err()
}
func (s *SQLiteStore) CloseActiveSessions(ctx context.Context, disconnectedAt time.Time) (int64, error) {
res, err := s.db.ExecContext(ctx, `
UPDATE sessions SET disconnected_at = ? WHERE disconnected_at IS NULL`,