Skip to content

api: uniform CORS + an SSE event stream for frontends - #63

Merged
vynulldev merged 2 commits into
mainfrom
api-cors-sse
Oct 10, 2026
Merged

vynulldev merged 2 commits into
mainfrom
api-cors-sse

Conversation

@vynulldev

@vynulldev vynulldev commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

What & why

Two small API changes that let an external frontend drive Vynull: uniform CORS, and a live event stream.

These are the two gaps the frontends direction has been waiting on, so they go in together.

  • Uniform CORS. corsMiddleware wraps the whole mux, setting the permissive CORS headers on every response and answering preflight OPTIONS uniformly. Vynull is a trusted-LAN tool with no auth and an already-open posture; several handlers set the same header by hand, so this just makes it consistent everywhere (preflight included) instead of a per-handler patchwork, so a UI served from another origin or device can call the API.
  • SSE stream (/api/events). A Server-Sent-Events endpoint that pushes live deck state as it changes, so a frontend gets push updates instead of polling /api/players. Self-contained in api/events.go plus one route.

The endpoints are documented in the OpenAPI spec in #58 (the spec is one document, so it owns the whole surface); this PR is code only.

Hardware testing

Both are HTTP-side only: a response-header middleware and a new read-only event stream. Neither touches the Pro DJ Link protocol, the load path, or anything a deck reacts to.

  • Tested on: N/A: no deck-facing change

Checklist

  • go build ./..., go vet ./..., and go test ./... pass
  • gofmt -l . is clean
  • New source files carry an SPDX header (GPL-3.0-or-later)
  • Tested on real hardware (deck + firmware noted above), or this change doesn't affect deck behaviour
  • I agree my contribution is licensed under the project's GPLv3

vynulldev added a commit that referenced this pull request Oct 10, 2026
Document the whole HTTP API with an OpenAPI 3.0 spec, and serve it with a
self-hosted Swagger UI. The API is how the CLI, the web UI, and any external
frontend drive a running Vynull, but it was documented only by the handlers
themselves.

Add docs/api/openapi.yaml covering every /api/* endpoint (56 paths, 75
operations, 31 schemas), and serve it at /api/openapi.yaml with a browsable
Swagger UI at /api/docs. The UI bundle is vendored under docs/api/swagger/ and
embedded, so it works offline with no CDN; the serving routes are always on,
not gated on the web UI, so a headless daemon self-documents.

The spec is the whole surface in one document, so it also describes the
uniform CORS headers and the /api/events SSE stream whose behaviour lands in
#63. Documenting them here keeps the spec complete rather than split across
files.

Docs and tooling only: a static spec file, the embedded UI assets, and the
routes that serve them. No existing endpoint changes behaviour. The spec was
audited field by field against the Go types it documents (every schema's
properties checked against the serialized struct's JSON tags) and is
drift-free.
Wrap the API mux in a CORS middleware so every endpoint sends permissive
CORS (Access-Control-Allow-Origin: *, methods, Content-Type) and answers
preflight OPTIONS uniformly — instead of the former per-handler patchwork
where some endpoints set it and some (e.g. /api/status) didn't, and most
POST endpoints had no preflight at all. This lets a browser frontend on
another origin call the whole API.

Vynull is a trusted-LAN tool with no auth, so this only makes the already
open posture consistent. Existing per-handler CORS lines remain correct
(the middleware sets the same header via Set, so no duplication — verified)
and are now redundant; they can be removed in a later cleanup. Spec's CORS
note updated to match.
Add GET /api/events, a Server-Sent Events stream pushing live deck +
now-playing state, so a frontend gets real-time updates instead of polling
/api/players at ~1 Hz. Each message is an 'event: state' carrying
{players, nowplaying} (the same shapes those endpoints return), sent on
change — effectively ~4 Hz while a deck is active (beat_age_ms keeps
moving) and nothing while idle, with a heartbeat to hold the connection.
A client interpolates the playhead between events with beat_age_ms, so a
few per second render smoothly. CORS applies via the mux middleware.

Documented in the spec; the 'live updates' note now points at the stream.
Verified live: text/event-stream, initial snapshot primed on connect.
@vynulldev vynulldev added the enhancement New feature or request label Oct 10, 2026
@vynulldev
vynulldev marked this pull request as ready for review October 10, 2026 21:23
@vynulldev
vynulldev merged commit 1ad1dc6 into main Oct 10, 2026
1 check passed
@vynulldev
vynulldev deleted the api-cors-sse branch October 10, 2026 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant