diff --git a/Cargo.lock b/Cargo.lock index 6b36474..ba53279 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -306,6 +306,15 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "async_cell" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447ab28afbb345f5408b120702a44e5529ebf90b1796ec76e9528df8e288e6c2" +dependencies = [ + "loom", +] + [[package]] name = "asynchronous-codec" version = "0.6.2" @@ -2160,6 +2169,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "generator" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.61.3", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2631,9 +2654,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -2687,21 +2710,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2908,6 +2938,16 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "itertools" version = "0.12.1" @@ -2985,10 +3025,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -3075,9 +3116,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" @@ -3207,6 +3248,19 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "mac" version = "0.1.1" @@ -3489,6 +3543,7 @@ dependencies = [ name = "mumble-web2-gui" version = "0.1.0" dependencies = [ + "async_cell", "asynchronous-codec", "base64 0.22.1", "byteorder", @@ -3514,6 +3569,7 @@ dependencies = [ "once_cell", "opus", "ordermap", + "reqwest", "rfd 0.15.2", "serde", "serde-wasm-bindgen 0.6.5", @@ -3750,7 +3806,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 2.0.87", @@ -4711,9 +4767,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64 0.22.1", "bytes", @@ -4728,31 +4784,29 @@ dependencies = [ "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", "mime_guess", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", - "system-configuration", + "sync_wrapper", "tokio", "tokio-native-tls", "tokio-util", + "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "windows-registry", ] [[package]] @@ -4982,6 +5036,8 @@ version = "0.74.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a941a79d92a35187603ddb3ee6010649e687341a943666224642bf9c1c6b4f8" dependencies = [ + "salvo-cors", + "salvo-craft", "salvo-jwt-auth", "salvo-proxy", "salvo-serve-static", @@ -4989,6 +5045,39 @@ dependencies = [ "salvo_extra", ] +[[package]] +name = "salvo-cors" +version = "0.74.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ad895197577cca6e7b35f5ec0810a84a42aa229c501985153e3b7cdce27136" +dependencies = [ + "bytes", + "salvo_core", + "tracing", +] + +[[package]] +name = "salvo-craft" +version = "0.74.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef9718312065c4762a3a8c697e1937fe5ffee615f2cf4e7986414de82088d1e0" +dependencies = [ + "salvo-craft-macros", +] + +[[package]] +name = "salvo-craft-macros" +version = "0.74.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fa3960c9ae2af644d0561a1ff24e43bcd4b7e32175584c663196868406963af" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "regex", + "syn 2.0.87", +] + [[package]] name = "salvo-http3" version = "0.3.1" @@ -5115,7 +5204,7 @@ dependencies = [ "serde", "serde-xml-rs", "serde_json", - "sync_wrapper 1.0.1", + "sync_wrapper", "tempfile", "thiserror 2.0.3", "tokio", @@ -5152,7 +5241,7 @@ version = "0.74.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddf066ba183548285f7dc8db26a35767e94ac2fc95f397913c98d5b63b45bed4" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "regex", @@ -5606,9 +5695,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -5718,12 +5807,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.1" @@ -6106,14 +6189,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 0.1.2", + "sync_wrapper", "tokio", "tokio-util", "tower-layer", @@ -6121,6 +6204,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.6.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -6707,8 +6808,8 @@ dependencies = [ "webview2-com-sys", "windows 0.58.0", "windows-core 0.58.0", - "windows-implement", - "windows-interface", + "windows-implement 0.58.0", + "windows-interface 0.58.0", ] [[package]] @@ -6796,6 +6897,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.54.0" @@ -6812,13 +6935,37 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.58.0", + "windows-interface 0.58.0", "windows-result 0.2.0", - "windows-strings", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -6830,6 +6977,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -6842,14 +7000,41 @@ dependencies = [ ] [[package]] -name = "windows-registry" +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-numerics" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-result 0.2.0", - "windows-strings", - "windows-targets 0.52.6", + "windows-core 0.61.2", + "windows-link", +] + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -6870,6 +7055,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-strings" version = "0.1.0" @@ -6880,6 +7074,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -6962,6 +7165,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-version" version = "0.1.1" diff --git a/docker/Caddyfile b/docker/Caddyfile new file mode 100644 index 0000000..c165096 --- /dev/null +++ b/docker/Caddyfile @@ -0,0 +1,10 @@ +localhost:64444 { + tls internal + + # Proxy /config path to mumble-web2-proxy + reverse_proxy /config http://127.0.0.1:4400 + + # Proxy root path to dx-serve + reverse_proxy http://127.0.0.1:8080 + +} diff --git a/docker/dioxus.Dockerfile b/docker/dioxus.Dockerfile new file mode 100644 index 0000000..dfc4158 --- /dev/null +++ b/docker/dioxus.Dockerfile @@ -0,0 +1,22 @@ +FROM rust:1-bookworm AS base + +# Install cargo-binstall for faster CLI installation +#RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash + +RUN apt-get update && apt-get install -y screen + +# Install dioxus-cli version 0.6.3 specifically +RUN cargo install dioxus-cli --version 0.6.3 + +# Set working directory +WORKDIR /app + +# Add wasm32 target for web development +RUN rustup target add wasm32-unknown-unknown + +# Set environment variables +ENV PATH="/root/.cargo/bin:$PATH" + +# Default command (can be overridden in docker-compose) +CMD ["dx", "--help"] + diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 0000000..9b2fe81 --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,60 @@ +services: + caddy: + image: caddy:latest + ports: + - "64444:64444/tcp" + - "64444:64444/udp" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile + #- caddy_data:/data + #- caddy_config:/config + depends_on: + #- dx-serve + - mumble-web2-proxy + network_mode: host + + #dx-serve: + # build: + # dockerfile: ./dioxus.Dockerfile + # working_dir: /app + # volumes: + # - ..:/app + # environment: + # - MUMBLE_WEB2_GUI_CONFIG_URL=https://localhost:64444/config + # stdin_open: true + # tty: true + # command: > + # bash -c " + # screen -dmS serve bash -c 'dx serve -p mumble-web2-gui --platform web' && + # tail -f /dev/null + # " + # networks: + # - app-network + + mumble-web2-proxy: + image: rust:latest + working_dir: /app + volumes: + - ..:/app + - ./proxy-config.toml:/app/config.toml + ports: + - "4433:4433/tcp" + - "4433:4433/udp" + command: ["cargo", "run", "-p", "mumble-web2-proxy"] + network_mode: host + + mumble-server: + image: mumblevoip/mumble-server:latest + ports: + - "64738:64738/tcp" + - "64738:64738/udp" + environment: + - MUMBLE_CONFIG_WELCOMETEXT=Welcome to the Mumble server + network_mode: host +#volumes: +# caddy_data: +# caddy_config: +# +#networks: +# app-network: +# driver: bridge diff --git a/docker/proxy-config.toml b/docker/proxy-config.toml new file mode 100644 index 0000000..db51597 --- /dev/null +++ b/docker/proxy-config.toml @@ -0,0 +1,12 @@ +https_listen_address = "127.0.0.1:4433" +http_listen_address = "127.0.0.1:4400" +#cert_path = "./cert.pem" +#key_path = "./key.pem" +#mumble_server_url = "voip.ohea.xyz:64738" +mumble_server_url = "127.0.0.1:64738" +#gui_path = "./target/dx/mumble-web2-gui/release/web/public" +gui_path = "./target/dx/mumble-web2-gui/debug/web/public" + +[gui] +force_proxy = true +proxy_url = "https://127.0.0.1:4433/proxy" diff --git a/gui/Cargo.toml b/gui/Cargo.toml index 652df26..39d0434 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -95,6 +95,8 @@ lol_html = "2.2.0" rfd = "0.15.2" base64 = "0.22" mime_guess = "2.0.5" +async_cell = "0.2.3" +reqwest = { version = "0.12.22", features = ["json"] } [features] web = [ diff --git a/gui/src/app.rs b/gui/src/app.rs index 9c149eb..b36614e 100644 --- a/gui/src/app.rs +++ b/gui/src/app.rs @@ -3,9 +3,11 @@ use base64::{display::Base64Display, prelude::BASE64_URL_SAFE}; use dioxus::prelude::*; use mime_guess::Mime; +use mumble_web2_common::GuiConfig; use ordermap::OrderSet; use sir::{css, global_css}; use std::collections::HashMap; +use tracing::error; use crate::{imp, CONFIG}; @@ -279,6 +281,13 @@ pub fn ChatView() -> Element { let server = STATE.server.read(); let mut draft = use_signal(|| "".to_string()); + let chat_panel = css!( + " + display: flex; + flex-direction: column; + " + ); + let chat_history = css!( " overflow-y: auto; @@ -296,18 +305,44 @@ pub fn ChatView() -> Element { " ); + let chat_box_wrapper = css!( + " + padding: 16px; + border-top: solid var(--line-color) var(--line-width); + " + ); + let chat_box = css!( " display: flex; flex-direction: row; - padding: 16px; - gap: 8px; - border-top: solid var(--line-color) var(--line-width); + gap: 16px; + + background-color: var(--light-bg-color); + + padding-top: 16px; + padding-bottom: 16px; + + padding-left: 8px; + padding-right: 16px; + + border-radius: 8px; input { + color: white; + background-color: var(--light-bg-color); + + font-size: larger; + flex-grow: 1; - padding: 8px; + + border: none; } + + input:focus { + outline: none; + } + " ); @@ -322,49 +357,88 @@ pub fn ChatView() -> Element { rsx!( div { - class: "{chat_history}", - for chat in server.chat.iter() { - div { - class: "{chat_message}", - if let Some(sender) = chat.sender.and_then(|u| server.users.get(&u)) { - UserPill { - name: sender.name.clone(), - icon: UserIcon::None, - isself: false, + class: "{chat_panel}", + div { + class: "{chat_history}", + for chat in server.chat.iter() { + div { + class: "{chat_message}", + if let Some(sender) = chat.sender.and_then(|u| server.users.get(&u)) { + UserPill { + name: sender.name.clone(), + icon: UserIcon::None, + isself: false, + } + } + span { + dangerous_inner_html: "{chat.dangerous_html}", } } - span { - dangerous_inner_html: "{chat.dangerous_html}", - } } } - } - div { - class: "{chat_box}", - input { - placeholder: "say something", - value: "{draft.read()}", - oninput: move |evt| draft.set(evt.value().clone()), - onkeypress: move |evt: Event| { - if evt.code() == Code::Enter && evt.modifiers().is_empty() { - do_send(); + div { + class: "{chat_box_wrapper}", + div { + class: "{chat_box}", + input { + placeholder: "say something", + value: "{draft.read()}", + oninput: move |evt| draft.set(evt.value().clone()), + onkeypress: move |evt: Event| { + if evt.code() == Code::Enter && evt.modifiers().is_empty() { + do_send(); + } + } + } + div { + span { + onclick: move |_| pick_and_send_file(&net), + class: "material-symbols-outlined", + style: "color: rgba(255, 255, 255, 0.5); font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48; vertical-align: middle; font-size: 35px; user-select: none;", + "attach_file", + } + } + div { + span { + onclick: move |_| do_send(), + class: "material-symbols-outlined", + style: "color: rgba(255, 255, 255, 0.5); font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48; vertical-align: middle; font-size: 35px; user-select: none;", + "send", + } } } - } - button { - onclick: move |_| pick_and_send_file(&net), - "File" - } - button { - onclick: move |_| do_send(), - "Send" + //button { + // onclick: move |_| do_send(), + // "Send" + //} } } ) } +// true => rsx!(span { class: "material-symbols-outlined", style: "{button_style}", "mic_off"}), +//Connecting => rsx! { +// div { +// class: "{connecting_status}", +// span { +// class: "material-symbols-outlined", +// style: "vertical-align: middle; font-size: 30px;", +// "signal_cellular_alt_2_bar" +// } +// span { +// style: "width: 5px; display: inline-block;" +// } +// span { +// style: "vertical-align: middle; font-size: 30px;", +// "Connecting" +// } +// } +//}, + #[component] pub fn ControlView() -> Element { + let config_future = use_resource(|| CONFIG.get()); + let net: Coroutine = use_coroutine_handle(); let status = &STATE.status; let server = STATE.server.read(); @@ -382,7 +456,12 @@ pub fn ControlView() -> Element { }; let current_channel_name = server.channels[&channel].name.clone(); - let Some(proxy_url) = &CONFIG.proxy_url else { + + let Some(proxy_url) = config_future + .read_unchecked() + .as_ref() + .and_then(|gui_config| gui_config.proxy_url.clone()) + else { return rsx!(); }; @@ -675,7 +754,7 @@ pub fn ServerView() -> Element { let chat_box = css!( " display: flex; - flex-direction: column; + flex-direction: row; grid-area: chat; border-left: solid var(--line-color) var(--line-width); @@ -727,7 +806,15 @@ pub fn ServerView() -> Element { #[component] pub fn LoginView() -> Element { let net: Coroutine = use_coroutine_handle(); - let default_address = CONFIG.proxy_url.as_deref().unwrap_or(""); + + let config_future = use_resource(|| CONFIG.get()); + + let default_address = &*config_future + .read_unchecked() + .as_ref() + .and_then(|gui_config| gui_config.proxy_url.clone()) + .unwrap_or("".to_string()); + let mut address = use_signal(|| default_address.to_string()); let previous_username = imp::load_username(); @@ -840,6 +927,11 @@ pub fn LoginView() -> Element { pub fn app() -> Element { use_coroutine(|rx: UnboundedReceiver| super::network_entrypoint(rx)); + use_future(|| async move { + if let Err(err) = imp::load_config().await { + error!("{}", err) + } + }); global_css!( " diff --git a/gui/src/imp/desktop.rs b/gui/src/imp/desktop.rs index e1ac2ca..9ed2beb 100644 --- a/gui/src/imp/desktop.rs +++ b/gui/src/imp/desktop.rs @@ -224,8 +224,10 @@ pub fn load_username() -> Option { return None; } -pub fn load_config() -> Option { - None +pub async fn load_config() -> color_eyre::Result { + color_eyre::eyre::bail!( + "there is no config on desktop because desktops cannot be configured as they are tables" + ) } pub fn init_logging() { diff --git a/gui/src/imp/web.rs b/gui/src/imp/web.rs index b70e7e9..4267560 100644 --- a/gui/src/imp/web.rs +++ b/gui/src/imp/web.rs @@ -320,7 +320,8 @@ pub async fn network_connect( ) .ey()?; - if let Some(server_hash) = &CONFIG.cert_hash { + if let Some(server_hash) = &CONFIG.try_get().and_then(|cfg| cfg.cert_hash) { + error!("{:?}", server_hash); let hash = web_sys::js_sys::Uint8Array::from(server_hash.as_slice()); web_sys::js_sys::Reflect::set(&object, &"value".into(), &hash).ey()?; } @@ -394,8 +395,19 @@ fn load_config_from_env() -> Option { serde_json::from_str(option_env!("MUMBLE_WEB2_GUI_CONFIG")?).ok()? } -pub fn load_config() -> Option { - load_config_from_window().or_else(load_config_from_env) +pub async fn load_config() -> color_eyre::Result<()> { + let config_url = option_env!("MUMBLE_WEB2_GUI_CONFIG_URL").ok_or(eyre!("foo"))?; + + let config = reqwest::get(config_url) + .await + .unwrap() + .json::() + .await + .unwrap(); + + crate::CONFIG.set(config); + + Ok(()) } pub fn init_logging() { diff --git a/gui/src/lib.rs b/gui/src/lib.rs index 734d267..ae0f7d3 100644 --- a/gui/src/lib.rs +++ b/gui/src/lib.rs @@ -33,7 +33,8 @@ pub mod app; pub mod imp; mod msghtml; -pub static CONFIG: Lazy = Lazy::new(|| imp::load_config().unwrap_or_default()); +//pub static CONFIG: Lazy = Lazy::new(|| imp::load_config().unwrap_or_default()); +pub static CONFIG: async_cell::sync::AsyncCell = async_cell::sync::AsyncCell::new(); pub async fn network_entrypoint(mut event_rx: UnboundedReceiver) { loop { diff --git a/proxy/Cargo.toml b/proxy/Cargo.toml index bad728f..060674f 100644 --- a/proxy/Cargo.toml +++ b/proxy/Cargo.toml @@ -13,7 +13,15 @@ toml = "0.8.19" tracing = { version = "0.1.40", features = ["async-await"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } mumble-web2-common = { workspace = true } -salvo = { version = "0.74.2", features = ["quinn", "eyre", "rustls", "serve-static", "logging"] } +salvo = { version = "0.74.2", features = [ + "quinn", + "eyre", + "rustls", + "serve-static", + "logging", + "craft", + "cors", +] } once_cell = "1.20.2" rustls = { version = "^0.23", features = ["aws_lc_rs"] } rcgen = "0.13.2" diff --git a/proxy/src/main.rs b/proxy/src/main.rs index dbb6205..91b75df 100644 --- a/proxy/src/main.rs +++ b/proxy/src/main.rs @@ -4,10 +4,11 @@ use mumble_web2_common::GuiConfig; use once_cell::sync::OnceCell; use rcgen::date_time_ymd; use salvo::conn::rustls::{Keycert, RustlsConfig}; +use salvo::cors::{AllowOrigin, Cors}; use salvo::logging::Logger; use salvo::prelude::*; use salvo::proto::quic::BidiStream; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; @@ -106,13 +107,6 @@ async fn main() -> Result<()> { info!("config\n{}", toml::to_string_pretty(&config.gui)?); info!("gui config\n{}", serde_json::to_string_pretty(&config.gui)?); - // Server routing - let router = Router::new() - .get(serve_gui_index_html) - .push(Router::with_path("/proxy").goal(connect_proxy)) - .push(Router::with_path("/<*+rest>").get(StaticDir::new(config.gui_path.clone()))) - .hoop(Logger::new()); - rustls::crypto::aws_lc_rs::default_provider() .install_default() .map_err(|e| anyhow!("could not install crypto provider {e:?}"))?; @@ -155,6 +149,26 @@ async fn main() -> Result<()> { }; let rustls_config = RustlsConfig::new(Keycert::new().cert(cert.as_slice()).key(key.as_slice())); + let config_craft = ConfigCraft { + client_config: MumbleClientConfig { + force_proxy: true, + proxy_url: "https://localhost:4433".to_string(), + cert_hash: config.gui.lock().unwrap().cert_hash.clone().unwrap(), + }, + }; + + // Server routing + let router = Router::new() + .get(serve_gui_index_html) + .push(Router::with_path("/proxy").goal(connect_proxy)) + .push(Router::with_path("/config").get(config_craft.get_config())) + .push(Router::with_path("/<*+rest>").get(StaticDir::new(config.gui_path.clone()))) + .hoop(Logger::new()); + + let cors = Cors::new().allow_origin(AllowOrigin::any()).into_handler(); + + let service = Service::new(router).hoop(cors); + // Create http listeners let http_listener = config.http_listen_address.map(TcpListener::new); let https_listener = @@ -165,17 +179,37 @@ async fn main() -> Result<()> { match (http_listener, https_listener, http3_listener) { (Some(a), b, c) => { let accepter = a.join(b).join(c).bind().await; - Server::new(accepter).serve(router).await; + Server::new(accepter).serve(service).await; } (None, b, c) => { let accepter = b.join(c).bind().await; - Server::new(accepter).serve(router).await; + Server::new(accepter).serve(service).await; } } Ok(()) } +#[derive(Serialize, Clone)] +struct MumbleClientConfig { + force_proxy: bool, + proxy_url: String, + cert_hash: Vec, +} + +#[derive(Clone)] +pub struct ConfigCraft { + client_config: MumbleClientConfig, +} + +#[craft] +impl ConfigCraft { + #[craft(handler)] + async fn get_config(&self) -> Json { + Json(self.client_config.clone()) + } +} + #[handler] #[instrument] async fn connect_proxy(req: &mut Request, res: &mut Response) {