From 783d3598753bf84756296a2016e5dab30300519b Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 25 Jan 2026 23:32:53 +0100 Subject: work on login --- server/src/api.rs | 8 ++- server/src/compat/youtube.rs | 74 +++++++++++------------ server/src/logic/playersync.rs | 6 +- server/src/logic/stream.rs | 133 ++++++++++++++++++++--------------------- server/src/logic/userdata.rs | 101 ++++++++++++++----------------- server/src/main.rs | 2 +- server/src/request_info.rs | 9 ++- server/src/routes.rs | 92 +++++++++++----------------- server/src/ui/account/mod.rs | 22 ++++--- server/src/ui/assets.rs | 77 ++++++++++++------------ server/src/ui/error.rs | 36 +++-------- server/src/ui/mod.rs | 79 +++++++++++------------- server/src/ui/node.rs | 44 +++----------- server/src/ui/search.rs | 41 ++++++------- server/src/ui/stats.rs | 20 ++----- 15 files changed, 322 insertions(+), 422 deletions(-) (limited to 'server/src') diff --git a/server/src/api.rs b/server/src/api.rs index 2b3d016..9d16433 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -4,6 +4,7 @@ Copyright (C) 2026 metamuffin */ use super::ui::error::MyResult; +use crate::request_info::RequestInfo; use rocket::{get, response::Redirect, serde::json::Json}; #[get("/api")] @@ -38,7 +39,8 @@ pub fn r_version() -> &'static str { // } #[get("/nodes_modified?")] -pub fn r_nodes_modified_since(session: A, since: u64) -> MyResult>> { - let nodes = get_nodes_modified_since(&session.0, since)?; - Ok(Json(nodes)) +pub fn r_nodes_modified_since(ri: RequestInfo<'_>, since: u64) -> MyResult>> { + // let nodes = get_nodes_modified_since(&session.0, since)?; + // Ok(Json(nodes)) + todo!() } diff --git a/server/src/compat/youtube.rs b/server/src/compat/youtube.rs index 5e86014..e511d9b 100644 --- a/server/src/compat/youtube.rs +++ b/server/src/compat/youtube.rs @@ -3,53 +3,47 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ -use crate::{request_info::A, ui::error::MyResult}; -use anyhow::anyhow; -use jellycommon::{ - routes::{u_node_slug, u_node_slug_player}, - IdentifierType, -}; -use jellylogic::{ - node::{get_node_by_eid, node_id_to_slug}, - session::Session, -}; +use crate::{request_info::RequestInfo, ui::error::MyResult}; use rocket::{get, response::Redirect}; #[get("/watch?")] -pub fn r_youtube_watch(session: A, v: &str) -> MyResult { - if v.len() != 11 { - Err(anyhow!("video id length incorrect"))? - } - let Some(id) = get_node_by_eid(&session.0, IdentifierType::YoutubeVideo, v)? else { - Err(anyhow!("element not found"))? - }; - let slug = node_id_to_slug(&session.0, id)?; - Ok(Redirect::to(u_node_slug_player(&slug))) +pub fn r_youtube_watch(ri: RequestInfo<'_>, v: &str) -> MyResult { + // if v.len() != 11 { + // Err(anyhow!("video id length incorrect"))? + // } + // let Some(id) = get_node_by_eid(&session.0, IdentifierType::YoutubeVideo, v)? else { + // Err(anyhow!("element not found"))? + // }; + // let slug = node_id_to_slug(&session.0, id)?; + // Ok(Redirect::to(u_node_slug_player(&slug))) + todo!() } #[get("/channel/")] -pub fn r_youtube_channel(session: A, id: &str) -> MyResult { - let Some(id) = (if id.starts_with("UC") { - get_node_by_eid(&session.0, IdentifierType::YoutubeChannel, id)? - } else if id.starts_with("@") { - get_node_by_eid(&session.0, IdentifierType::YoutubeChannelHandle, id)? - } else { - Err(anyhow!("unknown channel id format"))? - }) else { - Err(anyhow!("channel not found"))? - }; - let slug = node_id_to_slug(&session.0, id)?; - Ok(Redirect::to(u_node_slug(&slug))) +pub fn r_youtube_channel(ri: RequestInfo<'_>, id: &str) -> MyResult { + // let Some(id) = (if id.starts_with("UC") { + // get_node_by_eid(&session.0, IdentifierType::YoutubeChannel, id)? + // } else if id.starts_with("@") { + // get_node_by_eid(&session.0, IdentifierType::YoutubeChannelHandle, id)? + // } else { + // Err(anyhow!("unknown channel id format"))? + // }) else { + // Err(anyhow!("channel not found"))? + // }; + // let slug = node_id_to_slug(&session.0, id)?; + // Ok(Redirect::to(u_node_slug(&slug))) + todo!() } #[get("/embed/")] -pub fn r_youtube_embed(session: A, v: &str) -> MyResult { - if v.len() != 11 { - Err(anyhow!("video id length incorrect"))? - } - let Some(id) = get_node_by_eid(&session.0, IdentifierType::YoutubeVideo, v)? else { - Err(anyhow!("element not found"))? - }; - let slug = node_id_to_slug(&session.0, id)?; - Ok(Redirect::to(u_node_slug_player(&slug))) +pub fn r_youtube_embed(ri: RequestInfo<'_>, v: &str) -> MyResult { + // if v.len() != 11 { + // Err(anyhow!("video id length incorrect"))? + // } + // let Some(id) = get_node_by_eid(&session.0, IdentifierType::YoutubeVideo, v)? else { + // Err(anyhow!("element not found"))? + // }; + // let slug = node_id_to_slug(&session.0, id)?; + // Ok(Redirect::to(u_node_slug_player(&slug))) + todo!() } diff --git a/server/src/logic/playersync.rs b/server/src/logic/playersync.rs index 6c1f9f4..71e2809 100644 --- a/server/src/logic/playersync.rs +++ b/server/src/logic/playersync.rs @@ -2,12 +2,12 @@ use anyhow::bail; use chashmap::CHashMap; use futures::{SinkExt, StreamExt}; use log::warn; -use rocket::{get, State}; -use rocket_ws::{stream::DuplexStream, Channel, Message, WebSocket}; +use rocket::{State, get}; +use rocket_ws::{Channel, Message, WebSocket, stream::DuplexStream}; use serde::{Deserialize, Serialize}; use tokio::sync::broadcast::{self, Sender}; -use crate::request_info::cors::Cors; +use crate::responders::cors::Cors; #[derive(Default)] pub struct PlayersyncChannels { diff --git a/server/src/logic/stream.rs b/server/src/logic/stream.rs index 55d6850..430c10c 100644 --- a/server/src/logic/stream.rs +++ b/server/src/logic/stream.rs @@ -3,18 +3,16 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ -use crate::{request_info::A, ui::error::MyError}; -use anyhow::{anyhow, Result}; -use jellycommon::{api::NodeFilterSort, stream::StreamSpec, NodeID, TrackSource}; -use jellylogic::{node::get_node, session::Session}; +use crate::{request_info::RequestInfo, ui::error::MyError}; +use anyhow::{Result, anyhow}; +use jellycommon::stream::StreamSpec; use jellystream::SMediaInfo; use log::{info, warn}; use rocket::{ - get, head, + Either, Request, Response, get, head, http::{Header, Status}, request::{self, FromRequest}, response::{self, Redirect, Responder}, - Either, Request, Response, }; use std::{ collections::{BTreeMap, BTreeSet}, @@ -22,14 +20,14 @@ use std::{ sync::Arc, }; use tokio::{ - io::{duplex, DuplexStream}, + io::{DuplexStream, duplex}, task::spawn_blocking, }; use tokio_util::io::SyncIoBridge; #[head("/n/<_id>/stream?")] pub async fn r_stream_head( - _sess: A, + _sess: RequestInfo<'_>, _id: &str, spec: BTreeMap, ) -> Result, MyError> { @@ -45,27 +43,27 @@ pub async fn r_stream_head( #[get("/n//stream?")] pub async fn r_stream( - session: A, + session: RequestInfo<'_>, id: &str, range: Option, spec: BTreeMap, ) -> Result, MyError> { let spec = StreamSpec::from_query_kv(&spec).map_err(|x| anyhow!("spec invalid: {x}"))?; - // TODO perm - let node = get_node( - &session.0, - NodeID::from_slug(id), - false, - false, - NodeFilterSort::default(), - )? - .node; + // // TODO perm + // let node = get_node( + // &session.0, + // NodeID::from_slug(id), + // false, + // false, + // NodeFilterSort::default(), + // )? + // .node; - let media = Arc::new( - node.media - .clone() - .ok_or(anyhow!("item does not contain media"))?, - ); + // let media = Arc::new( + // node.media + // .clone() + // .ok_or(anyhow!("item does not contain media"))?, + // ); // TODO its unclear how requests with multiple tracks should be handled. // if spec.track.len() == 1 { @@ -115,55 +113,56 @@ pub async fn r_stream( // } // } - info!( - "stream request (range={})", - range - .as_ref() - .map(|r| r.to_cr_hv()) - .unwrap_or("none".to_string()) - ); + // info!( + // "stream request (range={})", + // range + // .as_ref() + // .map(|r| r.to_cr_hv()) + // .unwrap_or("none".to_string()) + // ); - let urange = match &range { - Some(r) => { - let r = r.0.first().unwrap_or(&(None..None)); - r.start.unwrap_or(0)..r.end.unwrap_or(u64::MAX) - } - None => 0..u64::MAX, - }; + // let urange = match &range { + // Some(r) => { + // let r = r.0.first().unwrap_or(&(None..None)); + // r.start.unwrap_or(0)..r.end.unwrap_or(u64::MAX) + // } + // None => 0..u64::MAX, + // }; - let head = jellystream::stream_head(&spec); + // let head = jellystream::stream_head(&spec); - let mut sources = BTreeSet::new(); - for t in &media.tracks { - if let TrackSource::Local(path, _) = &t.source { - sources.insert(path.to_owned()); - } - } - let media = Arc::new(SMediaInfo { - files: sources, - title: node.title.clone(), - }); + // let mut sources = BTreeSet::new(); + // for t in &media.tracks { + // if let TrackSource::Local(path, _) = &t.source { + // sources.insert(path.to_owned()); + // } + // } + // let media = Arc::new(SMediaInfo { + // files: sources, + // title: node.title.clone(), + // }); - // TODO cleaner solution needed - let mut reader = match spawn_blocking(move || jellystream::stream(media, spec, urange)) - .await - .unwrap() - { - Ok(o) => o, - Err(e) => { - warn!("stream error: {e:?}"); - Err(e)? - } - }; - let (stream_write, stream_read) = duplex(4096); - spawn_blocking(move || std::io::copy(&mut reader, &mut SyncIoBridge::new(stream_write))); + // // TODO cleaner solution needed + // let mut reader = match spawn_blocking(move || jellystream::stream(media, spec, urange)) + // .await + // .unwrap() + // { + // Ok(o) => o, + // Err(e) => { + // warn!("stream error: {e:?}"); + // Err(e)? + // } + // }; + // let (stream_write, stream_read) = duplex(4096); + // spawn_blocking(move || std::io::copy(&mut reader, &mut SyncIoBridge::new(stream_write))); - Ok(Either::Left(StreamResponse { - stream: stream_read, - range, - advertise_range: head.range_supported, - content_type: head.content_type, - })) + // Ok(Either::Left(StreamResponse { + // stream: stream_read, + // range, + // advertise_range: head.range_supported, + // content_type: head.content_type, + // })) + todo!() } pub struct RedirectResponse(String); diff --git a/server/src/logic/userdata.rs b/server/src/logic/userdata.rs index 104de4a..ea96507 100644 --- a/server/src/logic/userdata.rs +++ b/server/src/logic/userdata.rs @@ -3,23 +3,10 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ -use crate::{request_info::A, ui::error::MyResult}; -use jellycommon::{ - api::NodeFilterSort, - routes::u_node_id, - user::{NodeUserData, WatchedState}, - NodeID, -}; -use jellylogic::{ - node::{ - get_node, update_node_userdata_rating, update_node_userdata_watched, - update_node_userdata_watched_progress, - }, - session::Session, -}; +use crate::ui::error::MyResult; use rocket::{ - form::Form, get, post, response::Redirect, serde::json::Json, FromForm, FromFormField, - UriDisplayQuery, + FromForm, FromFormField, UriDisplayQuery, form::Form, get, post, response::Redirect, + serde::json::Json, }; #[derive(Debug, FromFormField, UriDisplayQuery)] @@ -29,48 +16,48 @@ pub enum UrlWatchedState { Pending, } -#[get("/n//userdata")] -pub fn r_node_userdata(session: A, id: A) -> MyResult> { - let u = get_node(&session.0, id.0, false, false, NodeFilterSort::default())?.userdata; - Ok(Json(u)) -} +// #[get("/n//userdata")] +// pub fn r_node_userdata(session: A, id: A) -> MyResult> { +// let u = get_node(&session.0, id.0, false, false, NodeFilterSort::default())?.userdata; +// Ok(Json(u)) +// } -#[post("/n//watched?")] -pub async fn r_node_userdata_watched( - session: A, - id: A, - state: UrlWatchedState, -) -> MyResult { - update_node_userdata_watched( - &session.0, - id.0, - match state { - UrlWatchedState::None => WatchedState::None, - UrlWatchedState::Watched => WatchedState::Watched, - UrlWatchedState::Pending => WatchedState::Pending, - }, - )?; - Ok(Redirect::found(u_node_id(id.0))) -} +// #[post("/n//watched?")] +// pub async fn r_node_userdata_watched( +// session: A, +// id: A, +// state: UrlWatchedState, +// ) -> MyResult { +// update_node_userdata_watched( +// &session.0, +// id.0, +// match state { +// UrlWatchedState::None => WatchedState::None, +// UrlWatchedState::Watched => WatchedState::Watched, +// UrlWatchedState::Pending => WatchedState::Pending, +// }, +// )?; +// Ok(Redirect::found(u_node_id(id.0))) +// } -#[derive(FromForm)] -pub struct UpdateRating { - #[field(validate = range(-10..=10))] - rating: i32, -} +// #[derive(FromForm)] +// pub struct UpdateRating { +// #[field(validate = range(-10..=10))] +// rating: i32, +// } -#[post("/n//update_rating", data = "
")] -pub async fn r_node_userdata_rating( - session: A, - id: A, - form: Form, -) -> MyResult { - update_node_userdata_rating(&session.0, id.0, form.rating)?; - Ok(Redirect::found(u_node_id(id.0))) -} +// #[post("/n//update_rating", data = "")] +// pub async fn r_node_userdata_rating( +// session: A, +// id: A, +// form: Form, +// ) -> MyResult { +// update_node_userdata_rating(&session.0, id.0, form.rating)?; +// Ok(Redirect::found(u_node_id(id.0))) +// } -#[post("/n//progress?")] -pub async fn r_node_userdata_progress(session: A, id: A, t: f64) -> MyResult<()> { - update_node_userdata_watched_progress(&session.0, id.0, t)?; - Ok(()) -} +// #[post("/n//progress?")] +// pub async fn r_node_userdata_progress(session: A, id: A, t: f64) -> MyResult<()> { +// update_node_userdata_watched_progress(&session.0, id.0, t)?; +// Ok(()) +// } diff --git a/server/src/main.rs b/server/src/main.rs index bd9901a..aab335a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -48,7 +48,7 @@ async fn main() { } } -pub(crate) struct State { +pub struct State { pub config: Config, pub cache: Cache, diff --git a/server/src/request_info.rs b/server/src/request_info.rs index 3468c58..0f2fd3a 100644 --- a/server/src/request_info.rs +++ b/server/src/request_info.rs @@ -9,7 +9,8 @@ use crate::{ auth::token_to_user, ui::error::{MyError, MyResult}, }; -use jellycommon::jellyobject::ObjectBuffer; +use anyhow::anyhow; +use jellycommon::jellyobject::{Object, ObjectBuffer}; use jellyui::RenderInfo; use rocket::{ Request, async_trait, @@ -46,6 +47,12 @@ impl<'a> RequestInfo<'a> { state: state.clone(), }) } + pub fn require_user(&'a self) -> MyResult> { + self.user + .as_ref() + .map(|u| u.as_object()) + .ok_or(MyError(anyhow!("user required"))) + } pub fn render_info(&'a self) -> RenderInfo<'a> { RenderInfo { lang: self.lang, diff --git a/server/src/routes.rs b/server/src/routes.rs index 19c76b2..01d0081 100644 --- a/server/src/routes.rs +++ b/server/src/routes.rs @@ -10,32 +10,12 @@ use crate::{ logic::{ playersync::{PlayersyncChannels, r_playersync}, stream::r_stream, - userdata::{ - r_node_userdata, r_node_userdata_progress, r_node_userdata_rating, - r_node_userdata_watched, - }, }, ui::{ - account::{ - r_account_login, r_account_login_post, r_account_logout, r_account_logout_post, - r_account_register, r_account_register_post, - settings::{r_account_settings, r_account_settings_post}, - }, - admin::{ - import::{r_admin_import, r_admin_import_post, r_admin_import_stream}, - log::{r_admin_log, r_admin_log_stream}, - r_admin_dashboard, r_admin_invite, r_admin_remove_invite, r_admin_update_search, - user::{r_admin_remove_user, r_admin_user, r_admin_user_permission, r_admin_users}, - }, - assets::{r_image, r_item_poster, r_node_thumbnail}, + assets::r_image, error::{r_api_catch, r_catch}, - home::r_home, - items::r_items, node::r_node, - player::r_player, - r_favicon, r_index, - search::r_search, - stats::r_stats, + r_favicon, style::{r_assets_font, r_assets_js, r_assets_js_map, r_assets_style}, }, }; @@ -52,7 +32,7 @@ macro_rules! uri { }; } -pub fn build_rocket(state: Arc) -> Rocket { +pub(super) fn build_rocket(state: Arc) -> Rocket { rocket::build() .configure(Config { address: std::env::var("BIND_ADDR") @@ -84,47 +64,47 @@ pub fn build_rocket(state: Arc) -> Rocket { "/", routes![ // Frontend - r_account_login_post, - r_account_login, - r_account_logout_post, - r_account_logout, - r_account_register_post, - r_account_register, - r_account_settings_post, - r_account_settings, - r_admin_dashboard, - r_admin_import, - r_admin_import_post, - r_admin_import_stream, - r_admin_invite, - r_admin_log_stream, - r_admin_log, - r_admin_remove_invite, - r_admin_remove_user, - r_admin_update_search, - r_admin_user_permission, - r_admin_user, - r_admin_users, - r_items, + // r_account_login_post, + // r_account_login, + // r_account_logout_post, + // r_account_logout, + // r_account_register_post, + // r_account_register, + // r_account_settings_post, + // r_account_settings, + // r_admin_dashboard, + // r_admin_import, + // r_admin_import_post, + // r_admin_import_stream, + // r_admin_invite, + // r_admin_log_stream, + // r_admin_log, + // r_admin_remove_invite, + // r_admin_remove_user, + // r_admin_update_search, + // r_admin_user_permission, + // r_admin_user, + // r_admin_users, + // r_items, r_image, r_assets_font, r_assets_js_map, r_assets_js, r_assets_style, r_favicon, - r_home, - r_index, - r_item_poster, + // r_home, + // r_index, + // r_item_poster, r_node, - r_node_thumbnail, - r_node_userdata_progress, - r_node_userdata_rating, - r_node_userdata_watched, - r_node_userdata, - r_player, + // r_node_thumbnail, + // r_node_userdata_progress, + // r_node_userdata_rating, + // r_node_userdata_watched, + // r_node_userdata, + // r_player, r_playersync, - r_search, - r_stats, + // r_search, + // r_stats, r_stream, // API r_nodes_modified_since, diff --git a/server/src/ui/account/mod.rs b/server/src/ui/account/mod.rs index 429b70a..ec8bd49 100644 --- a/server/src/ui/account/mod.rs +++ b/server/src/ui/account/mod.rs @@ -3,12 +3,17 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ -pub mod settings; +// pub mod settings; use super::error::MyError; -use crate::ui::{error::MyResult, home::rocket_uri_macro_r_home}; +use crate::{ + request_info::RequestInfo, + ui::{error::MyResult, home::rocket_uri_macro_r_home}, +}; use anyhow::anyhow; +use jellycommon::{VIEW_ACCOUNT_LOGIN, jellyobject::Object}; use jellyimport::is_importing; +use jellyui::render_view; use rocket::{ FromForm, form::{Contextual, Form}, @@ -30,16 +35,9 @@ pub struct RegisterForm { } #[get("/account/register")] -pub async fn r_account_register(lang: AcceptLanguage) -> RawHtml { - let AcceptLanguage(lang) = lang; - RawHtml(render_page( - &AccountRegister { lang: &lang }, - RenderInfo { - importing: false, - session: None, - lang, - }, - )) +pub async fn r_account_register(ri: RequestInfo<'_>) -> RawHtml { + let ob = Object::EMPTY.insert(VIEW_ACCOUNT_LOGIN, ()); + RawHtml(render_view(ri.render_info(), ob.as_object())) } #[derive(FromForm, Serialize, Deserialize)] diff --git a/server/src/ui/assets.rs b/server/src/ui/assets.rs index 8f3fb4a..c956672 100644 --- a/server/src/ui/assets.rs +++ b/server/src/ui/assets.rs @@ -4,58 +4,57 @@ Copyright (C) 2026 metamuffin */ use super::error::MyResult; -use anyhow::{Context, anyhow}; -use rocket::{get, http::ContentType, response::Redirect}; -use std::str::FromStr; +use crate::{request_info::RequestInfo, responders::cache::CacheControlImage}; +use anyhow::Context; +use rocket::{get, http::ContentType}; +use std::path::PathBuf; pub const AVIF_QUALITY: u32 = 70; pub const AVIF_SPEED: u8 = 5; -#[get("/image/?")] +#[get("/image/?")] pub async fn r_image( - _session: A, - key: A, + ri: RequestInfo<'_>, + path: PathBuf, size: Option, ) -> MyResult<(ContentType, CacheControlImage)> { let size = size.unwrap_or(2048); - - if !key.0.0.ends_with(".image") { - Err(anyhow!("request to non-image"))? - } + let path = path.to_string_lossy().to_string(); // fit the resolution into a finite set so the maximum cache is finite too. let width = 2usize.pow(size.clamp(128, 2048).ilog2()); - let encoded = jellytranscoder::image::transcode(&key.0.0, AVIF_QUALITY, AVIF_SPEED, width) - .context("transcoding asset")?; + let encoded = + jellytranscoder::image::transcode(&ri.state.cache, &path, AVIF_QUALITY, AVIF_SPEED, width) + .context("transcoding asset")?; Ok((ContentType::AVIF, CacheControlImage(encoded))) } -#[get("/n//image/?")] -pub async fn r_item_poster( - session: A, - id: A, - slot: &str, - size: Option, -) -> MyResult { - let slot = PictureSlot::from_str(slot).map_err(|_| anyhow!("slot invalid"))?; - let node = get_node(&session.0, id.0, false, false, NodeFilterSort::default())?; - let picture = node - .node - .pictures - .get(&slot) - .cloned() - .ok_or(anyhow!("no pic todo"))?; - Ok(Redirect::permanent(rocket::uri!(r_image(picture, size)))) -} +// #[get("/n//image/?")] +// pub async fn r_item_poster( +// session: A, +// id: A, +// slot: &str, +// size: Option, +// ) -> MyResult { +// let slot = PictureSlot::from_str(slot).map_err(|_| anyhow!("slot invalid"))?; +// let node = get_node(&session.0, id.0, false, false, NodeFilterSort::default())?; +// let picture = node +// .node +// .pictures +// .get(&slot) +// .cloned() +// .ok_or(anyhow!("no pic todo"))?; +// Ok(Redirect::permanent(rocket::uri!(r_image(picture, size)))) +// } -#[get("/n//thumbnail?&")] -pub async fn r_node_thumbnail( - session: A, - id: A, - t: f64, - size: Option, -) -> MyResult { - let picture = get_node_thumbnail(&session.0, id.0, t).await?; - Ok(Redirect::permanent(rocket::uri!(r_image(picture, size)))) -} +// #[get("/n//thumbnail?&")] +// pub async fn r_node_thumbnail( +// session: A, +// id: A, +// t: f64, +// size: Option, +// ) -> MyResult { +// let picture = get_node_thumbnail(&session.0, id.0, t).await?; +// Ok(Redirect::permanent(rocket::uri!(r_image(picture, size)))) +// } diff --git a/server/src/ui/error.rs b/server/src/ui/error.rs index 0f279fc..578d841 100644 --- a/server/src/ui/error.rs +++ b/server/src/ui/error.rs @@ -3,38 +3,20 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ -use log::info; use rocket::{ - catch, - http::{ContentType, Status}, - response::{self, content::RawHtml, Responder}, - Request, + Request, catch, + http::Status, + response::{self, Responder, content::RawHtml}, }; -use serde_json::{json, Value}; -use std::{fmt::Display, fs::File, io::Read, sync::LazyLock}; - -static ERROR_IMAGE: LazyLock> = LazyLock::new(|| { - info!("loading error image"); - let mut f = File::open(CONF.asset_path.join("error.avif")) - .expect("please create error.avif in the asset dir"); - let mut o = Vec::new(); - f.read_to_end(&mut o).unwrap(); - o -}); +use serde_json::{Value, json}; +use std::fmt::Display; #[catch(default)] pub fn r_catch(status: Status, _request: &Request) -> RawHtml { catch_with_message(format!("{status}")) } fn catch_with_message(message: String) -> RawHtml { - RawHtml(render_page( - &ErrorPage { status: message }, - RenderInfo { - importing: false, - session: None, - lang: Language::English, - }, - )) + RawHtml(message) // TODO } #[catch(default)] @@ -52,9 +34,9 @@ impl<'r> Responder<'r, 'static> for MyError { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { match req.accept().map(|a| a.preferred()) { Some(x) if x.is_json() => json!({ "error": format!("{}", self.0) }).respond_to(req), - Some(x) if x.is_avif() || x.is_png() || x.is_jpeg() => { - (ContentType::AVIF, ERROR_IMAGE.as_slice()).respond_to(req) - } + // Some(x) if x.is_avif() || x.is_png() || x.is_jpeg() => { + // (ContentType::AVIF, ERROR_IMAGE.as_slice()).respond_to(req) + // } _ => catch_with_message(format!("{:#}", self.0)).respond_to(req), } } diff --git a/server/src/ui/mod.rs b/server/src/ui/mod.rs index 92b93fe..55fad6a 100644 --- a/server/src/ui/mod.rs +++ b/server/src/ui/mod.rs @@ -4,58 +4,51 @@ Copyright (C) 2026 metamuffin */ use error::MyResult; -use home::rocket_uri_macro_r_home; -use rocket::{ - Either, - futures::FutureExt, - get, - response::{Redirect, content::RawHtml}, -}; -use std::{future::Future, pin::Pin}; -use tokio::{ - fs::{File, read_to_string}, - io::AsyncRead, -}; +use rocket::{futures::FutureExt, get}; +use std::{future::Future, pin::Pin, sync::Arc}; +use tokio::{fs::File, io::AsyncRead}; + +use crate::State; pub mod account; -pub mod admin; +// pub mod admin; pub mod assets; pub mod error; -pub mod home; -pub mod items; +// pub mod home; +// pub mod items; pub mod node; -pub mod player; -pub mod search; -pub mod stats; +// pub mod player; +// pub mod search; +// pub mod stats; pub mod style; -#[get("/")] -pub async fn r_index( - lang: AcceptLanguage, - sess: Option>, -) -> MyResult>> { - let AcceptLanguage(lang) = lang; - if sess.is_some() { - Ok(Either::Left(Redirect::temporary(rocket::uri!(r_home())))) - } else { - let front = read_to_string(CONF.asset_path.join("front.htm")).await?; - Ok(Either::Right(RawHtml(render_page( - &CustomPage { - title: "Jellything".to_string(), - body: front, - }, - RenderInfo { - importing: false, - session: None, - lang, - }, - )))) - } -} +// #[get("/")] +// pub async fn r_index( +// lang: AcceptLanguage, +// sess: Option>, +// ) -> MyResult>> { +// let AcceptLanguage(lang) = lang; +// if sess.is_some() { +// Ok(Either::Left(Redirect::temporary(rocket::uri!(r_home())))) +// } else { +// let front = read_to_string(CONF.asset_path.join("front.htm")).await?; +// Ok(Either::Right(RawHtml(render_page( +// &CustomPage { +// title: "Jellything".to_string(), +// body: front, +// }, +// RenderInfo { +// importing: false, +// session: None, +// lang, +// }, +// )))) +// } +// } #[get("/favicon.ico")] -pub async fn r_favicon() -> MyResult { - Ok(File::open(CONF.asset_path.join("favicon.ico")).await?) +pub async fn r_favicon(s: &rocket::State>) -> MyResult { + Ok(File::open(s.config.asset_path.join("favicon.ico")).await?) } pub struct Defer(Pin + Send>>); diff --git a/server/src/ui/node.rs b/server/src/ui/node.rs index 85beac6..cf5c793 100644 --- a/server/src/ui/node.rs +++ b/server/src/ui/node.rs @@ -5,42 +5,12 @@ */ use super::error::MyResult; use crate::request_info::RequestInfo; -use rocket::{Either, get, response::content::RawHtml, serde::json::Json}; +use jellycommon::jellyobject::Object; +use jellyui::render_view; +use rocket::{get, response::content::RawHtml}; -#[get("/n/?&&")] -pub async fn r_node( - ri: RequestInfo<'_>, - id: A, - filter: Option, - parents: bool, - children: bool, -) -> MyResult, Json>> { - let filter: Option = filter.map(Into::into); - let filter = filter.unwrap_or_default(); - - let r = get_node( - &ri.session, - id.0, - !ri.accept.is_json() || children, - !ri.accept.is_json() || parents, - filter.clone(), - )?; - - Ok(if ri.accept.is_json() { - Either::Right(Json(r)) - } else { - Either::Left(RawHtml(render_page( - &NodePage { - node: &r.node, - udata: &r.userdata, - children: &r.children, - parents: &r.parents, - similar: &[], - filter: &filter, - lang: &ri.lang, - player: false, - }, - ri.render_info(), - ))) - }) +#[get("/n/")] +pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult> { + ri.require_user()?; + Ok(RawHtml(render_view(ri.render_info(), Object::EMPTY))) } diff --git a/server/src/ui/search.rs b/server/src/ui/search.rs index fce79e3..8ec2697 100644 --- a/server/src/ui/search.rs +++ b/server/src/ui/search.rs @@ -10,27 +10,28 @@ use rocket::{Either, get, response::content::RawHtml, serde::json::Json}; #[get("/search?&")] pub async fn r_search( - ri: RequestInfo, + ri: RequestInfo<'_>, query: Option<&str>, page: Option, -) -> MyResult, Json>> { - let r = query - .map(|query| search(&ri.session, query, page)) - .transpose()?; +) -> MyResult> { + // let r = query + // .map(|query| search(&ri.session, query, page)) + // .transpose()?; - Ok(if ri.accept.is_json() { - let Some(r) = r else { - Err(anyhow!("no query"))? - }; - Either::Right(Json(r)) - } else { - Either::Left(RawHtml(render_page( - &SearchPage { - lang: &ri.lang, - query: &query.map(|s| s.to_string()), - r, - }, - ri.render_info(), - ))) - }) + // Ok(if ri.accept.is_json() { + // let Some(r) = r else { + // Err(anyhow!("no query"))? + // }; + // Either::Right(Json(r)) + // } else { + // Either::Left(RawHtml(render_page( + // &SearchPage { + // lang: &ri.lang, + // query: &query.map(|s| s.to_string()), + // r, + // }, + // ri.render_info(), + // ))) + // }) + todo!() } diff --git a/server/src/ui/stats.rs b/server/src/ui/stats.rs index fc4ae64..387ca63 100644 --- a/server/src/ui/stats.rs +++ b/server/src/ui/stats.rs @@ -3,22 +3,10 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ -use super::error::MyError; -use crate::request_info::RequestInfo; -use rocket::{Either, get, response::content::RawHtml, serde::json::Json}; +use crate::{request_info::RequestInfo, ui::error::MyResult}; +use rocket::{get, response::content::RawHtml}; #[get("/stats")] -pub fn r_stats( - ri: RequestInfo, -) -> Result, Json>, MyError> { - let r = stats(&ri.session)?; - - Ok(if ri.accept.is_json() { - Either::Right(Json(r)) - } else { - Either::Left(RawHtml(render_page( - &StatsPage { lang: &ri.lang, r }, - ri.render_info(), - ))) - }) +pub fn r_stats(ri: RequestInfo) -> MyResult> { + todo!() } -- cgit v1.3