aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-03-18 17:56:11 +0100
committermetamuffin <metamuffin@disroot.org>2026-03-18 17:56:11 +0100
commitbef95130a2bdabd196f595a1129ac07bd3f5054b (patch)
tree705b284ac4a0c2d6cc2ed893d8c992a507073f3c
parent53b0268eaa850d0a7250c94373d76906a7b28250 (diff)
downloadjellything-bef95130a2bdabd196f595a1129ac07bd3f5054b.tar
jellything-bef95130a2bdabd196f595a1129ac07bd3f5054b.tar.bz2
jellything-bef95130a2bdabd196f595a1129ac07bd3f5054b.tar.zst
send page title with header when jst
-rw-r--r--server/src/request_info.rs14
-rw-r--r--server/src/responders/mod.rs9
-rw-r--r--server/src/responders/ui_page.rs21
-rw-r--r--server/src/routes/account/mod.rs9
-rw-r--r--server/src/routes/account/settings.rs8
-rw-r--r--server/src/routes/admin/import.rs9
-rw-r--r--server/src/routes/admin/log.rs5
-rw-r--r--server/src/routes/admin/mod.rs8
-rw-r--r--server/src/routes/admin/users.rs10
-rw-r--r--server/src/routes/assets.rs2
-rw-r--r--server/src/routes/error.rs9
-rw-r--r--server/src/routes/home.rs6
-rw-r--r--server/src/routes/items.rs5
-rw-r--r--server/src/routes/node.rs6
-rw-r--r--server/src/routes/player.rs6
-rw-r--r--server/src/routes/playersync.rs9
-rw-r--r--server/src/routes/search.rs6
-rw-r--r--server/src/routes/stats.rs2
-rw-r--r--ui/client-scripts/src/transition.ts6
19 files changed, 97 insertions, 53 deletions
diff --git a/server/src/request_info.rs b/server/src/request_info.rs
index aa0665d..c7389d8 100644
--- a/server/src/request_info.rs
+++ b/server/src/request_info.rs
@@ -7,6 +7,7 @@
use crate::{
State,
auth::token_to_user,
+ responders::UiPage,
routes::error::{MyError, MyResult},
};
use anyhow::anyhow;
@@ -16,7 +17,6 @@ use rocket::{
Request, async_trait,
http::MediaType,
request::{FromRequest, Outcome},
- response::content::RawHtml,
};
use std::sync::Arc;
@@ -74,11 +74,17 @@ impl<'a> RequestInfo<'a> {
message: self.flash.as_ref().map(|f| (f.kind, f.message)),
}
}
- pub fn respond_ui(&self, page: &dyn Page) -> RawHtml<String> {
+ pub fn respond_ui(&self, page: &dyn Page) -> UiPage {
if self.no_scaffold {
- RawHtml(page.render().to_string())
+ UiPage {
+ html: page.render().to_string(),
+ title: Some(page.title().to_string()),
+ }
} else {
- RawHtml(Scaffold { page }.to_string())
+ UiPage {
+ html: Scaffold { page }.to_string(),
+ title: None,
+ }
}
}
}
diff --git a/server/src/responders/mod.rs b/server/src/responders/mod.rs
index b62fe40..cdb6c85 100644
--- a/server/src/responders/mod.rs
+++ b/server/src/responders/mod.rs
@@ -4,5 +4,10 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-pub mod cache;
-pub mod cors;
+mod cache;
+mod cors;
+mod ui_page;
+
+pub use cache::{CacheControlFile, CacheControlImage};
+pub use cors::Cors;
+pub use ui_page::UiPage;
diff --git a/server/src/responders/ui_page.rs b/server/src/responders/ui_page.rs
new file mode 100644
index 0000000..5a7a4b3
--- /dev/null
+++ b/server/src/responders/ui_page.rs
@@ -0,0 +1,21 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2026 metamuffin <metamuffin.org>
+*/
+
+use rocket::response::{Responder, content::RawHtml};
+
+pub struct UiPage {
+ pub title: Option<String>,
+ pub html: String,
+}
+impl<'r, 'o: 'r> Responder<'r, 'o> for UiPage {
+ fn respond_to(self, request: &'r rocket::Request<'_>) -> rocket::response::Result<'o> {
+ let mut r = RawHtml(self.html).respond_to(request)?;
+ if let Some(title) = self.title {
+ r.adjoin_raw_header("x-title", title);
+ }
+ Ok(r)
+ }
+}
diff --git a/server/src/routes/account/mod.rs b/server/src/routes/account/mod.rs
index 1ca0ce1..beea214 100644
--- a/server/src/routes/account/mod.rs
+++ b/server/src/routes/account/mod.rs
@@ -10,6 +10,7 @@ pub mod settings;
use crate::{
auth::{hash_password, login},
request_info::RequestInfo,
+ responders::UiPage,
routes::error::MyResult,
};
use jellycommon::{
@@ -25,19 +26,19 @@ use rocket::{
get,
http::{Cookie, CookieJar},
post,
- response::{Flash, Redirect, content::RawHtml},
+ response::{Flash, Redirect},
};
use serde::{Deserialize, Serialize};
#[get("/account/login")]
-pub async fn r_account_login(ri: RequestInfo<'_>) -> RawHtml<String> {
+pub async fn r_account_login(ri: RequestInfo<'_>) -> UiPage {
ri.respond_ui(&AccountLogin {
ri: &ri.render_info(),
})
}
#[get("/account/logout")]
-pub fn r_account_logout(ri: RequestInfo<'_>) -> RawHtml<String> {
+pub fn r_account_logout(ri: RequestInfo<'_>) -> UiPage {
ri.respond_ui(&AccountLogout {
ri: &ri.render_info(),
})
@@ -63,7 +64,7 @@ pub fn r_account_login_post(
ri: RequestInfo<'_>,
jar: &CookieJar,
form: Form<Contextual<LoginForm>>,
-) -> MyResult<Either<Redirect, Either<Flash<Redirect>, RawHtml<String>>>> {
+) -> MyResult<Either<Redirect, Either<Flash<Redirect>, UiPage>>> {
let form = match &form.value {
Some(v) => v,
None => {
diff --git a/server/src/routes/account/settings.rs b/server/src/routes/account/settings.rs
index 54ecf22..3fe444c 100644
--- a/server/src/routes/account/settings.rs
+++ b/server/src/routes/account/settings.rs
@@ -4,7 +4,9 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
use super::format_form_error;
-use crate::{auth::hash_password, request_info::RequestInfo, routes::error::MyResult};
+use crate::{
+ auth::hash_password, request_info::RequestInfo, responders::UiPage, routes::error::MyResult,
+};
use anyhow::anyhow;
use jellycommon::{
jellyobject::{Object, Path, Tag},
@@ -17,7 +19,7 @@ use rocket::{
FromForm,
form::{self, Contextual, Form, validate::len},
get, post,
- response::{Flash, Redirect, content::RawHtml},
+ response::{Flash, Redirect},
};
use std::ops::Range;
@@ -36,7 +38,7 @@ fn option_len<'v>(value: &Option<String>, range: Range<usize>) -> form::Result<'
}
#[get("/account/settings")]
-pub fn r_account_settings(ri: RequestInfo) -> MyResult<RawHtml<String>> {
+pub fn r_account_settings(ri: RequestInfo) -> MyResult<UiPage> {
let user = ri.require_user()?;
Ok(ri.respond_ui(&UserSettings {
ri: &ri.render_info(),
diff --git a/server/src/routes/admin/import.rs b/server/src/routes/admin/import.rs
index f58901a..5185c8c 100644
--- a/server/src/routes/admin/import.rs
+++ b/server/src/routes/admin/import.rs
@@ -4,23 +4,20 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{request_info::RequestInfo, routes::error::MyResult};
+use crate::{request_info::RequestInfo, responders::UiPage, routes::error::MyResult};
use jellycommon::routes::u_admin_import;
use jellyimport::{
ImportConfig, import_wrap, is_importing,
reporting::{IMPORT_ERRORS, IMPORT_PROGRESS},
};
use jellyui::components::admin::AdminImport;
-use rocket::{
- get, post,
- response::{Redirect, content::RawHtml},
-};
+use rocket::{get, post, response::Redirect};
use rocket_ws::{Message, Stream, WebSocket};
use std::time::Duration;
use tokio::{spawn, time::sleep};
#[get("/admin/import", rank = 2)]
-pub async fn r_admin_import(ri: RequestInfo<'_>) -> MyResult<RawHtml<String>> {
+pub async fn r_admin_import(ri: RequestInfo<'_>) -> MyResult<UiPage> {
ri.require_admin()?;
let last_import_err = IMPORT_ERRORS.read().await.clone();
diff --git a/server/src/routes/admin/log.rs b/server/src/routes/admin/log.rs
index bf8126a..242681d 100644
--- a/server/src/routes/admin/log.rs
+++ b/server/src/routes/admin/log.rs
@@ -6,15 +6,16 @@
use crate::{
logger::{get_log_buffer, get_log_stream},
request_info::RequestInfo,
+ responders::UiPage,
routes::error::MyResult,
};
use jellyui::components::admin_log::{ServerLogPage, render_log_line};
-use rocket::{get, response::content::RawHtml};
+use rocket::get;
use rocket_ws::{Message, Stream, WebSocket};
use serde_json::json;
#[get("/admin/log?<warnonly>", rank = 2)]
-pub fn r_admin_log(ri: RequestInfo, warnonly: bool) -> MyResult<RawHtml<String>> {
+pub fn r_admin_log(ri: RequestInfo, warnonly: bool) -> MyResult<UiPage> {
ri.require_admin()?;
let messages = get_log_buffer(warnonly)
.into_iter()
diff --git a/server/src/routes/admin/mod.rs b/server/src/routes/admin/mod.rs
index 714b574..0c918ec 100644
--- a/server/src/routes/admin/mod.rs
+++ b/server/src/routes/admin/mod.rs
@@ -9,13 +9,13 @@ pub mod log;
pub mod users;
use super::error::MyResult;
-use crate::request_info::RequestInfo;
+use crate::{request_info::RequestInfo, responders::UiPage};
use jellyui::components::admin::{AdminDashboard, AdminDebug};
-use rocket::{get, response::content::RawHtml};
+use rocket::get;
use std::fmt::Write;
#[get("/admin/dashboard")]
-pub async fn r_admin_dashboard(ri: RequestInfo<'_>) -> MyResult<RawHtml<String>> {
+pub async fn r_admin_dashboard(ri: RequestInfo<'_>) -> MyResult<UiPage> {
ri.require_admin()?;
// let mut db_debug = String::new();
@@ -30,7 +30,7 @@ pub async fn r_admin_dashboard(ri: RequestInfo<'_>) -> MyResult<RawHtml<String>>
}
#[get("/admin/debug")]
-pub async fn r_admin_debug(ri: RequestInfo<'_>) -> MyResult<RawHtml<String>> {
+pub async fn r_admin_debug(ri: RequestInfo<'_>) -> MyResult<UiPage> {
ri.require_admin()?;
let mut o = String::new();
writeln!(o, "===== DATABASE =====")?;
diff --git a/server/src/routes/admin/users.rs b/server/src/routes/admin/users.rs
index 01a6403..ee8a0c9 100644
--- a/server/src/routes/admin/users.rs
+++ b/server/src/routes/admin/users.rs
@@ -6,7 +6,9 @@
use std::str::FromStr;
-use crate::{auth::hash_password, request_info::RequestInfo, routes::error::MyResult};
+use crate::{
+ auth::hash_password, request_info::RequestInfo, responders::UiPage, routes::error::MyResult,
+};
use anyhow::anyhow;
use base64::{Engine, prelude::BASE64_URL_SAFE};
use jellycommon::{
@@ -24,11 +26,11 @@ use rocket::{
FromForm,
form::Form,
get, post,
- response::{Flash, Redirect, content::RawHtml},
+ response::{Flash, Redirect},
};
#[get("/admin/users")]
-pub fn r_admin_users(ri: RequestInfo) -> MyResult<RawHtml<String>> {
+pub fn r_admin_users(ri: RequestInfo) -> MyResult<UiPage> {
ri.require_admin()?;
let mut users = Vec::new();
@@ -78,7 +80,7 @@ pub fn r_admin_new_user(ri: RequestInfo, form: Form<NewUser>) -> MyResult<Flash<
}
#[get("/admin/user/<name>")]
-pub fn r_admin_user(ri: RequestInfo<'_>, name: &str) -> MyResult<RawHtml<String>> {
+pub fn r_admin_user(ri: RequestInfo<'_>, name: &str) -> MyResult<UiPage> {
ri.require_admin()?;
let mut user = None;
ri.state.database.transaction(&mut |txn| {
diff --git a/server/src/routes/assets.rs b/server/src/routes/assets.rs
index 089f293..41760a1 100644
--- a/server/src/routes/assets.rs
+++ b/server/src/routes/assets.rs
@@ -4,7 +4,7 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
use super::error::MyResult;
-use crate::{request_info::RequestInfo, responders::cache::CacheControlImage};
+use crate::{request_info::RequestInfo, responders::CacheControlImage};
use anyhow::Context;
use jellycache::HashKey;
use jellycommon::routes::u_image;
diff --git a/server/src/routes/error.rs b/server/src/routes/error.rs
index 6330b27..768d242 100644
--- a/server/src/routes/error.rs
+++ b/server/src/routes/error.rs
@@ -3,22 +3,21 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
+use crate::{request_info::RequestInfo, responders::UiPage};
use jellyui::components::message::Message;
use rocket::{
Request, catch,
http::Status,
- response::{self, Responder, content::RawHtml},
+ response::{self, Responder},
};
use serde_json::{Value, json};
use thiserror::Error;
-use crate::request_info::RequestInfo;
-
#[catch(default)]
-pub fn r_catch(status: Status, request: &Request) -> RawHtml<String> {
+pub fn r_catch(status: Status, request: &Request) -> UiPage {
catch_with_message(&RequestInfo::from_request_ut(request), format!("{status}"))
}
-fn catch_with_message(ri: &RequestInfo, message: String) -> RawHtml<String> {
+fn catch_with_message(ri: &RequestInfo, message: String) -> UiPage {
ri.respond_ui(&Message {
ri: &ri.render_info(),
kind: "error",
diff --git a/server/src/routes/home.rs b/server/src/routes/home.rs
index cb3fffd..5453b3a 100644
--- a/server/src/routes/home.rs
+++ b/server/src/routes/home.rs
@@ -5,15 +5,15 @@
*/
use super::error::MyResult;
-use crate::{request_info::RequestInfo, routes::node::create_nku};
+use crate::{request_info::RequestInfo, responders::UiPage, routes::node::create_nku};
use anyhow::{Context, Result};
use jellydb::{Query, helper::DatabaseReturnExt};
use jellyui::components::home::{Home, HomeRow};
-use rocket::{get, response::content::RawHtml};
+use rocket::get;
use std::str::FromStr;
#[get("/home")]
-pub fn r_home(ri: RequestInfo<'_>) -> MyResult<RawHtml<String>> {
+pub fn r_home(ri: RequestInfo<'_>) -> MyResult<UiPage> {
ri.require_user()?;
let mut rows = Vec::new();
diff --git a/server/src/routes/items.rs b/server/src/routes/items.rs
index 8db60da..e841d1b 100644
--- a/server/src/routes/items.rs
+++ b/server/src/routes/items.rs
@@ -6,6 +6,7 @@
use crate::{
request_info::RequestInfo,
+ responders::UiPage,
routes::{error::MyResult, node::create_nku},
};
use anyhow::anyhow;
@@ -13,10 +14,10 @@ use base64::{Engine, prelude::BASE64_URL_SAFE};
use jellycommon::{jellyobject::Path, *};
use jellydb::{Filter, MultiBehaviour, Query, Sort, SortOrder, ValueSort};
use jellyui::components::items::Items;
-use rocket::{get, response::content::RawHtml};
+use rocket::get;
#[get("/items?<cont>")]
-pub fn r_items(ri: RequestInfo, cont: Option<&str>) -> MyResult<RawHtml<String>> {
+pub fn r_items(ri: RequestInfo, cont: Option<&str>) -> MyResult<UiPage> {
let cont_in = cont
.map(|s| BASE64_URL_SAFE.decode(s))
.transpose()
diff --git a/server/src/routes/node.rs b/server/src/routes/node.rs
index 240486e..d7a585a 100644
--- a/server/src/routes/node.rs
+++ b/server/src/routes/node.rs
@@ -5,7 +5,7 @@
*/
use super::error::MyResult;
-use crate::request_info::RequestInfo;
+use crate::{request_info::RequestInfo, responders::UiPage};
use anyhow::{Result, anyhow};
use jellycommon::{
jellyobject::{EMPTY, Object, Path, Tag},
@@ -16,11 +16,11 @@ use jellyui::components::{
node_list::{NodeFilterInfo, NodeListData},
node_page::NodePage,
};
-use rocket::{get, response::content::RawHtml};
+use rocket::get;
use std::{borrow::Cow, collections::BTreeMap};
#[get("/n/<slug>")]
-pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<RawHtml<String>> {
+pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<UiPage> {
ri.require_user()?;
let filter = NodeFilter { kind: None };
diff --git a/server/src/routes/player.rs b/server/src/routes/player.rs
index 45fd6de..40b584c 100644
--- a/server/src/routes/player.rs
+++ b/server/src/routes/player.rs
@@ -4,12 +4,12 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
use super::error::MyResult;
-use crate::{request_info::RequestInfo, routes::node::create_nku};
+use crate::{request_info::RequestInfo, responders::UiPage, routes::node::create_nku};
use anyhow::anyhow;
use jellycommon::{jellyobject::Path, *};
use jellydb::{Filter, Query};
use jellyui::components::node_page::Player;
-use rocket::{get, response::content::RawHtml};
+use rocket::get;
// fn jellynative_url(action: &str, seek: f64, secret: &str, node: &str, session: &str) -> String {
// let protocol = if CONF.tls { "https" } else { "http" };
@@ -25,7 +25,7 @@ use rocket::{get, response::content::RawHtml};
// }
#[get("/n/<slug>/player?<t>", rank = 4)]
-pub fn r_player(ri: RequestInfo<'_>, t: Option<f64>, slug: &str) -> MyResult<RawHtml<String>> {
+pub fn r_player(ri: RequestInfo<'_>, t: Option<f64>, slug: &str) -> MyResult<UiPage> {
ri.require_user()?;
let _ = t;
diff --git a/server/src/routes/playersync.rs b/server/src/routes/playersync.rs
index 71e2809..bf36f04 100644
--- a/server/src/routes/playersync.rs
+++ b/server/src/routes/playersync.rs
@@ -1,3 +1,10 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2026 metamuffin <metamuffin.org>
+*/
+
+use crate::responders::Cors;
use anyhow::bail;
use chashmap::CHashMap;
use futures::{SinkExt, StreamExt};
@@ -7,8 +14,6 @@ use rocket_ws::{Channel, Message, WebSocket, stream::DuplexStream};
use serde::{Deserialize, Serialize};
use tokio::sync::broadcast::{self, Sender};
-use crate::responders::cors::Cors;
-
#[derive(Default)]
pub struct PlayersyncChannels {
channels: CHashMap<String, broadcast::Sender<Message>>,
diff --git a/server/src/routes/search.rs b/server/src/routes/search.rs
index 1339b5d..6e7bab1 100644
--- a/server/src/routes/search.rs
+++ b/server/src/routes/search.rs
@@ -5,16 +5,16 @@
*/
use super::error::MyResult;
-use crate::{request_info::RequestInfo, routes::node::create_nku};
+use crate::{request_info::RequestInfo, responders::UiPage, routes::node::create_nku};
use jellycommon::{jellyobject::Path, *};
use jellydb::{Filter, Query, Sort};
use jellyui::components::search::Search;
use log::info;
-use rocket::{get, response::content::RawHtml};
+use rocket::get;
use std::time::Instant;
#[get("/search?<q>")]
-pub async fn r_search(ri: RequestInfo<'_>, q: Option<&str>) -> MyResult<RawHtml<String>> {
+pub async fn r_search(ri: RequestInfo<'_>, q: Option<&str>) -> MyResult<UiPage> {
ri.require_user()?;
let mut items = Vec::new();
diff --git a/server/src/routes/stats.rs b/server/src/routes/stats.rs
index 387ca63..f8afe55 100644
--- a/server/src/routes/stats.rs
+++ b/server/src/routes/stats.rs
@@ -7,6 +7,6 @@ use crate::{request_info::RequestInfo, ui::error::MyResult};
use rocket::{get, response::content::RawHtml};
#[get("/stats")]
-pub fn r_stats(ri: RequestInfo) -> MyResult<RawHtml<String>> {
+pub fn r_stats(ri: RequestInfo) -> MyResult<UiPage> {
todo!()
}
diff --git a/ui/client-scripts/src/transition.ts b/ui/client-scripts/src/transition.ts
index d68444a..deeecd8 100644
--- a/ui/client-scripts/src/transition.ts
+++ b/ui/client-scripts/src/transition.ts
@@ -60,8 +60,9 @@ function prepare_load(href: string, state?: HistoryState) {
const r_promise = fetch(href_url, { headers: { accept: "text/html" }, redirect: "manual" })
return async () => {
let rt = ""
+ let r
try {
- const r = await r_promise
+ r = await r_promise
if (r.type == "opaqueredirect") {
globalThis.location.href = href
show_message("Native Player Started.", "success")
@@ -77,7 +78,10 @@ function prepare_load(href: string, state?: HistoryState) {
globalThis.history.replaceState({ top: globalThis.scrollY, index: i++ } as HistoryState, "")
if (!state) globalThis.history.pushState({}, "", href)
clear_spinner()
+
+ document.title = r.headers.get("x-title") ?? ""
document.getElementById("main")!.innerHTML = rt
+
globalThis.dispatchEvent(new Event("DOMContentLoaded"))
globalThis.scrollTo({ top: state?.top ?? 0 });
fade(true)