diff --git a/pkg/api/provider.go b/pkg/api/provider.go index 7f4e2ad..71c48c3 100644 --- a/pkg/api/provider.go +++ b/pkg/api/provider.go @@ -275,6 +275,9 @@ func (p *ProcessProvider) listDaemons(c *gin.Context) { if entries == nil { entries = []process.DaemonEntry{} } + for _, entry := range entries { + p.emitEvent("process.daemon.started", daemonEventPayload(entry)) + } c.JSON(http.StatusOK, api.OK(entries)) } @@ -287,6 +290,7 @@ func (p *ProcessProvider) getDaemon(c *gin.Context) { c.JSON(http.StatusNotFound, api.Fail("not_found", "daemon not found or not running")) return } + p.emitEvent("process.daemon.started", daemonEventPayload(*entry)) c.JSON(http.StatusOK, api.OK(entry)) } @@ -503,6 +507,18 @@ func (p *ProcessProvider) emitEvent(channel string, data any) { _ = p.hub.SendToChannel(channel, msg) } +func daemonEventPayload(entry process.DaemonEntry) map[string]any { + return map[string]any{ + "code": entry.Code, + "daemon": entry.Daemon, + "pid": entry.PID, + "health": entry.Health, + "project": entry.Project, + "binary": entry.Binary, + "started": entry.Started, + } +} + // PIDAlive checks whether a PID is still running. Exported for use by // consumers that need to verify daemon liveness outside the REST API. func PIDAlive(pid int) bool { diff --git a/pkg/api/provider_test.go b/pkg/api/provider_test.go index c8ee7c7..f5aa610 100644 --- a/pkg/api/provider_test.go +++ b/pkg/api/provider_test.go @@ -87,6 +87,48 @@ func TestProcessProvider_ListDaemons_Good(t *testing.T) { assert.True(t, resp.Success) } +func TestProcessProvider_ListDaemons_BroadcastsStarted_Good(t *testing.T) { + dir := t.TempDir() + registry := newTestRegistry(dir) + require.NoError(t, registry.Register(process.DaemonEntry{ + Code: "test", + Daemon: "serve", + PID: os.Getpid(), + })) + + hub := corews.NewHub() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go hub.Run(ctx) + + p := processapi.NewProvider(registry, nil, hub) + server := httptest.NewServer(hub.Handler()) + defer server.Close() + + conn := connectWS(t, server.URL) + defer conn.Close() + + require.Eventually(t, func() bool { + return hub.ClientCount() == 1 + }, time.Second, 10*time.Millisecond) + + r := setupRouter(p) + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/api/process/daemons", nil) + r.ServeHTTP(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + events := readWSEvents(t, conn, "process.daemon.started") + started := events["process.daemon.started"] + require.NotNil(t, started) + + startedData := started.Data.(map[string]any) + assert.Equal(t, "test", startedData["code"]) + assert.Equal(t, "serve", startedData["daemon"]) + assert.Equal(t, float64(os.Getpid()), startedData["pid"]) +} + func TestProcessProvider_GetDaemon_Bad(t *testing.T) { dir := t.TempDir() registry := newTestRegistry(dir)