You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/synctv-web-ui
zijiren 9ebfef16e9
feat(web): support runtime UI assets (#440)
## Summary

- add a `web-ui-dynamic` feature that serves a mutable Web distribution
without compile-time embedding
- add `server.web_ui_directory` and `SYNCTV_SERVER_WEB_UI_DIRECTORY`,
with per-request disk reads, SPA fallback, security headers, and path
containment checks
- keep `web-ui` release builds embedded while allowing the same
production binary and Docker image to use a mounted runtime directory
- configure `make dev-serve` to use `synctv-web-ui/dist` and document
both modes

## Docker behavior

CI continues to build release images with `web-ui`, so embedded assets
remain the default. Mounting a distribution and setting
`SYNCTV_SERVER_WEB_UI_DIRECTORY` makes that directory authoritative at
runtime.

## Verification

- `cargo fmt --all -- --check`
- `git diff --check`
- `cargo test -p synctv-api-http --features web-ui-dynamic
http::web_ui::tests`
- `cargo test -p synctv-api-http --features web-ui http::web_ui::tests`
- `cargo check -p synctv --features web-ui-dynamic`
- `cargo check -p synctv --features web-ui`
1 month ago
..
dist feat(web): add client-aware playback and reproducible UI (#433) 1 month ago
src feat(web): add client-aware playback and reproducible UI (#433) 1 month ago
Cargo.toml feat(web): add client-aware playback and reproducible UI (#433) 1 month ago
README.md feat(web): support runtime UI assets (#440) 1 month ago
build.rs feat(web): add client-aware playback and reproducible UI (#433) 1 month ago
web-ui.production.toml feat(web): add client-aware playback and reproducible UI (#433) 1 month ago
web-ui.toml feat(web): add client-aware playback and reproducible UI (#433) 1 month ago

README.md

SyncTV Web UI assets

This crate owns acquisition, optional Flutter builds, compression, manifests, and compile-time embedding of the SyncTV browser client. synctv-api-http can also serve a distribution directly from disk during development.

Sources

web-ui.toml reads prebuilt files from dist/. The directory is empty in Git apart from .gitkeep.

web-ui.production.toml is the versioned production source used by CI. Its Git source pins both the requested revision and its expected full lowercase commit SHA. The build fails when the revision resolves to another commit.

For local development, create the ignored web-ui.local.toml beside the default file. It takes precedence unless SYNCTV_WEB_CONFIG names another configuration. Relative paths resolve from the selected configuration file.

Prebuilt distribution:

schema-version = 1

[source]
kind = "dist"
path = "../synctv-app/build/web"

Local Flutter project:

schema-version = 1

[source]
kind = "local-project"
path = "../../flutter/synctv-app"
allow-dirty = true

Immutable Git checkout:

schema-version = 1

[source]
kind = "git"
repository = "https://github.com/synctv-org/synctv-app.git"
revision = "refs/tags/v1.2.3"
commit = "0123456789abcdef0123456789abcdef01234567"

The optional [build] table accepts flutter, arguments, and a dart-defines mapping. Arguments and defines participate in the build fingerprint.

Commands

Run the server against a mutable local distribution:

make dev-serve

dev-serve enables the web-ui-dynamic feature and sets SYNCTV_SERVER_WEB_UI_DIRECTORY to synctv-web-ui/dist. Override the directory with DEV_WEB_UI_DIR=/path/to/dist. Files are read for every request, use Cache-Control: no-store, and can be replaced without rebuilding or restarting the Rust server. A relative runtime directory is resolved from the server's working directory.

Build and export the Web distribution:

SYNCTV_WEB_CONFIG=synctv-web-ui/web-ui.production.toml \
  make web-ui-build WEB_UI_EXPORT_DIR=synctv-web-ui/dist

Build the release server with the assets embedded:

make web-release-build

The existing web-ui feature embeds the distribution for release deployment. Both features expose the same routes. When server.web_ui_directory is configured, its disk contents are authoritative and take precedence over embedded assets.

The Web-only command exports to target/web-ui-dist by default. CI uploads the exported distribution once, then passes its authenticated artifact URL and SHA-256 digest to the existing multi-platform Docker build. Docker verifies and embeds the archive; it never installs Flutter or builds the frontend.

Build controls

Variable Behavior
SYNCTV_WEB_CONFIG Select a configuration file explicitly.
SYNCTV_WEB_DIST Use a prebuilt directory. This compatibility override takes precedence over configured sources.
SYNCTV_WEB_CACHE_DIR Select the Git, Flutter output, and compression cache root.
SYNCTV_WEB_EXPORT_DIR Copy the final uncompressed distribution to a disjoint directory.
SYNCTV_WEB_OFFLINE Disable Git fetches and use flutter pub get --offline. Missing cache entries fail.
SYNCTV_WEB_FORCE_REBUILD Fetch the pinned revision again and rebuild Flutter output.

Relative paths in these controls resolve from the workspace root containing the synctv-web-ui crate. Paths inside a selected configuration resolve from that configuration file.

The fingerprint includes the source file hash or pinned commit, Flutter version, build arguments, dart-defines, builder version, and final distribution hash. Git checkout, Flutter output, and compression data use separate cache layers. Ordinary workspace builds do not enable the embed feature and require no Flutter installation, Git access, or network access.