This is an automated email from the ASF dual-hosted git repository.
gkoszyk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/master by this push:
new d6a6d4ffe feat(server): add http.web_ui config option to control
embedded Web UI (#2494)
d6a6d4ffe is described below
commit d6a6d4ffee0215ba1a7479e55aeb63257911caab
Author: Hubert Gruszecki <[email protected]>
AuthorDate: Wed Dec 17 16:38:23 2025 +0100
feat(server): add http.web_ui config option to control embedded Web UI
(#2494)
- Add `web_ui` field to HttpConfig (default: false)
- Add build.rs verification for web assets when iggy-web feature enabled
- Log warning at startup if web_ui=true but iggy-web feature not
compiled
- Document config option in server.toml
---
core/configs/server.toml | 10 +++++
core/server/build.rs | 77 +++++++++++++++++++++++++++----------
core/server/src/configs/defaults.rs | 1 +
core/server/src/configs/displays.rs | 3 +-
core/server/src/configs/http.rs | 1 +
core/server/src/http/http_server.rs | 11 +++++-
6 files changed, 81 insertions(+), 22 deletions(-)
diff --git a/core/configs/server.toml b/core/configs/server.toml
index 9e075eff4..d795de0a9 100644
--- a/core/configs/server.toml
+++ b/core/configs/server.toml
@@ -36,6 +36,16 @@ address = "127.0.0.1:3000"
# Maximum size of the request body in bytes. For security reasons, the default
limit is 2 MB.
max_request_size = "2 MB"
+# Enables the embedded Web UI dashboard at '/ui'.
+# When set to `true` and the server is compiled with the 'iggy-web' feature,
+# the Svelte dashboard will be served at the '/ui' endpoint, providing a
+# browser-based interface for managing streams, topics, and viewing messages.
+# If the server is compiled without 'iggy-web' feature and this is set to
`true`,
+# a warning will be logged at startup but the server will continue to run.
+# `true` enables the embedded Web UI (requires server built with 'iggy-web'
feature).
+# `false` disables the embedded Web UI (default).
+web_ui = false
+
# Configuration for Cross-Origin Resource Sharing (CORS).
[http.cors]
# Controls whether CORS is enabled for the HTTP server.
diff --git a/core/server/build.rs b/core/server/build.rs
index 03d508966..9256b28e4 100644
--- a/core/server/build.rs
+++ b/core/server/build.rs
@@ -20,31 +20,68 @@ use std::path::PathBuf;
use std::{env, error};
use vergen_git2::{BuildBuilder, CargoBuilder, Emitter, Git2Builder,
RustcBuilder, SysinfoBuilder};
+const WEB_ASSETS_PATH: &str = "web/build/static";
+const WEB_INDEX_FILE: &str = "web/build/static/index.html";
+
fn main() -> Result<(), Box<dyn error::Error>> {
- if option_env!("IGGY_CI_BUILD") == Some("true") {
- Emitter::default()
- .add_instructions(&BuildBuilder::all_build()?)?
- .add_instructions(&CargoBuilder::all_cargo()?)?
- .add_instructions(&Git2Builder::all_git()?)?
- .add_instructions(&RustcBuilder::all_rustc()?)?
- .add_instructions(&SysinfoBuilder::all_sysinfo()?)?
- .emit()?;
+ verify_web_assets_if_enabled();
+ emit_vergen_instructions()?;
+ Ok(())
+}
- let workspace_root =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..");
+/// Returns the workspace root (iggy/), two levels up from core/server.
+fn workspace_root() -> PathBuf {
+ PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
+ .parent()
+ .and_then(|p| p.parent())
+ .expect("server crate must be at core/server within workspace")
+ .to_path_buf()
+}
+fn emit_vergen_instructions() -> Result<(), Box<dyn error::Error>> {
+ if option_env!("IGGY_CI_BUILD") != Some("true") {
+ println!("cargo:info=Skipping vergen because IGGY_CI_BUILD is not set
to 'true'");
+ return Ok(());
+ }
+
+ Emitter::default()
+ .add_instructions(&BuildBuilder::all_build()?)?
+ .add_instructions(&CargoBuilder::all_cargo()?)?
+ .add_instructions(&Git2Builder::all_git()?)?
+ .add_instructions(&RustcBuilder::all_rustc()?)?
+ .add_instructions(&SysinfoBuilder::all_sysinfo()?)?
+ .emit()?;
+
+ let configs_path = workspace_root()
+ .join("core/configs")
+ .canonicalize()
+ .unwrap_or_else(|e| panic!("Failed to canonicalize configs path:
{e}"));
+
+ println!("cargo:rerun-if-changed={}", configs_path.display());
+ Ok(())
+}
+
+fn verify_web_assets_if_enabled() {
+ if env::var("CARGO_FEATURE_IGGY_WEB").is_err() {
+ return;
+ }
+
+ let assets_dir = workspace_root().join(WEB_ASSETS_PATH);
+ let index_file = workspace_root().join(WEB_INDEX_FILE);
+
+ println!("cargo:rerun-if-changed={}", assets_dir.display());
+
+ if !assets_dir.exists() || !index_file.exists() {
println!(
- "cargo:rerun-if-changed={}",
- workspace_root
- .join("configs")
- .canonicalize()
- .unwrap_or_else(|e| panic!("Failed to canonicalize path,
error: {e}"))
- .display()
- );
- } else {
- println!(
- "cargo:info=Skipping build script because CI environment variable
IGGY_CI_BUILD is not set to 'true'"
+ "cargo:warning=Web UI assets not found at {}. \
+ To build them, run: npm --prefix web ci && npm --prefix web run
build:static",
+ assets_dir.display()
);
+ return;
}
- Ok(())
+ println!(
+ "cargo:info=Web UI assets verified at {}",
+ assets_dir.display()
+ );
}
diff --git a/core/server/src/configs/defaults.rs
b/core/server/src/configs/defaults.rs
index c864618ee..b2d810e1e 100644
--- a/core/server/src/configs/defaults.rs
+++ b/core/server/src/configs/defaults.rs
@@ -190,6 +190,7 @@ impl Default for HttpConfig {
enabled: SERVER_CONFIG.http.enabled,
address: SERVER_CONFIG.http.address.parse().unwrap(),
max_request_size:
SERVER_CONFIG.http.max_request_size.parse().unwrap(),
+ web_ui: SERVER_CONFIG.http.web_ui,
cors: HttpCorsConfig::default(),
jwt: HttpJwtConfig::default(),
metrics: HttpMetricsConfig::default(),
diff --git a/core/server/src/configs/displays.rs
b/core/server/src/configs/displays.rs
index e15680608..a3e64bc62 100644
--- a/core/server/src/configs/displays.rs
+++ b/core/server/src/configs/displays.rs
@@ -38,10 +38,11 @@ impl Display for HttpConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
- "{{ enabled: {}, address: {}, max_request_size: {}, cors: {}, jwt:
{}, metrics: {}, tls: {} }}",
+ "{{ enabled: {}, address: {}, max_request_size: {}, web_ui: {},
cors: {}, jwt: {}, metrics: {}, tls: {} }}",
self.enabled,
self.address,
self.max_request_size,
+ self.web_ui,
self.cors,
self.jwt,
self.metrics,
diff --git a/core/server/src/configs/http.rs b/core/server/src/configs/http.rs
index 83552da45..770632170 100644
--- a/core/server/src/configs/http.rs
+++ b/core/server/src/configs/http.rs
@@ -30,6 +30,7 @@ pub struct HttpConfig {
pub enabled: bool,
pub address: String,
pub max_request_size: IggyByteSize,
+ pub web_ui: bool,
pub cors: HttpCorsConfig,
pub jwt: HttpJwtConfig,
pub metrics: HttpMetricsConfig,
diff --git a/core/server/src/http/http_server.rs
b/core/server/src/http/http_server.rs
index e83c2191d..669ff772c 100644
--- a/core/server/src/http/http_server.rs
+++ b/core/server/src/http/http_server.rs
@@ -118,11 +118,20 @@ pub async fn start_http_server(
app = app.layer(middleware::from_fn(request_diagnostics));
#[cfg(feature = "iggy-web")]
- {
+ if config.web_ui {
app = app.merge(web::router());
info!("Web UI enabled at /ui");
}
+ #[cfg(not(feature = "iggy-web"))]
+ if config.web_ui {
+ tracing::warn!(
+ "Web UI is enabled in configuration (http.web_ui = true) but the
server \
+ was not compiled with 'iggy-web' feature. The Web UI will not be
available. \
+ To enable it, rebuild the server with: cargo build --features
iggy-web"
+ );
+ }
+
if !config.tls.enabled {
let listener = TcpListener::bind(config.address.clone())
.await