mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 01:18:34 +00:00
Merge pull request #219 from storytold/dec-18-mithoo
document media files list endpoints, fix public page offset bug
This commit is contained in:
14 files changed
+182
-93
No files matched your search
@@ -6,12 +6,13 @@ use serde::Serialize;
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Used in the `media_files` table in a `VARCHAR` field.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MediaFileOriginCategory {
|
||||
/// ML model inference output - uploaded models or zero shot.
|
||||
|
||||
@@ -6,12 +6,13 @@ use serde::Serialize;
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Used in the `media_files` table in a `VARCHAR` field.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MediaFileOriginModelType {
|
||||
/// RVC (v2) voice conversion models
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::collections::BTreeSet;
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Used in the `media_files` table in `VARCHAR(16)` field `origin_product_category`.
|
||||
///
|
||||
@@ -14,7 +15,7 @@ use strum::EnumIter;
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
|
||||
pub enum MediaFileOriginProductCategory {
|
||||
/// Media files created by (or uploaded for) the Face Animator product.
|
||||
/// The underlying model could be SadTalker, Wav2Lip, or some future model
|
||||
|
||||
@@ -4,12 +4,13 @@ use std::collections::BTreeSet;
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Used in the `media_files` table in a `VARCHAR` field.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum MediaFileType {
|
||||
/// Audio files: wav, mp3, etc.
|
||||
|
||||
@@ -2,11 +2,12 @@ use std::fmt::Debug;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::prefixes::TokenPrefix;
|
||||
|
||||
/// The primary key for Audit Logs.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type, Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct MediaFileToken(pub String);
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ pub struct MediaFileListItem {
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub comment_count: u64,
|
||||
pub favorite_count: u64,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -100,6 +103,8 @@ pub async fn list_media_files(args: ListMediaFilesArgs<'_>) -> AnyhowResult<Medi
|
||||
maybe_creator_display_name: record.maybe_creator_display_name,
|
||||
maybe_creator_gravatar_hash: record.maybe_creator_gravatar_hash,
|
||||
creator_set_visibility: record.creator_set_visibility,
|
||||
comment_count: record.comment_count as u64,
|
||||
favorite_count: record.favorite_count as u64,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
@@ -148,14 +153,22 @@ SELECT
|
||||
|
||||
m.creator_set_visibility,
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
m.updated_at,
|
||||
|
||||
COUNT(f.id) as favorite_count,
|
||||
COUNT(c.id) as comment_count
|
||||
|
||||
FROM media_files AS m
|
||||
|
||||
LEFT OUTER JOIN users AS u
|
||||
ON m.maybe_creator_user_token = u.token
|
||||
|
||||
WHERE m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
LEFT OUTER JOIN favorites as f
|
||||
ON f.entity_type = 'media_file' AND f.entity_token = m.token
|
||||
LEFT OUTER JOIN comments as c
|
||||
ON c.entity_type = 'media_file' AND c.entity_token = c.token
|
||||
WHERE
|
||||
m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
"#
|
||||
);
|
||||
|
||||
@@ -186,6 +199,9 @@ AND m.mod_deleted_at IS NULL
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
query_builder.push(" GROUP BY m.id");
|
||||
|
||||
if cursor_is_reversed {
|
||||
query_builder.push(" ORDER BY m.id ASC ");
|
||||
} else {
|
||||
@@ -218,6 +234,9 @@ struct MediaFileListItemInternal {
|
||||
|
||||
creator_set_visibility: Visibility,
|
||||
|
||||
comment_count: i64,
|
||||
favorite_count: i64,
|
||||
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -257,6 +276,8 @@ impl FromRow<'_, MySqlRow> for MediaFileListItemInternal {
|
||||
creator_set_visibility: Visibility::try_from_mysql_row(row, "creator_set_visibility")?,
|
||||
created_at: row.try_get("created_at")?,
|
||||
updated_at: row.try_get("updated_at")?,
|
||||
comment_count: row.try_get("comment_count")?,
|
||||
favorite_count: row.try_get("favorite_count")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ pub struct ListMediaFileForUserArgs<'a> {
|
||||
pub maybe_filter_media_type: Option<MediaFileType>,
|
||||
pub page_size: usize,
|
||||
pub page_index: usize,
|
||||
pub cursor_is_reversed: bool,
|
||||
pub sort_ascending: bool,
|
||||
pub view_as: ViewAs,
|
||||
pub mysql_pool: &'a MySqlPool,
|
||||
}
|
||||
@@ -66,16 +66,13 @@ pub async fn list_media_files_for_user(args: ListMediaFileForUserArgs<'_>) -> An
|
||||
false,
|
||||
0,
|
||||
0,
|
||||
args.cursor_is_reversed,
|
||||
args.sort_ascending,
|
||||
args.view_as,
|
||||
count_fields.as_str(),
|
||||
);
|
||||
|
||||
let row_count_query = count_query_builder.build_query_scalar::<i64>();
|
||||
let row_count_result = row_count_query.fetch_one(args.mysql_pool).await?;
|
||||
/// Figure out limit start and end based on page size and indexes as requested
|
||||
let limit_start = args.page_size * args.page_index;
|
||||
let limit_end = limit_start + args.page_size;
|
||||
|
||||
/// Now fetch the actual results with all the fields
|
||||
let result_fields = select_result_fields();
|
||||
@@ -83,9 +80,9 @@ pub async fn list_media_files_for_user(args: ListMediaFileForUserArgs<'_>) -> An
|
||||
args.maybe_filter_media_type,
|
||||
args.username,
|
||||
true,
|
||||
limit_start,
|
||||
limit_end,
|
||||
args.cursor_is_reversed,
|
||||
args.page_index,
|
||||
args.page_size,
|
||||
args.sort_ascending,
|
||||
args.view_as,
|
||||
result_fields.as_str(),
|
||||
);
|
||||
@@ -115,7 +112,7 @@ pub async fn list_media_files_for_user(args: ListMediaFileForUserArgs<'_>) -> An
|
||||
|
||||
Ok(MediaFileListPage {
|
||||
records: results,
|
||||
sort_ascending: !args.cursor_is_reversed,
|
||||
sort_ascending: args.sort_ascending,
|
||||
current_page: args.page_index,
|
||||
total_page_count: number_of_pages,
|
||||
})
|
||||
@@ -157,9 +154,9 @@ fn query_builder<'a>(
|
||||
maybe_filter_media_type: Option<MediaFileType>,
|
||||
username: &'a str,
|
||||
enforce_limits: bool,
|
||||
limit_start: usize,
|
||||
limit_end: usize,
|
||||
cursor_is_reversed: bool,
|
||||
page_index: usize,
|
||||
page_size: usize,
|
||||
sort_ascending: bool,
|
||||
view_as: ViewAs,
|
||||
select_fields: &'a str,
|
||||
) -> QueryBuilder<'a, MySql> {
|
||||
@@ -201,18 +198,15 @@ WHERE m.user_deleted_at IS NULL
|
||||
}
|
||||
}
|
||||
|
||||
if cursor_is_reversed {
|
||||
query_builder.push(" ORDER BY m.id ASC ");
|
||||
if sort_ascending {
|
||||
query_builder.push(" ORDER BY m.created_at ASC ");
|
||||
} else {
|
||||
query_builder.push(" ORDER BY m.id DESC ");
|
||||
query_builder.push(" ORDER BY m.created_at DESC ");
|
||||
}
|
||||
|
||||
if enforce_limits {
|
||||
if cursor_is_reversed {
|
||||
query_builder.push(format!(" LIMIT {limit_start}, {limit_end} "));
|
||||
} else {
|
||||
query_builder.push(format!(" LIMIT {limit_end}, {limit_start} "));
|
||||
}
|
||||
let offset = page_index * page_size;
|
||||
query_builder.push(format!(" LIMIT {page_size} OFFSET {offset} "));
|
||||
}
|
||||
|
||||
query_builder
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
use crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::*;
|
||||
|
||||
use crate::http_server::endpoints::weights::list_available_weights::*;
|
||||
@@ -8,26 +7,36 @@ use crate::http_server::endpoints::weights::get_weight::*;
|
||||
use crate::http_server::endpoints::weights::update_weight::*;
|
||||
use crate::http_server::endpoints::weights::list_weights_by_user::*;
|
||||
use crate::http_server::common_responses::user_details_lite::{DefaultAvatarInfo, UserDetailsLight};
|
||||
use enums::by_table::model_weights::{
|
||||
weights_types::WeightsType,
|
||||
weights_category::WeightsCategory,
|
||||
};
|
||||
use crate::http_server::common_responses::media_file_social_meta_lite::MediaFileSocialMetaLight;
|
||||
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
|
||||
use crate::http_server::common_responses::pagination_page::PaginationPage;
|
||||
use crate::http_server::endpoints::media_files::list_featured_media_files::*;
|
||||
use crate::http_server::endpoints::media_files::list_media_files::*;
|
||||
use crate::http_server::endpoints::media_files::list_media_files_for_user::*;
|
||||
use enums::by_table::model_weights::{weights_types::WeightsType, weights_category::WeightsCategory};
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
|
||||
use tokens::tokens::generic_inference_jobs::*;
|
||||
use tokens::tokens::model_weights::*;
|
||||
use tokens::tokens::users::*;
|
||||
use tokens::tokens::media_files::*;
|
||||
use enums::common::visibility::Visibility;
|
||||
use chrono::{DateTime, Utc};
|
||||
use utoipa::OpenApi;
|
||||
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(paths(crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::enqueue_tts_request,
|
||||
crate::http_server::endpoints::weights::list_available_weights::list_available_weights_handler,
|
||||
crate::http_server::endpoints::weights::delete_weight::delete_weight_handler,
|
||||
crate::http_server::endpoints::weights::update_weight::update_weight_handler,
|
||||
crate::http_server::endpoints::weights::get_weight::get_weight_handler,
|
||||
crate::http_server::endpoints::weights::list_weights_by_user::list_weights_by_user_handler
|
||||
crate::http_server::endpoints::weights::list_weights_by_user::list_weights_by_user_handler,
|
||||
crate::http_server::endpoints::media_files::list_featured_media_files::list_featured_media_files_handler,
|
||||
crate::http_server::endpoints::media_files::list_media_files::list_media_files_handler,
|
||||
crate::http_server::endpoints::media_files::list_media_files_for_user::list_media_files_for_user_handler
|
||||
),components(schemas(
|
||||
// Tokens
|
||||
UserToken,Visibility,
|
||||
@@ -40,10 +49,17 @@ use utoipa::OpenApi;
|
||||
GetWeightPathInfo,GetWeightResponse,GetWeightError,
|
||||
UpdateWeightRequest,UpdateWeightRequest,UpdateWeightPathInfo,UpdateWeightRequest,UpdateWeightError,
|
||||
DeleteWeightPathInfo,DeleteWeightRequest,DeleteWeightError,DeleteWeightRequest,DeleteWeightError,
|
||||
ListWeightError,ModelWeightForList,ListWeightError,
|
||||
ListWeightError,ModelWeightForList,ListWeightError,
|
||||
ListAvailableWeightsQuery,ListAvailableWeightsSuccessResponse,ModelWeightForList,
|
||||
ListWeightsByUserError,ListWeightsByUserSuccessResponse,ListWeightsByUserPathInfo,Weight,
|
||||
UserDetailsLight,DefaultAvatarInfo
|
||||
UserDetailsLight,DefaultAvatarInfo,
|
||||
PaginationCursors,
|
||||
PaginationPage,
|
||||
MediaFileOriginModelType,MediaFileOriginProductCategory,MediaFileOriginCategory,
|
||||
ListFeaturedMediaFilesSuccessResponse,MediaFile,MediaFileToken,MediaFileType, ListFeaturedMediaFilesError,
|
||||
ListMediaFilesSuccessResponse, ListMediaFilesError, MediaFileListItem, ListMediaFilesQueryParams,
|
||||
MediaFileSocialMetaLight,
|
||||
ListMediaFilesForUserSuccessResponse,ListMediaFilesForUserQueryParams,ListMediaFilesForUserError,ListMediaFilesForUserPathInfo,
|
||||
MediaFileForUserListItem, MediaFileForUserListItem
|
||||
)))]
|
||||
pub struct ApiDoc;
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
use utoipa::ToSchema;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
|
||||
/// Fields useful for enriching media file listings
|
||||
#[derive(Clone, Serialize, ToSchema)]
|
||||
pub struct MediaFileSocialMetaLight {
|
||||
pub favorites_count: u64,
|
||||
pub comments_count: u64,
|
||||
}
|
||||
|
||||
impl MediaFileSocialMetaLight {
|
||||
pub fn from_db_fields(
|
||||
favorites_count: u64,
|
||||
comments_count: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
favorites_count,
|
||||
comments_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod pagination_cursors;
|
||||
pub mod pagination_page;
|
||||
pub mod user_details_lite;
|
||||
pub mod user_details_lite;
|
||||
pub mod media_file_social_meta_lite;
|
||||
+3
-1
@@ -1,10 +1,12 @@
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Pagination by cursors
|
||||
/// These types of pagination are for "infinite scrolling", which do not reveal a number of pages.
|
||||
/// This is good so that investors and competitors cannot reveal how many database records we have.
|
||||
/// This is typically used for "discovery" type pages, not user profiles.
|
||||
///
|
||||
/// See `PaginationPage` for the other type of pagination.
|
||||
#[derive(Serialize)]
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct PaginationCursors {
|
||||
|
||||
/// The "next" cursor.
|
||||
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Pagination by page id
|
||||
/// This type of pagination is by "page id" and "page count".
|
||||
/// This should never be used to walk the entire public database as competitors and investors
|
||||
@@ -5,7 +7,7 @@
|
||||
/// This is best used for user profiles and scoped down results.
|
||||
///
|
||||
/// See `PaginationCursors` for the other type of pagination.
|
||||
#[derive(Serialize)]
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct PaginationPage {
|
||||
/// The current page number
|
||||
pub current: usize,
|
||||
|
||||
+41
-23
@@ -7,6 +7,7 @@ use actix_web::web::{Path, Query};
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::{info, warn};
|
||||
use buckets::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use utoipa::{IntoParams, path, ToSchema};
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
@@ -15,14 +16,15 @@ use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use mysql_queries::queries::media_files::list_media_files::{list_media_files, ListMediaFilesArgs, ViewAs};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use crate::http_server::common_responses::media_file_social_meta_lite::MediaFileSocialMetaLight;
|
||||
|
||||
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
|
||||
use crate::http_server::common_responses::user_details_lite::UserDetailsLight;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct QueryParams {
|
||||
#[derive(Deserialize, ToSchema, IntoParams)]
|
||||
pub struct ListMediaFilesQueryParams {
|
||||
pub sort_ascending: Option<bool>,
|
||||
pub per_page: Option<usize>,
|
||||
pub cursor: Option<String>,
|
||||
@@ -30,14 +32,14 @@ pub struct QueryParams {
|
||||
pub filter_media_type: Option<MediaFileType>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct SuccessResponse {
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListMediaFilesSuccessResponse {
|
||||
pub success: bool,
|
||||
pub results: Vec<MediaFileListItem>,
|
||||
pub pagination: PaginationCursors,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct MediaFileListItem {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
@@ -52,6 +54,7 @@ pub struct MediaFileListItem {
|
||||
pub public_bucket_path: String,
|
||||
|
||||
pub maybe_creator: Option<UserDetailsLight>,
|
||||
pub maybe_social_meta: Option<MediaFileSocialMetaLight>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
@@ -59,38 +62,49 @@ pub struct MediaFileListItem {
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorResponse {
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum ListMediaFilesError {
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for ErrorResponse {
|
||||
impl ResponseError for ListMediaFilesError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
ErrorResponse::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ListMediaFilesError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
ErrorResponse::ServerError => "server error".to_string(),
|
||||
ListMediaFilesError::ServerError => "server error".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ErrorResponse {
|
||||
impl std::fmt::Display for ListMediaFilesError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/v1/media_files/list",
|
||||
params(
|
||||
ListMediaFilesQueryParams,
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "List Featured Media Files", body = ListMediaFilesSuccessResponse),
|
||||
(status = 500, description = "Server error", body = ListMediaFilesError),
|
||||
),
|
||||
)]
|
||||
pub async fn list_media_files_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<QueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, ErrorResponse>
|
||||
http_request: HttpRequest,
|
||||
query: Query<ListMediaFilesQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, ListMediaFilesError>
|
||||
{
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
@@ -98,7 +112,7 @@ pub async fn list_media_files_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
ListMediaFilesError::ServerError
|
||||
})?;
|
||||
|
||||
let mut is_mod = false;
|
||||
@@ -120,7 +134,7 @@ pub async fn list_media_files_handler(
|
||||
let cursor = server_state.sort_key_crypto.decrypt_id(cursor)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
ListMediaFilesError::ServerError
|
||||
})?;
|
||||
Some(cursor as usize)
|
||||
} else {
|
||||
@@ -147,7 +161,7 @@ pub async fn list_media_files_handler(
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(ErrorResponse::ServerError);
|
||||
return Err(ListMediaFilesError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -155,7 +169,7 @@ pub async fn list_media_files_handler(
|
||||
let cursor = server_state.sort_key_crypto.encrypt_id(id as u64)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
ListMediaFilesError::ServerError
|
||||
})?;
|
||||
Some(cursor)
|
||||
} else {
|
||||
@@ -166,7 +180,7 @@ pub async fn list_media_files_handler(
|
||||
let cursor = server_state.sort_key_crypto.encrypt_id(id as u64)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
ListMediaFilesError::ServerError
|
||||
})?;
|
||||
Some(cursor)
|
||||
} else {
|
||||
@@ -175,7 +189,7 @@ pub async fn list_media_files_handler(
|
||||
|
||||
let results = results_page.records.into_iter()
|
||||
.map(|record| MediaFileListItem {
|
||||
token: record.token,
|
||||
token: record.token.clone(),
|
||||
origin_category: record.origin_category,
|
||||
origin_product_category: record.origin_product_category,
|
||||
maybe_origin_model_type: record.maybe_origin_model_type,
|
||||
@@ -193,13 +207,17 @@ pub async fn list_media_files_handler(
|
||||
record.maybe_creator_display_name,
|
||||
record.maybe_creator_gravatar_hash,
|
||||
),
|
||||
maybe_social_meta: Option::from(MediaFileSocialMetaLight::from_db_fields(
|
||||
record.favorite_count,
|
||||
record.comment_count,
|
||||
)),
|
||||
creator_set_visibility: record.creator_set_visibility,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let response = SuccessResponse {
|
||||
let response = ListMediaFilesSuccessResponse {
|
||||
success: true,
|
||||
results,
|
||||
pagination: PaginationCursors {
|
||||
@@ -210,7 +228,7 @@ pub async fn list_media_files_handler(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| ErrorResponse::ServerError)?;
|
||||
.map_err(|e| ListMediaFilesError::ServerError)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
+36
-28
@@ -7,6 +7,7 @@ use actix_web::web::{Path, Query};
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::{info, warn};
|
||||
use buckets::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use utoipa::{IntoParams, path, ToSchema};
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
@@ -16,35 +17,32 @@ use enums::common::visibility::Visibility;
|
||||
use mysql_queries::queries::media_files::list_media_files_for_user::{list_media_files_for_user, ListMediaFileForUserArgs, ViewAs};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
|
||||
use crate::http_server::common_responses::pagination_page::PaginationPage;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PathInfo {
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ListMediaFilesForUserPathInfo {
|
||||
username: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct QueryParams {
|
||||
#[derive(Deserialize, ToSchema, IntoParams)]
|
||||
pub struct ListMediaFilesForUserQueryParams {
|
||||
pub sort_ascending: Option<bool>,
|
||||
pub page_size: Option<usize>,
|
||||
pub page_index: Option<usize>,
|
||||
pub cursor: Option<String>,
|
||||
pub cursor_is_reversed: Option<bool>,
|
||||
pub filter_media_type: Option<MediaFileType>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct SuccessResponse {
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListMediaFilesForUserSuccessResponse {
|
||||
pub success: bool,
|
||||
pub results: Vec<MediaFileListItem>,
|
||||
pub results: Vec<MediaFileForUserListItem>,
|
||||
pub pagination: PaginationPage,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MediaFileListItem {
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct MediaFileForUserListItem {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub origin_category: MediaFileOriginCategory,
|
||||
@@ -63,39 +61,50 @@ pub struct MediaFileListItem {
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorResponse {
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum ListMediaFilesForUserError {
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for ErrorResponse {
|
||||
impl ResponseError for ListMediaFilesForUserError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
ErrorResponse::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ListMediaFilesForUserError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
ErrorResponse::ServerError => "server error".to_string(),
|
||||
ListMediaFilesForUserError::ServerError => "server error".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ErrorResponse {
|
||||
impl std::fmt::Display for ListMediaFilesForUserError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/v1/media_files/list/user/{username}",
|
||||
params(
|
||||
ListMediaFilesForUserQueryParams,
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "List Featured Media Files", body = ListMediaFilesForUserSuccessResponse),
|
||||
(status = 500, description = "Server error", body = ListMediaFilesForUserError),
|
||||
),
|
||||
)]
|
||||
pub async fn list_media_files_for_user_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<PathInfo>,
|
||||
query: Query<QueryParams>,
|
||||
path: Path<ListMediaFilesForUserPathInfo>,
|
||||
query: Query<ListMediaFilesForUserQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, ErrorResponse>
|
||||
) -> Result<HttpResponse, ListMediaFilesForUserError>
|
||||
{
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
@@ -103,7 +112,7 @@ pub async fn list_media_files_for_user_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
ListMediaFilesForUserError::ServerError
|
||||
})?;
|
||||
|
||||
let mut is_author = false;
|
||||
@@ -121,7 +130,6 @@ pub async fn list_media_files_for_user_handler(
|
||||
let limit = query.page_size.unwrap_or(25);
|
||||
|
||||
let sort_ascending = query.sort_ascending.unwrap_or(false);
|
||||
let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false);
|
||||
|
||||
let page_size = query.page_size.unwrap_or_else(|| 25);
|
||||
let page_index = query.page_index.unwrap_or_else(|| 0);
|
||||
@@ -140,7 +148,7 @@ pub async fn list_media_files_for_user_handler(
|
||||
maybe_filter_media_type: query.filter_media_type,
|
||||
page_size,
|
||||
page_index,
|
||||
cursor_is_reversed,
|
||||
sort_ascending,
|
||||
view_as,
|
||||
mysql_pool: &server_state.mysql_pool,
|
||||
}).await;
|
||||
@@ -150,13 +158,13 @@ pub async fn list_media_files_for_user_handler(
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(ErrorResponse::ServerError);
|
||||
return Err(ListMediaFilesForUserError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let results = results_page.records.into_iter()
|
||||
.map(|record| MediaFileListItem {
|
||||
.map(|record| MediaFileForUserListItem {
|
||||
token: record.token,
|
||||
origin_category: record.origin_category,
|
||||
origin_product_category: record.origin_product_category,
|
||||
@@ -175,7 +183,7 @@ pub async fn list_media_files_for_user_handler(
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let response = SuccessResponse {
|
||||
let response = ListMediaFilesForUserSuccessResponse {
|
||||
success: true,
|
||||
results,
|
||||
pagination: PaginationPage{
|
||||
@@ -185,7 +193,7 @@ pub async fn list_media_files_for_user_handler(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| ErrorResponse::ServerError)?;
|
||||
.map_err(|e| ListMediaFilesForUserError::ServerError)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
Reference in new issue
Block a user