Split image endpoints into API v3 and v4

This commit is contained in:
Felix Ableitner 2024-12-12 15:59:07 +01:00
parent 8a5daeec97
commit 56ab67f804
3 changed files with 286 additions and 289 deletions

View file

@ -18,27 +18,13 @@ use lemmy_db_schema::source::{
local_site::LocalSite, local_site::LocalSite,
}; };
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{error::LemmyResult, rate_limit::RateLimitCell, REQWEST_TIMEOUT}; use lemmy_utils::{error::LemmyResult, REQWEST_TIMEOUT};
use reqwest::Body; use reqwest::Body;
use reqwest_middleware::{ClientWithMiddleware, RequestBuilder}; use reqwest_middleware::RequestBuilder;
use serde::Deserialize; use serde::Deserialize;
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
pub fn config(cfg: &mut ServiceConfig, client: ClientWithMiddleware, rate_limit: &RateLimitCell) {
cfg
.app_data(Data::new(client))
.service(
resource("/pictrs/image")
.wrap(rate_limit.image())
.route(post().to(upload)),
)
// This has optional query params: /image/{filename}?format=jpg&thumbnail=256
.service(resource("/pictrs/image/{filename}").route(get().to(full_res)))
.service(resource("/pictrs/image/delete/{token}/{filename}").route(get().to(delete)))
.service(resource("/pictrs/healthz").route(get().to(healthz)));
}
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.
/// Otherwise, it uses the normal pictrs url (IE image/original). /// Otherwise, it uses the normal pictrs url (IE image/original).
@ -46,7 +32,7 @@ trait ProcessUrl {
} }
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
struct PictrsGetParams { pub struct PictrsGetParams {
format: Option<String>, format: Option<String>,
thumbnail: Option<i32>, thumbnail: Option<i32>,
} }
@ -99,15 +85,12 @@ impl ProcessUrl for ImageProxyParams {
} }
} }
} }
fn adapt_request( fn adapt_request(request: &HttpRequest, context: &LemmyContext, url: String) -> RequestBuilder {
request: &HttpRequest,
client: &ClientWithMiddleware,
url: String,
) -> RequestBuilder {
// remove accept-encoding header so that pictrs doesn't compress the response // remove accept-encoding header so that pictrs doesn't compress the response
const INVALID_HEADERS: &[HeaderName] = &[ACCEPT_ENCODING, HOST]; const INVALID_HEADERS: &[HeaderName] = &[ACCEPT_ENCODING, HOST];
let client_request = client let client_request = context
.client()
.request(convert_method(request.method()), url) .request(convert_method(request.method()), url)
.timeout(REQWEST_TIMEOUT); .timeout(REQWEST_TIMEOUT);
@ -124,19 +107,17 @@ fn adapt_request(
}) })
} }
async fn upload( pub async fn upload_image(
req: HttpRequest, req: HttpRequest,
body: Payload, body: Payload,
// require login // require login
local_user_view: LocalUserView, local_user_view: LocalUserView,
client: Data<ClientWithMiddleware>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<HttpResponse> { ) -> LemmyResult<HttpResponse> {
// TODO: check rate limit here
let pictrs_config = context.settings().pictrs_config()?; let pictrs_config = context.settings().pictrs_config()?;
let image_url = format!("{}image", pictrs_config.url); let image_url = format!("{}image", pictrs_config.url);
let mut client_req = adapt_request(&req, &client, image_url); let mut client_req = adapt_request(&req, &context, image_url);
if let Some(addr) = req.head().peer_addr { if let Some(addr) = req.head().peer_addr {
client_req = client_req.header("X-Forwarded-For", addr.to_string()) client_req = client_req.header("X-Forwarded-For", addr.to_string())
@ -169,11 +150,10 @@ async fn upload(
Ok(HttpResponse::build(convert_status(status)).json(images)) Ok(HttpResponse::build(convert_status(status)).json(images))
} }
async fn full_res( pub async fn get_full_res_image(
filename: Path<String>, filename: Path<String>,
Query(params): Query<PictrsGetParams>, Query(params): Query<PictrsGetParams>,
req: HttpRequest, req: HttpRequest,
client: Data<ClientWithMiddleware>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> LemmyResult<HttpResponse> { ) -> LemmyResult<HttpResponse> {
@ -189,15 +169,11 @@ async fn full_res(
let processed_url = params.process_url(name, &pictrs_config.url); let processed_url = params.process_url(name, &pictrs_config.url);
image(processed_url, req, &client).await image(processed_url, req, &context).await
} }
async fn image( async fn image(url: String, req: HttpRequest, context: &LemmyContext) -> LemmyResult<HttpResponse> {
url: String, let mut client_req = adapt_request(&req, context, url);
req: HttpRequest,
client: &ClientWithMiddleware,
) -> LemmyResult<HttpResponse> {
let mut client_req = adapt_request(&req, client, url);
if let Some(addr) = req.head().peer_addr { if let Some(addr) = req.head().peer_addr {
client_req = client_req.header("X-Forwarded-For", addr.to_string()); client_req = client_req.header("X-Forwarded-For", addr.to_string());
@ -222,10 +198,9 @@ async fn image(
Ok(client_res.body(BodyStream::new(res.bytes_stream()))) Ok(client_res.body(BodyStream::new(res.bytes_stream())))
} }
async fn delete( pub async fn delete_image(
components: Path<(String, String)>, components: Path<(String, String)>,
req: HttpRequest, req: HttpRequest,
client: Data<ClientWithMiddleware>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
// require login // require login
_local_user_view: LocalUserView, _local_user_view: LocalUserView,
@ -235,7 +210,7 @@ async fn delete(
let pictrs_config = context.settings().pictrs_config()?; let pictrs_config = context.settings().pictrs_config()?;
let url = format!("{}image/delete/{}/{}", pictrs_config.url, &token, &file); let url = format!("{}image/delete/{}/{}", pictrs_config.url, &token, &file);
let mut client_req = adapt_request(&req, &client, url); let mut client_req = adapt_request(&req, &context, url);
if let Some(addr) = req.head().peer_addr { if let Some(addr) = req.head().peer_addr {
client_req = client_req.header("X-Forwarded-For", addr.to_string()); client_req = client_req.header("X-Forwarded-For", addr.to_string());
@ -248,15 +223,11 @@ async fn delete(
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())))
} }
async fn healthz( pub async fn pictrs_healthz(req: HttpRequest, context: Data<LemmyContext>) -> LemmyResult<HttpResponse> {
req: HttpRequest,
client: Data<ClientWithMiddleware>,
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);
let mut client_req = adapt_request(&req, &client, url); let mut client_req = adapt_request(&req, &context, url);
if let Some(addr) = req.head().peer_addr { if let Some(addr) = req.head().peer_addr {
client_req = client_req.header("X-Forwarded-For", addr.to_string()); client_req = client_req.header("X-Forwarded-For", addr.to_string());
@ -270,7 +241,6 @@ async fn healthz(
pub async fn image_proxy( pub async fn image_proxy(
Query(params): Query<ImageProxyParams>, Query(params): Query<ImageProxyParams>,
req: HttpRequest, req: HttpRequest,
client: Data<ClientWithMiddleware>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<Either<HttpResponse<()>, HttpResponse<BoxBody>>> { ) -> LemmyResult<Either<HttpResponse<()>, HttpResponse<BoxBody>>> {
let url = Url::parse(&params.url)?; let url = Url::parse(&params.url)?;
@ -291,7 +261,7 @@ pub async fn image_proxy(
Ok(Either::Left(Redirect::to(url.to_string()).respond_to(&req))) Ok(Either::Left(Redirect::to(url.to_string()).respond_to(&req)))
} else { } else {
// Proxy the image data through Lemmy // Proxy the image data through Lemmy
Ok(Either::Right(image(processed_url, req, &client).await?)) Ok(Either::Right(image(processed_url, req, &context).await?))
} }
} }

View file

@ -134,252 +134,262 @@ use lemmy_apub::api::{
search::search, search::search,
user_settings_backup::{export_settings, import_settings}, user_settings_backup::{export_settings, import_settings},
}; };
use lemmy_routes::images::image_proxy; use lemmy_routes::images::{delete_image, get_full_res_image, image_proxy, pictrs_healthz, upload_image};
use lemmy_utils::rate_limit::RateLimitCell; 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.service( cfg
scope("/api/v3") .service(
.route("/image_proxy", get().to(image_proxy)) resource("/pictrs/image")
// Site .wrap(rate_limit.image())
.service( .route(post().to(upload_image)),
scope("/site") )
.wrap(rate_limit.message()) .service(resource("/pictrs/image/{filename}").route(get().to(get_full_res_image)))
.route("", get().to(get_site_v3)) .service(resource("/pictrs/image/delete/{token}/{filename}").route(get().to(delete_image)))
// Admin Actions .service(resource("/pictrs/healthz").route(get().to(pictrs_healthz)))
.route("", post().to(create_site)) .service(
.route("", put().to(update_site)) scope("/api/v3")
.route("/block", post().to(user_block_instance)), .route("/image_proxy", get().to(image_proxy))
) // Site
.service( .service(
resource("/modlog") scope("/site")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route(get().to(get_mod_log)), .route("", get().to(get_site_v3))
) // Admin Actions
.service( .route("", post().to(create_site))
resource("/search") .route("", put().to(update_site))
.wrap(rate_limit.search()) .route("/block", post().to(user_block_instance)),
.route(get().to(search)), )
) .service(
.service( resource("/modlog")
resource("/resolve_object") .wrap(rate_limit.message())
.wrap(rate_limit.message()) .route(get().to(get_mod_log)),
.route(get().to(resolve_object)), )
) .service(
// Community resource("/search")
.service( .wrap(rate_limit.search())
resource("/community") .route(get().to(search)),
.guard(guard::Post()) )
.wrap(rate_limit.register()) .service(
.route(post().to(create_community)), resource("/resolve_object")
) .wrap(rate_limit.message())
.service( .route(get().to(resolve_object)),
scope("/community") )
.wrap(rate_limit.message()) // Community
.route("", get().to(get_community)) .service(
.route("", put().to(update_community)) resource("/community")
.route("/hide", put().to(hide_community)) .guard(guard::Post())
.route("/list", get().to(list_communities)) .wrap(rate_limit.register())
.route("/follow", post().to(follow_community)) .route(post().to(create_community)),
.route("/block", post().to(user_block_community)) )
.route("/delete", post().to(delete_community)) .service(
// Mod Actions scope("/community")
.route("/remove", post().to(remove_community)) .wrap(rate_limit.message())
.route("/transfer", post().to(transfer_community)) .route("", get().to(get_community))
.route("/ban_user", post().to(ban_from_community)) .route("", put().to(update_community))
.route("/mod", post().to(add_mod_to_community)), .route("/hide", put().to(hide_community))
) .route("/list", get().to(list_communities))
.service( .route("/follow", post().to(follow_community))
scope("/federated_instances") .route("/block", post().to(user_block_community))
.wrap(rate_limit.message()) .route("/delete", post().to(delete_community))
.route("", get().to(get_federated_instances)), // Mod Actions
) .route("/remove", post().to(remove_community))
// Post .route("/transfer", post().to(transfer_community))
.service( .route("/ban_user", post().to(ban_from_community))
// Handle POST to /post separately to add the post() rate limitter .route("/mod", post().to(add_mod_to_community)),
resource("/post") )
.guard(guard::Post()) .service(
.wrap(rate_limit.post()) scope("/federated_instances")
.route(post().to(create_post)), .wrap(rate_limit.message())
) .route("", get().to(get_federated_instances)),
.service( )
scope("/post") // Post
.wrap(rate_limit.message()) .service(
.route("", get().to(get_post)) // Handle POST to /post separately to add the post() rate limitter
.route("", put().to(update_post)) resource("/post")
.route("/delete", post().to(delete_post)) .guard(guard::Post())
.route("/remove", post().to(remove_post)) .wrap(rate_limit.post())
.route("/mark_as_read", post().to(mark_post_as_read)) .route(post().to(create_post)),
.route("/hide", post().to(hide_post)) )
.route("/lock", post().to(lock_post)) .service(
.route("/feature", post().to(feature_post)) scope("/post")
.route("/list", get().to(list_posts)) .wrap(rate_limit.message())
.route("/like", post().to(like_post)) .route("", get().to(get_post))
.route("/like/list", get().to(list_post_likes)) .route("", put().to(update_post))
.route("/save", put().to(save_post)) .route("/delete", post().to(delete_post))
.route("/report", post().to(create_post_report)) .route("/remove", post().to(remove_post))
.route("/report/resolve", put().to(resolve_post_report)) .route("/mark_as_read", post().to(mark_post_as_read))
.route("/report/list", get().to(list_post_reports)) .route("/hide", post().to(hide_post))
.route("/site_metadata", get().to(get_link_metadata)), .route("/lock", post().to(lock_post))
) .route("/feature", post().to(feature_post))
// Comment .route("/list", get().to(list_posts))
.service( .route("/like", post().to(like_post))
// Handle POST to /comment separately to add the comment() rate limitter .route("/like/list", get().to(list_post_likes))
resource("/comment") .route("/save", put().to(save_post))
.guard(guard::Post()) .route("/report", post().to(create_post_report))
.wrap(rate_limit.comment()) .route("/report/resolve", put().to(resolve_post_report))
.route(post().to(create_comment)), .route("/report/list", get().to(list_post_reports))
) .route("/site_metadata", get().to(get_link_metadata)),
.service( )
scope("/comment") // Comment
.wrap(rate_limit.message()) .service(
.route("", get().to(get_comment)) // Handle POST to /comment separately to add the comment() rate limitter
.route("", put().to(update_comment)) resource("/comment")
.route("/delete", post().to(delete_comment)) .guard(guard::Post())
.route("/remove", post().to(remove_comment)) .wrap(rate_limit.comment())
.route("/mark_as_read", post().to(mark_reply_as_read)) .route(post().to(create_comment)),
.route("/distinguish", post().to(distinguish_comment)) )
.route("/like", post().to(like_comment)) .service(
.route("/like/list", get().to(list_comment_likes)) scope("/comment")
.route("/save", put().to(save_comment)) .wrap(rate_limit.message())
.route("/list", get().to(list_comments)) .route("", get().to(get_comment))
.route("/report", post().to(create_comment_report)) .route("", put().to(update_comment))
.route("/report/resolve", put().to(resolve_comment_report)) .route("/delete", post().to(delete_comment))
.route("/report/list", get().to(list_comment_reports)), .route("/remove", post().to(remove_comment))
) .route("/mark_as_read", post().to(mark_reply_as_read))
// Private Message .route("/distinguish", post().to(distinguish_comment))
.service( .route("/like", post().to(like_comment))
scope("/private_message") .route("/like/list", get().to(list_comment_likes))
.wrap(rate_limit.message()) .route("/save", put().to(save_comment))
.route("/list", get().to(get_private_message)) .route("/list", get().to(list_comments))
.route("", post().to(create_private_message)) .route("/report", post().to(create_comment_report))
.route("", put().to(update_private_message)) .route("/report/resolve", put().to(resolve_comment_report))
.route("/delete", post().to(delete_private_message)) .route("/report/list", get().to(list_comment_reports)),
.route("/mark_as_read", post().to(mark_pm_as_read)) )
.route("/report", post().to(create_pm_report)) // Private Message
.route("/report/resolve", put().to(resolve_pm_report)) .service(
.route("/report/list", get().to(list_pm_reports)), scope("/private_message")
) .wrap(rate_limit.message())
// User .route("/list", get().to(get_private_message))
.service( .route("", post().to(create_private_message))
// Account action, I don't like that it's in /user maybe /accounts .route("", put().to(update_private_message))
// Handle /user/register separately to add the register() rate limiter .route("/delete", post().to(delete_private_message))
resource("/user/register") .route("/mark_as_read", post().to(mark_pm_as_read))
.guard(guard::Post()) .route("/report", post().to(create_pm_report))
.wrap(rate_limit.register()) .route("/report/resolve", put().to(resolve_pm_report))
.route(post().to(register)), .route("/report/list", get().to(list_pm_reports)),
) )
// User // User
.service( .service(
// Handle /user/login separately to add the register() rate limiter // Account action, I don't like that it's in /user maybe /accounts
// TODO: pretty annoying way to apply rate limits for register and login, we should // Handle /user/register separately to add the register() rate limiter
// group them under a common path so that rate limit is only applied once (eg under resource("/user/register")
// /account). .guard(guard::Post())
resource("/user/login") .wrap(rate_limit.register())
.guard(guard::Post()) .route(post().to(register)),
.wrap(rate_limit.register()) )
.route(post().to(login)), // User
) .service(
.service( // Handle /user/login separately to add the register() rate limiter
resource("/user/password_reset") // TODO: pretty annoying way to apply rate limits for register and login, we should
.wrap(rate_limit.register()) // group them under a common path so that rate limit is only applied once (eg under
.route(post().to(reset_password)), // /account).
) resource("/user/login")
.service( .guard(guard::Post())
// Handle captcha separately .wrap(rate_limit.register())
resource("/user/get_captcha") .route(post().to(login)),
.wrap(rate_limit.post()) )
.route(get().to(get_captcha)), .service(
) resource("/user/password_reset")
.service( .wrap(rate_limit.register())
resource("/user/export_settings") .route(post().to(reset_password)),
.wrap(rate_limit.import_user_settings()) )
.route(get().to(export_settings)), .service(
) // Handle captcha separately
.service( resource("/user/get_captcha")
resource("/user/import_settings") .wrap(rate_limit.post())
.wrap(rate_limit.import_user_settings()) .route(get().to(get_captcha)),
.route(post().to(import_settings)), )
) .service(
// TODO, all the current account related actions under /user need to get moved here eventually resource("/user/export_settings")
.service( .wrap(rate_limit.import_user_settings())
scope("/account") .route(get().to(export_settings)),
.wrap(rate_limit.message()) )
.route("/list_media", get().to(list_media)), .service(
) resource("/user/import_settings")
// User actions .wrap(rate_limit.import_user_settings())
.service( .route(post().to(import_settings)),
scope("/user") )
.wrap(rate_limit.message()) // TODO, all the current account related actions under /user need to get moved here
.route("", get().to(read_person)) // eventually
.route("/mention", get().to(list_mentions)) .service(
.route( scope("/account")
"/mention/mark_as_read", .wrap(rate_limit.message())
post().to(mark_person_mention_as_read), .route("/list_media", get().to(list_media)),
) )
.route("/replies", get().to(list_replies)) // User actions
// Admin action. I don't like that it's in /user .service(
.route("/ban", post().to(ban_from_site)) scope("/user")
.route("/banned", get().to(list_banned_users)) .wrap(rate_limit.message())
.route("/block", post().to(user_block_person)) .route("", get().to(read_person))
// TODO Account actions. I don't like that they're in /user maybe /accounts .route("/mention", get().to(list_mentions))
.route("/logout", post().to(logout)) .route(
.route("/delete_account", post().to(delete_account)) "/mention/mark_as_read",
.route("/password_change", post().to(change_password_after_reset)) post().to(mark_person_mention_as_read),
// TODO mark_all_as_read feels off being in this section as well )
.route("/mark_all_as_read", post().to(mark_all_notifications_read)) .route("/replies", get().to(list_replies))
.route("/save_user_settings", put().to(save_user_settings)) // Admin action. I don't like that it's in /user
.route("/change_password", put().to(change_password)) .route("/ban", post().to(ban_from_site))
.route("/report_count", get().to(report_count)) .route("/banned", get().to(list_banned_users))
.route("/unread_count", get().to(unread_count)) .route("/block", post().to(user_block_person))
.route("/verify_email", post().to(verify_email)) // TODO Account actions. I don't like that they're in /user maybe /accounts
.route("/leave_admin", post().to(leave_admin)) .route("/logout", post().to(logout))
.route("/totp/generate", post().to(generate_totp_secret)) .route("/delete_account", post().to(delete_account))
.route("/totp/update", post().to(update_totp)) .route("/password_change", post().to(change_password_after_reset))
.route("/list_logins", get().to(list_logins)) // TODO mark_all_as_read feels off being in this section as well
.route("/validate_auth", get().to(validate_auth)), .route("/mark_all_as_read", post().to(mark_all_notifications_read))
) .route("/save_user_settings", put().to(save_user_settings))
// Admin Actions .route("/change_password", put().to(change_password))
.service( .route("/report_count", get().to(report_count))
scope("/admin") .route("/unread_count", get().to(unread_count))
.wrap(rate_limit.message()) .route("/verify_email", post().to(verify_email))
.route("/add", post().to(add_admin)) .route("/leave_admin", post().to(leave_admin))
.route( .route("/totp/generate", post().to(generate_totp_secret))
"/registration_application/count", .route("/totp/update", post().to(update_totp))
get().to(get_unread_registration_application_count), .route("/list_logins", get().to(list_logins))
) .route("/validate_auth", get().to(validate_auth)),
.route( )
"/registration_application/list", // Admin Actions
get().to(list_registration_applications), .service(
) scope("/admin")
.route( .wrap(rate_limit.message())
"/registration_application/approve", .route("/add", post().to(add_admin))
put().to(approve_registration_application), .route(
) "/registration_application/count",
.route( get().to(get_unread_registration_application_count),
"/registration_application", )
get().to(get_registration_application), .route(
) "/registration_application/list",
.route("/list_all_media", get().to(list_all_media)) get().to(list_registration_applications),
.service( )
scope("/purge") .route(
.route("/person", post().to(purge_person)) "/registration_application/approve",
.route("/community", post().to(purge_community)) put().to(approve_registration_application),
.route("/post", post().to(purge_post)) )
.route("/comment", post().to(purge_comment)), .route(
), "/registration_application",
) get().to(get_registration_application),
.service( )
scope("/custom_emoji") .route("/list_all_media", get().to(list_all_media))
.wrap(rate_limit.message()) .service(
.route("", post().to(create_custom_emoji)) scope("/purge")
.route("", put().to(update_custom_emoji)) .route("/person", post().to(purge_person))
.route("/delete", post().to(delete_custom_emoji)), .route("/community", post().to(purge_community))
), .route("/post", post().to(purge_post))
); .route("/comment", post().to(purge_comment)),
),
)
.service(
scope("/custom_emoji")
.wrap(rate_limit.message())
.route("", post().to(create_custom_emoji))
.route("", put().to(update_custom_emoji))
.route("/delete", post().to(delete_custom_emoji)),
),
);
cfg.service( cfg.service(
scope("/sitemap.xml") scope("/sitemap.xml")
.wrap(rate_limit.message()) .wrap(rate_limit.message())

View file

@ -159,7 +159,13 @@ use lemmy_apub::api::{
search::search, search::search,
user_settings_backup::{export_settings, import_settings}, user_settings_backup::{export_settings, import_settings},
}; };
use lemmy_routes::images::image_proxy; use lemmy_routes::images::{
delete_image,
get_full_res_image,
image_proxy,
pictrs_healthz,
upload_image,
};
use lemmy_utils::rate_limit::RateLimitCell; use lemmy_utils::rate_limit::RateLimitCell;
pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
@ -387,6 +393,17 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
.wrap(rate_limit.register()) .wrap(rate_limit.register())
.route("/authenticate", post().to(authenticate_with_oauth)), .route("/authenticate", post().to(authenticate_with_oauth)),
) )
.route("/sitemap.xml", get().to(get_sitemap)), .route("/sitemap.xml", get().to(get_sitemap))
.service(
scope("/image")
.service(
resource("")
.wrap(rate_limit.image())
.route(post().to(upload_image)),
)
.route("/{filename}", get().to(get_full_res_image))
.route("{token}/{filename}", delete().to(delete_image))
.route("/healthz", get().to(pictrs_healthz)),
),
); );
} }