Files
tiny-forge/plans/docker-watcher-core/phase-11-embed-sse.md
T
alexei.dolgolyov 5558396bb7 feat(docker-watcher): phase 11 - frontend embed & SSE
Embed SvelteKit static build in Go binary via go:embed. Event bus
for pub/sub with deploy log, instance status, and deploy status events.
SSE endpoints for real-time streaming. Frontend SSE client with
exponential backoff reconnection. Makefile for build pipeline.
Update Phase 12 auth plan with OAuth2/OIDC support.
2026-03-27 22:30:25 +03:00

4.9 KiB

Phase 11: Frontend Embed & Real-Time Updates

Status: Done Parent plan: PLAN.md Domain: fullstack

Objective

Build SvelteKit to static files, embed into the Go binary with go:embed, serve from Go, and implement SSE for real-time deploy progress and instance status updates.

Tasks

  • Task 1: Configure SvelteKit static adapter to output to web/build/ (already configured)
  • Task 2: Add //go:embed web/build directive in Go — web.go at project root
  • Task 3: Create Go handler for serving embedded SPA — internal/api/static.go with SPA fallback
  • Task 4: Implement SSE endpoint for deploy logs — GET /api/deploys/:id/logs (SSE + JSON fallback)
  • Task 5: Implement SSE endpoint for instance status — GET /api/events streams instance status changes
  • Task 6: Create event bus/broadcaster in Go — internal/events/bus.go with pub/sub channels
  • Task 7: Frontend: connect to SSE for deploy progress — connectDeployLogs() in web/src/lib/sse.ts
  • Task 8: Frontend: connect to SSE for instance status — global SSE in +layout.svelte via store
  • Task 9: Handle SSE reconnection in frontend — exponential backoff with jitter in connectSSE()
  • Task 10: Build script/Makefile — make build builds frontend then Go binary

Files to Modify/Create

  • web/svelte.config.js — already configured with static adapter outputting to web/build/
  • web.go — root-level embed directive (//go:embed web/build)
  • internal/api/static.go — embedded static file server with SPA fallback
  • internal/api/sse.go — SSE endpoints for deploy logs and instance events
  • internal/events/bus.go — event bus for publishing/subscribing to events
  • web/src/lib/sse.ts — SSE client helper with auto-reconnect
  • web/src/lib/stores/instance-status.ts — Svelte store for real-time instance status
  • web/src/routes/+layout.svelte — wired up global SSE connection for instance status
  • Makefile — build frontend + backend
  • cmd/server/main.go — wired embedded static serving and event bus
  • internal/api/router.go — added eventBus to Server, SSE routes
  • internal/api/deploys.go — removed old JSON stub, replaced by SSE handler
  • internal/deployer/deployer.go — added event publishing for deploy logs, status, instance status

Acceptance Criteria

  • make build produces a single Go binary with embedded frontend
  • Go binary serves the SvelteKit SPA on all non-API routes
  • Deploy progress streams in real-time via SSE
  • Instance status updates appear without page refresh
  • SSE reconnects automatically after network hiccups

Notes

  • go:embed requires the embedded directory to be relative to the Go source file
  • SPA fallback: any request that doesn't match /api/* gets index.html
  • Event bus: simple pub/sub with channels — no external dependency needed
  • SSE format: data: {"type": "deploy_log", "payload": {...}}\n\n
  • Keep SSE connections lightweight — use context cancellation for cleanup
  • WriteTimeout on HTTP server set to 0 to support long-lived SSE connections
  • Deploy logs endpoint serves both SSE (Accept: text/event-stream) and JSON (default)

Review Checklist

  • All tasks completed
  • Single binary serves both API and frontend
  • SSE handles multiple concurrent clients (buffered channels, non-blocking publish)
  • No goroutine leaks on SSE disconnect (context cancellation + Unsubscribe)
  • Build process is reproducible (Makefile)

Handoff to Next Phase

What was implemented

  • Event bus (internal/events/bus.go): In-process pub/sub with topic filtering, buffered subscriber channels (64 events), non-blocking publish. Supports EventDeployLog, EventInstanceStatus, and EventDeployStatus event types.
  • SSE endpoints: GET /api/deploys/{id}/logs streams deploy logs with JSON fallback; GET /api/events streams global instance/deploy status changes.
  • Static file serving: web.go at project root embeds web/build/, internal/api/static.go serves SPA with fallback. Mounted via chi's NotFound handler.
  • Frontend SSE client (web/src/lib/sse.ts): connectSSE() with exponential backoff + jitter, connectDeployLogs() and connectGlobalEvents() convenience functions.
  • Instance status store (web/src/lib/stores/instance-status.ts): Svelte writable store updated by global SSE connection in +layout.svelte.
  • Deployer integration: deployer.go now publishes deploy log, deploy status, and instance status events via EventPublisher interface.

Key integration points for next phase

  • events.Bus is passed to both api.NewServer and deployer.New
  • api.NewServer now requires an *events.Bus parameter (6th arg before encKey)
  • deployer.New now requires an EventPublisher parameter (6th arg before encKey)
  • HTTP server WriteTimeout is 0 to support SSE
  • The web.go file at project root uses package name dockerwatcher (imported as github.com/alexei/docker-watcher)