mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-09 19:41:32 +00:00
update characters
This commit is contained in:
1 parent
80a5a9ad04
commit
c058850cfe
3 files changed
+168
-6
No files matched your search
@@ -4,3 +4,4 @@ pub mod delete_character;
|
||||
pub mod get_character_by_token;
|
||||
pub mod get_character_by_token_including_deleted;
|
||||
pub mod list_active_characters_for_user;
|
||||
pub mod update_character_name_and_description;
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::MySql;
|
||||
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
|
||||
/// Update a character's name, kinovi name, and/or description.
|
||||
///
|
||||
/// All fields are always SET in the query — the caller should pass the
|
||||
/// existing values for fields that should not change.
|
||||
pub async fn update_character_name_and_description(
|
||||
character_token: &CharacterToken,
|
||||
character_name: &str,
|
||||
kinovi_character_name: &str,
|
||||
maybe_description: Option<&str>,
|
||||
connection: &mut PoolConnection<MySql>,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE characters
|
||||
SET
|
||||
character_name = ?,
|
||||
kinovi_character_name = ?,
|
||||
maybe_description = ?
|
||||
WHERE token = ?
|
||||
AND deleted_at IS NULL
|
||||
"#,
|
||||
character_name,
|
||||
kinovi_character_name,
|
||||
maybe_description,
|
||||
character_token.as_str(),
|
||||
)
|
||||
.execute(&mut **connection)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+131
-6
@@ -2,10 +2,16 @@ use std::sync::Arc;
|
||||
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::{error, info, warn};
|
||||
|
||||
use artcraft_api_defs::characters::edit_character::{EditCharacterRequest, EditCharacterResponse};
|
||||
use mysql_queries::queries::characters::get_character_by_token::get_character_by_token;
|
||||
use mysql_queries::queries::characters::update_character_name_and_description::update_character_name_and_description;
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use seedance2pro_client::requests::update_character::update_character::{update_character, UpdateCharacterArgs};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Edit a character's name or description.
|
||||
@@ -23,9 +29,128 @@ use crate::state::server_state::ServerState;
|
||||
),
|
||||
)]
|
||||
pub async fn edit_character_handler(
|
||||
_http_request: HttpRequest,
|
||||
_request: Json<EditCharacterRequest>,
|
||||
_server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<EditCharacterResponse>, CommonWebError> {
|
||||
todo!()
|
||||
http_request: HttpRequest,
|
||||
request: Json<EditCharacterRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<EditCharacterResponse>, AdvancedCommonWebError> {
|
||||
|
||||
// --- Auth ---
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request,
|
||||
&server_state.session_checker,
|
||||
&mut mysql_connection,
|
||||
).await?;
|
||||
|
||||
let user_token = &user_session.user_token;
|
||||
let is_mod = user_session.is_mod();
|
||||
|
||||
// --- Look up character ---
|
||||
|
||||
let character = get_character_by_token(&request.token, &mut mysql_connection)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
warn!("Character not found: {}", request.token);
|
||||
AdvancedCommonWebError::NotFound
|
||||
})?;
|
||||
|
||||
// --- Ownership check ---
|
||||
|
||||
let is_owner = character.maybe_creator_user_token
|
||||
.as_ref()
|
||||
.map(|owner| owner == user_token)
|
||||
.unwrap_or(false);
|
||||
|
||||
if !is_owner && !is_mod {
|
||||
warn!("User {} tried to edit character {} they don't own", user_token, request.token);
|
||||
return Err(AdvancedCommonWebError::NotFound);
|
||||
}
|
||||
|
||||
// --- Determine what to update ---
|
||||
|
||||
let new_name = resolve_name_update(&request.updated_name);
|
||||
let new_description = resolve_description_update(&request.updated_description);
|
||||
|
||||
let has_name_change = new_name.is_some();
|
||||
let has_description_change = new_description.is_some();
|
||||
|
||||
if !has_name_change && !has_description_change {
|
||||
return Ok(Json(EditCharacterResponse { success: true }));
|
||||
}
|
||||
|
||||
// --- If renaming, update Kinovi first ---
|
||||
|
||||
let final_name = new_name.unwrap_or_else(|| character.character_name.clone());
|
||||
let final_kinovi_name = if has_name_change { final_name.clone() } else {
|
||||
character.kinovi_character_name.clone().unwrap_or_else(|| character.character_name.clone())
|
||||
};
|
||||
|
||||
let final_description = match new_description {
|
||||
Some(desc) => desc,
|
||||
None => character.maybe_description.clone(),
|
||||
};
|
||||
|
||||
if has_name_change {
|
||||
if let Some(ref kinovi_id) = character.kinovi_character_id {
|
||||
let session = Seedance2ProSession::from_cookies_string(
|
||||
server_state.seedance2pro.cookies.clone()
|
||||
);
|
||||
|
||||
update_character(UpdateCharacterArgs {
|
||||
session: &session,
|
||||
character_id: kinovi_id.clone(),
|
||||
name: final_name.clone(),
|
||||
description: final_description.clone().unwrap_or_default(),
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error updating character on Kinovi: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Updated character {} on Kinovi (name='{}')", kinovi_id, final_name);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Update database ---
|
||||
|
||||
update_character_name_and_description(
|
||||
&request.token,
|
||||
&final_name,
|
||||
&final_kinovi_name,
|
||||
final_description.as_deref(),
|
||||
&mut mysql_connection,
|
||||
).await?;
|
||||
|
||||
info!("Updated character {} in database", request.token);
|
||||
|
||||
Ok(Json(EditCharacterResponse { success: true }))
|
||||
}
|
||||
|
||||
// =============== Private helpers ===============
|
||||
|
||||
/// Determine the new name, if any.
|
||||
/// Returns None if the name should not be updated.
|
||||
fn resolve_name_update(updated_name: &Option<String>) -> Option<String> {
|
||||
let name = updated_name.as_ref()?;
|
||||
let trimmed = name.trim();
|
||||
if trimmed.is_empty() { None } else { Some(trimmed.to_string()) }
|
||||
}
|
||||
|
||||
/// Determine the new description, if any.
|
||||
/// Returns:
|
||||
/// - None if the description should not be updated
|
||||
/// - Some(None) if the description should be cleared
|
||||
/// - Some(Some(text)) if the description should be set
|
||||
fn resolve_description_update(updated_description: &Option<String>) -> Option<Option<String>> {
|
||||
let desc = updated_description.as_ref()?;
|
||||
let trimmed = desc.trim();
|
||||
if trimmed.is_empty() {
|
||||
Some(None) // clear the description
|
||||
} else {
|
||||
Some(Some(trimmed.to_string())) // set the description
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user