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},
http::{
header::{HeaderName, ACCEPT_ENCODING, HOST},
Method,
StatusCode,
},
web::*,
@ -10,8 +9,6 @@ use actix_web::{
HttpResponse,
Responder,
};
use futures::stream::{Stream, StreamExt};
use http::HeaderValue;
use lemmy_api_common::{context::LemmyContext, request::PictrsResponse};
use lemmy_db_schema::source::{
images::{LocalImage, LocalImageForm, RemoteImage},
@ -24,6 +21,9 @@ use reqwest_middleware::RequestBuilder;
use serde::Deserialize;
use std::time::Duration;
use url::Url;
use utils::{convert_header, convert_method, convert_status, make_send};
mod utils;
trait ProcessUrl {
/// 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())))
}
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 url = format!("{}healthz", pictrs_config.url);
@ -264,60 +267,3 @@ pub async fn image_proxy(
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 parsed.domain() != Some(&SETTINGS.hostname) {
let mut proxied = format!(
"{}/api/v4/image_proxy?url={}",
"{}/api/v4/image/proxy?url={}",
SETTINGS.get_protocol_and_hostname(),
encode(url),
);
@ -115,7 +115,7 @@ mod tests {
(
"remote image proxied",
"![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",
@ -125,7 +125,7 @@ mod tests {
(
"multiple image links",
"![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",
@ -135,7 +135,7 @@ mod tests {
(
"empty label handled",
"![](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",
@ -145,12 +145,12 @@ mod tests {
(
"label with nested markdown handled",
"![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",
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.
// 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) {
cfg
.service(

View file

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