add: upload alt text

This commit is contained in:
trisua 2025-07-14 15:30:17 -04:00
parent 3b5b0ce1a1
commit e0e38b2b32
13 changed files with 224 additions and 59 deletions

View file

@ -152,10 +152,11 @@ pub async fn create_request(
}
// ...
match data.create_post(props.clone()).await {
let uploads = props.uploads.clone();
match data.create_post(props).await {
Ok(id) => {
// write to uploads
for (i, upload_id) in props.uploads.iter().enumerate() {
for (i, upload_id) in uploads.iter().enumerate() {
let image = match images.get(i) {
Some(img) => img,
None => {

View file

@ -637,6 +637,8 @@ pub fn routes() -> Router {
// uploads
.route("/uploads/{id}", get(uploads::get_request))
.route("/uploads/{id}", delete(uploads::delete_request))
.route("/uploads/{id}/data", get(uploads::get_json_request))
.route("/uploads/{id}/alt", post(uploads::update_alt_request))
// services
.route("/services", get(services::list_request))
.route("/services", post(services::create_request))
@ -1124,3 +1126,8 @@ pub struct UpdateProductDescription {
pub struct UpdateProductPrice {
pub price: ProductPrice,
}
#[derive(Deserialize)]
pub struct UpdateUploadAlt {
pub alt: String,
}

View file

@ -1,13 +1,20 @@
use crate::{
get_user_from_token,
image::{save_webp_buffer, JsonMultipart},
routes::api::v1::{
CreateProduct, UpdateProductDescription, UpdateProductName, UpdateProductPrice,
communities::posts::MAXIMUM_FILE_SIZE, CreateProduct, UpdateProductDescription,
UpdateProductName, UpdateProductPrice,
},
State,
};
use axum::{extract::Path, response::IntoResponse, Extension, Json};
use axum_extra::extract::CookieJar;
use tetratto_core::model::{products::Product, oauth, ApiReturn, Error};
use tetratto_core::model::{
oauth,
products::Product,
uploads::{MediaType, MediaUpload},
ApiReturn, Error,
};
pub async fn get_request(
Path(id): Path<usize>,
@ -44,7 +51,7 @@ pub async fn list_request(jar: CookieJar, Extension(data): Extension<State>) ->
pub async fn create_request(
jar: CookieJar,
Extension(data): Extension<State>,
Json(req): Json<CreateProduct>,
JsonMultipart(uploads, req): JsonMultipart<CreateProduct>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateProducts) {
@ -52,21 +59,75 @@ pub async fn create_request(
None => return Json(Error::NotAllowed.into()),
};
match data
.create_product(Product::new(
user.id,
req.name,
req.description,
req.price,
req.product_type,
))
.await
{
Ok(x) => Json(ApiReturn {
ok: true,
message: "Product created".to_string(),
payload: x.id.to_string(),
}),
if uploads.len() > 4 {
return Json(
Error::MiscError("Too many uploads. Please use a maximum of 4".to_string()).into(),
);
}
let mut product = Product::new(
user.id,
req.name,
req.description,
req.price,
req.product_type,
);
// check sizes
for img in &uploads {
if img.len() > MAXIMUM_FILE_SIZE {
return Json(Error::FileTooLarge.into());
}
}
// create uploads
for _ in 0..uploads.len() {
product.uploads.push(
match data
.create_upload(MediaUpload::new(MediaType::Webp, product.owner))
.await
{
Ok(u) => u.id,
Err(e) => return Json(e.into()),
},
);
}
let product_uploads = product.uploads.clone();
match data.create_product(product).await {
Ok(x) => {
// store uploads
for (i, upload_id) in product_uploads.iter().enumerate() {
let image = match uploads.get(i) {
Some(img) => img,
None => {
if let Err(e) = data.delete_upload(*upload_id).await {
return Json(e.into());
}
continue;
}
};
let upload = match data.get_upload_by_id(*upload_id).await {
Ok(u) => u,
Err(e) => return Json(e.into()),
};
if let Err(e) =
save_webp_buffer(&upload.path(&data.0.0).to_string(), image.to_vec(), None)
{
return Json(Error::MiscError(e.to_string()).into());
}
}
// ...
Json(ApiReturn {
ok: true,
message: "Product created".to_string(),
payload: x.id.to_string(),
})
}
Err(e) => Json(e.into()),
}
}

View file

@ -2,7 +2,7 @@ use std::fs::exists;
use axum::{body::Body, extract::Path, response::IntoResponse, Extension, Json};
use axum_extra::extract::CookieJar;
use pathbufd::PathBufD;
use crate::{get_user_from_token, State};
use crate::{get_user_from_token, routes::api::v1::UpdateUploadAlt, State};
use super::auth::images::read_image;
use tetratto_core::model::{carp::CarpGraph, oauth, uploads::MediaType, ApiReturn, Error};
@ -52,6 +52,24 @@ pub async fn get_request(
Ok(([("Content-Type", upload.what.mime())], Body::from(bytes)))
}
pub async fn get_json_request(
Path(id): Path<usize>,
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let upload = match data.get_upload_by_id(id).await {
Ok(u) => u,
Err(e) => return Json(e.into()),
};
Json(ApiReturn {
ok: true,
message: "Success".to_string(),
payload: Some(upload),
})
}
pub async fn delete_request(
jar: CookieJar,
Extension(data): Extension<State>,
@ -72,3 +90,25 @@ pub async fn delete_request(
Err(e) => Json(e.into()),
}
}
pub async fn update_alt_request(
jar: CookieJar,
Extension(data): Extension<State>,
Path(id): Path<usize>,
Json(props): Json<UpdateUploadAlt>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageUploads) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
match data.update_upload_alt(id, &user, &props.alt).await {
Ok(_) => Json(ApiReturn {
ok: true,
message: "Upload updated".to_string(),
payload: (),
}),
Err(e) => Json(e.into()),
}
}