From cbc5639f99e7e27318bb7931f556407311b752ee Mon Sep 17 00:00:00 2001 From: Ultradesu Date: Tue, 17 Mar 2026 15:17:30 +0000 Subject: [PATCH] Fixed UI --- Cargo.lock | 10 +++--- furumi-client-core/Cargo.toml | 2 +- furumi-common/Cargo.toml | 2 +- furumi-mount-linux/Cargo.toml | 2 +- furumi-mount-macos/Cargo.toml | 2 +- furumi-server/Cargo.toml | 2 +- furumi-server/src/web/auth.rs | 25 +++++++------ furumi-server/src/web/mod.rs | 10 ++++-- furumi-server/src/web/player.html | 59 +++++++++++++++++++++++++++++-- 9 files changed, 90 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9830a12..45f1501 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,7 +910,7 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "furumi-client-core" -version = "0.3.1" +version = "0.3.2" dependencies = [ "anyhow", "async-trait", @@ -932,7 +932,7 @@ dependencies = [ [[package]] name = "furumi-common" -version = "0.3.1" +version = "0.3.2" dependencies = [ "prost", "protobuf-src", @@ -942,7 +942,7 @@ dependencies = [ [[package]] name = "furumi-mount-linux" -version = "0.3.1" +version = "0.3.2" dependencies = [ "anyhow", "clap", @@ -959,7 +959,7 @@ dependencies = [ [[package]] name = "furumi-mount-macos" -version = "0.3.1" +version = "0.3.2" dependencies = [ "anyhow", "async-trait", @@ -977,7 +977,7 @@ dependencies = [ [[package]] name = "furumi-server" -version = "0.3.1" +version = "0.3.2" dependencies = [ "anyhow", "async-stream", diff --git a/furumi-client-core/Cargo.toml b/furumi-client-core/Cargo.toml index 41d736b..18ca4d2 100644 --- a/furumi-client-core/Cargo.toml +++ b/furumi-client-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "furumi-client-core" -version = "0.3.2" +version = "0.3.3" edition = "2024" [dependencies] diff --git a/furumi-common/Cargo.toml b/furumi-common/Cargo.toml index 7f02183..4f2b3ed 100644 --- a/furumi-common/Cargo.toml +++ b/furumi-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "furumi-common" -version = "0.3.2" +version = "0.3.3" edition = "2024" [dependencies] diff --git a/furumi-mount-linux/Cargo.toml b/furumi-mount-linux/Cargo.toml index d894ba0..41368f0 100644 --- a/furumi-mount-linux/Cargo.toml +++ b/furumi-mount-linux/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "furumi-mount-linux" -version = "0.3.2" +version = "0.3.3" edition = "2024" [dependencies] diff --git a/furumi-mount-macos/Cargo.toml b/furumi-mount-macos/Cargo.toml index bd27768..d5f3fe2 100644 --- a/furumi-mount-macos/Cargo.toml +++ b/furumi-mount-macos/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "furumi-mount-macos" -version = "0.3.2" +version = "0.3.3" edition = "2024" [dependencies] diff --git a/furumi-server/Cargo.toml b/furumi-server/Cargo.toml index 8b89c27..38f5546 100644 --- a/furumi-server/Cargo.toml +++ b/furumi-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "furumi-server" -version = "0.3.2" +version = "0.3.3" edition = "2024" [dependencies] diff --git a/furumi-server/src/web/auth.rs b/furumi-server/src/web/auth.rs index fce7add..626c309 100644 --- a/furumi-server/src/web/auth.rs +++ b/furumi-server/src/web/auth.rs @@ -31,14 +31,14 @@ pub fn token_hash(token: &str) -> String { format!("{:x}", h.finalize()) } -/// axum middleware: if token is configured, requires a valid session cookie. pub async fn require_auth( State(state): State, - req: Request, + mut req: Request, next: Next, ) -> Response { // Auth disabled when token is empty if state.token.is_empty() { + req.extensions_mut().insert(super::AuthUserInfo("Unauthenticated".to_string())); return next.run(req).await; } @@ -49,23 +49,24 @@ pub async fn require_auth( .unwrap_or(""); let expected = token_hash(&state.token); - let mut authed = false; + let mut authed_user = None; for c in cookies.split(';') { let c = c.trim(); if let Some(val) = c.strip_prefix(&format!("{}=", SESSION_COOKIE)) { if val == expected { - authed = true; + authed_user = Some("Master Token".to_string()); break; } else if let Some(oidc) = &state.oidc { - if verify_sso_cookie(&oidc.session_secret, val) { - authed = true; + if let Some(user) = verify_sso_cookie(&oidc.session_secret, val) { + authed_user = Some(user); break; } } } } - if authed { + if let Some(user) = authed_user { + req.extensions_mut().insert(super::AuthUserInfo(user)); next.run(req).await } else { let uri = req.uri().path(); @@ -86,10 +87,10 @@ pub fn generate_sso_cookie(secret: &[u8], user_id: &str) -> String { format!("sso:{}:{}", user_id, sig) } -pub fn verify_sso_cookie(secret: &[u8], cookie_val: &str) -> bool { +pub fn verify_sso_cookie(secret: &[u8], cookie_val: &str) -> Option { let parts: Vec<&str> = cookie_val.split(':').collect(); if parts.len() != 3 || parts[0] != "sso" { - return false; + return None; } let user_id = parts[1]; let sig = parts[2]; @@ -98,7 +99,11 @@ pub fn verify_sso_cookie(secret: &[u8], cookie_val: &str) -> bool { mac.update(user_id.as_bytes()); let expected_sig = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(mac.finalize().into_bytes()); - sig == expected_sig + if sig == expected_sig { + Some(user_id.to_string()) + } else { + None + } } /// GET /login — show login form. diff --git a/furumi-server/src/web/mod.rs b/furumi-server/src/web/mod.rs index 523541f..d2f4a2c 100644 --- a/furumi-server/src/web/mod.rs +++ b/furumi-server/src/web/mod.rs @@ -53,6 +53,12 @@ pub fn build_router(root: PathBuf, token: String, oidc: Option>) .with_state(state) } -async fn player_html() -> axum::response::Html<&'static str> { - axum::response::Html(include_str!("player.html")) +#[derive(Clone)] +pub struct AuthUserInfo(pub String); + +async fn player_html( + axum::extract::Extension(user_info): axum::extract::Extension, +) -> axum::response::Html { + let html = include_str!("player.html").replace("", &user_info.0); + axum::response::Html(html) } diff --git a/furumi-server/src/web/player.html b/furumi-server/src/web/player.html index c53df79..7a56701 100644 --- a/furumi-server/src/web/player.html +++ b/furumi-server/src/web/player.html @@ -58,14 +58,30 @@ body { padding: 0.3rem 0.75rem; cursor: pointer; transition: all 0.2s; } .btn-logout:hover { border-color: var(--danger); color: var(--danger); } +.btn-menu { + display: none; + background: none; border: none; color: var(--text); + font-size: 1.2rem; cursor: pointer; padding: 0.1rem 0.5rem; + margin-right: 0.2rem; border-radius: 4px; transition: all 0.2s; +} +.btn-menu:hover { background: var(--bg-hover); } /* ─── Main layout ─── */ .main { display: flex; flex: 1; overflow: hidden; + position: relative; } +/* Mobile Overlay */ +.sidebar-overlay { + display: none; + position: absolute; top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.6); z-index: 20; +} +.sidebar-overlay.show { display: block; } + /* ─── File browser ─── */ .sidebar { width: 280px; @@ -322,6 +338,30 @@ body { transition: all 0.25s; pointer-events: none; z-index: 100; } .toast.show { opacity: 1; transform: translateY(0); } + +/* ─── Responsive (Mobile) ─── */ +@media (max-width: 768px) { + .btn-menu { display: inline-block; } + .header { padding: 0.75rem 1rem; } + .sidebar { + position: absolute; + top: 0; bottom: 0; left: -100%; + width: 85%; max-width: 320px; + z-index: 30; + transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 4px 0 20px rgba(0,0,0,0.6); + } + .sidebar.open { left: 0; } + .queue-panel { flex: 1; min-width: 0; } + .player-bar { + grid-template-columns: 1fr; + grid-template-rows: auto auto auto; + gap: 0.75rem; + padding: 0.75rem 1rem; + } + .np-info { display: grid; grid-template-columns: auto 1fr; text-align: left; } + .volume-row { display: none; /* Hide volume on mobile to save space, rely on hardware buttons */ } +} @@ -329,19 +369,27 @@ body {
- +
+ + 👤 + + + +
+ -