This commit introduces a major overhaul of both the frontend web UI and the Go backend API, transitioning to a highly modular architecture and integrating new core features. Backend: - Refactored monolithic API endpoints into domain-specific modules (config, gateway, log, models, pico, session). - Cleaned up obsolete files (`server.go`, `status.go`, WebSocket handlers) and outdated tests. - Implemented Gateway process lifecycle management (start/stop/restart) and real-time log streaming. Frontend: - Integrated Shadcn UI components to establish a modern, consistent design system. - Introduced a new application layout featuring a responsive sidebar (`app-sidebar`) and header. - Implemented internationalization (i18n) with initial support for English and Chinese. - Restructured API clients, hooks, and Zustand stores into logical domains. - Added new management pages for Settings, Logs, Models, Providers, and Credentials. - Upgraded the Pico chat interface with session history management and dynamic model selection. Build & Config: - Updated frontend dependencies, Vite configuration, and lockfiles. - Refined routing setup and overarching application stylesheets.
62 lines
1.4 KiB
Go
62 lines
1.4 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sync"
|
|
)
|
|
|
|
// GatewayEvent represents a state change event for the gateway process.
|
|
type GatewayEvent struct {
|
|
Status string `json:"gateway_status"` // "running", "starting", "stopped", "error"
|
|
PID int `json:"pid,omitempty"`
|
|
}
|
|
|
|
// EventBroadcaster manages SSE client subscriptions and broadcasts events.
|
|
type EventBroadcaster struct {
|
|
mu sync.RWMutex
|
|
clients map[chan string]struct{}
|
|
}
|
|
|
|
// NewEventBroadcaster creates a new broadcaster.
|
|
func NewEventBroadcaster() *EventBroadcaster {
|
|
return &EventBroadcaster{
|
|
clients: make(map[chan string]struct{}),
|
|
}
|
|
}
|
|
|
|
// Subscribe adds a new listener channel and returns it.
|
|
// The caller must call Unsubscribe when done.
|
|
func (b *EventBroadcaster) Subscribe() chan string {
|
|
ch := make(chan string, 8)
|
|
b.mu.Lock()
|
|
b.clients[ch] = struct{}{}
|
|
b.mu.Unlock()
|
|
return ch
|
|
}
|
|
|
|
// Unsubscribe removes a listener channel and closes it.
|
|
func (b *EventBroadcaster) Unsubscribe(ch chan string) {
|
|
b.mu.Lock()
|
|
delete(b.clients, ch)
|
|
b.mu.Unlock()
|
|
close(ch)
|
|
}
|
|
|
|
// Broadcast sends a GatewayEvent to all connected SSE clients.
|
|
func (b *EventBroadcaster) Broadcast(event GatewayEvent) {
|
|
data, err := json.Marshal(event)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
|
|
for ch := range b.clients {
|
|
// Non-blocking send; drop event if client is slow
|
|
select {
|
|
case ch <- string(data):
|
|
default:
|
|
}
|
|
}
|
|
}
|