From 45f5448ecec02a189ef94ec4ca31bb530e4dcf2a Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 26 Oct 2023 12:39:00 +0200 Subject: [PATCH] proxy images through pictrs --- crates/routes/src/image_proxy.rs | 30 -------------------------- crates/routes/src/images.rs | 36 +++++++++++++++++++++++++++++--- crates/routes/src/lib.rs | 1 - src/api_routes_http.rs | 6 ------ 4 files changed, 33 insertions(+), 40 deletions(-) delete mode 100644 crates/routes/src/image_proxy.rs diff --git a/crates/routes/src/image_proxy.rs b/crates/routes/src/image_proxy.rs deleted file mode 100644 index 185ae830e..000000000 --- a/crates/routes/src/image_proxy.rs +++ /dev/null @@ -1,30 +0,0 @@ -use actix_web::{web, web::Query, HttpResponse}; -use lemmy_api_common::context::LemmyContext; -use lemmy_db_schema::source::images::RemoteImage; -use lemmy_utils::error::LemmyResult; -use serde::Deserialize; -use url::Url; -use urlencoding::decode; - -#[derive(Deserialize)] -pub struct ImageProxyParams { - url: String, -} - -pub async fn image_proxy( - Query(params): Query, - context: web::Data, -) -> LemmyResult { - let url = Url::parse(&decode(¶ms.url)?)?; - - // Check that url corresponds to a federated image so that this can't be abused as a proxy - // for arbitrary purposes. - RemoteImage::validate(&mut context.pool(), url.clone().into()).await?; - - // TODO: Once pictrs 0.5 is out, use it for proxying like `GET /image/original?proxy={url}`. In - // case pictrs is unavailable, fallback to this impl. - // https://git.asonix.dog/asonix/pict-rs/#api - let image_response = context.client().get(url).send().await?; - - Ok(HttpResponse::Ok().streaming(image_response.bytes_stream())) -} diff --git a/crates/routes/src/images.rs b/crates/routes/src/images.rs index 36593e6c1..26ca3807b 100644 --- a/crates/routes/src/images.rs +++ b/crates/routes/src/images.rs @@ -6,6 +6,7 @@ use actix_web::{ StatusCode, }, web, + web::Query, Error, HttpRequest, HttpResponse, @@ -13,15 +14,17 @@ use actix_web::{ use futures::stream::{Stream, StreamExt}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::{ - images::{LocalImage, LocalImageForm}, + images::{LocalImage, LocalImageForm, RemoteImage}, local_site::LocalSite, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::{rate_limit::RateLimitCell, REQWEST_TIMEOUT}; +use lemmy_utils::{error::LemmyResult, rate_limit::RateLimitCell, REQWEST_TIMEOUT}; use reqwest::Body; use reqwest_middleware::{ClientWithMiddleware, RequestBuilder}; use serde::{Deserialize, Serialize}; use std::time::Duration; +use url::Url; +use urlencoding::decode; pub fn config( cfg: &mut web::ServiceConfig, @@ -37,7 +40,12 @@ pub fn config( ) // This has optional query params: /image/{filename}?format=jpg&thumbnail=256 .service(web::resource("/pictrs/image/{filename}").route(web::get().to(full_res))) - .service(web::resource("/pictrs/image/delete/{token}/{filename}").route(web::get().to(delete))); + .service(web::resource("/pictrs/image/delete/{token}/{filename}").route(web::get().to(delete))) + .service( + web::scope("/api/v3") + .wrap(rate_limit.message()) + .route("image_proxy", web::post().to(image_proxy)), + ); } #[derive(Debug, Serialize, Deserialize)] @@ -228,6 +236,28 @@ async fn delete( Ok(HttpResponse::build(res.status()).body(BodyStream::new(res.bytes_stream()))) } +#[derive(Deserialize)] +pub struct ImageProxyParams { + url: String, +} + +pub async fn image_proxy( + Query(params): Query, + context: web::Data, +) -> LemmyResult { + let url = Url::parse(&decode(¶ms.url)?)?; + + // Check that url corresponds to a federated image so that this can't be abused as a proxy + // for arbitrary purposes. + RemoteImage::validate(&mut context.pool(), url.clone().into()).await?; + + let pictrs_config = context.settings().pictrs_config()?; + let url = format!("{}image/original?proxy={}", pictrs_config.url, ¶ms.url); + let image_response = context.client().get(url).send().await?; + + Ok(HttpResponse::Ok().streaming(image_response.bytes_stream())) +} + fn make_send(mut stream: S) -> impl Stream + Send + Unpin + 'static where S: Stream + Unpin + 'static, diff --git a/crates/routes/src/lib.rs b/crates/routes/src/lib.rs index 0b9642811..ec28fda45 100644 --- a/crates/routes/src/lib.rs +++ b/crates/routes/src/lib.rs @@ -3,7 +3,6 @@ use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::LemmyError; pub mod feeds; -pub mod image_proxy; pub mod images; pub mod nodeinfo; pub mod webfinger; diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index 5e66723a8..fb784b3b3 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -124,17 +124,11 @@ use lemmy_apub::api::{ search::search, user_settings_backup::{export_settings, import_settings}, }; -use lemmy_routes::image_proxy::image_proxy; use lemmy_utils::rate_limit::RateLimitCell; pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { cfg.service( web::scope("/api/v3") - .service( - web::scope("") - .wrap(rate_limit.message()) - .route("image_proxy", web::post().to(image_proxy)), - ) // Site .service( web::scope("/site")