mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-18 23:33:51 +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.8 KiB
Go
92 lines
2.8 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/yusing/godoxy/internal/docker"
|
|
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
|
"github.com/yusing/godoxy/internal/types"
|
|
"github.com/yusing/godoxy/internal/watcher"
|
|
gperr "github.com/yusing/goutils/errs"
|
|
)
|
|
|
|
type DockerProvider struct {
|
|
client *docker.SharedClient
|
|
watcher watcher.DockerWatcher
|
|
containerID string
|
|
}
|
|
|
|
var startOptions = container.StartOptions{}
|
|
|
|
func NewDockerProvider(dockerHost, containerID string) (idlewatcher.Provider, error) {
|
|
client, err := docker.NewClient(dockerHost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DockerProvider{
|
|
client: client,
|
|
watcher: watcher.NewDockerWatcher(dockerHost),
|
|
containerID: containerID,
|
|
}, nil
|
|
}
|
|
|
|
func (p *DockerProvider) ContainerPause(ctx context.Context) error {
|
|
return p.client.ContainerPause(ctx, p.containerID)
|
|
}
|
|
|
|
func (p *DockerProvider) ContainerUnpause(ctx context.Context) error {
|
|
return p.client.ContainerUnpause(ctx, p.containerID)
|
|
}
|
|
|
|
func (p *DockerProvider) ContainerStart(ctx context.Context) error {
|
|
return p.client.ContainerStart(ctx, p.containerID, startOptions)
|
|
}
|
|
|
|
func (p *DockerProvider) ContainerStop(ctx context.Context, signal types.ContainerSignal, timeout int) error {
|
|
return p.client.ContainerStop(ctx, p.containerID, container.StopOptions{
|
|
Signal: string(signal),
|
|
Timeout: &timeout,
|
|
})
|
|
}
|
|
|
|
func (p *DockerProvider) ContainerKill(ctx context.Context, signal types.ContainerSignal) error {
|
|
return p.client.ContainerKill(ctx, p.containerID, string(signal))
|
|
}
|
|
|
|
func (p *DockerProvider) ContainerStatus(ctx context.Context) (idlewatcher.ContainerStatus, error) {
|
|
status, err := p.client.ContainerInspect(ctx, p.containerID)
|
|
if err != nil {
|
|
return idlewatcher.ContainerStatusError, err
|
|
}
|
|
switch status.State.Status {
|
|
case container.StateRunning:
|
|
return idlewatcher.ContainerStatusRunning, nil
|
|
case container.StateExited, container.StateDead, container.StateRestarting:
|
|
return idlewatcher.ContainerStatusStopped, nil
|
|
case container.StatePaused:
|
|
return idlewatcher.ContainerStatusPaused, nil
|
|
}
|
|
return idlewatcher.ContainerStatusError, idlewatcher.ErrUnexpectedContainerStatus.Subject(status.State.Status)
|
|
}
|
|
|
|
func (p *DockerProvider) Watch(ctx context.Context) (eventCh <-chan watcher.Event, errCh <-chan gperr.Error) {
|
|
return p.watcher.EventsWithOptions(ctx, watcher.DockerListOptions{
|
|
Filters: watcher.NewDockerFilter(
|
|
watcher.DockerFilterContainer,
|
|
watcher.DockerFilterContainerNameID(p.containerID),
|
|
watcher.DockerFilterStart,
|
|
watcher.DockerFilterStop,
|
|
watcher.DockerFilterDie,
|
|
watcher.DockerFilterKill,
|
|
watcher.DockerFilterDestroy,
|
|
watcher.DockerFilterPause,
|
|
watcher.DockerFilterUnpause,
|
|
),
|
|
})
|
|
}
|
|
|
|
func (p *DockerProvider) Close() {
|
|
p.client.Close()
|
|
}
|