aboutsummaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-01-25 23:32:53 +0100
committermetamuffin <metamuffin@disroot.org>2026-01-25 23:32:53 +0100
commit783d3598753bf84756296a2016e5dab30300519b (patch)
treef7eaf276b65de8aab10db21d27e534f775d83167 /server
parent5075aede44cb8ab2df10e6debba38483e8d11e96 (diff)
downloadjellything-783d3598753bf84756296a2016e5dab30300519b.tar
jellything-783d3598753bf84756296a2016e5dab30300519b.tar.bz2
jellything-783d3598753bf84756296a2016e5dab30300519b.tar.zst
work on login
Diffstat (limited to 'server')
-rw-r--r--server/Cargo.toml2
-rw-r--r--server/src/api.rs8
-rw-r--r--server/src/compat/youtube.rs74
-rw-r--r--server/src/logic/playersync.rs6
-rw-r--r--server/src/logic/stream.rs133
-rw-r--r--server/src/logic/userdata.rs101
-rw-r--r--server/src/main.rs2
-rw-r--r--server/src/request_info.rs9
-rw-r--r--server/src/routes.rs92
-rw-r--r--server/src/ui/account/mod.rs22
-rw-r--r--server/src/ui/assets.rs77
-rw-r--r--server/src/ui/error.rs36
-rw-r--r--server/src/ui/mod.rs79
-rw-r--r--server/src/ui/node.rs44
-rw-r--r--server/src/ui/search.rs41
-rw-r--r--server/src/ui/stats.rs20
16 files changed, 323 insertions, 423 deletions
diff --git a/server/Cargo.toml b/server/Cargo.toml
index 534dce5..f12b8fe 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -25,7 +25,7 @@ env_logger = "0.11.8"
futures = "0.3.31"
log = { workspace = true }
rand = "0.9.2"
-rocket = { version = "0.5", features = ["secrets", "json"] }
+rocket = { version = "0.5", features = ["json"] }
rocket_ws = "0.1"
serde = { version = "1.0.228", features = ["derive", "rc"] }
serde_json = "1.0.145"
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 <metamuffin.org>
*/
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?<since>")]
-pub fn r_nodes_modified_since(session: A<Session>, since: u64) -> MyResult<Json<Vec<NodeID>>> {
- let nodes = get_nodes_modified_since(&session.0, since)?;
- Ok(Json(nodes))
+pub fn r_nodes_modified_since(ri: RequestInfo<'_>, since: u64) -> MyResult<Json<Vec<String>>> {
+ // 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 <metamuffin.org>
*/
-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?<v>")]
-pub fn r_youtube_watch(session: A<Session>, v: &str) -> MyResult<Redirect> {
- 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<Redirect> {
+ // 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/<id>")]
-pub fn r_youtube_channel(session: A<Session>, id: &str) -> MyResult<Redirect> {
- 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<Redirect> {
+ // 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/<v>")]
-pub fn r_youtube_embed(session: A<Session>, v: &str) -> MyResult<Redirect> {
- 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<Redirect> {
+ // 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 <metamuffin.org>
*/
-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?<spec..>")]
pub async fn r_stream_head(
- _sess: A<Session>,
+ _sess: RequestInfo<'_>,
_id: &str,
spec: BTreeMap<String, String>,
) -> Result<Either<StreamResponse, Redirect>, MyError> {
@@ -45,27 +43,27 @@ pub async fn r_stream_head(
#[get("/n/<id>/stream?<spec..>")]
pub async fn r_stream(
- session: A<Session>,
+ session: RequestInfo<'_>,
id: &str,
range: Option<RequestRange>,
spec: BTreeMap<String, String>,
) -> Result<Either<StreamResponse, RedirectResponse>, 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 <metamuffin.org>
*/
-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/<id>/userdata")]
-pub fn r_node_userdata(session: A<Session>, id: A<NodeID>) -> MyResult<Json<NodeUserData>> {
- let u = get_node(&session.0, id.0, false, false, NodeFilterSort::default())?.userdata;
- Ok(Json(u))
-}
+// #[get("/n/<id>/userdata")]
+// pub fn r_node_userdata(session: A<Session>, id: A<NodeID>) -> MyResult<Json<NodeUserData>> {
+// let u = get_node(&session.0, id.0, false, false, NodeFilterSort::default())?.userdata;
+// Ok(Json(u))
+// }
-#[post("/n/<id>/watched?<state>")]
-pub async fn r_node_userdata_watched(
- session: A<Session>,
- id: A<NodeID>,
- state: UrlWatchedState,
-) -> MyResult<Redirect> {
- 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/<id>/watched?<state>")]
+// pub async fn r_node_userdata_watched(
+// session: A<Session>,
+// id: A<NodeID>,
+// state: UrlWatchedState,
+// ) -> MyResult<Redirect> {
+// 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/<id>/update_rating", data = "<form>")]
-pub async fn r_node_userdata_rating(
- session: A<Session>,
- id: A<NodeID>,
- form: Form<UpdateRating>,
-) -> MyResult<Redirect> {
- update_node_userdata_rating(&session.0, id.0, form.rating)?;
- Ok(Redirect::found(u_node_id(id.0)))
-}
+// #[post("/n/<id>/update_rating", data = "<form>")]
+// pub async fn r_node_userdata_rating(
+// session: A<Session>,
+// id: A<NodeID>,
+// form: Form<UpdateRating>,
+// ) -> MyResult<Redirect> {
+// update_node_userdata_rating(&session.0, id.0, form.rating)?;
+// Ok(Redirect::found(u_node_id(id.0)))
+// }
-#[post("/n/<id>/progress?<t>")]
-pub async fn r_node_userdata_progress(session: A<Session>, id: A<NodeID>, t: f64) -> MyResult<()> {
- update_node_userdata_watched_progress(&session.0, id.0, t)?;
- Ok(())
-}
+// #[post("/n/<id>/progress?<t>")]
+// pub async fn r_node_userdata_progress(session: A<Session>, id: A<NodeID>, 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<Object<'a>> {
+ 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<State>) -> Rocket<Build> {
+pub(super) fn build_rocket(state: Arc<State>) -> Rocket<Build> {
rocket::build()
.configure(Config {
address: std::env::var("BIND_ADDR")
@@ -84,47 +64,47 @@ pub fn build_rocket(state: Arc<State>) -> Rocket<Build> {
"/",
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 <metamuffin.org>
*/
-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<String> {
- 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<String> {
+ 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 <metamuffin.org>
*/
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/<key..>?<size>")]
+#[get("/image/<path..>?<size>")]
pub async fn r_image(
- _session: A<Session>,
- key: A<Asset>,
+ ri: RequestInfo<'_>,
+ path: PathBuf,
size: Option<usize>,
) -> 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/<id>/image/<slot>?<size>")]
-pub async fn r_item_poster(
- session: A<Session>,
- id: A<NodeID>,
- slot: &str,
- size: Option<usize>,
-) -> MyResult<Redirect> {
- 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/<id>/image/<slot>?<size>")]
+// pub async fn r_item_poster(
+// session: A<Session>,
+// id: A<NodeID>,
+// slot: &str,
+// size: Option<usize>,
+// ) -> MyResult<Redirect> {
+// 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/<id>/thumbnail?<t>&<size>")]
-pub async fn r_node_thumbnail(
- session: A<Session>,
- id: A<NodeID>,
- t: f64,
- size: Option<usize>,
-) -> MyResult<Redirect> {
- let picture = get_node_thumbnail(&session.0, id.0, t).await?;
- Ok(Redirect::permanent(rocket::uri!(r_image(picture, size))))
-}
+// #[get("/n/<id>/thumbnail?<t>&<size>")]
+// pub async fn r_node_thumbnail(
+// session: A<Session>,
+// id: A<NodeID>,
+// t: f64,
+// size: Option<usize>,
+// ) -> MyResult<Redirect> {
+// 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 <metamuffin.org>
*/
-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<Vec<u8>> = 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<String> {
catch_with_message(format!("{status}"))
}
fn catch_with_message(message: String) -> RawHtml<String> {
- 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 <metamuffin.org>
*/
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<A<Session>>,
-) -> MyResult<Either<Redirect, RawHtml<String>>> {
- 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<A<Session>>,
+// ) -> MyResult<Either<Redirect, RawHtml<String>>> {
+// 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<File> {
- Ok(File::open(CONF.asset_path.join("favicon.ico")).await?)
+pub async fn r_favicon(s: &rocket::State<Arc<State>>) -> MyResult<File> {
+ Ok(File::open(s.config.asset_path.join("favicon.ico")).await?)
}
pub struct Defer(Pin<Box<dyn Future<Output = String> + 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/<id>?<parents>&<children>&<filter..>")]
-pub async fn r_node(
- ri: RequestInfo<'_>,
- id: A<NodeID>,
- filter: Option<ANodeFilterSort>,
- parents: bool,
- children: bool,
-) -> MyResult<Either<RawHtml<String>, Json<ApiNodeResponse>>> {
- let filter: Option<NodeFilterSort> = 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/<slug>")]
+pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<RawHtml<String>> {
+ 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?<query>&<page>")]
pub async fn r_search(
- ri: RequestInfo,
+ ri: RequestInfo<'_>,
query: Option<&str>,
page: Option<usize>,
-) -> MyResult<Either<RawHtml<String>, Json<ApiSearchResponse>>> {
- let r = query
- .map(|query| search(&ri.session, query, page))
- .transpose()?;
+) -> MyResult<RawHtml<String>> {
+ // 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 <metamuffin.org>
*/
-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<Either<RawHtml<String>, Json<ApiStatsResponse>>, 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<RawHtml<String>> {
+ todo!()
}