/* 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) 2025 metamuffin */ use crate::logic::session::Session; use error::MyResult; use home::rocket_uri_macro_r_home; use jellybase::CONF; use log::debug; use rocket::{ futures::FutureExt, get, http::{ContentType, Header, Status}, response::{self, Redirect, Responder}, Either, Request, Response, }; use std::{ collections::hash_map::DefaultHasher, future::Future, hash::{Hash, Hasher}, io::Cursor, os::unix::prelude::MetadataExt, path::Path, pin::Pin, }; use tokio::{ fs::{read_to_string, File}, io::AsyncRead, }; pub mod account; pub mod admin; pub mod assets; pub mod browser; pub mod error; pub mod home; pub mod node; pub mod player; pub mod search; pub mod sort; pub mod stats; pub mod style; impl<'r, Main: Render> Responder<'r, 'static> for LayoutPage
{ fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { // TODO blocking the event loop here. it seems like there is no other way to // TODO offload this, since the guard references `req` which has a lifetime. // TODO therefore we just block. that is fine since the database is somewhat fast. let lang = lang_from_request(&req); let session = block_on(req.guard::>()).unwrap(); let mut out = String::new(); Scaffold { main: self.content, title: self.title, class: &format!( "{} theme-{:?}", self.class.unwrap_or(""), session .as_ref() .map(|s| s.user.theme) .unwrap_or(Theme::Dark) ), session, lang, } .render(&mut out) .unwrap(); Response::build() .header(ContentType::HTML) .streamed_body(Cursor::new(out)) .ok() } } #[get("/")] pub async fn r_index(sess: Option) -> MyResult>> { 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(LayoutPage { title: "Home".to_string(), content: markup::new! { @markup::raw(&front) }, ..Default::default() })) } } #[get("/favicon.ico")] pub async fn r_favicon() -> MyResult { Ok(File::open(CONF.asset_path.join("favicon.ico")).await?) } pub struct HtmlTemplate<'a>(pub markup::DynRender<'a>); impl<'r> Responder<'r, 'static> for HtmlTemplate<'_> { fn respond_to(self, _req: &'r Request<'_>) -> response::Result<'static> { let mut out = String::new(); self.0.render(&mut out).unwrap(); Response::build() .header(ContentType::HTML) .sized_body(out.len(), Cursor::new(out)) .ok() } } pub struct Defer(Pin + Send>>); impl AsyncRead for Defer { fn poll_read( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &mut tokio::io::ReadBuf<'_>, ) -> std::task::Poll> { match self.0.poll_unpin(cx) { std::task::Poll::Ready(r) => { buf.put_slice(r.as_bytes()); std::task::Poll::Ready(Ok(())) } std::task::Poll::Pending => std::task::Poll::Pending, } } }