mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-01-03 16:51:35 +00:00
Move into subfolders
This commit is contained in:
parent
56ab67f804
commit
05843fb7b5
5 changed files with 78 additions and 69 deletions
|
@ -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())
|
|
||||||
}
|
|
63
crates/routes/src/images/utils.rs
Normal file
63
crates/routes/src/images/utils.rs
Normal 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())
|
||||||
|
}
|
|
@ -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")"#
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
Loading…
Reference in a new issue