feat: add session replay with terminal playback via xterm.js
Persist byte-level I/O events from SSH sessions to SQLite and add a web
UI to replay them with original timing. Events are buffered in memory
and flushed every 2s to avoid blocking SSH I/O on database writes.
- Add session_events table (migration 002)
- Add SessionEvent type and storage methods (SQLite + MemoryStore)
- Change RecordingChannel to support multiple callbacks
- Add EventRecorder for buffered event persistence
- Add session detail page with xterm.js terminal replay
- Add /api/sessions/{id}/events JSON endpoint
- Linkify session IDs in dashboard and active sessions
- Vendor xterm.js v5.3.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,13 @@ import (
|
||||
//go:embed templates/*.html templates/fragments/*.html
|
||||
var templateFS embed.FS
|
||||
|
||||
func loadTemplates() (*template.Template, error) {
|
||||
funcMap := template.FuncMap{
|
||||
type templateSet struct {
|
||||
dashboard *template.Template
|
||||
sessionDetail *template.Template
|
||||
}
|
||||
|
||||
func templateFuncMap() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"formatTime": func(t time.Time) string {
|
||||
return t.Format("2006-01-02 15:04:05 UTC")
|
||||
},
|
||||
@@ -40,11 +45,31 @@ func loadTemplates() (*template.Template, error) {
|
||||
return fmt.Sprintf("%.0f%%", *f*100)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return template.New("").Funcs(funcMap).ParseFS(templateFS,
|
||||
func loadTemplates() (*templateSet, error) {
|
||||
funcMap := templateFuncMap()
|
||||
|
||||
dashboard, err := template.New("").Funcs(funcMap).ParseFS(templateFS,
|
||||
"templates/layout.html",
|
||||
"templates/dashboard.html",
|
||||
"templates/fragments/stats.html",
|
||||
"templates/fragments/active_sessions.html",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing dashboard templates: %w", err)
|
||||
}
|
||||
|
||||
sessionDetail, err := template.New("").Funcs(funcMap).ParseFS(templateFS,
|
||||
"templates/layout.html",
|
||||
"templates/session_detail.html",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing session detail templates: %w", err)
|
||||
}
|
||||
|
||||
return &templateSet{
|
||||
dashboard: dashboard,
|
||||
sessionDetail: sessionDetail,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user