From 1723d87df63a6daabf1c31455346d1f6a10181cd Mon Sep 17 00:00:00 2001 From: arferreira Date: Wed, 18 Feb 2026 22:05:45 -0500 Subject: [PATCH 1/2] Add dashboard foundation with Leptos CSR --- .gitignore | 2 + Cargo.lock | 1178 ++++++++++++++++++++++- Cargo.toml | 5 + crates/dashboard/Cargo.toml | 17 + crates/dashboard/Trunk.toml | 9 + crates/dashboard/index.html | 11 + crates/dashboard/src/api.rs | 65 ++ crates/dashboard/src/app.rs | 19 + crates/dashboard/src/lib.rs | 10 + crates/dashboard/src/pages/dashboard.rs | 177 ++++ crates/dashboard/src/pages/home.rs | 37 + crates/dashboard/src/pages/login.rs | 110 +++ crates/dashboard/src/pages/mod.rs | 24 + crates/dashboard/src/pages/signup.rs | 130 +++ crates/dashboard/style.css | 115 +++ src/lib.rs | 1 + src/main.rs | 2 + src/static_files.rs | 86 ++ 18 files changed, 1985 insertions(+), 13 deletions(-) create mode 100644 crates/dashboard/Cargo.toml create mode 100644 crates/dashboard/Trunk.toml create mode 100644 crates/dashboard/index.html create mode 100644 crates/dashboard/src/api.rs create mode 100644 crates/dashboard/src/app.rs create mode 100644 crates/dashboard/src/lib.rs create mode 100644 crates/dashboard/src/pages/dashboard.rs create mode 100644 crates/dashboard/src/pages/home.rs create mode 100644 crates/dashboard/src/pages/login.rs create mode 100644 crates/dashboard/src/pages/mod.rs create mode 100644 crates/dashboard/src/pages/signup.rs create mode 100644 crates/dashboard/style.css create mode 100644 src/static_files.rs diff --git a/.gitignore b/.gitignore index 60625ff..f29d515 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .env *.db *.db-journal +dist/ +node_modules/ diff --git a/Cargo.lock b/Cargo.lock index 199ef23..dda07d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" version = "0.7.8" @@ -99,6 +105,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "any_spawner" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41058deaa38c9d9dd933d6d238d825227cffa668e2839b52879f6619c63eee3b" +dependencies = [ + "futures", + "thiserror 2.0.18", + "wasm-bindgen-futures", +] + [[package]] name = "anyhow" version = "1.0.101" @@ -111,6 +128,17 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -159,6 +187,36 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "attribute-derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05832cdddc8f2650cc2cc187cc2e952b8c133a48eb055f35211f61ee81502d77" +dependencies = [ + "attribute-derive-macro", + "derive-where", + "manyhow", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "attribute-derive-macro" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7cdbbd4bd005c5d3e2e9c885e6fa575db4f4a3572335b974d8db853b6beb61" +dependencies = [ + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.116", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -313,6 +371,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" + [[package]] name = "cc" version = "1.2.56" @@ -320,6 +384,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -399,6 +465,23 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +[[package]] +name = "codee" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dbbdc4b4d349732bc6690de10a9de952bd39ba6a065c586e26600b6b0b91f5" +dependencies = [ + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "collection_literals" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2550f75b8cfac212855f6b1885455df8eaee8fe8e246b647d69146142e016084" + [[package]] name = "colorchoice" version = "1.0.4" @@ -414,18 +497,84 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "config" +version = "0.15.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" +dependencies = [ + "convert_case 0.6.0", + "pathdiff", + "serde_core", + "toml", + "winnow", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "const_str_slice_concat" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67855af358fcb20fac58f9d714c94e2b228fe5694c1c9b4ead4a366343eda1b" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -558,6 +707,26 @@ dependencies = [ "syn 2.0.116", ] +[[package]] +name = "dary_heap" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d2e3287df1c007e74221c49ca10a95d557349e54b3a75dc2fb14712c751f04" + +[[package]] +name = "dashboard" +version = "0.1.0" +dependencies = [ + "gloo-net", + "gloo-storage", + "leptos", + "leptos_router", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -593,6 +762,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive-where" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -644,6 +824,12 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "drain_filter_polyfill" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -697,6 +883,16 @@ dependencies = [ "serde", ] +[[package]] +name = "either_of" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216d23e0ec69759a17f05e1c553f3a6870e5ec73420fbb07807a6f34d5d1d5a4" +dependencies = [ + "paste", + "pin-project-lite", +] + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -756,6 +952,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "ff" version = "0.13.1" @@ -811,6 +1017,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -834,6 +1046,7 @@ checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -884,6 +1097,17 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -902,8 +1126,10 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -933,6 +1159,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + [[package]] name = "getrandom" version = "0.4.1" @@ -952,6 +1190,55 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[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", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "group" version = "0.13.0" @@ -963,6 +1250,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "guardian" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" + [[package]] name = "h2" version = "0.4.13" @@ -1005,7 +1298,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1013,6 +1306,11 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "hashlink" @@ -1068,6 +1366,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + [[package]] name = "http" version = "1.4.0" @@ -1113,6 +1420,20 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hydration_context" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d35485b3dcbf7e044b8f28c73f04f13e7b509c2466fd10cb2a8a447e38f8a93a" +dependencies = [ + "futures", + "once_cell", + "or_poisoned", + "pin-project-lite", + "serde", + "throw_error", +] + [[package]] name = "hyper" version = "1.8.1" @@ -1294,6 +1615,43 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "include-flate" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01b7cb6ca682a621e7cda1c358c9724b53a7b4409be9be1dd443b7f3a26f998" +dependencies = [ + "include-flate-codegen", + "include-flate-compress", + "libflate", + "zstd", +] + +[[package]] +name = "include-flate-codegen" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f49bf5274aebe468d6e6eba14a977eaf1efa481dc173f361020de70c1c48050" +dependencies = [ + "include-flate-compress", + "libflate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.116", + "zstd", +] + +[[package]] +name = "include-flate-compress" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae6a40e716bcd5931f5dbb79cd921512a4f647e2e9413fded3171fca3824dbc" +dependencies = [ + "libflate", + "zstd", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -1326,18 +1684,43 @@ dependencies = [ "generic-array", ] +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.85" @@ -1386,12 +1769,195 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "leptos" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b8731cb00f3f0894058155410b95c8955b17273181d2bc72600ab84edd24f1" +dependencies = [ + "any_spawner", + "cfg-if", + "either_of", + "futures", + "hydration_context", + "leptos_config", + "leptos_dom", + "leptos_hot_reload", + "leptos_macro", + "leptos_server", + "oco_ref", + "or_poisoned", + "paste", + "reactive_graph", + "rustc-hash", + "send_wrapper", + "serde", + "serde_qs", + "server_fn", + "slotmap", + "tachys", + "thiserror 2.0.18", + "throw_error", + "typed-builder", + "typed-builder-macro", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_config" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bae3e0ead5a7a814c8340eef7cb8b6cba364125bd8174b15dc9fe1b3cab7e03" +dependencies = [ + "config", + "regex", + "serde", + "thiserror 2.0.18", + "typed-builder", +] + +[[package]] +name = "leptos_dom" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89d4eb263bd5a9e7c49f780f17063f15aca56fd638c90b9dfd5f4739152e87d" +dependencies = [ + "js-sys", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "tachys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_hot_reload" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e80219388501d99b246f43b6e7d08a28f327cdd34ba630a35654d917f3e1788e" +dependencies = [ + "anyhow", + "camino", + "indexmap", + "parking_lot", + "proc-macro2", + "quote", + "rstml", + "serde", + "syn 2.0.116", + "walkdir", +] + +[[package]] +name = "leptos_macro" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e621f8f5342b9bdc93bb263b839cee7405027a74560425a2dabea9de7952b1fd" +dependencies = [ + "attribute-derive", + "cfg-if", + "convert_case 0.7.1", + "html-escape", + "itertools", + "leptos_hot_reload", + "prettyplease", + "proc-macro-error2", + "proc-macro2", + "quote", + "rstml", + "server_fn_macro", + "syn 2.0.116", + "uuid", +] + +[[package]] +name = "leptos_router" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4168ead6a9715daba953aa842795cb2ad81b6e011a15745bd3d1baf86f76de95" +dependencies = [ + "any_spawner", + "either_of", + "futures", + "gloo-net", + "js-sys", + "leptos", + "leptos_router_macro", + "once_cell", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "tachys", + "thiserror 2.0.18", + "url", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_router_macro" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31197af38d209ffc5d9f89715381c415a1570176f8d23455fbe00d148e79640" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "leptos_server" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66985242812ec95e224fb48effe651ba02728beca92c461a9464c811a71aab11" +dependencies = [ + "any_spawner", + "base64", + "codee", + "futures", + "hydration_context", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "serde", + "serde_json", + "server_fn", + "tachys", +] + [[package]] name = "libc" version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +[[package]] +name = "libflate" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3248b8d211bd23a104a42d81b4fa8bb8ac4a3b75e7a43d85d2c9ccb6179cd74" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a599cb10a9cd92b1300debcef28da8f70b935ec937f44fcd1b70a7c986a11c5c" +dependencies = [ + "core2", + "hashbrown 0.16.1", + "rle-decode-fast", +] + [[package]] name = "libm" version = "0.2.16" @@ -1419,6 +1985,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linear-map" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" + [[package]] name = "litemap" version = "0.8.1" @@ -1440,6 +2012,29 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "manyhow" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "manyhow-macros" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + [[package]] name = "matchers" version = "0.2.0" @@ -1465,6 +2060,22 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1486,6 +2097,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "next_tuple" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60993920e071b0c9b66f14e2b32740a4e27ffc82854dcd72035887f336a09a28" + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1557,6 +2174,16 @@ dependencies = [ "libm", ] +[[package]] +name = "oco_ref" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0423ff9973dea4d6bd075934fdda86ebb8c05bdf9d6b0507067d4a1226371d" +dependencies = [ + "serde", + "thiserror 2.0.18", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1569,6 +2196,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "or_poisoned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c04f5d74368e4d0dfe06c45c8627c81bd7c317d52762d118fb9b3076f6420fd" + [[package]] name = "ordered-float" version = "4.6.0" @@ -1655,6 +2288,18 @@ dependencies = [ "windows-link", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "pem" version = "3.0.6" @@ -1689,6 +2334,26 @@ dependencies = [ "serde", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1780,6 +2445,30 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -1802,6 +2491,17 @@ dependencies = [ "syn 2.0.116", ] +[[package]] +name = "proc-macro-utils" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1853,6 +2553,28 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quote-use" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e" +dependencies = [ + "quote", + "quote-use-macros", +] + +[[package]] +name = "quote-use-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "r-efi" version = "5.3.0" @@ -1926,12 +2648,61 @@ dependencies = [ ] [[package]] -name = "rapina-macros" -version = "0.5.0" +name = "rapina-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6775674a6acfc640c1623cd6075afdb3986cf9cfcf35cdb2348744d8ccfb32e" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "reactive_graph" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a0ccddbc11a648bd09761801dac9e3f246ef7641130987d6120fced22515e6" +dependencies = [ + "any_spawner", + "async-lock", + "futures", + "guardian", + "hydration_context", + "or_poisoned", + "pin-project-lite", + "rustc-hash", + "send_wrapper", + "serde", + "slotmap", + "thiserror 2.0.18", + "web-sys", +] + +[[package]] +name = "reactive_stores" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6775674a6acfc640c1623cd6075afdb3986cf9cfcf35cdb2348744d8ccfb32e" +checksum = "aadc7c19e3a360bf19cd595d2dc8b58ce67b9240b95a103fbc1317a8ff194237" dependencies = [ - "heck 0.5.0", + "guardian", + "itertools", + "or_poisoned", + "paste", + "reactive_graph", + "reactive_stores_macro", + "rustc-hash", +] + +[[package]] +name = "reactive_stores_macro" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221095cb028dc51fbc2833743ea8b1a585da1a2af19b440b3528027495bf1f2d" +dependencies = [ + "convert_case 0.7.1", + "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.116", @@ -1962,7 +2733,9 @@ dependencies = [ "bcrypt", "chrono", "dotenvy", + "mime_guess", "rapina", + "rust-embed", "serde", "serde_json", "tokio", @@ -2080,6 +2853,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + [[package]] name = "rsa" version = "0.9.10" @@ -2100,6 +2879,56 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rstml" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61cf4616de7499fc5164570d40ca4e1b24d231c6833a88bff0fe00725080fd56" +dependencies = [ + "derive-where", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.116", + "syn_derive", + "thiserror 2.0.18", +] + +[[package]] +name = "rust-embed" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +dependencies = [ + "include-flate", + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.116", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rust_decimal" version = "1.40.0" @@ -2116,6 +2945,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -2171,6 +3006,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schemars" version = "1.2.1" @@ -2239,7 +3083,7 @@ dependencies = [ "serde_json", "sqlx", "strum", - "thiserror", + "thiserror 2.0.18", "time", "tracing", "url", @@ -2339,7 +3183,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.116", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -2393,6 +3237,15 @@ version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + [[package]] name = "serde" version = "1.0.228" @@ -2447,6 +3300,26 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_qs" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2459,6 +3332,60 @@ dependencies = [ "serde", ] +[[package]] +name = "server_fn" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d05a9e3fd8d7404985418db38c6617cc793a1a27f398d4fbc9dfe8e41b804e6" +dependencies = [ + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http", + "js-sys", + "once_cell", + "pin-project-lite", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror 2.0.18", + "throw_error", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504b35e883267b3206317b46d02952ed7b8bf0e11b2e209e2eb453b609a5e052" +dependencies = [ + "const_format", + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.116", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb8b274f568c94226a8045668554aace8142a59b8bca5414ac5a79627c825568" +dependencies = [ + "server_fn_macro", + "syn 2.0.116", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2536,7 +3463,7 @@ checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 2.0.18", "time", ] @@ -2546,6 +3473,15 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -2628,7 +3564,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror", + "thiserror 2.0.18", "time", "tokio", "tokio-stream", @@ -2716,7 +3652,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.18", "time", "tracing", "uuid", @@ -2759,7 +3695,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.18", "time", "tracing", "uuid", @@ -2786,7 +3722,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror", + "thiserror 2.0.18", "time", "tracing", "url", @@ -2856,6 +3792,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb066a04799e45f5d582e8fc6ec8e6d6896040d00898eb4e6a835196815b219" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -2867,19 +3815,73 @@ dependencies = [ "syn 2.0.116", ] +[[package]] +name = "tachys" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66c3b70c32844a6f1e2943c72a33ebb777ad6acbeb20d1329d62e3a7806d6ec" +dependencies = [ + "any_spawner", + "async-trait", + "const_str_slice_concat", + "drain_filter_polyfill", + "dyn-clone", + "either_of", + "futures", + "html-escape", + "indexmap", + "itertools", + "js-sys", + "linear-map", + "next_tuple", + "oco_ref", + "once_cell", + "or_poisoned", + "parking_lot", + "paste", + "reactive_graph", + "reactive_stores", + "rustc-hash", + "send_wrapper", + "slotmap", + "throw_error", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", ] [[package]] @@ -2902,6 +3904,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "throw_error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ef8bf264c6ae02a065a4a16553283f0656bd6266fc1fcb09fd2e6b5e91427b" +dependencies = [ + "pin-project-lite", +] + [[package]] name = "time" version = "0.3.47" @@ -3010,6 +4021,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "winnow", +] + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -3127,12 +4151,38 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-builder" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9d30e3a08026c78f246b173243cf07b3696d274debd26680773b6773c2afc7" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c36781cc0e46a83726d9879608e4cf6c2505237e263a8eb8c24502989cfdb28" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "typenum" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -3160,6 +4210,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -3184,6 +4240,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf8-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -3256,6 +4318,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -3308,6 +4380,20 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.108" @@ -3362,6 +4448,19 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" @@ -3374,6 +4473,16 @@ dependencies = [ "semver", ] +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.26.11" @@ -3402,6 +4511,15 @@ dependencies = [ "wasite", ] +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -3795,6 +4913,12 @@ dependencies = [ "tap", ] +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yansi" version = "1.0.1" @@ -3909,3 +5033,31 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index ab3d57c..61851bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[workspace] +members = [".", "crates/dashboard"] + [package] name = "reeverb" version = "0.1.0" @@ -19,6 +22,8 @@ uuid = { version = "1", features = ["v4", "serde"] } chrono = { version = "0.4", features = ["serde"] } dotenvy = "0.15" bcrypt = "0.16" +rust-embed = { version = "8", features = ["compression"] } +mime_guess = "2" [profile.release] lto = true diff --git a/crates/dashboard/Cargo.toml b/crates/dashboard/Cargo.toml new file mode 100644 index 0000000..10024aa --- /dev/null +++ b/crates/dashboard/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dashboard" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +leptos = { version = "0.7", features = ["csr"] } +leptos_router = "0.7" +gloo-net = { version = "0.6", features = ["http"] } +gloo-storage = "0.3" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +wasm-bindgen = "0.2" +web-sys = { version = "0.3", features = ["Window", "Location"] } diff --git a/crates/dashboard/Trunk.toml b/crates/dashboard/Trunk.toml new file mode 100644 index 0000000..481eb70 --- /dev/null +++ b/crates/dashboard/Trunk.toml @@ -0,0 +1,9 @@ +[build] +target = "index.html" +dist = "../../dist" + +[serve] +port = 8080 + +[[proxy]] +backend = "http://127.0.0.1:3000/api" diff --git a/crates/dashboard/index.html b/crates/dashboard/index.html new file mode 100644 index 0000000..177f19b --- /dev/null +++ b/crates/dashboard/index.html @@ -0,0 +1,11 @@ + + + + + + Reeverb + + + + + diff --git a/crates/dashboard/src/api.rs b/crates/dashboard/src/api.rs new file mode 100644 index 0000000..24c5adf --- /dev/null +++ b/crates/dashboard/src/api.rs @@ -0,0 +1,65 @@ +use gloo_net::http::{Request, RequestBuilder}; +use gloo_storage::Storage; +use serde::de::DeserializeOwned; + +fn get_token() -> Option { + gloo_storage::LocalStorage::raw() + .get_item("token") + .ok() + .flatten() +} + +fn clear_token_and_redirect() { + let _ = gloo_storage::LocalStorage::raw().remove_item("token"); + if let Some(window) = web_sys::window() { + let _ = window.location().set_href("/login"); + } +} + +fn with_auth(builder: RequestBuilder) -> RequestBuilder { + match get_token() { + Some(token) => builder.header("Authorization", &format!("Bearer {}", token)), + None => builder, + } +} + +pub async fn get(path: &str) -> Result { + let resp = with_auth(Request::get(path)) + .send() + .await + .map_err(|e| e.to_string())?; + + if resp.status() == 401 { + clear_token_and_redirect(); + return Err("Unauthorized".into()); + } + + if !resp.ok() { + return Err(format!("Request failed: {}", resp.status())); + } + + resp.json::().await.map_err(|e| e.to_string()) +} + +pub async fn post( + path: &str, + body: &B, +) -> Result { + let resp = with_auth(Request::post(path)) + .json(body) + .map_err(|e| e.to_string())? + .send() + .await + .map_err(|e| e.to_string())?; + + if resp.status() == 401 { + clear_token_and_redirect(); + return Err("Unauthorized".into()); + } + + if !resp.ok() { + return Err(format!("Request failed: {}", resp.status())); + } + + resp.json::().await.map_err(|e| e.to_string()) +} diff --git a/crates/dashboard/src/app.rs b/crates/dashboard/src/app.rs new file mode 100644 index 0000000..59037db --- /dev/null +++ b/crates/dashboard/src/app.rs @@ -0,0 +1,19 @@ +use leptos::prelude::*; +use leptos_router::components::{Route, Router, Routes}; +use leptos_router::path; + +use crate::pages::{DashboardPage, HomePage, LoginPage, NotFoundPage, SignupPage}; + +#[component] +pub fn App() -> impl IntoView { + view! { + + + + + + + + + } +} diff --git a/crates/dashboard/src/lib.rs b/crates/dashboard/src/lib.rs new file mode 100644 index 0000000..836d804 --- /dev/null +++ b/crates/dashboard/src/lib.rs @@ -0,0 +1,10 @@ +mod api; +mod app; +mod pages; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(start)] +pub fn main() { + leptos::mount::mount_to_body(app::App); +} diff --git a/crates/dashboard/src/pages/dashboard.rs b/crates/dashboard/src/pages/dashboard.rs new file mode 100644 index 0000000..c8fab13 --- /dev/null +++ b/crates/dashboard/src/pages/dashboard.rs @@ -0,0 +1,177 @@ +use gloo_storage::Storage; +use leptos::prelude::*; +use serde::{Deserialize, Serialize}; + +use crate::api; + +#[derive(Clone, Serialize, Deserialize)] +struct Project { + pid: String, + name: String, + slug: String, +} + +fn logout() { + let _ = gloo_storage::LocalStorage::raw().remove_item("token"); + if let Some(window) = web_sys::window() { + let _ = window.location().set_href("/login"); + } +} + +#[component] +pub fn DashboardPage() -> impl IntoView { + let token_exists = gloo_storage::LocalStorage::raw() + .get_item("token") + .ok() + .flatten() + .is_some(); + + if !token_exists { + if let Some(window) = web_sys::window() { + let _ = window.location().set_href("/login"); + } + return view! {
}.into_any(); + } + + let (projects, set_projects) = signal(None::, String>>); + let (loading, set_loading) = signal(true); + + let fetch_projects = move || { + set_loading.set(true); + leptos::task::spawn_local(async move { + let result = api::get::>("/api/v1/projects").await; + set_projects.set(Some(result)); + set_loading.set(false); + }); + }; + + fetch_projects(); + + let (show_form, set_show_form) = signal(false); + let (new_name, set_new_name) = signal(String::new()); + let (new_slug, set_new_slug) = signal(String::new()); + let (form_error, set_form_error) = signal(Option::::None); + + let on_create = move |ev: web_sys::SubmitEvent| { + ev.prevent_default(); + set_form_error.set(None); + + let name = new_name.get_untracked(); + let slug = new_slug.get_untracked(); + + leptos::task::spawn_local(async move { + #[derive(serde::Serialize)] + struct CreateProject { + name: String, + slug: String, + } + + match api::post::("/api/v1/projects", &CreateProject { name, slug }).await { + Ok(_) => { + set_show_form.set(false); + set_new_name.set(String::new()); + set_new_slug.set(String::new()); + // Refetch projects + let result = api::get::>("/api/v1/projects").await; + set_projects.set(Some(result)); + } + Err(e) => set_form_error.set(Some(e)), + } + }); + }; + + view! { +
+
+
+

"Reeverb"

+ +
+
+ +
+
+

"Projects"

+ +
+ + {move || show_form.get().then(|| view! { +
+ {move || form_error.get().map(|e| view! { +
{e}
+ })} +
+
+ + +
+
+ + +
+ +
+
+ })} + + {move || { + if loading.get() && projects.get().is_none() { + return view! { +

"Loading projects..."

+ }.into_any(); + } + + match projects.get() { + Some(Ok(list)) if list.is_empty() => view! { +
+

"No projects yet. Create your first one."

+
+ }.into_any(), + Some(Ok(list)) => view! { +
+ {list.into_iter().map(|project| view! { +
+

{project.name}

+

+ {project.slug} +

+
+ }).collect::>()} +
+ }.into_any(), + Some(Err(e)) => view! { +
{e}
+ }.into_any(), + None => view! { +

"Loading projects..."

+ }.into_any(), + } + }} +
+
+ } + .into_any() +} diff --git a/crates/dashboard/src/pages/home.rs b/crates/dashboard/src/pages/home.rs new file mode 100644 index 0000000..09ac9f1 --- /dev/null +++ b/crates/dashboard/src/pages/home.rs @@ -0,0 +1,37 @@ +use leptos::prelude::*; + +#[component] +pub fn HomePage() -> impl IntoView { + view! { +
+
+ +
+ +
+

+ "Social proof that " + "resonates" +

+

+ "Collect, manage, and display testimonials. One platform for all your social proof." +

+ +
+ +
+
+

"Open source. MIT licensed."

+
+
+
+ } +} diff --git a/crates/dashboard/src/pages/login.rs b/crates/dashboard/src/pages/login.rs new file mode 100644 index 0000000..a4fddef --- /dev/null +++ b/crates/dashboard/src/pages/login.rs @@ -0,0 +1,110 @@ +use gloo_storage::Storage; +use leptos::prelude::*; +use serde::{Deserialize, Serialize}; + +use crate::api; + +#[derive(Serialize)] +struct LoginRequest { + email: String, + password: String, +} + +#[derive(Deserialize)] +struct LoginResponse { + token: String, +} + +#[component] +pub fn LoginPage() -> impl IntoView { + let (email, set_email) = signal(String::new()); + let (password, set_password) = signal(String::new()); + let (error, set_error) = signal(Option::::None); + let (loading, set_loading) = signal(false); + + let on_submit = move |ev: web_sys::SubmitEvent| { + ev.prevent_default(); + set_error.set(None); + set_loading.set(true); + + let email_val = email.get_untracked(); + let password_val = password.get_untracked(); + + leptos::task::spawn_local(async move { + let body = LoginRequest { + email: email_val, + password: password_val, + }; + + match api::post::("/api/v1/auth/login", &body).await { + Ok(resp) => { + let _ = gloo_storage::LocalStorage::raw().set_item("token", &resp.token); + if let Some(window) = web_sys::window() { + let _ = window.location().set_href("/dashboard"); + } + } + Err(e) => { + set_error.set(Some(e)); + set_loading.set(false); + } + } + }); + }; + + view! { +
+
+
+

"Reeverb"

+

"Sign in to your account"

+
+ + {move || error.get().map(|e| view! { +
{e}
+ })} + +
+
+ + +
+ +
+ + +
+ + +
+ +

+ "Don't have an account? " + "Sign up" +

+
+
+ } +} diff --git a/crates/dashboard/src/pages/mod.rs b/crates/dashboard/src/pages/mod.rs new file mode 100644 index 0000000..dbde449 --- /dev/null +++ b/crates/dashboard/src/pages/mod.rs @@ -0,0 +1,24 @@ +mod dashboard; +mod home; +mod login; +mod signup; + +pub use dashboard::DashboardPage; +pub use home::HomePage; +pub use login::LoginPage; +pub use signup::SignupPage; + +use leptos::prelude::*; + +#[component] +pub fn NotFoundPage() -> impl IntoView { + view! { +
+
+

"404"

+

"Page not found"

+ "Back to dashboard" +
+
+ } +} diff --git a/crates/dashboard/src/pages/signup.rs b/crates/dashboard/src/pages/signup.rs new file mode 100644 index 0000000..7e79b9b --- /dev/null +++ b/crates/dashboard/src/pages/signup.rs @@ -0,0 +1,130 @@ +use gloo_storage::Storage; +use leptos::prelude::*; +use serde::{Deserialize, Serialize}; + +use crate::api; + +#[derive(Serialize)] +struct RegisterRequest { + email: String, + password: String, + name: Option, +} + +#[derive(Deserialize)] +struct AuthResponse { + token: String, +} + +#[component] +pub fn SignupPage() -> impl IntoView { + let (name, set_name) = signal(String::new()); + let (email, set_email) = signal(String::new()); + let (password, set_password) = signal(String::new()); + let (error, set_error) = signal(Option::::None); + let (loading, set_loading) = signal(false); + + let on_submit = move |ev: web_sys::SubmitEvent| { + ev.prevent_default(); + set_error.set(None); + set_loading.set(true); + + let name_val = name.get_untracked(); + let email_val = email.get_untracked(); + let password_val = password.get_untracked(); + + leptos::task::spawn_local(async move { + let body = RegisterRequest { + email: email_val, + password: password_val, + name: if name_val.is_empty() { + None + } else { + Some(name_val) + }, + }; + + match api::post::("/api/v1/auth/register", &body).await { + Ok(resp) => { + let _ = gloo_storage::LocalStorage::raw().set_item("token", &resp.token); + if let Some(window) = web_sys::window() { + let _ = window.location().set_href("/dashboard"); + } + } + Err(e) => { + set_error.set(Some(e)); + set_loading.set(false); + } + } + }); + }; + + view! { +
+
+
+

"Reeverb"

+

"Create your account"

+
+ + {move || error.get().map(|e| view! { +
{e}
+ })} + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +

+ "Already have an account? " + "Sign in" +

+
+
+ } +} diff --git a/crates/dashboard/style.css b/crates/dashboard/style.css new file mode 100644 index 0000000..28dfd6c --- /dev/null +++ b/crates/dashboard/style.css @@ -0,0 +1,115 @@ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + --color-bg: #fafafa; + --color-surface: #ffffff; + --color-text: #1a1a1a; + --color-text-secondary: #6b7280; + --color-primary: #ef4444; + --color-primary-hover: #dc2626; + --color-border: #e5e7eb; + --color-error: #dc2626; + --color-error-bg: #fef2f2; + --radius: 8px; + --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + --font-mono: "JetBrains Mono", "Fira Code", monospace; +} + +html { + font-family: var(--font-sans); + font-size: 16px; + line-height: 1.6; + color: var(--color-text); + background-color: var(--color-bg); + -webkit-font-smoothing: antialiased; +} + +body { + min-height: 100vh; +} + +a { + color: var(--color-primary); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +input { + font-family: inherit; + font-size: inherit; +} + +button { + font-family: inherit; + font-size: inherit; + cursor: pointer; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 24px; +} + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 10px 20px; + border-radius: var(--radius); + font-weight: 500; + border: none; + transition: background-color 0.15s ease; +} + +.btn-primary { + background-color: var(--color-primary); + color: white; +} + +.btn-primary:hover { + background-color: var(--color-primary-hover); +} + +.btn-primary:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.input { + width: 100%; + padding: 10px 14px; + border: 1px solid var(--color-border); + border-radius: var(--radius); + outline: none; + transition: border-color 0.15s ease; + background: var(--color-surface); +} + +.input:focus { + border-color: var(--color-primary); +} + +.card { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius); + padding: 24px; +} + +.error-message { + background: var(--color-error-bg); + color: var(--color-error); + padding: 12px 16px; + border-radius: var(--radius); + font-size: 0.875rem; +} diff --git a/src/lib.rs b/src/lib.rs index 0377820..679f415 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod api; pub mod db; +pub mod static_files; diff --git a/src/main.rs b/src/main.rs index b376a5a..a9b2adf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use reeverb::api::v1::auth; use reeverb::api::v1::projects; use reeverb::api::v1::tags; use reeverb::api::v1::testimonials; +use reeverb::static_files::DashboardMiddleware; #[derive(Clone, Config)] struct AppConfig { @@ -66,6 +67,7 @@ async fn main() -> std::io::Result<()> { let addr = format!("{}:{}", config.host, config.port); app.openapi("Reeverb API", env!("CARGO_PKG_VERSION")) + .middleware(DashboardMiddleware) .middleware(RequestLogMiddleware::new()) .state(auth_config) .with_database(DatabaseConfig::new(&config.database_url)) diff --git a/src/static_files.rs b/src/static_files.rs new file mode 100644 index 0000000..eabe401 --- /dev/null +++ b/src/static_files.rs @@ -0,0 +1,86 @@ +use rapina::context::RequestContext; +use rapina::http::{Response, StatusCode}; +use rapina::hyper::Request; +use rapina::hyper::body::Incoming; +use rapina::middleware::{BoxFuture, Middleware, Next}; +use rapina::response::BoxBody; + +use rust_embed::Embed; + +#[derive(Embed)] +#[folder = "dist/"] +struct DashboardAssets; + +fn mime_for(path: &str) -> &'static str { + match path.rsplit('.').next() { + Some("html") => "text/html; charset=utf-8", + Some("js") => "application/javascript", + Some("wasm") => "application/wasm", + Some("css") => "text/css; charset=utf-8", + Some("json") => "application/json", + Some("png") => "image/png", + Some("svg") => "image/svg+xml", + Some("ico") => "image/x-icon", + _ => "application/octet-stream", + } +} + +fn cache_header(path: &str) -> &'static str { + if path.ends_with(".wasm") || path.ends_with(".js") || path.ends_with(".css") { + "public, max-age=31536000, immutable" + } else { + "no-cache" + } +} + +fn serve_file(path: &str) -> Response { + use rapina::response::IntoResponse; + + let path = path.trim_start_matches('/'); + + if let Some(file) = DashboardAssets::get(path) { + return Response::builder() + .status(StatusCode::OK) + .header("content-type", mime_for(path)) + .header("cache-control", cache_header(path)) + .body(BoxBody::new(file.data.into_owned().into())) + .unwrap(); + } + + // SPA fallback: serve index.html + if let Some(index) = DashboardAssets::get("index.html") { + return Response::builder() + .status(StatusCode::OK) + .header("content-type", "text/html; charset=utf-8") + .header("cache-control", "no-cache") + .body(BoxBody::new(index.data.into_owned().into())) + .unwrap(); + } + + StatusCode::NOT_FOUND.into_response() +} + +fn is_api_path(path: &str) -> bool { + path.starts_with("/api/") || path == "/health" || path.starts_with("/__rapina") +} + +pub struct DashboardMiddleware; + +impl Middleware for DashboardMiddleware { + fn handle<'a>( + &'a self, + req: Request, + _ctx: &'a RequestContext, + next: Next<'a>, + ) -> BoxFuture<'a, Response> { + Box::pin(async move { + let path = req.uri().path().to_string(); + + if is_api_path(&path) { + return next.run(req).await; + } + + serve_file(&path) + }) + } +} From 6c61d54c3294832e570966d0b709ee78a288cab1 Mon Sep 17 00:00:00 2001 From: arferreira Date: Wed, 18 Feb 2026 22:10:20 -0500 Subject: [PATCH 2/2] Create dist/ in CI for rust-embed --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 893f73e..3a441b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ jobs: with: toolchain: "1.88.0" - uses: Swatinem/rust-cache@v2 + - run: mkdir -p dist - run: cargo check fmt: @@ -43,6 +44,7 @@ jobs: toolchain: "1.88.0" components: clippy - uses: Swatinem/rust-cache@v2 + - run: mkdir -p dist - run: cargo clippy -- -D warnings test: @@ -71,6 +73,7 @@ jobs: with: toolchain: "1.88.0" - uses: Swatinem/rust-cache@v2 + - run: mkdir -p dist - run: cargo test api: @@ -101,6 +104,7 @@ jobs: with: toolchain: "1.88.0" - uses: Swatinem/rust-cache@v2 + - run: mkdir -p dist - run: cargo install rapina-cli --locked - run: cargo build --release - name: Start server