feat: Identity JSON sync + schema-aware column selection
- storage.rs: add local_profile field, check disk for profile.jpg - 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: Lazy Sync (generates JSON from DB if missing) - identities.rs + identity_api.rs: use schema-aware column selection (dev:name vs public:real_name) - Fixes 500 errors on identities endpoints across schemas
This commit is contained in:
@@ -92,18 +92,21 @@ async fn create_identity(
|
||||
)
|
||||
})?;
|
||||
|
||||
let query = r#"
|
||||
SELECT uuid, reference_data->'total_references' as total,
|
||||
let id_table = crate::core::db::schema::table_name("identities");
|
||||
let name_col = if id_table.starts_with("dev.") { "name" } else { "real_name" };
|
||||
let query = format!(
|
||||
"SELECT uuid, reference_data->'total_references' as total,
|
||||
reference_data->'angles_covered' as angles,
|
||||
reference_data->'quality_avg' as quality
|
||||
FROM identities
|
||||
WHERE real_name = $1
|
||||
FROM {}
|
||||
WHERE {} = $1
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1
|
||||
"#;
|
||||
LIMIT 1",
|
||||
id_table, name_col
|
||||
);
|
||||
|
||||
let row: Option<(String, Option<i32>, Option<Vec<String>>, Option<f64>)> =
|
||||
sqlx::query_as(query)
|
||||
sqlx::query_as(&query)
|
||||
.bind(&req.identity_name)
|
||||
.fetch_optional(db.pool())
|
||||
.await
|
||||
@@ -168,7 +171,8 @@ async fn list_identities(
|
||||
.fetch_one(db.pool()).await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Count error: {}", e)))?;
|
||||
|
||||
let sql = format!("SELECT id::int, uuid, real_name, metadata FROM {} ORDER BY id DESC LIMIT $1 OFFSET $2", id_table);
|
||||
let name_col = if id_table.starts_with("dev.") { "name" } else { "real_name" };
|
||||
let sql = format!("SELECT id::int, uuid, {} AS name, metadata FROM {} ORDER BY id DESC LIMIT $1 OFFSET $2", name_col, id_table);
|
||||
|
||||
let rows: Vec<(i32, uuid::Uuid, String, Option<serde_json::Value>)> = match sqlx::query_as(&sql)
|
||||
.bind(page_size as i64)
|
||||
|
||||
@@ -889,7 +889,7 @@ async fn search_identity_text(
|
||||
|
||||
let query = format!(
|
||||
r#"SELECT c.file_uuid, c.chunk_id, c.start_time, c.end_time, c.text_content,
|
||||
fd.identity_id, COALESCE(i.real_name, i.actor_name) AS identity_name, i.source AS identity_source,
|
||||
fd.identity_id, CASE WHEN id_table LIKE 'dev.%' THEN i.name ELSE i.real_name END AS identity_name, i.source AS identity_source,
|
||||
fd.trace_id
|
||||
FROM {} c
|
||||
LEFT JOIN {} fd ON fd.file_uuid = c.file_uuid
|
||||
@@ -965,7 +965,7 @@ async fn search_identities_by_text(
|
||||
let limit = params.limit.unwrap_or(50).min(100);
|
||||
|
||||
let query = format!(
|
||||
r#"SELECT i.id::int, COALESCE(i.real_name, i.actor_name) AS name, i.source, i.tmdb_id,
|
||||
r#"SELECT i.id::int, COALESCE(i.real_name, i.actor_name, i.name) AS name, i.source, i.tmdb_id,
|
||||
fd.file_uuid, fd.trace_id,
|
||||
c.chunk_id, c.start_time, c.text_content
|
||||
FROM {} i
|
||||
@@ -973,9 +973,9 @@ async fn search_identities_by_text(
|
||||
JOIN {} c ON c.file_uuid = fd.file_uuid
|
||||
AND c.start_time <= fd.frame_number / COALESCE(c.fps, 25.0)
|
||||
AND c.end_time >= fd.frame_number / COALESCE(c.fps, 25.0)
|
||||
WHERE COALESCE(i.real_name, i.actor_name) ILIKE $1
|
||||
WHERE COALESCE(i.real_name, i.actor_name, i.name) ILIKE $1
|
||||
AND ($2::text IS NULL OR fd.file_uuid = $2)
|
||||
ORDER BY COALESCE(i.real_name, i.actor_name), c.start_time
|
||||
ORDER BY COALESCE(i.real_name, i.actor_name, i.name), c.start_time
|
||||
LIMIT $3"#,
|
||||
id_table, fd_table, chunk_table
|
||||
);
|
||||
|
||||
@@ -187,15 +187,18 @@ pub async fn save_identity_file_by_pool(pool: &sqlx::PgPool, uuid: &str) -> Resu
|
||||
let identity_table = crate::core::db::schema::table_name("identities");
|
||||
let fd_table = crate::core::db::schema::table_name("face_detections");
|
||||
|
||||
// Schema-aware column selection: dev uses 'name', public uses 'real_name'
|
||||
let name_col = if identity_table.starts_with("dev.") { "name" } else { "real_name" };
|
||||
|
||||
let clean = uuid.replace('-', "");
|
||||
let record = sqlx::query_as::<_, crate::core::db::IdentityDetailRecord>(
|
||||
&format!(
|
||||
"SELECT id, uuid::text, COALESCE(real_name, actor_name, name) AS name, identity_type, source, status, metadata, reference_data, \
|
||||
"SELECT id, uuid::text, {} AS name, identity_type, source, status, metadata, reference_data, \
|
||||
NULL::real[] as voice_embedding, NULL::real[] as identity_embedding, \
|
||||
face_embedding::real[] as face_embedding, \
|
||||
tmdb_id, tmdb_profile, created_at::timestamptz as created_at, NULL::timestamptz as updated_at \
|
||||
FROM {} WHERE REPLACE(uuid::text, '-', '') = $1",
|
||||
identity_table
|
||||
name_col, identity_table
|
||||
)
|
||||
)
|
||||
.bind(&clean)
|
||||
|
||||
Reference in New Issue
Block a user