feat: Identity JSON sync mechanism
- storage.rs: add local_profile field, check disk for profile.jpg in save_identity_file_by_pool - tmdb_api.rs: trigger JSON sync after TMDb probe - identity_api.rs: upload_profile_image triggers JSON sync - identity_binding.rs: bind/unbind/merge trigger JSON sync - get_identity_json: replace DB fallback with Lazy Sync (generates JSON from DB if missing) - Fixes missing/obsolete JSON files for all identity mutations
This commit is contained in:
@@ -115,10 +115,9 @@ pub async fn bind_identity(
|
||||
})?;
|
||||
|
||||
let uuid_clean = identity_uuid.replace('-', "");
|
||||
if let Ok(ref db) = PostgresDb::init().await {
|
||||
if let Err(e) = crate::core::identity::storage::save_identity_file(db, &uuid_clean).await {
|
||||
tracing::warn!("[bind] Failed to save identity file for {}: {}", uuid_clean, e);
|
||||
}
|
||||
// Sync identity JSON file
|
||||
if let Err(e) = crate::core::identity::storage::save_identity_file_by_pool(&db, &uuid_clean).await {
|
||||
tracing::warn!("[bind] Failed to sync identity file for {}: {}", uuid_clean, e);
|
||||
}
|
||||
|
||||
Ok(Json(ApiResponse {
|
||||
@@ -136,6 +135,7 @@ pub async fn unbind_identity(
|
||||
Json(req): Json<UnbindIdentityRequest>,
|
||||
) -> Result<Json<ApiResponse<serde_json::Value>>, (StatusCode, Json<serde_json::Value>)> {
|
||||
let table = crate::core::db::schema::table_name("face_detections");
|
||||
let id_table = crate::core::db::schema::table_name("identities");
|
||||
|
||||
let db = sqlx::PgPool::connect(&crate::core::config::DATABASE_URL)
|
||||
.await
|
||||
@@ -146,6 +146,22 @@ pub async fn unbind_identity(
|
||||
)
|
||||
})?;
|
||||
|
||||
// Find the identity_id before unbinding to sync it later
|
||||
let identity_id: Option<i64> = sqlx::query_scalar(&format!(
|
||||
"SELECT identity_id FROM {} WHERE file_uuid = $1 AND face_id = $2 AND identity_id IS NOT NULL",
|
||||
table
|
||||
))
|
||||
.bind(&req.file_uuid)
|
||||
.bind(&req.face_id)
|
||||
.fetch_optional(&db)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(serde_json::json!({"error": e.to_string()})),
|
||||
)
|
||||
})?;
|
||||
|
||||
let result = sqlx::query(&format!(
|
||||
"UPDATE {} SET identity_id = NULL WHERE file_uuid = $1 AND face_id = $2",
|
||||
table
|
||||
@@ -161,6 +177,24 @@ pub async fn unbind_identity(
|
||||
)
|
||||
})?;
|
||||
|
||||
// Sync the identity JSON if we found an identity
|
||||
if let Some(id) = identity_id {
|
||||
let uuid: Option<String> = sqlx::query_scalar(&format!(
|
||||
"SELECT uuid::text FROM {} WHERE id = $1",
|
||||
id_table
|
||||
))
|
||||
.bind(id)
|
||||
.fetch_optional(&db)
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
if let Some(identity_uuid) = uuid {
|
||||
if let Err(e) = crate::core::identity::storage::save_identity_file_by_pool(&db, &identity_uuid).await {
|
||||
tracing::warn!("[unbind] Failed to sync identity file for {}: {}", identity_uuid, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Json(ApiResponse {
|
||||
success: true,
|
||||
message: format!("Unbound face {} from {}", req.face_id, req.file_uuid),
|
||||
@@ -263,6 +297,18 @@ pub async fn merge_identities(
|
||||
})?;
|
||||
}
|
||||
|
||||
// Sync target identity JSON
|
||||
let into_uuid_clean = req.into_uuid.replace('-', "");
|
||||
if let Err(e) = crate::core::identity::storage::save_identity_file_by_pool(&db, &into_uuid_clean).await {
|
||||
tracing::warn!("[merge] Failed to sync target identity file for {}: {}", into_uuid_clean, e);
|
||||
}
|
||||
|
||||
// Delete source identity JSON if not keeping history
|
||||
if !keep {
|
||||
let from_uuid_clean = identity_uuid.replace('-', "");
|
||||
let _ = crate::core::identity::storage::delete_identity_file(&from_uuid_clean);
|
||||
}
|
||||
|
||||
Ok(Json(ApiResponse {
|
||||
success: true,
|
||||
message: format!(
|
||||
|
||||
Reference in New Issue
Block a user