parent
6882b49336
commit
24975c2e0d
7 changed files with 117 additions and 52 deletions
|
|
@ -1,6 +1,7 @@
|
|||
use axum::{
|
||||
Json, Router,
|
||||
extract::{Multipart, Path, Query, State, WebSocketUpgrade},
|
||||
body::Bytes,
|
||||
extract::{DefaultBodyLimit, Path, Query, State, WebSocketUpgrade},
|
||||
http::{HeaderMap, StatusCode, header},
|
||||
response::{Html, IntoResponse, Redirect},
|
||||
routing::{get, post},
|
||||
|
|
@ -35,6 +36,7 @@ pub fn routes() -> Router<AppState> {
|
|||
"/dms/{other_user_id}/attachments",
|
||||
post(upload_dm_attachment),
|
||||
)
|
||||
.layer(DefaultBodyLimit::max(MAX_MEDIA_UPLOAD_BYTES))
|
||||
.route("/presence", get(presence_list))
|
||||
.route("/rtc-config", get(rtc_config))
|
||||
.route("/guilds", get(list_guilds).post(create_guild))
|
||||
|
|
@ -50,6 +52,7 @@ pub fn routes() -> Router<AppState> {
|
|||
"/guilds/{guild_id}/sounds",
|
||||
get(list_sounds).post(upload_sound),
|
||||
)
|
||||
.layer(DefaultBodyLimit::max(MAX_MEDIA_UPLOAD_BYTES))
|
||||
.route(
|
||||
"/guilds/{guild_id}/sounds/{sound_id}",
|
||||
post(delete_sound_post).delete(delete_sound),
|
||||
|
|
@ -63,6 +66,7 @@ pub fn routes() -> Router<AppState> {
|
|||
"/channels/{channel_id}/attachments",
|
||||
post(upload_channel_attachment),
|
||||
)
|
||||
.layer(DefaultBodyLimit::max(MAX_MEDIA_UPLOAD_BYTES))
|
||||
.route("/channels/{channel_id}/voice/ws", get(voice_ws))
|
||||
.route("/ws", get(chat_ws))
|
||||
}
|
||||
|
|
@ -693,12 +697,17 @@ async fn upload_channel_attachment(
|
|||
State(state): State<AppState>,
|
||||
user: AuthUser,
|
||||
Path(channel_id): Path<Uuid>,
|
||||
multipart: Multipart,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
ensure_channel_member(&state, channel_id, user.id).await?;
|
||||
let uploaded =
|
||||
upload_media_from_multipart(&state, multipart, channel_object_key_prefix(channel_id))
|
||||
.await?;
|
||||
let uploaded = upload_media_from_request(
|
||||
&state,
|
||||
&headers,
|
||||
body,
|
||||
channel_object_key_prefix(channel_id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let message = db::create_message_with_attachment(
|
||||
&state.db,
|
||||
|
|
@ -731,7 +740,8 @@ async fn upload_dm_attachment(
|
|||
State(state): State<AppState>,
|
||||
user: AuthUser,
|
||||
Path(other_user_id): Path<Uuid>,
|
||||
multipart: Multipart,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
if other_user_id == user.id {
|
||||
return Err(ApiError::bad_request("cannot send dm to yourself"));
|
||||
|
|
@ -747,9 +757,10 @@ async fn upload_dm_attachment(
|
|||
});
|
||||
}
|
||||
|
||||
let uploaded = upload_media_from_multipart(
|
||||
let uploaded = upload_media_from_request(
|
||||
&state,
|
||||
multipart,
|
||||
&headers,
|
||||
body,
|
||||
dm_object_key_prefix(user.id, other_user_id),
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -886,7 +897,7 @@ async fn upload_sound(
|
|||
Ok(Json(sound))
|
||||
}
|
||||
|
||||
const MAX_MEDIA_UPLOAD_BYTES: usize = 25 * 1024 * 1024;
|
||||
const MAX_MEDIA_UPLOAD_BYTES: usize = 50 * 1024 * 1024;
|
||||
|
||||
struct UploadedMedia {
|
||||
object_key: String,
|
||||
|
|
@ -896,9 +907,10 @@ struct UploadedMedia {
|
|||
original_filename: String,
|
||||
}
|
||||
|
||||
async fn upload_media_from_multipart(
|
||||
async fn upload_media_from_request(
|
||||
state: &AppState,
|
||||
mut multipart: Multipart,
|
||||
headers: &HeaderMap,
|
||||
body: Bytes,
|
||||
object_key_prefix: String,
|
||||
) -> Result<UploadedMedia, ApiError> {
|
||||
let storage = state
|
||||
|
|
@ -906,47 +918,34 @@ async fn upload_media_from_multipart(
|
|||
.as_ref()
|
||||
.ok_or_else(|| ApiError::internal("media storage is not configured"))?;
|
||||
|
||||
let mut file_name = None;
|
||||
let mut file_data = None;
|
||||
let mut mime_type = None;
|
||||
let original_filename = headers
|
||||
.get("x-file-name")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(str::trim)
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(ToString::to_string)
|
||||
.ok_or_else(|| ApiError::bad_request("missing x-file-name header"))?;
|
||||
|
||||
while let Some(field) = multipart
|
||||
.next_field()
|
||||
.await
|
||||
.map_err(|e| ApiError::bad_request(&e.to_string()))?
|
||||
{
|
||||
if field.name().unwrap_or_default() != "file" {
|
||||
continue;
|
||||
}
|
||||
let mime_type = headers
|
||||
.get(header::CONTENT_TYPE)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(str::trim)
|
||||
.filter(|v| !v.is_empty())
|
||||
.unwrap_or("application/octet-stream")
|
||||
.to_string();
|
||||
|
||||
file_name = Some(field.file_name().unwrap_or("upload.bin").to_string());
|
||||
mime_type = Some(
|
||||
field
|
||||
.content_type()
|
||||
.unwrap_or("application/octet-stream")
|
||||
.to_string(),
|
||||
);
|
||||
let bytes = field
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|e| ApiError::bad_request(&e.to_string()))?;
|
||||
if bytes.len() > MAX_MEDIA_UPLOAD_BYTES {
|
||||
return Err(ApiError::bad_request("file exceeds 25MB upload limit"));
|
||||
}
|
||||
file_data = Some(bytes.to_vec());
|
||||
break;
|
||||
if body.is_empty() {
|
||||
return Err(ApiError::bad_request("missing file upload"));
|
||||
}
|
||||
if body.len() > MAX_MEDIA_UPLOAD_BYTES {
|
||||
return Err(ApiError::bad_request("file exceeds 50MB upload limit"));
|
||||
}
|
||||
|
||||
let (original_filename, mime_type, file_data) = match (file_name, mime_type, file_data) {
|
||||
(Some(name), Some(mime), Some(data)) => (name, mime, data),
|
||||
_ => return Err(ApiError::bad_request("missing file upload")),
|
||||
};
|
||||
|
||||
let safe_name = sanitize_file_name(&original_filename);
|
||||
let object_key = format!("{}/{}-{}", object_key_prefix, Uuid::new_v4(), safe_name);
|
||||
let size_bytes = file_data.len() as i64;
|
||||
let size_bytes = body.len() as i64;
|
||||
let media_url = storage
|
||||
.upload_object(&object_key, file_data, &mime_type, &original_filename)
|
||||
.upload_object(&object_key, body.to_vec(), &mime_type, &original_filename)
|
||||
.await
|
||||
.map_err(|e| ApiError::internal(&e.to_string()))?;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue