use crate::core::db::schema; use crate::core::db::PostgresDb; use anyhow::Result; use sqlx::PgPool; pub struct TkgLogger { pool: PgPool, pub log_id: Option, } impl TkgLogger { pub fn new(pool: PgPool) -> Self { Self { pool, log_id: None } } pub async fn start_operation(pool: &PgPool, file_uuid: &str, operation: &str) -> Result { let table = schema::table_name("tkg_operation_log"); let id = sqlx::query_scalar::<_, i64>(&format!( "INSERT INTO {table} (file_uuid, operation, status, started_at) \ VALUES ($1, $2, 'pending', NOW()) RETURNING id" )) .bind(file_uuid) .bind(operation) .fetch_one(pool) .await?; tracing::info!("[TKG-Log] Started operation {} for {}", operation, file_uuid); Ok(id) } pub async fn update_progress( &self, node_type: &str, nodes_created: i32, edges_created: i32, ) -> Result<()> { if let Some(log_id) = self.log_id { let table = schema::table_name("tkg_operation_log"); sqlx::query(&format!( "UPDATE {table} \ SET nodes_created = nodes_created + $1, \ edges_created = edges_created + $2, \ node_type = COALESCE(node_type, $3) \ WHERE id = $4" )) .bind(nodes_created) .bind(edges_created) .bind(node_type) .bind(log_id) .execute(&self.pool) .await?; } Ok(()) } pub async fn complete_operation(&self, error: Option<&str>) -> Result<()> { if let Some(log_id) = self.log_id { let table = schema::table_name("tkg_operation_log"); let status = if error.is_some() { "failed" } else { "completed" }; sqlx::query(&format!( "UPDATE {table} \ SET status = $1, \ error_message = $2, \ completed_at = NOW() \ WHERE id = $3" )) .bind(status) .bind(error) .bind(log_id) .execute(&self.pool) .await?; tracing::info!("[TKG-Log] Operation {} completed with status: {}", log_id, status); } Ok(()) } pub async fn get_operations(pool: &PgPool, file_uuid: &str) -> Result> { let table = schema::table_name("tkg_operation_log"); let rows = sqlx::query_as::<_, (i64, String, String, Option, Option, i32, i32, i32, i32, i32, i32, String, Option, String, Option, Option)>(&format!( "SELECT id, file_uuid, operation, node_type, edge_type, \ nodes_created, nodes_updated, nodes_deleted, \ edges_created, edges_updated, edges_deleted, \ status, error_message, started_at::text, completed_at::text, properties \ FROM {table} WHERE file_uuid = $1 ORDER BY started_at DESC" )) .bind(file_uuid) .fetch_all(pool) .await?; Ok(rows.into_iter().map(|r| crate::core::tkg::models::TkgOperationLog { id: r.0, file_uuid: r.1, operation: r.2, node_type: r.3, edge_type: r.4, nodes_created: r.5, nodes_updated: r.6, nodes_deleted: r.7, edges_created: r.8, edges_updated: r.9, edges_deleted: r.10, status: r.11, error_message: r.12, started_at: r.13, completed_at: r.14, properties: r.15, }).collect()) } pub async fn delete_operations(pool: &PgPool, file_uuid: &str) -> Result { let table = schema::table_name("tkg_operation_log"); let result = sqlx::query(&format!( "DELETE FROM {table} WHERE file_uuid = $1" )) .bind(file_uuid) .execute(pool) .await?; Ok(result.rows_affected() as i64) } }