Skip to content

feat(ui): stream per-phase progress when installing a preset#257

Merged
geodro merged 1 commit intomainfrom
feat/preset-install-streaming
Apr 24, 2026
Merged

feat(ui): stream per-phase progress when installing a preset#257
geodro merged 1 commit intomainfrom
feat/preset-install-streaming

Conversation

@geodro
Copy link
Copy Markdown
Owner

@geodro geodro commented Apr 24, 2026

Summary

Clicking Add on a preset (elasticvue, phpMyAdmin, mongo, etc.) used to show a single opaque spinner for up to a minute. The biggest contributor to that latency is the first-time image pull, which happened silently inside podman when systemctl --user start lerd-<preset> ran, with no output reaching the UI.

This rewrites the install flow to stream per-phase progress.

Changes

BackendPOST /api/services/presets/{name} now returns application/x-ndjson. A new serviceops.InstallPresetStreaming orchestrator emits a PhaseEvent at every step:

{"phase":"installing_config"}
{"phase":"starting_deps","dep":"elasticsearch","state":"starting"}
{"phase":"starting_deps","dep":"elasticsearch","state":"ready"}
{"phase":"pulling_image","image":"docker.io/cars10/elasticvue:latest"}
{"phase":"pulling_image","message":"Copying blob sha256:... done"}
{"phase":"starting_unit","unit":"lerd-elasticvue"}
{"phase":"waiting_ready","unit":"lerd-elasticvue"}
{"phase":"done","name":"elasticvue","dashboard":"http://localhost:8083"}

The image pull is now explicit and happens before StartUnit, using a new podman.PullImageWithProgress(image, onLine) helper. A small lineWriter splits podman output on both \n and \r so TTY progress-bar rewrites still surface as discrete events.

FrontendinstallPreset() consumes the NDJSON stream via ReadableStream, tracks installingPhase / installingMessage / installingDep, and a new presetPhaseLabel(p) helper maps phase → button text ("Pulling image...", "Starting elasticsearch...", "Waiting for ready..."). The modal card shows the live Copying blob ... line underneath the button so a slow registry is distinguishable from a stuck install. Both install entry points (preset modal, service-detail suggestion banner) use the same labels.

Compatibility — CLI (lerd service preset <name>) and MCP (service_preset_install) still call serviceops.InstallPresetByName unchanged; only the HTTP UI response shape is new. macOS is fine: podman.StartUnit already abstracts launchd vs systemctl via UnitLifecycle, and the pull helper just shells out to podman pull.

Notes

publishAfter still wraps the handler, so the eventbus services/status broadcast fires after the stream closes and the WS snapshot refreshes as before.

The Add button on a preset opened with a single opaque spinner and a
60-second wait, with the biggest contributor (image pull) happening
silently inside podman on the first systemctl start.

This rewrites the POST /api/services/presets/{name} response into an
NDJSON stream of phase events:

  installing_config -> starting_deps{dep} -> pulling_image{message}
  -> starting_unit -> waiting_ready -> done

The image is pulled explicitly before StartUnit so the pull shows up
as visible, line-by-line progress instead of an invisible wait. Each
line of podman output is forwarded to the UI and surfaced under the
Add button, so a slow registry is distinguishable from a stuck
install.

Frontend consumes the stream via a ReadableStream reader, tracks the
current phase/message/dep, and swaps the button label per phase
("Pulling image...", "Starting elasticsearch...", "Waiting for
ready..."). Works for both the preset modal and the suggestion banner
on service detail pages.
@geodro geodro merged commit e7ce46a into main Apr 24, 2026
3 checks passed
geodro added a commit that referenced this pull request Apr 24, 2026
Bundles four landed changes since beta.6:

- #252  Add memcached, rabbitmq, elasticsearch service presets.
- #256  Fix check-upstream-php workflow dispatch (was silently broken).
- #257  Stream per-phase progress during preset install in the Web UI.
- #258  Offline landing page for the installed PWA with lerd start +
        copy button; nginx vhost allowlists the new SW routes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant