mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-08-31 00:10:30 +00:00
feat: moderation API for pulling reported images. (#880)
Signed-off-by: IAmTomahawkx <iamtomahawkx@gmail.com> Release-As: 0.15.1
This commit is contained in:
@@ -70,6 +70,8 @@ trust_cloudflare = false
|
||||
easypwned = ""
|
||||
# Tenor API Key
|
||||
tenor_key = ""
|
||||
# Admin keys for future admin api and for bonfire reported image fetching
|
||||
admin_keys = []
|
||||
|
||||
[api.security.captcha]
|
||||
# hCaptcha configuration
|
||||
|
||||
@@ -228,6 +228,7 @@ pub struct ApiSecurity {
|
||||
pub trust_cloudflare: bool,
|
||||
pub easypwned: String,
|
||||
pub tenor_key: String,
|
||||
pub admin_keys: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
|
||||
use axum::{
|
||||
extract::{DefaultBodyLimit, Path, State},
|
||||
http::{header, Method},
|
||||
http::{header, HeaderMap, Method},
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
@@ -58,6 +58,7 @@ pub async fn router() -> Router<AppState> {
|
||||
)
|
||||
.route("/:tag/:file_id", get(fetch_preview))
|
||||
.route("/:tag/:file_id/:file_name", get(fetch_file))
|
||||
.route("/mod/:tag/:file_id/:file_name", get(fetch_file_mod))
|
||||
.layer(cors)
|
||||
}
|
||||
|
||||
@@ -503,3 +504,67 @@ async fn fetch_file(
|
||||
.into_response()
|
||||
})
|
||||
}
|
||||
|
||||
/// Fetch original file (Moderation)
|
||||
///
|
||||
/// This is intentionally left out of the OpenApi docs.
|
||||
/// It uses the config key api.security.admin_keys to determine access. This is intended for server to server communication.
|
||||
async fn fetch_file_mod(
|
||||
State(db): State<Database>,
|
||||
headers: HeaderMap,
|
||||
Path((tag, file_id, file_name)): Path<(Tag, String, String)>,
|
||||
) -> Result<Response> {
|
||||
let config = revolt_config::config().await;
|
||||
|
||||
let token = headers
|
||||
.get("X-Admin-Token")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.ok_or_else(|| create_error!(NotAuthenticated))?;
|
||||
|
||||
if !config
|
||||
.api
|
||||
.security
|
||||
.admin_keys
|
||||
.iter()
|
||||
.any(|tok| tok == token)
|
||||
{
|
||||
return Err(create_error!(NotAuthenticated));
|
||||
}
|
||||
|
||||
let tag: &'static str = tag.clone().into();
|
||||
let file = db.fetch_attachment(tag, &file_id).await?;
|
||||
|
||||
// Ignore files that haven't been attached
|
||||
if file.used_for.is_none() {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
// Ensure filename is correct
|
||||
if file_name != file.filename {
|
||||
if file_name == "original" {
|
||||
let safe_filename = encode_component(&file.filename);
|
||||
|
||||
return Ok(
|
||||
Redirect::permanent(&format!("/{tag}/{file_id}/{}", safe_filename)).into_response(),
|
||||
);
|
||||
}
|
||||
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
let hash = file.as_hash(&db).await?;
|
||||
retrieve_file_by_hash(&hash).await.map(|data| {
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, hash.content_type),
|
||||
(header::CONTENT_DISPOSITION, "attachment".to_owned()),
|
||||
(
|
||||
header::CACHE_CONTROL,
|
||||
"private, max-age=300, must-revalidate".to_owned(),
|
||||
),
|
||||
],
|
||||
data,
|
||||
)
|
||||
.into_response()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user