mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-17 23:14:21 +01:00
This major overhaul of the idlewatcher system introduces a modern, real-time loading experience with Server-Sent Events (SSE) streaming and improved error handling. - **Real-time Event Streaming**: New SSE endpoint (`/$godoxy/wake-events`) provides live updates during container wake process - **Enhanced Loading Page**: Modern console-style interface with timestamped events and color-coded status messages - **Improved Static Asset Management**: Dedicated paths for CSS, JS, and favicon to avoid conflicting with upstream assets - **Event History Buffer**: Stores wake events for reconnecting clients and debugging - Refactored HTTP request handling with cleaner static asset routing - Added `WakeEvent` system with structured event types (starting, waking_dep, dep_ready, container_woke, waiting_ready, ready, error) - Implemented thread-safe event broadcasting using xsync.Map for concurrent SSE connections - Enhanced error handling with detailed logging and user-friendly error messages - Simplified loading page template system with better asset path management - Fixed race conditions in dependency waking and state management - Removed `common.go` functions (canceled, waitStarted) - moved inline for better context - Updated Waker interface to accept context parameter in Wake() method - New static asset paths use `/$godoxy/` prefix to avoid conflicts - Console-style output with Fira Code font for better readability - Color-coded event types (yellow for starting, blue for dependencies, green for success, red for errors) - Automatic page refresh when container becomes ready - Improved visual design with better glassmorphism effects and responsive layout - Real-time progress feedback during dependency wake and container startup This change transforms the static loading page into a dynamic, informative experience that keeps users informed during the wake process while maintaining backward compatibility with existing routing behavior.
92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
package idlewatcher
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
|
)
|
|
|
|
func (w *Watcher) running() bool {
|
|
return w.state.Load().status == idlewatcher.ContainerStatusRunning
|
|
}
|
|
|
|
func (w *Watcher) ready() bool {
|
|
return w.state.Load().ready
|
|
}
|
|
|
|
func (w *Watcher) error() error {
|
|
return w.state.Load().err
|
|
}
|
|
|
|
func (w *Watcher) setReady() {
|
|
w.state.Store(&containerState{
|
|
status: idlewatcher.ContainerStatusRunning,
|
|
ready: true,
|
|
})
|
|
// Send ready event via SSE
|
|
w.sendEvent(WakeEventReady, w.cfg.ContainerName()+" is ready!", nil)
|
|
// Notify waiting handlers that container is ready
|
|
select {
|
|
case w.readyNotifyCh <- struct{}{}:
|
|
default: // channel full, notification already pending
|
|
}
|
|
}
|
|
|
|
func (w *Watcher) setStarting() {
|
|
now := time.Now()
|
|
w.state.Store(&containerState{
|
|
status: idlewatcher.ContainerStatusRunning,
|
|
ready: false,
|
|
startedAt: now,
|
|
})
|
|
w.l.Debug().Time("started_at", now).Msg("container starting")
|
|
}
|
|
|
|
func (w *Watcher) setNapping(status idlewatcher.ContainerStatus) {
|
|
w.clearEventHistory() // Clear events on stop/pause
|
|
w.state.Store(&containerState{
|
|
status: status,
|
|
ready: false,
|
|
startedAt: time.Time{},
|
|
healthTries: 0,
|
|
})
|
|
}
|
|
|
|
func (w *Watcher) setError(err error) {
|
|
w.sendEvent(WakeEventError, "Container error", err)
|
|
w.state.Store(&containerState{
|
|
status: idlewatcher.ContainerStatusError,
|
|
ready: false,
|
|
err: err,
|
|
startedAt: time.Time{},
|
|
healthTries: 0,
|
|
})
|
|
}
|
|
|
|
// waitForReady waits for the container to become ready or context to be canceled.
|
|
// Returns true if ready, false if canceled.
|
|
func (w *Watcher) waitForReady(ctx context.Context) bool {
|
|
// Check if already ready
|
|
if w.ready() {
|
|
return true
|
|
}
|
|
|
|
// Wait for ready notification or context cancellation
|
|
select {
|
|
case <-w.readyNotifyCh:
|
|
return w.ready() // double-check in case of race condition
|
|
case <-ctx.Done():
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (w *Watcher) waitStarted(reqCtx context.Context) bool {
|
|
select {
|
|
case <-reqCtx.Done():
|
|
return false
|
|
case <-w.route.Started():
|
|
return true
|
|
}
|
|
}
|