feat: RHS panel initial fetch, floating widget, session cleanup (#5)
Phase 1: Fix RHS panel to fetch existing sessions on mount - Add initial API fetch in useAllStatusUpdates() hook - Allow GET /sessions endpoint without shared secret auth - RHS panel now shows sessions after page refresh Phase 2: Floating widget component (registerRootComponent) - New floating_widget.tsx with auto-show/hide behavior - Draggable, collapsible to pulsing dot with session count - Shows last 5 lines of most recent active session - Position persisted to localStorage - CSS styles using Mattermost theme variables Phase 3: Session cleanup and KV optimization - Add LastUpdateMs field to SessionData for staleness tracking - Set LastUpdateMs on session create and update - Add periodic cleanup goroutine (every 5 min) - Stale active sessions (>30 min no update) marked interrupted - Expired non-active sessions (>1 hr) deleted from KV - Add ListAllSessions and keep ListActiveSessions as helper - Add debug logging to daemon file polling Closes #5
This commit is contained in:
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/plugin"
|
||||
@@ -22,6 +23,9 @@ type Plugin struct {
|
||||
|
||||
// botUserID is the plugin's bot user ID (created on activation).
|
||||
botUserID string
|
||||
|
||||
// stopCleanup signals the cleanup goroutine to stop.
|
||||
stopCleanup chan struct{}
|
||||
}
|
||||
|
||||
// OnActivate is called when the plugin is activated.
|
||||
@@ -41,10 +45,36 @@ func (p *Plugin) OnActivate() error {
|
||||
p.API.LogInfo("Plugin bot user ensured", "botUserID", botID)
|
||||
}
|
||||
|
||||
// Start session cleanup goroutine
|
||||
p.stopCleanup = make(chan struct{})
|
||||
go p.sessionCleanupLoop()
|
||||
|
||||
p.API.LogInfo("OpenClaw Live Status plugin activated")
|
||||
return nil
|
||||
}
|
||||
|
||||
// sessionCleanupLoop runs periodically to clean up stale and expired sessions.
|
||||
func (p *Plugin) sessionCleanupLoop() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
staleThreshold := int64(30 * 60 * 1000) // 30 minutes — active sessions with no update
|
||||
expireThreshold := int64(60 * 60 * 1000) // 1 hour — completed/interrupted sessions
|
||||
cleaned, expired, err := p.store.CleanStaleSessions(staleThreshold, expireThreshold)
|
||||
if err != nil {
|
||||
p.API.LogWarn("Session cleanup error", "error", err.Error())
|
||||
} else if cleaned > 0 || expired > 0 {
|
||||
p.API.LogInfo("Session cleanup completed", "stale_marked", cleaned, "expired_deleted", expired)
|
||||
}
|
||||
case <-p.stopCleanup:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getBotUserID returns the plugin's bot user ID.
|
||||
func (p *Plugin) getBotUserID() string {
|
||||
return p.botUserID
|
||||
@@ -52,8 +82,13 @@ func (p *Plugin) getBotUserID() string {
|
||||
|
||||
// OnDeactivate is called when the plugin is deactivated.
|
||||
func (p *Plugin) OnDeactivate() error {
|
||||
// Stop cleanup goroutine
|
||||
if p.stopCleanup != nil {
|
||||
close(p.stopCleanup)
|
||||
}
|
||||
|
||||
// Mark all active sessions as interrupted
|
||||
sessions, err := p.store.ListActiveSessions()
|
||||
sessions, err := p.store.ListAllSessions()
|
||||
if err != nil {
|
||||
p.API.LogWarn("Failed to list sessions on deactivate", "error", err.Error())
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user