new endpoint in rust

This commit is contained in:
Brandon Thomas committed 2026-06-19 19:56:12 -04:00
1 parent 5a0751e0bc
commit 44d0efdd84
6 files changed
+177

No files matched your search

@@ -1,2 +1,3 @@
pub mod notes;
pub mod read_only;
pub mod upload;
@@ -0,0 +1,51 @@
//! Request/response types for `POST /v1/video_info/notes`.
//!
//! A note is user-submitted context about an uploaded video (what model they
//! believe it is, where it came from, free-form comments). Always tied to an
//! [`UploadedVideoToken`]; pass a [`UploadVideoNoteToken`] to update an existing
//! note instead of creating a new one.
use serde_derive::{Deserialize, Serialize};
use utoipa::ToSchema;
use enums::by_table::uploaded_video_notes::uploaded_video_note_reported_model_type::UploadedVideoNoteReportedModelType;
use enums::by_table::uploaded_video_notes::uploaded_video_note_reported_website::UploadedVideoNoteReportedWebsite;
use tokens::tokens::uploaded_video_notes::UploadVideoNoteToken;
use tokens::tokens::uploaded_videos::UploadedVideoToken;
/// Request body for `POST /v1/video_info/notes`.
#[derive(Deserialize, ToSchema, Debug, Clone)]
pub struct VideoInfoNoteRequest {
/// The uploaded video this note is about. Required.
pub uploaded_video_token: UploadedVideoToken,
/// If present, update this existing note instead of creating a new one.
pub maybe_uploaded_video_note_token: Option<UploadVideoNoteToken>,
/// Original filename of the submitted clip.
pub maybe_filename: Option<String>,
/// The model type the submitter claims this video is (dropdown / enum).
pub maybe_reported_model_type: Option<UploadedVideoNoteReportedModelType>,
/// Free-form custom model name (trimmed; empty becomes absent).
pub maybe_reported_model_name: Option<String>,
/// The website / platform the video came from (dropdown / enum).
pub maybe_website: Option<UploadedVideoNoteReportedWebsite>,
/// Free-form website (trimmed; empty becomes absent).
pub maybe_other_website: Option<String>,
/// Free-form comments (trimmed; empty becomes absent).
pub maybe_comments: Option<String>,
}
/// Response body for `POST /v1/video_info/notes`.
#[derive(Serialize, Deserialize, ToSchema, Debug, Clone)]
pub struct VideoInfoNoteResponse {
pub success: bool,
/// The created or updated note's token (e.g. `uvnote_…`).
pub uploaded_video_note_token: UploadVideoNoteToken,
}
@@ -366,8 +366,12 @@ use artcraft_api_defs::user_referral_codes::list_referral_codes::*;
use artcraft_api_defs::web_referrals::log_web_referral::*;
use artcraft_api_defs::video_info::read_only::*;
use artcraft_api_defs::video_info::upload::*;
use artcraft_api_defs::video_info::notes::*;
use enums::by_table::uploaded_video_notes::uploaded_video_note_reported_model_type::UploadedVideoNoteReportedModelType;
use enums::by_table::uploaded_video_notes::uploaded_video_note_reported_website::UploadedVideoNoteReportedWebsite;
use crate::http_server::endpoints::video_info::video_info_read_info_handler::*;
use crate::http_server::endpoints::video_info::video_info_upload_handler::*;
use crate::http_server::endpoints::video_info::video_info_notes_handler::*;
use crate::http_server::endpoints::web_referrals::log_web_referral_handler::*;
use crate::http_server::endpoints::image_studio::update_gpt_image_job_status_handler::*;
use crate::http_server::endpoints::voice_conversion::enqueue_seed_vc_inference_handler::*;
@@ -623,6 +627,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
// Web Referrals
crate::http_server::endpoints::video_info::video_info_read_info_handler::video_info_read_info_handler,
crate::http_server::endpoints::video_info::video_info_upload_handler::video_info_upload_handler,
crate::http_server::endpoints::video_info::video_info_notes_handler::video_info_notes_handler,
crate::http_server::endpoints::web_referrals::log_web_referral_handler::log_web_referral_handler,
// User Referral Codes
crate::http_server::endpoints::user_referral_codes::create_referral_code_handler::create_referral_code_handler,
@@ -1260,6 +1265,10 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
VideoInfoReadOnlyResponse,
VideoInfoUploadForm,
VideoInfoUploadResponse,
VideoInfoNoteRequest,
VideoInfoNoteResponse,
UploadedVideoNoteReportedModelType,
UploadedVideoNoteReportedWebsite,
VideoProvenanceKind,
SeedanceVideoInfo,
VeoVideoInfo,
@@ -1,3 +1,4 @@
pub mod provenance;
pub mod video_info_notes_handler;
pub mod video_info_read_info_handler;
pub mod video_info_upload_handler;
@@ -0,0 +1,110 @@
//! `POST /v1/video_info/notes` — attach (or update) a user-submitted note about
//! an uploaded video.
//!
//! Always tied to an `UploadedVideoToken`. If the request carries an
//! `UploadVideoNoteToken`, the existing note is updated; otherwise a new note is
//! created. The IP address is stamped on the create or update column accordingly.
use std::marker::PhantomData;
use std::sync::Arc;
use actix_web::web::Json;
use actix_web::{web, HttpRequest};
use artcraft_api_defs::video_info::notes::{VideoInfoNoteRequest, VideoInfoNoteResponse};
use http_server_common::request::get_request_ip::get_request_ip;
use mysql_queries::queries::uploaded_video_notes::insert_uploaded_video_note::{
insert_uploaded_video_note, InsertUploadedVideoNoteArgs,
};
use mysql_queries::queries::uploaded_video_notes::update_uploaded_video_note::{
update_uploaded_video_note, UpdateUploadedVideoNoteArgs,
};
use crate::http_server::common_responses::common_web_error::CommonWebError;
use crate::http_server::web_utils::redis_rate_limiter::RateLimiterError;
use crate::state::server_state::ServerState;
/// Create or update a note about an uploaded video.
#[utoipa::path(
post,
tag = "Video Info",
path = "/v1/video_info/notes",
request_body = VideoInfoNoteRequest,
responses(
(status = 200, description = "Note created or updated", body = VideoInfoNoteResponse),
(status = 400, description = "Bad input", body = CommonWebError),
(status = 500, description = "Server error", body = CommonWebError),
)
)]
pub async fn video_info_notes_handler(
http_request: HttpRequest,
server_state: web::Data<Arc<ServerState>>,
Json(request): Json<VideoInfoNoteRequest>,
) -> Result<Json<VideoInfoNoteResponse>, CommonWebError> {
// IP-based rate limit (shared with the other video-info endpoints). Fails open.
match server_state.redis_rate_limiters.video_info_read_only.rate_limit_request(&http_request).await {
Ok(()) => {}
Err(RateLimiterError::RateLimitExceededError) => return Err(CommonWebError::TooManyRequests),
Err(RateLimiterError::ClientError) => {} // fail open
}
let ip_address = get_request_ip(&http_request);
// Free-form text fields are trimmed; empty becomes absent.
let maybe_filename = request.maybe_filename.as_deref();
let maybe_reported_model_name = normalize_optional(&request.maybe_reported_model_name);
let maybe_other_website = normalize_optional(&request.maybe_other_website);
let maybe_comments = normalize_optional(&request.maybe_comments);
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
let uploaded_video_note_token = match &request.maybe_uploaded_video_note_token {
// Update an existing note.
Some(note_token) => {
update_uploaded_video_note(UpdateUploadedVideoNoteArgs {
token: note_token,
maybe_filename,
maybe_reported_model_type: request.maybe_reported_model_type,
maybe_reported_model_name: maybe_reported_model_name.as_deref(),
maybe_website: request.maybe_website,
maybe_other_website: maybe_other_website.as_deref(),
maybe_comments: maybe_comments.as_deref(),
maybe_comment_update_ip_address: Some(ip_address.as_str()),
mysql_executor: &mut *mysql_connection,
phantom: PhantomData,
})
.await?;
note_token.clone()
}
// Create a new note.
None => {
insert_uploaded_video_note(InsertUploadedVideoNoteArgs {
uploaded_video_token: &request.uploaded_video_token,
maybe_filename,
maybe_reported_model_type: request.maybe_reported_model_type,
maybe_reported_model_name: maybe_reported_model_name.as_deref(),
maybe_website: request.maybe_website,
maybe_other_website: maybe_other_website.as_deref(),
maybe_comments: maybe_comments.as_deref(),
comment_create_ip_address: &ip_address,
mysql_executor: &mut *mysql_connection,
phantom: PhantomData,
})
.await?
}
};
Ok(Json(VideoInfoNoteResponse {
success: true,
uploaded_video_note_token,
}))
}
/// Trim a free-form text field; a missing or whitespace-only value becomes `None`.
fn normalize_optional(value: &Option<String>) -> Option<String> {
value
.as_deref()
.map(str::trim)
.filter(|trimmed| !trimmed.is_empty())
.map(str::to_string)
}
@@ -4,6 +4,7 @@ use actix_web::dev::{ServiceRequest, ServiceResponse};
use actix_web::error::Error;
use actix_web::{web, App, HttpResponse};
use crate::http_server::endpoints::video_info::video_info_notes_handler::video_info_notes_handler;
use crate::http_server::endpoints::video_info::video_info_read_info_handler::video_info_read_info_handler;
use crate::http_server::endpoints::video_info::video_info_upload_handler::video_info_upload_handler;
@@ -27,5 +28,9 @@ where
.route(web::post().to(video_info_upload_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(web::resource("/notes")
.route(web::post().to(video_info_notes_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
)
}