use std::fs::exists; use pathbufd::PathBufD; use crate::{ get_user_from_token, image::{save_webp_buffer, Image}, routes::api::v1::{auth::images::read_image, UpdateEmojiName}, State, }; use axum::{body::Body, extract::Path, response::IntoResponse, Extension, Json}; use axum_extra::extract::CookieJar; use tetratto_core::model::{ uploads::{CustomEmoji, MediaType, MediaUpload}, ApiReturn, Error, }; /// Expand a unicode emoji into its Gemoji shortcode. pub async fn get_emoji_shortcode(emoji: String) -> impl IntoResponse { match emojis::get(&emoji) { Some(e) => match e.shortcode() { Some(s) => s.to_string(), None => e.name().replace(" ", "-"), }, None => String::new(), } } pub async fn get_request( Path((community, name)): Path<(usize, String)>, Extension(data): Extension, ) -> impl IntoResponse { let data = &(data.read().await).0; if community == 0 { return Err(( [("Content-Type", "image/svg+xml")], Body::from(read_image(PathBufD::current().extend(&[ data.0.0.dirs.media.as_str(), "images", "default-avatar.svg", ]))), )); } let emoji = match data.get_emoji_by_community_name(community, &name).await { Ok(ua) => ua, Err(_) => { return Err(( [("Content-Type", "image/svg+xml")], Body::from(read_image(PathBufD::current().extend(&[ data.0.0.dirs.media.as_str(), "images", "default-avatar.svg", ]))), )); } }; let upload = data .get_upload_by_id(emoji.0.unwrap().upload_id) .await .unwrap(); let path = upload.path(&data.0.0); if !exists(&path).unwrap() { return Err(( [("Content-Type", "image/svg+xml")], Body::from(read_image(PathBufD::current().extend(&[ data.0.0.dirs.media.as_str(), "images", "default-avatar.svg", ]))), )); } Ok(( [("Content-Type", upload.what.mime())], Body::from(read_image(path)), )) } // maximum file dimensions: 512x512px (256KiB) pub const MAXIMUM_FILE_SIZE: usize = 262144; pub async fn create_request( jar: CookieJar, Extension(data): Extension, Path((community, name)): Path<(usize, String)>, img: Image, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; // check file size if img.0.len() > MAXIMUM_FILE_SIZE { return Json(Error::DataTooLong("image".to_string()).into()); } // make sure emoji doesn't already exist in community if data .get_emoji_by_community_name(community, &name) .await .is_ok() { return Json( Error::MiscError("This emoji name is already in use in this community".to_string()) .into(), ); } // create upload let upload = match data .create_upload(MediaUpload::new( if img.1 == "image/gif" { MediaType::Gif } else { MediaType::Webp }, user.id, )) .await { Ok(u) => u, Err(e) => return Json(e.into()), }; // create emoji let is_animated = img.1 == "image/gif"; match data .create_emoji(CustomEmoji::new( user.id, community, upload.id, name, is_animated, )) .await { Ok(_) => { if is_animated { if let Err(e) = upload.write(&data.0.0, &img.0) { return Json(Error::MiscError(e.to_string()).into()); } } else { if let Err(e) = save_webp_buffer(&upload.path(&data.0.0).to_string(), img.0.to_vec(), None) { return Json(Error::MiscError(e.to_string()).into()); } } Json(ApiReturn { ok: true, message: "Emoji created".to_string(), payload: (), }) } Err(e) => { if let Err(e) = upload.remove(&data.0.0) { return Json(e.into()); } Json(e.into()) } } } pub async fn update_name_request( jar: CookieJar, Extension(data): Extension, Path(id): Path, Json(req): Json, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data.update_emoji_name(id, &user, &req.name).await { Ok(_) => Json(ApiReturn { ok: true, message: "Emoji updated".to_string(), payload: (), }), Err(e) => Json(e.into()), } } pub async fn delete_request( jar: CookieJar, Extension(data): Extension, Path(id): Path, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data.delete_emoji(id, &user).await { Ok(_) => Json(ApiReturn { ok: true, message: "Emoji deleted".to_string(), payload: (), }), Err(e) => Json(e.into()), } } pub async fn get_my_request( jar: CookieJar, Extension(data): Extension, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data.get_user_emojis(user.id).await { Ok(d) => Json(ApiReturn { ok: true, message: d.len().to_string(), payload: Some(d), }), Err(e) => Json(e.into()), } }