diff --git a/.gitignore b/.gitignore
index 2dfad02764..6d9af9d641 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,7 @@ foreign/node/.npm
__pycache__/
go.work
core/bench/dashboard/frontend/dist
+core/web_ui/dist
core/integration/tests/connectors/postgres/cdc_restart_connectors/*
!core/integration/tests/connectors/postgres/cdc_restart_connectors/config.toml
LICENSE-binary
diff --git a/Cargo.lock b/Cargo.lock
index cefd525776..264deab350 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -185,7 +185,7 @@ dependencies = [
"bytes",
"bytestring",
"cfg-if",
- "cookie",
+ "cookie 0.16.2",
"derive_more",
"encoding_rs",
"foldhash",
@@ -1178,7 +1178,7 @@ dependencies = [
"futures-core",
"libc",
"portable-atomic",
- "rustc-hash",
+ "rustc-hash 2.1.3",
"rustix 1.1.5",
"tokio",
"tokio-stream",
@@ -1444,6 +1444,22 @@ dependencies = [
"syn 3.0.9",
]
+[[package]]
+name = "async-tungstenite"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee88b4c88ac8c9ea446ad43498955750a4bbe64c4392f21ccfe5d952865e318f"
+dependencies = [
+ "atomic-waker",
+ "futures-core",
+ "futures-io",
+ "futures-task",
+ "futures-util",
+ "log",
+ "pin-project-lite",
+ "tungstenite 0.27.0",
+]
+
[[package]]
name = "async-tungstenite"
version = "0.34.1"
@@ -2031,6 +2047,7 @@ dependencies = [
"matchit",
"memchr",
"mime",
+ "multer",
"percent-encoding",
"pin-project-lite",
"serde_core",
@@ -2114,6 +2131,12 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "base16"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8"
+
[[package]]
name = "base16ct"
version = "0.2.0"
@@ -2274,7 +2297,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
- "rustc-hash",
+ "rustc-hash 2.1.3",
"shlex 1.3.0",
"syn 2.0.119",
]
@@ -2803,6 +2826,9 @@ name = "bytes"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
+dependencies = [
+ "serde",
+]
[[package]]
name = "bytes-utils"
@@ -2924,6 +2950,12 @@ dependencies = [
"shlex 2.0.1",
]
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
[[package]]
name = "cexpr"
version = "0.6.0"
@@ -2933,6 +2965,17 @@ dependencies = [
"nom 7.1.3",
]
+[[package]]
+name = "cfb"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
+dependencies = [
+ "byteorder",
+ "fnv",
+ "uuid",
+]
+
[[package]]
name = "cfg-if"
version = "1.0.5"
@@ -2988,6 +3031,16 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "charset"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1f927b07c74ba84c7e5fe4db2baeb3e996ab2688992e39ac68ce3220a677c7e"
+dependencies = [
+ "base64 0.22.1",
+ "encoding_rs",
+]
+
[[package]]
name = "chrono"
version = "0.4.45"
@@ -3012,6 +3065,33 @@ dependencies = [
"phf 0.12.1",
]
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
[[package]]
name = "cipher"
version = "0.4.4"
@@ -3358,7 +3438,7 @@ dependencies = [
"futures-util",
"libc",
"quinn-proto",
- "rustc-hash",
+ "rustc-hash 2.1.3",
"rustls",
"rustls-platform-verifier",
"synchrony",
@@ -3415,7 +3495,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0a9e5cbacce3ff2594cfd7321fd946d5f7afcf2dbc7537f6de7b42d798efb3a"
dependencies = [
- "async-tungstenite",
+ "async-tungstenite 0.34.1",
"compio-buf",
"compio-io",
"compio-log",
@@ -3564,12 +3644,90 @@ dependencies = [
"tiny-keccak",
]
+[[package]]
+name = "const-serialize"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad7154afa56de2f290e3c82c2c6dc4f5b282b6870903f56ef3509aba95866edc"
+dependencies = [
+ "const-serialize-macro 0.7.2",
+]
+
+[[package]]
+name = "const-serialize"
+version = "0.8.0-alpha.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e42cd5aabba86f128b3763da1fec1491c0f728ce99245062cd49b6f9e6d235b"
+dependencies = [
+ "const-serialize 0.7.2",
+ "const-serialize-macro 0.8.0-alpha.3",
+ "serde",
+]
+
+[[package]]
+name = "const-serialize-macro"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f160aad86b4343e8d4e261fee9965c3005b2fd6bc117d172ab65948779e4acf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "const-serialize-macro"
+version = "0.8.0-alpha.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7d372d7ea4c50890f188c72a08782d5c98bc8c7cc9dc4343b61d8ba8e20c26b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "const-str"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0664d2867b4a32697dfe655557f5c3b187e9b605b38612a748e5ec99811d160"
+
+[[package]]
+name = "const_format"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3decb4d6965448659923544a634a0571ba44cede4e11ef15a5a317126840454"
+dependencies = [
+ "const_format_proc_macros",
+ "konst",
+]
+
+[[package]]
+name = "const_format_proc_macros"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "941b570269a642506f08cb5b688c04bfa6cfd41a26f8bcdc49db8fd00c0ab659"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
[[package]]
name = "constant_time_eq"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
+[[package]]
+name = "content_disposition"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc14a88e1463ddd193906285abe5c360c7e8564e05ccc5d501755f7fbc9ca9c"
+dependencies = [
+ "charset",
+]
+
[[package]]
name = "convert_case"
version = "0.6.0"
@@ -3623,12 +3781,41 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "cookie"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87"
+dependencies = [
+ "percent-encoding",
+ "time",
+ "version_check",
+]
+
[[package]]
name = "cookie-factory"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2"
+[[package]]
+name = "cookie_store"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
+dependencies = [
+ "cookie 0.18.2",
+ "document-features",
+ "idna",
+ "log",
+ "publicsuffix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -4569,126 +4756,599 @@ dependencies = [
name = "derive-new"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc"
+checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "derive-syn-parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "derive-where"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e2b94854e8576378ccda7c8de8a66ed8b4e8acbd2c50ec3418ea6c8aaf4b567"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.9",
+]
+
+[[package]]
+name = "derive_builder"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
+dependencies = [
+ "derive_builder_macro",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
+dependencies = [
+ "darling 0.20.11",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
+dependencies = [
+ "derive_builder_core",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "derive_more"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
+dependencies = [
+ "convert_case 0.10.0",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.119",
+ "unicode-xid",
+]
+
+[[package]]
+name = "des"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e"
+dependencies = [
+ "cipher 0.4.4",
+]
+
+[[package]]
+name = "difflib"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer 0.10.4",
+ "const-oid 0.9.6",
+ "crypto-common 0.1.6",
+ "subtle",
+]
+
+[[package]]
+name = "digest"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
+dependencies = [
+ "block-buffer 0.12.1",
+ "const-oid 0.10.2",
+ "crypto-common 0.2.2",
+ "ctutils",
+]
+
+[[package]]
+name = "dioxus"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9320593eda4f01858f046698b42441603d2af5fcb62a6c1f9046a0c2eebd6d9b"
+dependencies = [
+ "dioxus-asset-resolver",
+ "dioxus-cli-config",
+ "dioxus-config-macro",
+ "dioxus-config-macros",
+ "dioxus-core",
+ "dioxus-core-macro",
+ "dioxus-devtools",
+ "dioxus-document",
+ "dioxus-fullstack",
+ "dioxus-history",
+ "dioxus-hooks",
+ "dioxus-html",
+ "dioxus-logger",
+ "dioxus-router",
+ "dioxus-signals",
+ "dioxus-stores",
+ "dioxus-web",
+ "manganis",
+ "subsecond",
+ "warnings",
+]
+
+[[package]]
+name = "dioxus-asset-resolver"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84235ff0e272f15d4537603cdd50df6d45d50e51bb96326dbc0bdfd01d825226"
+dependencies = [
+ "dioxus-cli-config",
+ "http 1.5.0",
+ "infer",
+ "jni 0.21.1",
+ "js-sys",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "percent-encoding",
+ "thiserror 2.0.21",
+ "tokio",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "dioxus-cli-config"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322d10f47effebd85ee0662a2cb083ff920d40dd6295a4afd8cfa4f9bc2e05c6"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "dioxus-config-macro"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f3a906c8219e94fa48189a793c760a4e3193cbb7791f799e9d37633fe41161d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "dioxus-config-macros"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20de015bc89c20f4ffc7b4f849943420308365fa66f991250b5c075261990a21"
+
+[[package]]
+name = "dioxus-core"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b232228ada232c0adc151e41ebd9ef70cc04683db097fa0dc6015979fa01722"
+dependencies = [
+ "anyhow",
+ "const_format",
+ "dioxus-core-types",
+ "futures-channel",
+ "futures-util",
+ "generational-box",
+ "longest-increasing-subsequence",
+ "rustc-hash 2.1.3",
+ "rustversion",
+ "serde",
+ "slab",
+ "slotmap",
+ "subsecond",
+ "tracing",
+]
+
+[[package]]
+name = "dioxus-core-macro"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6256a68462222f0600f5fd7d9542cbd9f299bc19355d900f7827dd3fc9e09fdb"
+dependencies = [
+ "convert_case 0.8.0",
+ "dioxus-rsx",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "dioxus-core-types"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96007ed6cbad951dfed82d83c581aabd95bc591f351f5666d454ddbe7845b324"
+
+[[package]]
+name = "dioxus-devtools"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "434cebb282b3f820a341582ebfd37dab907d5a25c3eeafd87802468afcd2a959"
+dependencies = [
+ "dioxus-cli-config",
+ "dioxus-core",
+ "dioxus-devtools-types",
+ "dioxus-signals",
+ "serde",
+ "serde_json",
+ "subsecond",
+ "thiserror 2.0.21",
+ "tracing",
+ "tungstenite 0.28.0",
+]
+
+[[package]]
+name = "dioxus-devtools-types"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfd6f3475e38c93be245a664a9ce11679a8f2b99832620705cf7837f42aa39ce"
+dependencies = [
+ "dioxus-core",
+ "serde",
+ "subsecond-types",
+]
+
+[[package]]
+name = "dioxus-document"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f6fadd5c6d886b48b762820abae1a08142a2159f8a1136c0466b4b4ed9f547e"
+dependencies = [
+ "dioxus-core",
+ "dioxus-core-macro",
+ "dioxus-core-types",
+ "dioxus-html",
+ "futures-channel",
+ "futures-util",
+ "generational-box",
+ "lazy-js-bundle",
+ "serde",
+ "serde_json",
+ "tracing",
+]
+
+[[package]]
+name = "dioxus-fullstack"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a91d5acabd1470145bcf164cab9fd2edcf2e4fba0fa6fb0fd6dca23079fcc169"
+dependencies = [
+ "anyhow",
+ "async-stream",
+ "async-tungstenite 0.31.0",
+ "axum",
+ "axum-core",
+ "base64 0.22.1",
+ "bytes",
+ "ciborium",
+ "const-str",
+ "const_format",
+ "content_disposition",
+ "derive_more",
+ "dioxus-asset-resolver",
+ "dioxus-cli-config",
+ "dioxus-core",
+ "dioxus-fullstack-core",
+ "dioxus-fullstack-macro",
+ "dioxus-hooks",
+ "dioxus-html",
+ "dioxus-signals",
+ "form_urlencoded",
+ "futures",
+ "futures-channel",
+ "futures-util",
+ "gloo-net 0.6.0",
+ "headers",
+ "http 1.5.0",
+ "http-body",
+ "http-body-util",
+ "js-sys",
+ "mime",
+ "pin-project",
+ "reqwest 0.12.28",
+ "rustversion",
+ "send_wrapper",
+ "serde",
+ "serde_json",
+ "serde_qs",
+ "serde_urlencoded",
+ "thiserror 2.0.21",
+ "tokio-util",
+ "tracing",
+ "tungstenite 0.27.0",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams 0.4.2",
+ "web-sys",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "dioxus-fullstack-core"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0011e9bca8da2ae6bf02ba9ef93ba6c5fa539e402de3aeb316ad86ca77c59079"
+dependencies = [
+ "anyhow",
+ "axum-core",
+ "base64 0.22.1",
+ "ciborium",
+ "dioxus-core",
+ "dioxus-document",
+ "dioxus-history",
+ "dioxus-hooks",
+ "dioxus-signals",
+ "futures-channel",
+ "futures-util",
+ "generational-box",
+ "http 1.5.0",
+ "inventory",
+ "parking_lot",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.21",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "dioxus-fullstack-macro"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "681f39665de36af7bb724b1cc0ca9c2b7c36a2b93febf1b2911716479eac3db0"
+dependencies = [
+ "const_format",
+ "convert_case 0.8.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "dioxus-history"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2404b77d4441e694a5e93afb5c9a729efb26a3f6920f769cfe6cfedf5c7ac210"
+dependencies = [
+ "dioxus-core",
+ "tracing",
+]
+
+[[package]]
+name = "dioxus-hooks"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d3807a1bc039299cd37ad85f1c56ae7088210e8a38e3e877f01034d68c15fbb"
dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.119",
+ "dioxus-core",
+ "dioxus-signals",
+ "futures-channel",
+ "futures-util",
+ "generational-box",
+ "rustversion",
+ "slab",
+ "tracing",
]
[[package]]
-name = "derive-syn-parse"
-version = "0.2.0"
+name = "dioxus-html"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762"
+checksum = "40b9a85679c4dfd8699407a9ad5041a6a4fe6be0ee386353951cd03b57a12030"
dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.119",
+ "async-trait",
+ "bytes",
+ "dioxus-core",
+ "dioxus-core-macro",
+ "dioxus-core-types",
+ "dioxus-hooks",
+ "dioxus-html-internal-macro",
+ "enumset",
+ "euclid",
+ "futures-channel",
+ "futures-util",
+ "generational-box",
+ "keyboard-types",
+ "lazy-js-bundle",
+ "rustversion",
+ "tracing",
]
[[package]]
-name = "derive-where"
-version = "1.7.0"
+name = "dioxus-html-internal-macro"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e2b94854e8576378ccda7c8de8a66ed8b4e8acbd2c50ec3418ea6c8aaf4b567"
+checksum = "500282dad5b62e91f93e127cbf41c587bf1c457d52514a75f3ff2a55fd0ea9f6"
dependencies = [
+ "convert_case 0.8.0",
"proc-macro2",
"quote",
- "syn 3.0.9",
+ "syn 2.0.119",
]
[[package]]
-name = "derive_builder"
-version = "0.20.2"
+name = "dioxus-interpreter-js"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
+checksum = "faa22cc3431e7efdbae28069542a1763c977d5edbe4d4fbfb0b7667b9c9966e9"
dependencies = [
- "derive_builder_macro",
+ "js-sys",
+ "lazy-js-bundle",
+ "rustc-hash 2.1.3",
+ "sledgehammer_bindgen",
+ "sledgehammer_utils",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
]
[[package]]
-name = "derive_builder_core"
-version = "0.20.2"
+name = "dioxus-logger"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
+checksum = "0f36b2dac4df8c747431f77f2a6644dd033ea98e7f8ac80271ff47fd4ba8a548"
dependencies = [
- "darling 0.20.11",
- "proc-macro2",
- "quote",
- "syn 2.0.119",
+ "dioxus-cli-config",
+ "tracing",
+ "tracing-subscriber",
+ "tracing-wasm",
]
[[package]]
-name = "derive_builder_macro"
-version = "0.20.2"
+name = "dioxus-router"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
+checksum = "e9652a5bcead34f687d7dbb12d5c8f6a7ecb342786273395ca0078e51b24320f"
dependencies = [
- "derive_builder_core",
- "syn 2.0.119",
+ "dioxus-cli-config",
+ "dioxus-core",
+ "dioxus-core-macro",
+ "dioxus-history",
+ "dioxus-hooks",
+ "dioxus-html",
+ "dioxus-router-macro",
+ "dioxus-signals",
+ "percent-encoding",
+ "rustversion",
+ "tracing",
+ "url",
]
[[package]]
-name = "derive_more"
-version = "2.1.1"
+name = "dioxus-router-macro"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
+checksum = "56d20cf2173183946fdf8ab069b606a840a9d06e949050619355fc8cb97aafb6"
dependencies = [
- "derive_more-impl",
+ "base16",
+ "digest 0.10.7",
+ "proc-macro2",
+ "quote",
+ "sha2 0.10.9",
+ "slab",
+ "syn 2.0.119",
]
[[package]]
-name = "derive_more-impl"
-version = "2.1.1"
+name = "dioxus-rsx"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
+checksum = "aea662c9e13a91279d8acbc0d02a80430c7081704c20e8c791e4c4a151268596"
dependencies = [
- "convert_case 0.10.0",
"proc-macro2",
+ "proc-macro2-diagnostics",
"quote",
- "rustc_version",
+ "rustversion",
"syn 2.0.119",
- "unicode-xid",
]
[[package]]
-name = "des"
-version = "0.8.1"
+name = "dioxus-signals"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e"
+checksum = "95d85e36fcaf7abdc986836801bf248776da7d5961f5fac65fdbf5ed491afb4f"
dependencies = [
- "cipher 0.4.4",
+ "dioxus-core",
+ "futures-channel",
+ "futures-util",
+ "generational-box",
+ "parking_lot",
+ "rustc-hash 2.1.3",
+ "tracing",
+ "warnings",
]
[[package]]
-name = "difflib"
-version = "0.4.0"
+name = "dioxus-stores"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
+checksum = "15eaa9f2ccc5ce962d056515e61345c846a54b13a2b880cf7e8ac93faa53a178"
+dependencies = [
+ "dioxus-core",
+ "dioxus-signals",
+ "dioxus-stores-macro",
+ "generational-box",
+]
[[package]]
-name = "digest"
-version = "0.10.7"
+name = "dioxus-stores-macro"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+checksum = "a13bd8a693d7042544fa3ac79698a08783bbc698d1ed257e88c5d4059e9ac476"
dependencies = [
- "block-buffer 0.10.4",
- "const-oid 0.9.6",
- "crypto-common 0.1.6",
- "subtle",
+ "convert_case 0.8.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
]
[[package]]
-name = "digest"
-version = "0.11.3"
+name = "dioxus-web"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
-dependencies = [
- "block-buffer 0.12.1",
- "const-oid 0.10.2",
- "crypto-common 0.2.2",
- "ctutils",
+checksum = "481093233d789c5ffbdde66e7970e803b01a199a7ab0c0f8ef84c65d30b7384f"
+dependencies = [
+ "dioxus-cli-config",
+ "dioxus-core",
+ "dioxus-core-types",
+ "dioxus-devtools",
+ "dioxus-document",
+ "dioxus-history",
+ "dioxus-html",
+ "dioxus-interpreter-js",
+ "dioxus-signals",
+ "futures-channel",
+ "futures-util",
+ "generational-box",
+ "gloo-timers 0.3.0",
+ "js-sys",
+ "lazy-js-bundle",
+ "rustc-hash 2.1.3",
+ "send_wrapper",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_json",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams 0.4.2",
+ "web-sys",
]
[[package]]
@@ -4802,6 +5462,15 @@ dependencies = [
"serde_json",
]
+[[package]]
+name = "document-features"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
+dependencies = [
+ "litrs",
+]
+
[[package]]
name = "dotenvy"
version = "0.15.7"
@@ -5694,6 +6363,16 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "generational-box"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8b2dc9b873cd1a8adb80aa5dcef9a8205f781a8a313d5ffb867248cb3bcb764"
+dependencies = [
+ "parking_lot",
+ "tracing",
+]
+
[[package]]
name = "generator"
version = "0.8.11"
@@ -6044,6 +6723,27 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "gloo-net"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-sink",
+ "gloo-utils 0.2.0",
+ "http 1.5.0",
+ "js-sys",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
[[package]]
name = "gloo-net"
version = "0.7.0"
@@ -6406,6 +7106,30 @@ dependencies = [
"hashbrown 0.16.1",
]
+[[package]]
+name = "headers"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc2700e3fe555c3310aa7286cac6167449f2c87e0eb58769c9208a1c58a1d106"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "headers-core",
+ "http 1.5.0",
+ "httpdate",
+ "mime",
+ "sha1 0.10.7",
+]
+
+[[package]]
+name = "headers-core"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
+dependencies = [
+ "http 1.5.0",
+]
+
[[package]]
name = "heapless"
version = "0.7.17"
@@ -6459,7 +7183,7 @@ dependencies = [
"hickory-proto",
"idna",
"ipnet",
- "jni",
+ "jni 0.22.4",
"rand 0.10.3",
"thiserror 2.0.21",
"tinyvec",
@@ -6477,7 +7201,7 @@ dependencies = [
"data-encoding",
"idna",
"ipnet",
- "jni",
+ "jni 0.22.4",
"once_cell",
"prefix-trie",
"rand 0.10.3",
@@ -6500,7 +7224,7 @@ dependencies = [
"hickory-proto",
"ipconfig",
"ipnet",
- "jni",
+ "jni 0.22.4",
"moka",
"ndk-context",
"once_cell",
@@ -7352,6 +8076,22 @@ dependencies = [
"tracing-subscriber",
]
+[[package]]
+name = "iggy-web-ui"
+version = "0.1.0"
+dependencies = [
+ "base64 0.23.1",
+ "chrono",
+ "dioxus",
+ "gloo-net 0.7.0",
+ "iggy_common",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.21",
+ "web-sys",
+]
+
[[package]]
name = "iggy_binary_protocol"
version = "0.11.1-edge.3"
@@ -8039,6 +8779,15 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "infer"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7"
+dependencies = [
+ "cfb",
+]
+
[[package]]
name = "inflections"
version = "1.1.1"
@@ -8321,6 +9070,22 @@ dependencies = [
"jiff-tzdb",
]
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys 0.3.1",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
[[package]]
name = "jni"
version = "0.22.4"
@@ -8330,7 +9095,7 @@ dependencies = [
"cfg-if",
"combine",
"jni-macros",
- "jni-sys",
+ "jni-sys 0.4.1",
"log",
"simd_cesu8",
"thiserror 2.0.21",
@@ -8351,6 +9116,15 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "jni-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
+dependencies = [
+ "jni-sys 0.4.1",
+]
+
[[package]]
name = "jni-sys"
version = "0.4.1"
@@ -8501,6 +9275,15 @@ dependencies = [
"cpufeatures 0.3.1",
]
+[[package]]
+name = "keyboard-types"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a"
+dependencies = [
+ "bitflags 2.13.2",
+]
+
[[package]]
name = "keyring-core"
version = "1.0.1"
@@ -8510,6 +9293,21 @@ dependencies = [
"log",
]
+[[package]]
+name = "konst"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb"
+dependencies = [
+ "konst_macro_rules",
+]
+
+[[package]]
+name = "konst_macro_rules"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37"
+
[[package]]
name = "kqueue"
version = "1.2.1"
@@ -8565,6 +9363,12 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "lazy-js-bundle"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51cdaa5abc885e2d606e6985281ace220778bde190be20aab1ec2346ed8bd1fa"
+
[[package]]
name = "lazy-regex"
version = "3.6.2"
@@ -8823,6 +9627,12 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
+[[package]]
+name = "litrs"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70"
+
[[package]]
name = "local-channel"
version = "0.1.5"
@@ -8893,6 +9703,12 @@ dependencies = [
"logos-codegen",
]
+[[package]]
+name = "longest-increasing-subsequence"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86"
+
[[package]]
name = "loom"
version = "0.7.2"
@@ -8961,6 +9777,17 @@ dependencies = [
"twox-hash",
]
+[[package]]
+name = "macro-string"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
[[package]]
name = "macro_magic"
version = "0.5.1"
@@ -9009,6 +9836,50 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "manganis"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ed2af894a724a0b6e521f9efe84f98cfdc74a4de6828efef77ffd3b1679fd52"
+dependencies = [
+ "const-serialize 0.7.2",
+ "const-serialize 0.8.0-alpha.0",
+ "jni 0.21.1",
+ "manganis-core",
+ "manganis-macro",
+ "ndk-context",
+ "objc2",
+ "thiserror 2.0.21",
+]
+
+[[package]]
+name = "manganis-core"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e258c71b137bf48deaffb48b8a8a2f994af9e5c3ca201daa20d41e26b379da11"
+dependencies = [
+ "const-serialize 0.7.2",
+ "const-serialize 0.8.0-alpha.0",
+ "dioxus-cli-config",
+ "dioxus-core-types",
+ "serde",
+ "winnow 0.7.15",
+]
+
+[[package]]
+name = "manganis-macro"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0abae8346bbf637e6d21ac3831863bd5a98657517f5cd2d69d651046b95e7ce9"
+dependencies = [
+ "dunce",
+ "macro-string",
+ "manganis-core",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
[[package]]
name = "matchers"
version = "0.2.0"
@@ -9130,6 +10001,15 @@ version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
+[[package]]
+name = "memfd"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57804b2c9b69967f1536a56f86297e367a33b19e98852ed624b84551cdbc0d90"
+dependencies = [
+ "rustix 1.1.5",
+]
+
[[package]]
name = "memmap2"
version = "0.9.11"
@@ -9446,6 +10326,23 @@ dependencies = [
"thiserror 2.0.21",
]
+[[package]]
+name = "multer"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
+dependencies = [
+ "bytes",
+ "encoding_rs",
+ "futures-util",
+ "http 1.5.0",
+ "httparse",
+ "memchr",
+ "mime",
+ "spin",
+ "version_check",
+]
+
[[package]]
name = "multiversion_no_op"
version = "1.0.0"
@@ -9458,12 +10355,36 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9252111cf132ba0929b6f8e030cac2a24b507f3a4d6db6fb2896f27b354c714b"
+[[package]]
+name = "ndk"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
+dependencies = [
+ "bitflags 2.13.2",
+ "jni-sys 0.3.1",
+ "log",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror 1.0.69",
+]
+
[[package]]
name = "ndk-context"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+[[package]]
+name = "ndk-sys"
+version = "0.6.0+11769913"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
+dependencies = [
+ "jni-sys 0.3.1",
+]
+
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
@@ -9734,6 +10655,28 @@ dependencies = [
"libc",
]
+[[package]]
+name = "num_enum"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
+dependencies = [
+ "proc-macro-crate 3.3.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
[[package]]
name = "num_threads"
version = "0.1.7"
@@ -11292,6 +12235,12 @@ dependencies = [
"thiserror 2.0.21",
]
+[[package]]
+name = "psl-types"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
+
[[package]]
name = "psm"
version = "0.1.32"
@@ -11302,6 +12251,16 @@ dependencies = [
"cc",
]
+[[package]]
+name = "publicsuffix"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
+dependencies = [
+ "idna",
+ "psl-types",
+]
+
[[package]]
name = "pulp"
version = "0.22.3"
@@ -11408,7 +12367,7 @@ dependencies = [
"pin-project-lite",
"quinn-proto",
"quinn-udp",
- "rustc-hash",
+ "rustc-hash 2.1.3",
"rustls",
"socket2",
"thiserror 2.0.21",
@@ -11431,7 +12390,7 @@ dependencies = [
"rand 0.10.3",
"rand_pcg",
"ring",
- "rustc-hash",
+ "rustc-hash 2.1.3",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
@@ -11636,6 +12595,12 @@ dependencies = [
"bitflags 2.13.2",
]
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
[[package]]
name = "rayon"
version = "1.12.0"
@@ -11855,6 +12820,8 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64 0.22.1",
"bytes",
+ "cookie 0.18.2",
+ "cookie_store",
"futures-core",
"futures-util",
"h2 0.4.20",
@@ -11866,6 +12833,7 @@ dependencies = [
"hyper-util",
"js-sys",
"log",
+ "mime_guess",
"percent-encoding",
"pin-project-lite",
"quinn",
@@ -12181,7 +13149,7 @@ version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "249896ab27ed630590971738264baa8f722f18965d2e387c706c40a3c2a572cc"
dependencies = [
- "async-tungstenite",
+ "async-tungstenite 0.34.1",
"rustls-native-certs",
"rustls-pki-types",
"rustls-webpki",
@@ -12196,7 +13164,7 @@ version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3767dde22a804ea806107d0f4331e33cb794d1b8468b11c86fb0c1366f07054"
dependencies = [
- "async-tungstenite",
+ "async-tungstenite 0.34.1",
"bytes",
"fixedbitset",
"flume",
@@ -12221,7 +13189,7 @@ version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfa6ddcc7a7dd5688f9bf78d8f81cb94f367bce56c055d8d94cf81ecb0518bf"
dependencies = [
- "async-tungstenite",
+ "async-tungstenite 0.34.1",
"bytes",
"fixedbitset",
"flume",
@@ -12338,6 +13306,12 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
[[package]]
name = "rustc-hash"
version = "2.1.3"
@@ -12468,7 +13442,7 @@ checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98"
dependencies = [
"core-foundation 0.10.1",
"core-foundation-sys",
- "jni",
+ "jni 0.22.4",
"log",
"once_cell",
"rustls",
@@ -12854,6 +13828,17 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "serde_qs"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352"
+dependencies = [
+ "percent-encoding",
+ "serde",
+ "thiserror 2.0.21",
+]
+
[[package]]
name = "serde_repr"
version = "0.1.21"
@@ -13344,12 +14329,42 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+[[package]]
+name = "sledgehammer_bindgen"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49e83e178d176459c92bc129cfd0958afac3ced925471b889b3a75546cfc4133"
+dependencies = [
+ "sledgehammer_bindgen_macro",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "sledgehammer_bindgen_macro"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb251b407f50028476a600541542b605bb864d35d9ee1de4f6cab45d88475e6d"
+dependencies = [
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "sledgehammer_utils"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "debdd4b83524961983cea3c55383b3910fd2f24fd13a188f5b091d2d504a61ae"
+dependencies = [
+ "rustc-hash 1.1.0",
+]
+
[[package]]
name = "slotmap"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
dependencies = [
+ "serde",
"version_check",
]
@@ -13437,7 +14452,7 @@ dependencies = [
"data-encoding",
"debugid",
"if_chain",
- "rustc-hash",
+ "rustc-hash 2.1.3",
"serde",
"serde_json",
"unicode-id-start",
@@ -13844,6 +14859,34 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "subsecond"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d350d5788fa94d560d92269266a50efc77b036bfca6675e420b64df7b3211f37"
+dependencies = [
+ "js-sys",
+ "libc",
+ "libloading",
+ "memfd",
+ "memmap2",
+ "serde",
+ "subsecond-types",
+ "thiserror 2.0.21",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "subsecond-types"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf32d66269b5fbb8558334e8d22b657b2f7af0dd2ef56c231541099a862e464"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "subtle"
version = "2.6.1"
@@ -14867,6 +15910,17 @@ dependencies = [
"tracing-serde",
]
+[[package]]
+name = "tracing-wasm"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07"
+dependencies = [
+ "tracing",
+ "tracing-subscriber",
+ "wasm-bindgen",
+]
+
[[package]]
name = "trait-variant"
version = "0.1.3"
@@ -14893,6 +15947,40 @@ dependencies = [
"core_maths",
]
+[[package]]
+name = "tungstenite"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d"
+dependencies = [
+ "bytes",
+ "data-encoding",
+ "http 1.5.0",
+ "httparse",
+ "log",
+ "rand 0.9.5",
+ "sha1 0.10.7",
+ "thiserror 2.0.21",
+ "utf-8",
+]
+
+[[package]]
+name = "tungstenite"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"
+dependencies = [
+ "bytes",
+ "data-encoding",
+ "http 1.5.0",
+ "httparse",
+ "log",
+ "rand 0.9.5",
+ "sha1 0.10.7",
+ "thiserror 2.0.21",
+ "utf-8",
+]
+
[[package]]
name = "tungstenite"
version = "0.29.0"
@@ -15277,6 +16365,12 @@ dependencies = [
"xmlwriter",
]
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
[[package]]
name = "utf8-width"
version = "0.1.9"
@@ -15498,6 +16592,28 @@ dependencies = [
"try-lock",
]
+[[package]]
+name = "warnings"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64f68998838dab65727c9b30465595c6f7c953313559371ca8bf31759b3680ad"
+dependencies = [
+ "pin-project",
+ "tracing",
+ "warnings-macro",
+]
+
+[[package]]
+name = "warnings-macro"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59195a1db0e95b920366d949ba5e0d3fc0e70b67c09be15ce5abb790106b0571"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
@@ -15965,6 +17081,15 @@ dependencies = [
"windows-link 0.2.1",
]
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -16001,6 +17126,21 @@ dependencies = [
"windows-link 0.2.1",
]
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
[[package]]
name = "windows-targets"
version = "0.52.6"
@@ -16052,6 +17192,12 @@ dependencies = [
"windows-link 0.2.1",
]
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@@ -16064,6 +17210,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
@@ -16076,6 +17228,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -16100,6 +17258,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
@@ -16112,6 +17276,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
@@ -16124,6 +17294,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
@@ -16136,6 +17312,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
@@ -16308,6 +17490,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
+[[package]]
+name = "xxhash-rust"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "550a2b930b62486a393c52d5c3b84bff264b28aa437ed64694d31e93b1757af7"
+
[[package]]
name = "y4m"
version = "0.8.0"
diff --git a/Cargo.toml b/Cargo.toml
index f9528ebf60..da5c42b41e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -71,6 +71,7 @@ members = [
"core/simulator",
"core/system_stats",
"core/tools",
+ "core/web_ui",
"examples/rust",
"gateways/kafka",
"gateways/kafka/tools/kafka-tool",
@@ -167,6 +168,7 @@ dashmap = "6.2.1"
deltalake = { version = "1.1.1", features = ["azure", "gcs", "s3"] }
derive_builder = "0.20.2"
derive_more = { version = "2.1.1", features = ["full"] }
+dioxus = { version = "0.7.10", features = ["router"] }
dircpy = "0.3.21"
dirs = "7.0.0"
dlopen2 = "0.9.0"
@@ -199,6 +201,7 @@ gcloud-googleapis = { version = "1.3.0", features = ["bigquery"] }
getrandom = { version = "0.4", features = ["wasm_js"] }
git2 = { version = "0.21.0", default-features = false, features = ["vendored-libgit2"] }
gloo = "0.12"
+gloo-net = { version = "0.7.0", default-features = false, features = ["http", "json"] }
governor = "0.10.4"
harness_derive = { path = "core/harness_derive" }
hash32 = "1.0.0"
diff --git a/core/web_ui/Cargo.toml b/core/web_ui/Cargo.toml
new file mode 100644
index 0000000000..a50cc704c3
--- /dev/null
+++ b/core/web_ui/Cargo.toml
@@ -0,0 +1,44 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+[package]
+name = "iggy-web-ui"
+license = "Apache-2.0"
+version = "0.1.0"
+edition = "2024"
+publish = false
+
+[features]
+default = ["web"]
+web = ["dioxus/web"]
+
+[dependencies]
+base64 = { workspace = true }
+chrono = { workspace = true }
+dioxus = { workspace = true }
+gloo-net = { workspace = true }
+js-sys = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+thiserror = { workspace = true }
+web-sys = { workspace = true, features = ["Storage"] }
+
+[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
+iggy_common = { workspace = true }
+
+[lints]
+workspace = true
diff --git a/core/web_ui/Dioxus.toml b/core/web_ui/Dioxus.toml
new file mode 100644
index 0000000000..8954ead77f
--- /dev/null
+++ b/core/web_ui/Dioxus.toml
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+[application]
+name = "iggy-web-ui"
+default_platform = "web"
+
+[web.app]
+title = "Apache Iggy"
+# The switch-over from the Svelte app will have the server embed the bundle
+# and serve it under /ui.
+base_path = "ui"
diff --git a/core/web_ui/README.md b/core/web_ui/README.md
new file mode 100644
index 0000000000..c98d3f448f
--- /dev/null
+++ b/core/web_ui/README.md
@@ -0,0 +1,51 @@
+# Apache Iggy Web UI
+
+A [Dioxus](https://dioxuslabs.com/) single-page app for browsing an Iggy
+server over its HTTP API. It compiles to `wasm32-unknown-unknown` and is
+built with the Dioxus CLI, `dx`.
+
+## Prerequisites
+
+```bash
+rustup target add wasm32-unknown-unknown
+cargo install dioxus-cli --version 0.7.10 --locked
+```
+
+Keep the `dx` version equal to the `dioxus` version in `Cargo.lock`.
+
+## Release bundle
+
+```bash
+./scripts/build-web-ui.sh
+```
+
+The bundle lands in `core/web_ui/dist`, with every asset path under `/ui`.
+The script clears dx's staging directory first, because dx copies every
+previously hashed asset into the bundle.
+
+## Development
+
+Start a server, then serve the UI with hot reload from this directory:
+
+```bash
+cargo run --bin iggy-server -- --with-default-root-credentials
+cd core/web_ui
+IGGY_WEB_UI_API_URL=http://localhost:3000 dx serve
+```
+
+Open and sign in as `iggy` / `iggy`.
+
+`IGGY_WEB_UI_API_URL` is read at build time and prefixes every API request.
+Left unset, requests go to the origin that served the UI, which is what an
+embedded build wants. The server's default CORS settings allow any origin, so
+the dev server on another port can reach it.
+
+## Tests
+
+```bash
+cargo test -p iggy-web-ui
+```
+
+The tests run on the host. Besides the formatting helpers, they serialize
+`iggy_common` types and decode them into the UI's mirrors in `src/models.rs`,
+so a renamed server field fails here instead of in the browser.
diff --git a/core/web_ui/assets/favicon.svg b/core/web_ui/assets/favicon.svg
new file mode 100644
index 0000000000..7048812d45
--- /dev/null
+++ b/core/web_ui/assets/favicon.svg
@@ -0,0 +1,98 @@
+
+
diff --git a/core/web_ui/assets/iggy-apache-darkbg.svg b/core/web_ui/assets/iggy-apache-darkbg.svg
new file mode 100644
index 0000000000..a03645fb3e
--- /dev/null
+++ b/core/web_ui/assets/iggy-apache-darkbg.svg
@@ -0,0 +1,112 @@
+
+
diff --git a/core/web_ui/assets/main.css b/core/web_ui/assets/main.css
new file mode 100644
index 0000000000..552ba14f08
--- /dev/null
+++ b/core/web_ui/assets/main.css
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+:root {
+ --bg: #1e1f22;
+ --surface: #2b2d31;
+ --surface-raised: #313338;
+ --border: #404249;
+ --text: #f2f3f5;
+ --text-muted: #949ba4;
+ --accent: #32ad84;
+ --accent-hover: #5abd9c;
+ --danger: #f23f43;
+ color-scheme: dark;
+ font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
+ font-size: 14px;
+ background: var(--bg);
+ color: var(--text);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+}
+
+a {
+ color: var(--accent);
+ text-decoration: none;
+}
+
+a:hover {
+ color: var(--accent-hover);
+ text-decoration: underline;
+}
+
+h1 {
+ margin: 0;
+ font-size: 1.6rem;
+ font-weight: 600;
+}
+
+h2 {
+ margin: 2rem 0 0.75rem;
+ font-size: 1.1rem;
+ font-weight: 600;
+}
+
+button {
+ padding: 0.55rem 1rem;
+ border: none;
+ border-radius: 6px;
+ background: var(--accent);
+ color: #fff;
+ font: inherit;
+ font-weight: 600;
+ cursor: pointer;
+}
+
+button:hover:not(:disabled) {
+ background: var(--accent-hover);
+}
+
+button:disabled {
+ opacity: 0.45;
+ cursor: default;
+}
+
+button.secondary {
+ background: var(--surface-raised);
+ color: var(--text);
+ border: 1px solid var(--border);
+}
+
+button.secondary:hover:not(:disabled) {
+ background: var(--border);
+}
+
+input {
+ width: 100%;
+ margin-top: 0.35rem;
+ padding: 0.55rem 0.7rem;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ background: var(--bg);
+ color: var(--text);
+ font: inherit;
+}
+
+input:focus {
+ outline: 2px solid var(--accent);
+ outline-offset: -1px;
+}
+
+.muted {
+ color: var(--text-muted);
+}
+
+.mono {
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+ font-size: 0.85rem;
+}
+
+.auth {
+ display: grid;
+ min-height: 100vh;
+ place-items: center;
+ padding: 1rem;
+}
+
+.auth-card {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ width: min(380px, 100%);
+ padding: 2rem;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ background: var(--surface);
+}
+
+.auth-card label {
+ color: var(--text-muted);
+ font-size: 0.85rem;
+}
+
+.auth-logo {
+ width: 70%;
+ margin: 0 auto 0.5rem;
+}
+
+.form-error,
+.error-box {
+ margin: 0;
+ color: var(--danger);
+}
+
+.error-box {
+ padding: 0.75rem 1rem;
+ border: 1px solid var(--danger);
+ border-radius: 8px;
+}
+
+.dashboard {
+ display: grid;
+ grid-template-columns: 220px 1fr;
+ min-height: 100vh;
+}
+
+.sidebar {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ padding: 1.25rem 1rem;
+ border-right: 1px solid var(--border);
+ background: var(--surface);
+}
+
+.sidebar nav {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.sidebar-logo {
+ width: 100%;
+}
+
+.sidebar-footer {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ margin-top: auto;
+}
+
+.nav-link {
+ padding: 0.5rem 0.75rem;
+ border-radius: 6px;
+ color: var(--text-muted);
+}
+
+.nav-link:hover {
+ background: var(--surface-raised);
+ color: var(--text);
+ text-decoration: none;
+}
+
+.nav-link.active {
+ background: var(--surface-raised);
+ color: var(--text);
+}
+
+.content {
+ min-width: 0;
+ padding: 2rem 2.5rem;
+}
+
+.page-header {
+ margin-bottom: 1.5rem;
+}
+
+.breadcrumbs {
+ display: flex;
+ gap: 0.4rem;
+ min-height: 1.2rem;
+ margin-bottom: 0.4rem;
+ font-size: 0.85rem;
+}
+
+.crumb-separator {
+ color: var(--text-muted);
+}
+
+.stats {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: 0.75rem;
+}
+
+.stat {
+ display: flex;
+ flex-direction: column;
+ gap: 0.3rem;
+ padding: 0.85rem 1rem;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--surface);
+}
+
+.stat-label {
+ color: var(--text-muted);
+ font-size: 0.8rem;
+}
+
+.stat-value {
+ font-size: 1.05rem;
+ font-weight: 600;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--surface);
+}
+
+th,
+td {
+ padding: 0.6rem 0.85rem;
+ border-bottom: 1px solid var(--border);
+ text-align: left;
+ vertical-align: top;
+}
+
+th {
+ color: var(--text-muted);
+ font-size: 0.8rem;
+ font-weight: 500;
+}
+
+tbody tr:last-child td {
+ border-bottom: none;
+}
+
+tbody tr:hover {
+ background: var(--surface-raised);
+}
+
+.num {
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+}
+
+.pager {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 1rem;
+}
+
+.pager .muted {
+ margin-right: auto;
+}
+
+details summary {
+ cursor: pointer;
+}
+
+details pre {
+ max-height: 320px;
+ margin: 0.5rem 0 0;
+ padding: 0.75rem;
+ overflow: auto;
+ border-radius: 6px;
+ background: var(--bg);
+ white-space: pre-wrap;
+ word-break: break-all;
+}
diff --git a/core/web_ui/src/api.rs b/core/web_ui/src/api.rs
new file mode 100644
index 0000000000..3c294f96b2
--- /dev/null
+++ b/core/web_ui/src/api.rs
@@ -0,0 +1,188 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use gloo_net::http::{Request, RequestBuilder, Response};
+use serde::Deserialize;
+use serde::de::DeserializeOwned;
+
+use crate::auth::Session;
+use crate::models::{
+ IdentityInfo, LoginUser, PolledMessages, Stream, StreamDetails, TopicDetails, UserInfoDetails,
+};
+
+/// Empty by default so requests go to the server that served the UI. Set at
+/// build time to point `dx serve` at a separately running server, see the
+/// crate README.
+const API_BASE_URL: &str = match option_env!("IGGY_WEB_UI_API_URL") {
+ Some(url) => url,
+ None => "",
+};
+
+#[derive(Debug, Clone, PartialEq, thiserror::Error)]
+pub enum ApiError {
+ /// The session is not valid any more.
+ #[error("session expired or not authorized")]
+ Unauthorized,
+ /// The account lacks a permission. The session stays valid.
+ #[error("you do not have permission to do this")]
+ Forbidden,
+ #[error("not found")]
+ NotFound,
+ /// The server rejected the request and said why.
+ #[error("{0}")]
+ Rejected(String),
+ #[error("server responded with {status}: {body}")]
+ Status { status: u16, body: String },
+ #[error("request failed: {0}")]
+ Network(String),
+ #[error("unexpected response: {0}")]
+ Decode(String),
+}
+
+/// Thin client for the server's HTTP API, scoped to one session.
+#[derive(Debug, Clone, Default)]
+pub struct ApiClient {
+ access_token: Option,
+}
+
+impl ApiClient {
+ pub fn new(session: Option<&Session>) -> Self {
+ Self {
+ access_token: session.map(|session| session.access_token.clone()),
+ }
+ }
+
+ pub async fn login(&self, credentials: &LoginUser) -> Result {
+ let request = self
+ .builder(Request::post(&url("/users/login")))
+ .json(credentials)
+ .map_err(|error| ApiError::Network(error.to_string()))?;
+ let response = request
+ .send()
+ .await
+ .map_err(|error| ApiError::Network(error.to_string()))?;
+ decode(response).await
+ }
+
+ pub async fn logout(&self) -> Result<(), ApiError> {
+ let response = self
+ .builder(Request::delete(&url("/users/logout")))
+ .send()
+ .await
+ .map_err(|error| ApiError::Network(error.to_string()))?;
+ check_status(response).await.map(drop)
+ }
+
+ pub async fn user(&self, user_id: u32) -> Result {
+ self.get(&format!("/users/{user_id}"), &[]).await
+ }
+
+ pub async fn streams(&self) -> Result, ApiError> {
+ self.get("/streams", &[]).await
+ }
+
+ pub async fn stream(&self, stream_id: u32) -> Result {
+ self.get(&format!("/streams/{stream_id}"), &[]).await
+ }
+
+ pub async fn topic(&self, stream_id: u32, topic_id: u32) -> Result {
+ self.get(&format!("/streams/{stream_id}/topics/{topic_id}"), &[])
+ .await
+ }
+
+ /// Reads without committing, so browsing never moves a consumer offset.
+ pub async fn poll_messages(
+ &self,
+ stream_id: u32,
+ topic_id: u32,
+ partition_id: u32,
+ offset: u64,
+ count: u32,
+ ) -> Result {
+ let query = [
+ ("kind", "offset".to_owned()),
+ ("value", offset.to_string()),
+ ("count", count.to_string()),
+ ("auto_commit", "false".to_owned()),
+ ("partition_id", partition_id.to_string()),
+ ];
+ self.get(
+ &format!("/streams/{stream_id}/topics/{topic_id}/messages"),
+ &query,
+ )
+ .await
+ }
+
+ async fn get(
+ &self,
+ path: &str,
+ query: &[(&str, String)],
+ ) -> Result {
+ let builder =
+ Request::get(&url(path)).query(query.iter().map(|(key, value)| (*key, value.as_str())));
+ let response = self
+ .builder(builder)
+ .send()
+ .await
+ .map_err(|error| ApiError::Network(error.to_string()))?;
+ decode(response).await
+ }
+
+ fn builder(&self, builder: RequestBuilder) -> RequestBuilder {
+ match &self.access_token {
+ Some(token) => builder.header("Authorization", &format!("Bearer {token}")),
+ None => builder,
+ }
+ }
+}
+
+fn url(path: &str) -> String {
+ format!("{API_BASE_URL}{path}")
+}
+
+/// Reads the body as bytes, because `Response::json` goes through one JS
+/// string, which browsers cap well below a page of large messages.
+async fn decode(response: Response) -> Result {
+ let body = check_status(response)
+ .await?
+ .binary()
+ .await
+ .map_err(|error| ApiError::Network(error.to_string()))?;
+ serde_json::from_slice(&body).map_err(|error| ApiError::Decode(error.to_string()))
+}
+
+async fn check_status(response: Response) -> Result {
+ match response.status() {
+ 200..=299 => Ok(response),
+ 401 => Err(ApiError::Unauthorized),
+ 403 => Err(ApiError::Forbidden),
+ 404 => Err(ApiError::NotFound),
+ status => {
+ let body = response.text().await.unwrap_or_default();
+ Err(match serde_json::from_str::(&body) {
+ Ok(error) => ApiError::Rejected(error.reason),
+ Err(_) => ApiError::Status { status, body },
+ })
+ }
+ }
+}
+
+/// The JSON body the server attaches to rejected requests.
+#[derive(Deserialize)]
+struct ErrorBody {
+ reason: String,
+}
diff --git a/core/web_ui/src/auth.rs b/core/web_ui/src/auth.rs
new file mode 100644
index 0000000000..8bb64fb832
--- /dev/null
+++ b/core/web_ui/src/auth.rs
@@ -0,0 +1,92 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use serde::{Deserialize, Serialize};
+use web_sys::Storage;
+
+use crate::models::IdentityInfo;
+
+const SESSION_KEY: &str = "iggy_session";
+
+/// The signed-in user's token, persisted so a reload keeps the session.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct Session {
+ pub user_id: u32,
+ pub access_token: String,
+ /// Unix seconds, as returned by `/users/login`.
+ pub expiry: u64,
+}
+
+impl Session {
+ /// Restores the stored session, dropping it if the token has expired.
+ /// Blocked or unreadable storage counts as no session.
+ pub fn restore() -> Option {
+ let storage = local_storage()?;
+ let stored = storage.get_item(SESSION_KEY).ok()??;
+ let session: Self = serde_json::from_str(&stored).ok()?;
+ if session.is_expired() {
+ let _ = storage.remove_item(SESSION_KEY);
+ return None;
+ }
+ Some(session)
+ }
+
+ pub fn persist(&self) {
+ // A failed write only costs the user a sign-in after the next reload.
+ let (Some(storage), Ok(json)) = (local_storage(), serde_json::to_string(self)) else {
+ return;
+ };
+ let _ = storage.set_item(SESSION_KEY, &json);
+ }
+
+ /// Removes this session from storage. Tabs share the stored session, so
+ /// one that another tab saved after signing in again is left alone.
+ pub fn forget(&self) {
+ let Some(storage) = local_storage() else {
+ return;
+ };
+ let stored = storage
+ .get_item(SESSION_KEY)
+ .ok()
+ .flatten()
+ .and_then(|stored| serde_json::from_str::(&stored).ok());
+ if stored.is_none_or(|stored| stored.access_token == self.access_token) {
+ let _ = storage.remove_item(SESSION_KEY);
+ }
+ }
+
+ fn is_expired(&self) -> bool {
+ let now_secs = (js_sys::Date::now() / 1000.0) as u64;
+ self.expiry <= now_secs
+ }
+}
+
+impl From for Session {
+ fn from(identity: IdentityInfo) -> Self {
+ Self {
+ user_id: identity.user_id,
+ access_token: identity.access_token.token,
+ expiry: identity.access_token.expiry,
+ }
+ }
+}
+
+/// `None` when the browser blocks site storage, where reading
+/// `window.localStorage` throws.
+fn local_storage() -> Option {
+ web_sys::window()?.local_storage().ok().flatten()
+}
diff --git a/core/web_ui/src/components.rs b/core/web_ui/src/components.rs
new file mode 100644
index 0000000000..e6f7a9dae5
--- /dev/null
+++ b/core/web_ui/src/components.rs
@@ -0,0 +1,89 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use dioxus::prelude::*;
+
+use crate::Route;
+use crate::api::{ApiClient, ApiError};
+use crate::auth::Session;
+
+pub fn use_session() -> Signal