aboutsummaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-02-26 03:02:24 +0100
committermetamuffin <metamuffin@disroot.org>2026-02-26 03:02:24 +0100
commiteb6648770e7de66ccafe44d114ecbb2c1eaf444d (patch)
tree2bce9f579b3ea7313f84df94b27fad813c66e9e1 /server/src
parent7f7deec27e69ed110c52caddaa3a0c04430e71d9 (diff)
downloadjellything-eb6648770e7de66ccafe44d114ecbb2c1eaf444d.tar
jellything-eb6648770e7de66ccafe44d114ecbb2c1eaf444d.tar.bz2
jellything-eb6648770e7de66ccafe44d114ecbb2c1eaf444d.tar.zst
implement application-side continuation tokens
Diffstat (limited to 'server/src')
-rw-r--r--server/src/auth.rs4
-rw-r--r--server/src/compat/youtube.rs4
-rw-r--r--server/src/logic/stream.rs4
-rw-r--r--server/src/main.rs4
-rw-r--r--server/src/routes.rs2
-rw-r--r--server/src/ui/account/mod.rs4
-rw-r--r--server/src/ui/account/settings.rs4
-rw-r--r--server/src/ui/admin/users.rs6
-rw-r--r--server/src/ui/items.rs69
-rw-r--r--server/src/ui/mod.rs1
-rw-r--r--server/src/ui/node.rs4
-rw-r--r--server/src/ui/player.rs4
12 files changed, 70 insertions, 40 deletions
diff --git a/server/src/auth.rs b/server/src/auth.rs
index 26da82b..d973dfc 100644
--- a/server/src/auth.rs
+++ b/server/src/auth.rs
@@ -11,7 +11,7 @@ use jellycommon::{
jellyobject::{ObjectBuffer, Path},
*,
};
-use jellydb::{Filter, Query, Sort};
+use jellydb::{Filter, Query};
pub fn token_to_user(state: &State, token: &str) -> Result<ObjectBuffer> {
let user_row = token::validate(&state.session_key, token)?;
@@ -38,7 +38,7 @@ pub fn login(
state.database.transaction(&mut |txn| {
user_row = txn.query_single(Query {
filter: Filter::Match(Path(vec![USER_LOGIN.0]), username.into()),
- sort: Sort::None,
+ ..Default::default()
})?;
if let Some(ur) = user_row {
user = txn.get(ur)?;
diff --git a/server/src/compat/youtube.rs b/server/src/compat/youtube.rs
index a5f540b..4ba3dc8 100644
--- a/server/src/compat/youtube.rs
+++ b/server/src/compat/youtube.rs
@@ -8,7 +8,7 @@ use anyhow::anyhow;
use jellycommon::{
IDENT_YOUTUBE_VIDEO, NO_IDENTIFIERS, NO_SLUG, jellyobject::Path, routes::u_node_id,
};
-use jellydb::{Filter, Query, Sort};
+use jellydb::{Filter, Query};
use rocket::{get, response::Redirect};
#[get("/watch?<v>")]
@@ -23,7 +23,7 @@ pub fn r_youtube_watch(ri: RequestInfo<'_>, v: &str) -> MyResult<Redirect> {
Path(vec![NO_IDENTIFIERS.0, IDENT_YOUTUBE_VIDEO.0]),
v.into(),
),
- sort: Sort::None,
+ ..Default::default()
})? {
res = txn.get(row)?;
}
diff --git a/server/src/logic/stream.rs b/server/src/logic/stream.rs
index 6f0fdc4..e332811 100644
--- a/server/src/logic/stream.rs
+++ b/server/src/logic/stream.rs
@@ -9,7 +9,7 @@ use jellycommon::{
NO_SLUG, NO_TITLE, NO_TRACK, TR_SOURCE, TRSOURCE_LOCAL_PATH, jellyobject::Path,
stream::StreamSpec,
};
-use jellydb::{Filter, Query, Sort};
+use jellydb::{Filter, Query};
use jellystream::SMediaInfo;
use log::{info, warn};
use rocket::{
@@ -58,7 +58,7 @@ pub async fn r_stream(
ri.state.database.transaction(&mut |txn| {
if let Some(row) = txn.query_single(Query {
filter: Filter::Match(Path(vec![NO_SLUG.0]), slug.into()),
- sort: Sort::None,
+ ..Default::default()
})? {
node = txn.get(row)?;
}
diff --git a/server/src/main.rs b/server/src/main.rs
index cbad704..f3eabcf 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -17,7 +17,7 @@ use jellycommon::{
USER_ADMIN, USER_LOGIN, USER_PASSWORD,
jellyobject::{ObjectBuffer, Path},
};
-use jellydb::{Database, Filter, Query, Sort};
+use jellydb::{Database, Filter, Query};
use log::{error, info};
use routes::build_rocket;
use serde::Deserialize;
@@ -102,7 +102,7 @@ fn create_admin_user(state: &State) -> Result<()> {
state.database.transaction(&mut |txn| {
let admin_row = txn.query_single(Query {
filter: Filter::Match(Path(vec![USER_LOGIN.0]), "admin".into()),
- sort: Sort::None,
+ ..Default::default()
})?;
if admin_row.is_none() {
info!("Creating new admin user");
diff --git a/server/src/routes.rs b/server/src/routes.rs
index f17952a..fc1d5e6 100644
--- a/server/src/routes.rs
+++ b/server/src/routes.rs
@@ -25,6 +25,7 @@ use crate::{
assets::{r_image, r_image_fallback_person},
error::{r_api_catch, r_catch},
home::r_home,
+ items::r_items,
node::r_node,
player::r_player,
r_favicon, r_index,
@@ -101,6 +102,7 @@ pub(super) fn build_rocket(state: Arc<State>) -> Rocket<Build> {
r_image_fallback_person,
r_image,
r_index,
+ r_items,
r_node,
r_player,
r_playersync,
diff --git a/server/src/ui/account/mod.rs b/server/src/ui/account/mod.rs
index 837d49a..ab5093d 100644
--- a/server/src/ui/account/mod.rs
+++ b/server/src/ui/account/mod.rs
@@ -20,7 +20,7 @@ use jellycommon::{
routes::{u_account_login, u_home},
*,
};
-use jellydb::{Filter, Query, Sort};
+use jellydb::{Filter, Query};
use rocket::{
Either, FromForm,
form::{Contextual, Form},
@@ -74,7 +74,7 @@ pub fn r_account_login_post(
ri.state.database.transaction(&mut |txn| {
let user_row = txn.query_single(Query {
filter: Filter::Match(Path(vec![USER_LOGIN.0]), form.username.clone().into()),
- sort: Sort::None,
+ ..Default::default()
})?;
if let Some(ur) = user_row {
let mut user = txn.get(ur)?.unwrap();
diff --git a/server/src/ui/account/settings.rs b/server/src/ui/account/settings.rs
index bb4b323..c1068f6 100644
--- a/server/src/ui/account/settings.rs
+++ b/server/src/ui/account/settings.rs
@@ -13,7 +13,7 @@ use jellycommon::{
routes::u_account_settings,
*,
};
-use jellydb::{Filter, Query, Sort};
+use jellydb::{Filter, Query};
use jellyui::tr;
use rocket::{
FromForm,
@@ -112,7 +112,7 @@ fn update_user(ri: &RequestInfo, update: impl Fn(Object) -> ObjectBuffer) -> MyR
let user_row = txn
.query_single(Query {
filter: Filter::Match(Path(vec![USER_LOGIN.0]), login.into()),
- sort: Sort::None,
+ ..Default::default()
})?
.ok_or(anyhow!("user vanished"))?;
diff --git a/server/src/ui/admin/users.rs b/server/src/ui/admin/users.rs
index 654a6b9..172facc 100644
--- a/server/src/ui/admin/users.rs
+++ b/server/src/ui/admin/users.rs
@@ -12,7 +12,7 @@ use jellycommon::{
routes::u_admin_users,
*,
};
-use jellydb::{Filter, Query, Sort};
+use jellydb::{Filter, Query};
use jellyui::tr;
use rand::random;
use rocket::{
@@ -88,8 +88,8 @@ pub fn r_admin_user(ri: RequestInfo<'_>, name: &str) -> MyResult<UiResponse> {
let mut page = OBB::new();
ri.state.database.transaction(&mut |txn| {
if let Some(row) = txn.query_single(Query {
- sort: Sort::None,
filter: Filter::Match(Path(vec![USER_LOGIN.0]), name.into()),
+ ..Default::default()
})? {
let user = txn.get(row)?.unwrap();
page = OBB::new();
@@ -106,8 +106,8 @@ pub fn r_admin_user_remove(ri: RequestInfo<'_>, name: &str) -> MyResult<Flash<Re
ri.require_admin()?;
ri.state.database.transaction(&mut |txn| {
if let Some(row) = txn.query_single(Query {
- sort: Sort::None,
filter: Filter::Match(Path(vec![USER_LOGIN.0]), name.into()),
+ ..Default::default()
})? {
txn.remove(row)?;
}
diff --git a/server/src/ui/items.rs b/server/src/ui/items.rs
index cc8c18f..6383830 100644
--- a/server/src/ui/items.rs
+++ b/server/src/ui/items.rs
@@ -3,27 +3,52 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use super::error::MyError;
-use rocket::{Either, get, response::content::RawHtml, serde::json::Json};
-#[get("/items?<page>&<filter..>")]
-pub fn r_items(
- ri: RequestInfo,
- page: Option<usize>,
- filter: ANodeFilterSort,
-) -> Result<Either<RawHtml<String>, Json<ApiItemsResponse>>, MyError> {
- let r = all_items(&ri.session, page, filter.clone().into())?;
- Ok(if matches!(ri.accept, Accept::Json) {
- Either::Right(Json(r))
- } else {
- Either::Left(RawHtml(render_page(
- &ItemsPage {
- lang: &ri.lang,
- r,
- filter: &filter.clone().into(),
- page: page.unwrap_or(0),
- },
- ri.render_info(),
- )))
- })
+use crate::{request_info::RequestInfo, ui::error::MyResult, ui_responder::UiResponse};
+use anyhow::anyhow;
+use base64::{Engine, prelude::BASE64_URL_SAFE};
+use jellycommon::{
+ jellyobject::{OBB, Path},
+ *,
+};
+use jellydb::{Filter, Query};
+use rocket::get;
+
+#[get("/items?<cont>")]
+pub fn r_items(ri: RequestInfo, cont: Option<&str>) -> MyResult<UiResponse> {
+ let cont = cont
+ .map(|s| BASE64_URL_SAFE.decode(s))
+ .transpose()
+ .map_err(|_| anyhow!("invalid contination token"))?;
+
+ let mut page = OBB::new();
+ ri.state.database.transaction(&mut |txn| {
+ let rows = txn
+ .query(Query {
+ filter: Filter::Has(Path(vec![NO_SLUG.0])),
+ continuation: cont.clone(),
+ ..Default::default()
+ })?
+ .take(64)
+ .collect::<Result<Vec<_>, _>>()?;
+
+ let mut list = OBB::new()
+ .with(NODELIST_DISPLAYSTYLE, NLSTYLE_GRID)
+ .with(NODELIST_TITLE, "items");
+
+ let mut iterstate = Vec::new();
+ for (r, is) in rows {
+ let node = txn.get(r)?.unwrap();
+ let nku = OBB::new().with(NKU_NODE, node.as_object()).finish();
+ list.push(NODELIST_ITEM, nku.as_object());
+ iterstate = is;
+ }
+ list.push(NODELIST_CONTINUATION, &BASE64_URL_SAFE.encode(iterstate));
+
+ page = OBB::new();
+ page.push(VIEW_NODE_LIST, list.finish().as_object());
+
+ Ok(())
+ })?;
+ Ok(ri.respond_ui(page))
}
diff --git a/server/src/ui/mod.rs b/server/src/ui/mod.rs
index 27535fa..6cb3cd2 100644
--- a/server/src/ui/mod.rs
+++ b/server/src/ui/mod.rs
@@ -18,6 +18,7 @@ pub mod home;
pub mod node;
pub mod player;
pub mod style;
+pub mod items;
#[get("/")]
pub async fn r_index(ri: RequestInfo<'_>) -> MyResult<Redirect> {
diff --git a/server/src/ui/node.rs b/server/src/ui/node.rs
index 509e9ae..55a1d09 100644
--- a/server/src/ui/node.rs
+++ b/server/src/ui/node.rs
@@ -23,7 +23,7 @@ pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<UiResponse> {
ri.state.database.transaction(&mut |txn| {
if let Some(row) = txn.query_single(Query {
filter: Filter::Match(Path(vec![NO_SLUG.0]), slug.into()),
- sort: Sort::None,
+ ..Default::default()
})? {
let n = txn.get(row)?.unwrap();
let nku = Object::EMPTY.insert(NKU_NODE, n.as_object());
@@ -78,6 +78,7 @@ fn c_children(
Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
Filter::Match(Path(vec![NO_PARENT.0]), row.into()),
]),
+ ..Default::default()
})?
.collect::<Result<Vec<_>>>()?;
@@ -163,6 +164,7 @@ fn c_credited(page: &mut ObjectBufferBuilder, txn: &mut dyn Transaction, row: u6
Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
Filter::Match(Path(vec![NO_CREDIT.0, CR_NODE.0]), row.into()),
]),
+ ..Default::default()
})?
.collect::<Result<Vec<_>>>()?;
diff --git a/server/src/ui/player.rs b/server/src/ui/player.rs
index 4c592e4..f0d6dea 100644
--- a/server/src/ui/player.rs
+++ b/server/src/ui/player.rs
@@ -9,7 +9,7 @@ use jellycommon::{
jellyobject::{OBB, Object, Path},
*,
};
-use jellydb::{Filter, Query, Sort};
+use jellydb::{Filter, Query};
use rocket::get;
// fn jellynative_url(action: &str, seek: f64, secret: &str, node: &str, session: &str) -> String {
@@ -34,7 +34,7 @@ pub fn r_player(ri: RequestInfo<'_>, t: Option<f64>, slug: &str) -> MyResult<UiR
ri.state.database.transaction(&mut |txn| {
if let Some(row) = txn.query_single(Query {
filter: Filter::Match(Path(vec![NO_SLUG.0]), slug.into()),
- sort: Sort::None,
+ ..Default::default()
})? {
let n = txn.get(row)?.unwrap();
let nku = Object::EMPTY.insert(NKU_NODE, n.as_object());