Move into subfolders

This commit is contained in:
Felix Ableitner 2024-12-12 16:06:51 +01:00
parent 56ab67f804
commit 05843fb7b5
5 changed files with 78 additions and 69 deletions

View file

@ -2,7 +2,6 @@ use actix_web::{
body::{BodyStream, BoxBody}, body::{BodyStream, BoxBody},
http::{ http::{
header::{HeaderName, ACCEPT_ENCODING, HOST}, header::{HeaderName, ACCEPT_ENCODING, HOST},
Method,
StatusCode, StatusCode,
}, },
web::*, web::*,
@ -10,8 +9,6 @@ use actix_web::{
HttpResponse, HttpResponse,
Responder, Responder,
}; };
use futures::stream::{Stream, StreamExt};
use http::HeaderValue;
use lemmy_api_common::{context::LemmyContext, request::PictrsResponse}; use lemmy_api_common::{context::LemmyContext, request::PictrsResponse};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
images::{LocalImage, LocalImageForm, RemoteImage}, images::{LocalImage, LocalImageForm, RemoteImage},
@ -24,6 +21,9 @@ use reqwest_middleware::RequestBuilder;
use serde::Deserialize; use serde::Deserialize;
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
use utils::{convert_header, convert_method, convert_status, make_send};
mod utils;
trait ProcessUrl { trait ProcessUrl {
/// If thumbnail or format is given, this uses the pictrs process endpoint. /// If thumbnail or format is given, this uses the pictrs process endpoint.
@ -223,7 +223,10 @@ pub async fn delete_image(
Ok(HttpResponse::build(convert_status(res.status())).body(BodyStream::new(res.bytes_stream()))) Ok(HttpResponse::build(convert_status(res.status())).body(BodyStream::new(res.bytes_stream())))
} }
pub async fn pictrs_healthz(req: HttpRequest, context: Data<LemmyContext>) -> LemmyResult<HttpResponse> { pub async fn pictrs_healthz(
req: HttpRequest,
context: Data<LemmyContext>,
) -> LemmyResult<HttpResponse> {
let pictrs_config = context.settings().pictrs_config()?; let pictrs_config = context.settings().pictrs_config()?;
let url = format!("{}healthz", pictrs_config.url); let url = format!("{}healthz", pictrs_config.url);
@ -264,60 +267,3 @@ pub async fn image_proxy(
Ok(Either::Right(image(processed_url, req, &context).await?)) Ok(Either::Right(image(processed_url, req, &context).await?))
} }
} }
fn make_send<S>(mut stream: S) -> impl Stream<Item = S::Item> + Send + Unpin + 'static
where
S: Stream + Unpin + 'static,
S::Item: Send,
{
// NOTE: the 8 here is arbitrary
let (tx, rx) = tokio::sync::mpsc::channel(8);
// NOTE: spawning stream into a new task can potentially hit this bug:
// - https://github.com/actix/actix-web/issues/1679
//
// Since 4.0.0-beta.2 this issue is incredibly less frequent. I have not personally reproduced it.
// That said, it is still technically possible to encounter.
actix_web::rt::spawn(async move {
while let Some(res) = stream.next().await {
if tx.send(res).await.is_err() {
break;
}
}
});
SendStream { rx }
}
struct SendStream<T> {
rx: tokio::sync::mpsc::Receiver<T>,
}
impl<T> Stream for SendStream<T>
where
T: Send,
{
type Item = T;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
std::pin::Pin::new(&mut self.rx).poll_recv(cx)
}
}
// TODO: remove these conversions after actix-web upgrades to http 1.0
#[allow(clippy::expect_used)]
fn convert_status(status: http::StatusCode) -> StatusCode {
StatusCode::from_u16(status.as_u16()).expect("status can be converted")
}
#[allow(clippy::expect_used)]
fn convert_method(method: &Method) -> http::Method {
http::Method::from_bytes(method.as_str().as_bytes()).expect("method can be converted")
}
fn convert_header<'a>(name: &'a http::HeaderName, value: &'a HeaderValue) -> (&'a str, &'a [u8]) {
(name.as_str(), value.as_bytes())
}

View file

@ -0,0 +1,63 @@
use actix_web::http::{Method, StatusCode};
use futures::stream::{Stream, StreamExt};
use http::HeaderValue;
pub(super) fn make_send<S>(mut stream: S) -> impl Stream<Item = S::Item> + Send + Unpin + 'static
where
S: Stream + Unpin + 'static,
S::Item: Send,
{
// NOTE: the 8 here is arbitrary
let (tx, rx) = tokio::sync::mpsc::channel(8);
// NOTE: spawning stream into a new task can potentially hit this bug:
// - https://github.com/actix/actix-web/issues/1679
//
// Since 4.0.0-beta.2 this issue is incredibly less frequent. I have not personally reproduced it.
// That said, it is still technically possible to encounter.
actix_web::rt::spawn(async move {
while let Some(res) = stream.next().await {
if tx.send(res).await.is_err() {
break;
}
}
});
SendStream { rx }
}
pub(super) struct SendStream<T> {
rx: tokio::sync::mpsc::Receiver<T>,
}
impl<T> Stream for SendStream<T>
where
T: Send,
{
type Item = T;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
std::pin::Pin::new(&mut self.rx).poll_recv(cx)
}
}
// TODO: remove these conversions after actix-web upgrades to http 1.0
#[allow(clippy::expect_used)]
pub(super) fn convert_status(status: http::StatusCode) -> StatusCode {
StatusCode::from_u16(status.as_u16()).expect("status can be converted")
}
#[allow(clippy::expect_used)]
pub(super) fn convert_method(method: &Method) -> http::Method {
http::Method::from_bytes(method.as_str().as_bytes()).expect("method can be converted")
}
pub(super) fn convert_header<'a>(
name: &'a http::HeaderName,
value: &'a HeaderValue,
) -> (&'a str, &'a [u8]) {
(name.as_str(), value.as_bytes())
}

View file

@ -18,7 +18,7 @@ pub fn markdown_rewrite_image_links(mut src: String) -> (String, Vec<Url>) {
// If link points to remote domain, replace with proxied link // If link points to remote domain, replace with proxied link
if parsed.domain() != Some(&SETTINGS.hostname) { if parsed.domain() != Some(&SETTINGS.hostname) {
let mut proxied = format!( let mut proxied = format!(
"{}/api/v4/image_proxy?url={}", "{}/api/v4/image/proxy?url={}",
SETTINGS.get_protocol_and_hostname(), SETTINGS.get_protocol_and_hostname(),
encode(url), encode(url),
); );
@ -115,7 +115,7 @@ mod tests {
( (
"remote image proxied", "remote image proxied",
"![link](http://example.com/image.jpg)", "![link](http://example.com/image.jpg)",
"![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)", "![link](https://lemmy-alpha/api/v4/image/proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)",
), ),
( (
"local image unproxied", "local image unproxied",
@ -125,7 +125,7 @@ mod tests {
( (
"multiple image links", "multiple image links",
"![link](http://example.com/image1.jpg) ![link](http://example.com/image2.jpg)", "![link](http://example.com/image1.jpg) ![link](http://example.com/image2.jpg)",
"![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage1.jpg) ![link](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage2.jpg)", "![link](https://lemmy-alpha/api/v4/image/proxy?url=http%3A%2F%2Fexample.com%2Fimage1.jpg) ![link](https://lemmy-alpha/api/v4/image/proxy?url=http%3A%2F%2Fexample.com%2Fimage2.jpg)",
), ),
( (
"empty link handled", "empty link handled",
@ -135,7 +135,7 @@ mod tests {
( (
"empty label handled", "empty label handled",
"![](http://example.com/image.jpg)", "![](http://example.com/image.jpg)",
"![](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" "![](https://lemmy-alpha/api/v4/image/proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)"
), ),
( (
"invalid image link removed", "invalid image link removed",
@ -145,12 +145,12 @@ mod tests {
( (
"label with nested markdown handled", "label with nested markdown handled",
"![a *b* c](http://example.com/image.jpg)", "![a *b* c](http://example.com/image.jpg)",
"![a *b* c](https://lemmy-alpha/api/v4/image_proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)" "![a *b* c](https://lemmy-alpha/api/v4/image/proxy?url=http%3A%2F%2Fexample.com%2Fimage.jpg)"
), ),
( (
"custom emoji support", "custom emoji support",
r#"![party-blob](https://www.hexbear.net/pictrs/image/83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"#, r#"![party-blob](https://www.hexbear.net/pictrs/image/83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"#,
r#"![party-blob](https://lemmy-alpha/api/v4/image_proxy?url=https%3A%2F%2Fwww.hexbear.net%2Fpictrs%2Fimage%2F83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"# r#"![party-blob](https://lemmy-alpha/api/v4/image/proxy?url=https%3A%2F%2Fwww.hexbear.net%2Fpictrs%2Fimage%2F83405746-0620-4728-9358-5f51b040ffee.gif "emoji party-blob")"#
) )
]; ];

View file

@ -139,7 +139,7 @@ use lemmy_utils::rate_limit::RateLimitCell;
// Deprecated, use api v4 instead. // Deprecated, use api v4 instead.
// When removing api v3, we also need to rewrite all links in database with // When removing api v3, we also need to rewrite all links in database with
// `/api/v3/image_proxy` to use `/api/v4/image_proxy` instead. // `/api/v3/image_proxy` to use `/api/v4/image/proxy` instead.
pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
cfg cfg
.service( .service(

View file

@ -172,7 +172,6 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
cfg.service( cfg.service(
scope("/api/v4") scope("/api/v4")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("/image_proxy", get().to(image_proxy))
// Site // Site
.service( .service(
scope("/site") scope("/site")
@ -401,6 +400,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
.wrap(rate_limit.image()) .wrap(rate_limit.image())
.route(post().to(upload_image)), .route(post().to(upload_image)),
) )
.route("/proxy", get().to(image_proxy))
.route("/{filename}", get().to(get_full_res_image)) .route("/{filename}", get().to(get_full_res_image))
.route("{token}/{filename}", delete().to(delete_image)) .route("{token}/{filename}", delete().to(delete_image))
.route("/healthz", get().to(pictrs_healthz)), .route("/healthz", get().to(pictrs_healthz)),