diff --git a/README.md b/README.md index 12af0f0..0f63d51 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ insist on writing my own containers). _How hard can it be_ I thought and went on hacking something together. The result does at most one tenth of what cops can do but luckily enough it is the part I need for myself. -![Screenshot](screenshot.jpg) +![Screenshot](screenshot.png) # Building diff --git a/little-hesinde/src/handlers/books.rs b/little-hesinde/src/handlers/books.rs index eace2ab..9944821 100644 --- a/little-hesinde/src/handlers/books.rs +++ b/little-hesinde/src/handlers/books.rs @@ -4,20 +4,12 @@ use std::sync::Arc; use calibre_db::data::pagination::SortOrder; use poem::{ - error::NotFoundError, handler, web::{Data, Path}, Response, }; -use tokio::fs::File; -use crate::{ - app_state::AppState, - data::book::{Book, Format}, - handlers::error::HandlerError, - opds::media_type::MediaType, - Accept, -}; +use crate::{app_state::AppState, Accept}; /// Handle a request for multiple books, starting at the first. #[handler] @@ -39,31 +31,6 @@ pub async fn handler( books(&accept, &state, Some(&cursor), &sort_order).await } -/// Handle a request for a book with id `id` in format `format`. -#[handler] -pub async fn handler_download( - Path((id, format)): Path<(u64, String)>, - state: Data<&Arc>, -) -> Result { - let book = state - .calibre - .scalar_book(id) - .map_err(HandlerError::DataError)?; - let book = Book::full_book(&book, &state).ok_or(NotFoundError)?; - let format = Format(format); - let file_name = book.formats.get(&format).ok_or(NotFoundError)?; - let file_path = state - .config - .library_path - .join(book.data.path) - .join(file_name); - let mut file = File::open(file_path).await.map_err(|_| NotFoundError)?; - let content_type: MediaType = format.into(); - let content_type = format!("{content_type}"); - - crate::handlers::download::handler(file_name, file, &content_type).await -} - async fn books( accept: &Accept, state: &Arc, diff --git a/little-hesinde/src/handlers/cover.rs b/little-hesinde/src/handlers/cover.rs index a97514b..4a0696d 100644 --- a/little-hesinde/src/handlers/cover.rs +++ b/little-hesinde/src/handlers/cover.rs @@ -1,24 +1,31 @@ //! Handle requests for cover images. -use std::sync::Arc; +use std::{fs::File, io::Read, sync::Arc}; -use crate::{app_state::AppState, handlers::error::HandlerError}; use poem::{ error::NotFoundError, handler, - web::{headers::ContentType, Data, Path}, - Response, + web::{headers::ContentType, Data, Path, WithContentType}, + IntoResponse, }; -use tokio::fs::File; + +use crate::{app_state::AppState, handlers::error::HandlerError}; /// Handle a request for the cover image of book with id `id`. #[handler] -pub async fn handler(id: Path, state: Data<&Arc>) -> Result { +pub async fn handler( + id: Path, + state: Data<&Arc>, +) -> Result>, poem::Error> { let book = state .calibre .scalar_book(*id) .map_err(HandlerError::DataError)?; let cover_path = state.config.library_path.join(book.path).join("cover.jpg"); - let mut cover = File::open(cover_path).await.map_err(|_| NotFoundError)?; - crate::handlers::download::handler("cover.jpg", cover, &ContentType::jpeg().to_string()).await + let mut cover = File::open(cover_path).map_err(|_| NotFoundError)?; + + let mut data = Vec::new(); + cover.read_to_end(&mut data).map_err(|_| NotFoundError)?; + + Ok(data.with_content_type(ContentType::jpeg().to_string())) } diff --git a/little-hesinde/src/handlers/download.rs b/little-hesinde/src/handlers/download.rs index ef9290a..1f25c56 100644 --- a/little-hesinde/src/handlers/download.rs +++ b/little-hesinde/src/handlers/download.rs @@ -1,23 +1,50 @@ //! Handle requests for specific formats of a book. -use tokio::io::AsyncRead; +use std::sync::Arc; -use poem::{Body, IntoResponse, Response}; +use tokio::fs::File; + +use poem::{ + error::NotFoundError, + handler, + web::{Data, Path}, + Body, IntoResponse, Response, +}; use tokio_util::io::ReaderStream; -/// Handle a request for file. -/// -/// Must not be used directly from a route as that makes it vulnerable to path traversal attacks. -pub async fn handler( - file_name: &str, - reader: A, - content_type: &str, +use crate::{ + app_state::AppState, + data::book::{Book, Format}, + handlers::error::HandlerError, + opds::media_type::MediaType, +}; + +/// Handle a request for a book with id `id` in format `format`. +#[handler] +pub async fn handler( + Path((id, format)): Path<(u64, String)>, + state: Data<&Arc>, ) -> Result { - let stream = ReaderStream::new(reader); + let book = state + .calibre + .scalar_book(id) + .map_err(HandlerError::DataError)?; + let book = Book::full_book(&book, &state).ok_or(NotFoundError)?; + let format = Format(format); + let file_name = book.formats.get(&format).ok_or(NotFoundError)?; + let file_path = state + .config + .library_path + .join(book.data.path) + .join(file_name); + + let mut file = File::open(file_path).await.map_err(|_| NotFoundError)?; + let stream = ReaderStream::new(file); let body = Body::from_bytes_stream(stream); + let content_type: MediaType = format.into(); Ok(body - .with_content_type(content_type) + .with_content_type(format!("{content_type}")) .with_header("Content-Disposition", format!("filename=\"{file_name}\"")) .into_response()) } diff --git a/little-hesinde/src/handlers/source_archive.rs b/little-hesinde/src/handlers/source_archive.rs index 7cbffa4..4fa0498 100644 --- a/little-hesinde/src/handlers/source_archive.rs +++ b/little-hesinde/src/handlers/source_archive.rs @@ -1,11 +1,20 @@ use crate::{APP_NAME, VERSION}; -use poem::{handler, Response}; +use poem::{handler, Body, IntoResponse, Response}; +use tokio_util::io::ReaderStream; const SOURCE_ARCHIVE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/archive.zip")); /// Handle a request for source code of the server.. #[handler] pub async fn handler() -> Result { - let file_name = format!("{APP_NAME}-{VERSION}.zip"); - crate::handlers::download::handler(&file_name, SOURCE_ARCHIVE, "application/zip").await + let stream = ReaderStream::new(SOURCE_ARCHIVE); + let body = Body::from_bytes_stream(stream); + + Ok(body + .with_content_type("application/zip") + .with_header( + "Content-Disposition", + format!("filename=\"{APP_NAME}-{VERSION}.zip\""), + ) + .into_response()) } diff --git a/little-hesinde/src/lib.rs b/little-hesinde/src/lib.rs index 89b9394..4983245 100644 --- a/little-hesinde/src/lib.rs +++ b/little-hesinde/src/lib.rs @@ -109,7 +109,7 @@ pub async fn run(config: Config) -> Result<(), std::io::Error> { get(handlers::authors::handler), ) .at("/cover/:id", get(handlers::cover::handler)) - .at("/book/:id/:format", get(handlers::books::handler_download)) + .at("/book/:id/:format", get(handlers::download::handler)) .at("/archive", get(handlers::source_archive::handler)) .nest("/static", EmbeddedFilesEndpoint::::new()) .data(Accept::Html); diff --git a/screenshot.jpg b/screenshot.jpg deleted file mode 100644 index 3e4a18c..0000000 Binary files a/screenshot.jpg and /dev/null differ diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..da98654 Binary files /dev/null and b/screenshot.png differ