mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-01-25 11:27:41 +00:00
proxy pictrs in request.rs (fixes #5270)
This commit is contained in:
parent
f040f9146c
commit
6123659711
8 changed files with 54 additions and 50 deletions
|
@ -15,6 +15,9 @@ use std::sync::Arc;
|
|||
pub struct LemmyContext {
|
||||
pool: ActualDbPool,
|
||||
client: Arc<ClientWithMiddleware>,
|
||||
/// Pictrs requests must bypass proxy. Unfortunately no_proxy can only be set on ClientBuilder
|
||||
/// and not on RequestBuilder, so we need a separate client here.
|
||||
pictrs_client: Arc<ClientWithMiddleware>,
|
||||
secret: Arc<Secret>,
|
||||
rate_limit_cell: RateLimitCell,
|
||||
}
|
||||
|
@ -23,12 +26,14 @@ impl LemmyContext {
|
|||
pub fn create(
|
||||
pool: ActualDbPool,
|
||||
client: ClientWithMiddleware,
|
||||
pictrs_client: ClientWithMiddleware,
|
||||
secret: Secret,
|
||||
rate_limit_cell: RateLimitCell,
|
||||
) -> LemmyContext {
|
||||
LemmyContext {
|
||||
pool,
|
||||
client: Arc::new(client),
|
||||
pictrs_client: Arc::new(pictrs_client),
|
||||
secret: Arc::new(secret),
|
||||
rate_limit_cell,
|
||||
}
|
||||
|
@ -42,6 +47,9 @@ impl LemmyContext {
|
|||
pub fn client(&self) -> &ClientWithMiddleware {
|
||||
&self.client
|
||||
}
|
||||
pub fn pictrs_client(&self) -> &ClientWithMiddleware {
|
||||
&self.pictrs_client
|
||||
}
|
||||
pub fn settings(&self) -> &'static Settings {
|
||||
&SETTINGS
|
||||
}
|
||||
|
@ -70,7 +78,13 @@ impl LemmyContext {
|
|||
|
||||
let rate_limit_cell = RateLimitCell::with_test_config();
|
||||
|
||||
let context = LemmyContext::create(pool, client, secret, rate_limit_cell.clone());
|
||||
let context = LemmyContext::create(
|
||||
pool,
|
||||
client.clone(),
|
||||
client,
|
||||
secret,
|
||||
rate_limit_cell.clone(),
|
||||
);
|
||||
|
||||
FederationConfig::builder()
|
||||
.domain(context.settings().hostname.clone())
|
||||
|
|
|
@ -319,7 +319,7 @@ struct PictrsPurgeResponse {
|
|||
/// - It might not be an image
|
||||
/// - Pictrs might not be set up
|
||||
pub async fn purge_image_from_pictrs(image_url: &Url, context: &LemmyContext) -> LemmyResult<()> {
|
||||
is_image_content_type(context.client(), image_url).await?;
|
||||
is_image_content_type(context.pictrs_client(), image_url).await?;
|
||||
|
||||
let alias = image_url
|
||||
.path_segments()
|
||||
|
@ -334,7 +334,7 @@ pub async fn purge_image_from_pictrs(image_url: &Url, context: &LemmyContext) ->
|
|||
.api_key
|
||||
.ok_or(LemmyErrorType::PictrsApiKeyNotProvided)?;
|
||||
let response = context
|
||||
.client()
|
||||
.pictrs_client()
|
||||
.post(&purge_url)
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.header("x-api-token", pictrs_api_key)
|
||||
|
@ -361,7 +361,7 @@ pub async fn delete_image_from_pictrs(
|
|||
pictrs_config.url, &delete_token, &alias
|
||||
);
|
||||
context
|
||||
.client()
|
||||
.pictrs_client()
|
||||
.delete(&url)
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.send()
|
||||
|
@ -384,7 +384,6 @@ async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> L
|
|||
};
|
||||
|
||||
// fetch remote non-pictrs images for persistent thumbnail link
|
||||
// TODO: should limit size once supported by pictrs
|
||||
let fetch_url = format!(
|
||||
"{}image/download?url={}&resize={}",
|
||||
pictrs_config.url,
|
||||
|
@ -393,7 +392,7 @@ async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> L
|
|||
);
|
||||
|
||||
let res = context
|
||||
.client()
|
||||
.pictrs_client()
|
||||
.get(&fetch_url)
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.send()
|
||||
|
@ -439,7 +438,7 @@ pub async fn fetch_pictrs_proxied_image_details(
|
|||
let proxy_url = format!("{pictrs_url}image/original?proxy={encoded_image_url}");
|
||||
|
||||
context
|
||||
.client()
|
||||
.pictrs_client()
|
||||
.get(&proxy_url)
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.send()
|
||||
|
@ -450,7 +449,7 @@ pub async fn fetch_pictrs_proxied_image_details(
|
|||
let details_url = format!("{pictrs_url}image/details/original?proxy={encoded_image_url}");
|
||||
|
||||
let res = context
|
||||
.client()
|
||||
.pictrs_client()
|
||||
.get(&details_url)
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.send()
|
||||
|
|
|
@ -17,7 +17,6 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
|
||||
pub async fn delete_site_icon(
|
||||
context: Data<LemmyContext>,
|
||||
|
@ -126,7 +125,6 @@ pub async fn delete_user_banner(
|
|||
pub async fn delete_image(
|
||||
data: Json<DeleteImageParams>,
|
||||
context: Data<LemmyContext>,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
// require login
|
||||
_local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
|
@ -136,7 +134,12 @@ pub async fn delete_image(
|
|||
pictrs_config.url, &data.token, &data.filename
|
||||
);
|
||||
|
||||
client.delete(url).send().await?.error_for_status()?;
|
||||
context
|
||||
.pictrs_client()
|
||||
.delete(url)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
LocalImage::delete_by_alias(&mut context.pool(), &data.filename).await?;
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::source::{images::RemoteImage, local_site::LocalSite};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
use url::Url;
|
||||
|
||||
pub async fn get_image(
|
||||
|
@ -23,7 +22,6 @@ pub async fn get_image(
|
|||
req: HttpRequest,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
context: Data<LemmyContext>,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
// block access to images if instance is private
|
||||
if local_user_view.is_none() {
|
||||
|
@ -48,13 +46,12 @@ pub async fn get_image(
|
|||
url
|
||||
};
|
||||
|
||||
do_get_image(processed_url, req, client).await
|
||||
do_get_image(processed_url, req, &context).await
|
||||
}
|
||||
|
||||
pub async fn image_proxy(
|
||||
Query(params): Query<ImageProxyParams>,
|
||||
req: HttpRequest,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Either<HttpResponse<()>, HttpResponse<BoxBody>>> {
|
||||
let url = Url::parse(¶ms.url)?;
|
||||
|
@ -89,7 +86,7 @@ pub async fn image_proxy(
|
|||
} else {
|
||||
// Proxy the image data through Lemmy
|
||||
Ok(Either::Right(
|
||||
do_get_image(processed_url, req, client).await?,
|
||||
do_get_image(processed_url, req, &context).await?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -97,9 +94,9 @@ pub async fn image_proxy(
|
|||
pub(super) async fn do_get_image(
|
||||
url: String,
|
||||
req: HttpRequest,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
let mut client_req = adapt_request(&req, url, client);
|
||||
let mut client_req = adapt_request(&req, url, context);
|
||||
|
||||
if let Some(addr) = req.head().peer_addr {
|
||||
client_req = client_req.header("X-Forwarded-For", addr.to_string());
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
use actix_web::web::*;
|
||||
use lemmy_api_common::{context::LemmyContext, SuccessResponse};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
|
||||
pub mod delete;
|
||||
pub mod download;
|
||||
pub mod upload;
|
||||
mod utils;
|
||||
|
||||
pub async fn pictrs_health(
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
pub async fn pictrs_health(context: Data<LemmyContext>) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let pictrs_config = context.settings().pictrs()?;
|
||||
let url = format!("{}healthz", pictrs_config.url);
|
||||
|
||||
client.get(url).send().await?.error_for_status()?;
|
||||
context
|
||||
.pictrs_client()
|
||||
.get(url)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ use lemmy_db_schema::{
|
|||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use reqwest::Body;
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
use std::time::Duration;
|
||||
use UploadType::*;
|
||||
|
||||
|
@ -34,7 +33,6 @@ pub async fn upload_image(
|
|||
req: HttpRequest,
|
||||
body: Payload,
|
||||
local_user_view: LocalUserView,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<UploadImageResponse>> {
|
||||
if context.settings().pictrs()?.image_upload_disabled {
|
||||
|
@ -42,7 +40,7 @@ pub async fn upload_image(
|
|||
}
|
||||
|
||||
Ok(Json(
|
||||
do_upload_image(req, body, Other, &local_user_view, client, &context).await?,
|
||||
do_upload_image(req, body, Other, &local_user_view, &context).await?,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -50,10 +48,9 @@ pub async fn upload_user_avatar(
|
|||
req: HttpRequest,
|
||||
body: Payload,
|
||||
local_user_view: LocalUserView,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let image = do_upload_image(req, body, Avatar, &local_user_view, client, &context).await?;
|
||||
let image = do_upload_image(req, body, Avatar, &local_user_view, &context).await?;
|
||||
delete_old_image(&local_user_view.person.avatar, &context).await?;
|
||||
|
||||
let form = PersonUpdateForm {
|
||||
|
@ -69,10 +66,9 @@ pub async fn upload_user_banner(
|
|||
req: HttpRequest,
|
||||
body: Payload,
|
||||
local_user_view: LocalUserView,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let image = do_upload_image(req, body, Banner, &local_user_view, client, &context).await?;
|
||||
let image = do_upload_image(req, body, Banner, &local_user_view, &context).await?;
|
||||
delete_old_image(&local_user_view.person.banner, &context).await?;
|
||||
|
||||
let form = PersonUpdateForm {
|
||||
|
@ -89,13 +85,12 @@ pub async fn upload_community_icon(
|
|||
query: Query<CommunityIdQuery>,
|
||||
body: Payload,
|
||||
local_user_view: LocalUserView,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let community: Community = Community::read(&mut context.pool(), query.id).await?;
|
||||
is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.id).await?;
|
||||
|
||||
let image = do_upload_image(req, body, Avatar, &local_user_view, client, &context).await?;
|
||||
let image = do_upload_image(req, body, Avatar, &local_user_view, &context).await?;
|
||||
delete_old_image(&community.icon, &context).await?;
|
||||
|
||||
let form = CommunityUpdateForm {
|
||||
|
@ -112,13 +107,12 @@ pub async fn upload_community_banner(
|
|||
query: Query<CommunityIdQuery>,
|
||||
body: Payload,
|
||||
local_user_view: LocalUserView,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
let community: Community = Community::read(&mut context.pool(), query.id).await?;
|
||||
is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.id).await?;
|
||||
|
||||
let image = do_upload_image(req, body, Banner, &local_user_view, client, &context).await?;
|
||||
let image = do_upload_image(req, body, Banner, &local_user_view, &context).await?;
|
||||
delete_old_image(&community.banner, &context).await?;
|
||||
|
||||
let form = CommunityUpdateForm {
|
||||
|
@ -134,13 +128,12 @@ pub async fn upload_site_icon(
|
|||
req: HttpRequest,
|
||||
body: Payload,
|
||||
local_user_view: LocalUserView,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
is_admin(&local_user_view)?;
|
||||
let site = Site::read_local(&mut context.pool()).await?;
|
||||
|
||||
let image = do_upload_image(req, body, Avatar, &local_user_view, client, &context).await?;
|
||||
let image = do_upload_image(req, body, Avatar, &local_user_view, &context).await?;
|
||||
delete_old_image(&site.icon, &context).await?;
|
||||
|
||||
let form = SiteUpdateForm {
|
||||
|
@ -156,13 +149,12 @@ pub async fn upload_site_banner(
|
|||
req: HttpRequest,
|
||||
body: Payload,
|
||||
local_user_view: LocalUserView,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<Json<SuccessResponse>> {
|
||||
is_admin(&local_user_view)?;
|
||||
let site = Site::read_local(&mut context.pool()).await?;
|
||||
|
||||
let image = do_upload_image(req, body, Banner, &local_user_view, client, &context).await?;
|
||||
let image = do_upload_image(req, body, Banner, &local_user_view, &context).await?;
|
||||
delete_old_image(&site.banner, &context).await?;
|
||||
|
||||
let form = SiteUpdateForm {
|
||||
|
@ -179,13 +171,12 @@ pub async fn do_upload_image(
|
|||
body: Payload,
|
||||
upload_type: UploadType,
|
||||
local_user_view: &LocalUserView,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<UploadImageResponse> {
|
||||
let pictrs = context.settings().pictrs()?;
|
||||
let image_url = format!("{}image", pictrs.url);
|
||||
|
||||
let mut client_req = adapt_request(&req, image_url, client);
|
||||
let mut client_req = adapt_request(&req, image_url, &context);
|
||||
|
||||
client_req = match upload_type {
|
||||
Avatar => {
|
||||
|
|
|
@ -11,17 +11,18 @@ use http::HeaderValue;
|
|||
use lemmy_api_common::{context::LemmyContext, request::delete_image_from_pictrs};
|
||||
use lemmy_db_schema::{newtypes::DbUrl, source::images::LocalImage};
|
||||
use lemmy_utils::{error::LemmyResult, REQWEST_TIMEOUT};
|
||||
use reqwest_middleware::{ClientWithMiddleware, RequestBuilder};
|
||||
use reqwest_middleware::RequestBuilder;
|
||||
|
||||
pub(super) fn adapt_request(
|
||||
request: &HttpRequest,
|
||||
url: String,
|
||||
client: Data<ClientWithMiddleware>,
|
||||
context: &LemmyContext,
|
||||
) -> RequestBuilder {
|
||||
// remove accept-encoding header so that pictrs doesn't compress the response
|
||||
const INVALID_HEADERS: &[HeaderName] = &[ACCEPT_ENCODING, HOST];
|
||||
|
||||
let client_request = client
|
||||
let client_request = context
|
||||
.pictrs_client()
|
||||
.request(convert_method(request.method()), url)
|
||||
.timeout(REQWEST_TIMEOUT);
|
||||
|
||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -195,9 +195,13 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
|||
let client = ClientBuilder::new(client_builder(&SETTINGS).build()?)
|
||||
.with(TracingMiddleware::default())
|
||||
.build();
|
||||
let pictrs_client = ClientBuilder::new(client_builder(&SETTINGS).no_proxy().build()?)
|
||||
.with(TracingMiddleware::default())
|
||||
.build();
|
||||
let context = LemmyContext::create(
|
||||
pool.clone(),
|
||||
client.clone(),
|
||||
pictrs_client,
|
||||
secret.clone(),
|
||||
rate_limit_cell.clone(),
|
||||
);
|
||||
|
@ -330,11 +334,6 @@ fn create_http_server(
|
|||
.build()
|
||||
.map_err(|e| LemmyErrorType::Unknown(format!("Should always be buildable: {e}")))?;
|
||||
|
||||
// Pictrs cannot use proxy
|
||||
let pictrs_client = ClientBuilder::new(client_builder(&SETTINGS).no_proxy().build()?)
|
||||
.with(TracingMiddleware::default())
|
||||
.build();
|
||||
|
||||
// Create Http server
|
||||
let bind = (settings.bind, settings.port);
|
||||
let server = HttpServer::new(move || {
|
||||
|
@ -355,7 +354,6 @@ fn create_http_server(
|
|||
.wrap(ErrorHandlers::new().default_handler(jsonify_plain_text_errors))
|
||||
.app_data(Data::new(context.clone()))
|
||||
.app_data(Data::new(rate_limit_cell.clone()))
|
||||
.app_data(Data::new(pictrs_client.clone()))
|
||||
.wrap(FederationMiddleware::new(federation_config.clone()))
|
||||
.wrap(SessionMiddleware::new(context.clone()))
|
||||
.wrap(Condition::new(
|
||||
|
|
Loading…
Reference in a new issue