Fix diesel join queries across schemas and FileItem missing fields
Some checks failed
BotServer CI/CD / build (push) Failing after 10m1s

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-04-05 12:06:35 -03:00
parent 508a3ed011
commit f6869e6b5c
3 changed files with 102 additions and 30 deletions

View file

@ -57,8 +57,9 @@ pub fn register_use_kb_keyword(
let conn = state_clone_for_syntax.conn.clone(); let conn = state_clone_for_syntax.conn.clone();
let kb_name_clone = kb_name.clone(); let kb_name_clone = kb_name.clone();
let result = let result = std::thread::spawn(move || {
std::thread::spawn(move || add_kb_to_session(conn, session_id, bot_id, user_id, &kb_name_clone)) add_kb_to_session(conn, session_id, bot_id, user_id, &kb_name_clone)
})
.join(); .join();
match result { match result {
@ -103,8 +104,9 @@ pub fn register_use_kb_keyword(
let conn = state_clone_lower.conn.clone(); let conn = state_clone_lower.conn.clone();
let kb_name_clone = kb_name.to_string(); let kb_name_clone = kb_name.to_string();
let result = let result = std::thread::spawn(move || {
std::thread::spawn(move || add_kb_to_session(conn, session_id, bot_id, user_id, &kb_name_clone)) add_kb_to_session(conn, session_id, bot_id, user_id, &kb_name_clone)
})
.join(); .join();
match result { match result {
@ -135,8 +137,9 @@ pub fn register_use_kb_keyword(
let conn = state_clone2.conn.clone(); let conn = state_clone2.conn.clone();
let kb_name_clone = kb_name.to_string(); let kb_name_clone = kb_name.to_string();
let result = let result = std::thread::spawn(move || {
std::thread::spawn(move || add_kb_to_session(conn, session_id, bot_id, user_id, &kb_name_clone)) add_kb_to_session(conn, session_id, bot_id, user_id, &kb_name_clone)
})
.join(); .join();
match result { match result {
@ -185,7 +188,11 @@ fn add_kb_to_session(
.map_err(|e| format!("Failed to check KB existence: {}", e))?; .map_err(|e| format!("Failed to check KB existence: {}", e))?;
let (kb_folder_path, qdrant_collection) = if let Some(kb_result) = kb_exists { let (kb_folder_path, qdrant_collection) = if let Some(kb_result) = kb_exists {
// CHECK ACCESS #[derive(QueryableByName)]
struct AccessCheck {
#[diesel(sql_type = diesel::sql_types::Bool)]
exists: bool,
}
let has_access: bool = diesel::sql_query( let has_access: bool = diesel::sql_query(
"SELECT EXISTS ( "SELECT EXISTS (
SELECT 1 FROM kb_collections kc SELECT 1 FROM kb_collections kc
@ -198,12 +205,13 @@ fn add_kb_to_session(
WHERE kga.kb_id = kc.id AND rug.user_id = $2 WHERE kga.kb_id = kc.id AND rug.user_id = $2
) )
) )
)" ) AS exists",
) )
.bind::<diesel::sql_types::Uuid, _>(kb_result.id) .bind::<diesel::sql_types::Uuid, _>(kb_result.id)
.bind::<diesel::sql_types::Uuid, _>(user_id) .bind::<diesel::sql_types::Uuid, _>(user_id)
.get_result::<bool>(&mut conn) .get_result::<AccessCheck>(&mut conn)
.map_err(|e| format!("Failed to check KB access: {}", e))?; .map_err(|e| format!("Failed to check KB access: {}", e))?
.exists;
if !has_access { if !has_access {
return Err(format!("Access denied for KB '{}'", kb_name)); return Err(format!("Access denied for KB '{}'", kb_name));

View file

@ -12,6 +12,9 @@ use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
use tokio::task::JoinError;
use diesel::prelude::*;
use diesel::sql_types::*;
pub mod drive_types; pub mod drive_types;
pub mod drive_handlers; pub mod drive_handlers;
@ -357,17 +360,28 @@ pub async fn list_files(
let mut items = Vec::new(); let mut items = Vec::new();
let prefix = params.path.as_deref().unwrap_or(""); let prefix = params.path.as_deref().unwrap_or("");
// Fetch KBs from database to mark them in the list
let kbs: Vec<(String, bool)> = { let kbs: Vec<(String, bool)> = {
let conn = state.conn.clone(); let conn = state.conn.clone();
tokio::task::spawn_blocking(move || { let kbs_result = tokio::task::spawn_blocking(move || -> Result<Vec<(String, bool)>, String> {
#[derive(QueryableByName)]
struct KbRow {
#[diesel(sql_type = diesel::sql_types::Text)]
name: String,
#[diesel(sql_type = diesel::sql_types::Bool)]
is_public: bool,
}
let mut db_conn = conn.get().map_err(|e| e.to_string())?; let mut db_conn = conn.get().map_err(|e| e.to_string())?;
use crate::core::shared::models::schema::kb_collections; let rows: Vec<KbRow> = diesel::sql_query(
kb_collections::table "SELECT name, COALESCE(is_public, false) as is_public FROM kb_collections"
.select((kb_collections::name, kb_collections::is_public)) )
.load::<(String, bool)>(&mut db_conn) .load(&mut db_conn)
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())?;
}).await.unwrap_or(Ok(vec![])).unwrap_or_default() Ok(rows.into_iter().map(|r| (r.name, r.is_public)).collect())
}).await;
match kbs_result {
Ok(Ok(kbs)) => kbs,
_ => vec![],
}
}; };
let paginator = s3_client let paginator = s3_client
@ -401,6 +415,8 @@ pub async fn list_files(
size: None, size: None,
modified: None, modified: None,
icon: get_file_icon(&dir), icon: get_file_icon(&dir),
is_kb: false,
is_public: true,
}); });
} }
} }
@ -946,6 +962,8 @@ pub async fn search_files(
size: obj.size(), size: obj.size(),
modified: obj.last_modified().map(|t| t.to_string()), modified: obj.last_modified().map(|t| t.to_string()),
icon: get_file_icon(key), icon: get_file_icon(key),
is_kb: false,
is_public: true,
}); });
} }
} else { } else {
@ -956,6 +974,8 @@ pub async fn search_files(
size: obj.size(), size: obj.size(),
modified: obj.last_modified().map(|t| t.to_string()), modified: obj.last_modified().map(|t| t.to_string()),
icon: get_file_icon(key), icon: get_file_icon(key),
is_kb: false,
is_public: true,
}); });
} }
} }
@ -1016,6 +1036,8 @@ pub async fn recent_files(
size: obj.size(), size: obj.size(),
modified: obj.last_modified().map(|t| t.to_string()), modified: obj.last_modified().map(|t| t.to_string()),
icon: get_file_icon(key), icon: get_file_icon(key),
is_kb: false,
is_public: true,
}); });
} }
} }

View file

@ -18,6 +18,9 @@ use axum::{
response::IntoResponse, response::IntoResponse,
Json, Json,
}; };
use diesel::prelude::*;
use diesel::sql_types::*;
use tokio::task::JoinError;
use chrono::Utc; use chrono::Utc;
use diesel::prelude::*; use diesel::prelude::*;
use log::info; use log::info;
@ -59,16 +62,55 @@ pub async fn get_kb_groups(
Path(kb_id): Path<Uuid>, Path(kb_id): Path<Uuid>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let conn = state.conn.clone(); let conn = state.conn.clone();
let result = tokio::task::spawn_blocking(move || { let result: Result<Result<Vec<RbacGroup>, String>, JoinError> = tokio::task::spawn_blocking(move || {
let mut db_conn = conn.get().map_err(|e| format!("DB error: {e}"))?; let mut db_conn = conn.get().map_err(|e| format!("DB error: {e}"))?;
use crate::core::shared::models::schema::{kb_group_associations, rbac_groups};
kb_group_associations::table #[derive(QueryableByName)]
.inner_join(rbac_groups::table.on(rbac_groups::id.eq(kb_group_associations::group_id))) struct GroupRow {
.filter(kb_group_associations::kb_id.eq(kb_id)) #[diesel(sql_type = diesel::sql_types::Uuid)]
.filter(rbac_groups::is_active.eq(true)) id: Uuid,
.select(RbacGroup::as_select()) #[diesel(sql_type = diesel::sql_types::Text)]
.load::<RbacGroup>(&mut db_conn) name: String,
.map_err(|e| format!("Query error: {e}")) #[diesel(sql_type = diesel::sql_types::Text)]
display_name: String,
#[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Text>)]
description: Option<String>,
#[diesel(sql_type = diesel::sql_types::Bool)]
is_active: bool,
#[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Uuid>)]
parent_group_id: Option<Uuid>,
#[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Uuid>)]
created_by: Option<Uuid>,
#[diesel(sql_type = diesel::sql_types::Timestamptz)]
created_at: chrono::DateTime<Utc>,
#[diesel(sql_type = diesel::sql_types::Timestamptz)]
updated_at: chrono::DateTime<Utc>,
}
let rows: Vec<GroupRow> = diesel::sql_query(
"SELECT rg.id, rg.name, rg.display_name, rg.description, rg.is_active,
rg.parent_group_id, rg.created_by, rg.created_at, rg.updated_at
FROM research.kb_group_associations kga
JOIN core.rbac_groups rg ON rg.id = kga.group_id
WHERE kga.kb_id = $1 AND rg.is_active = true"
)
.bind::<diesel::sql_types::Uuid, _>(kb_id)
.load(&mut db_conn)
.map_err(|e| format!("Query error: {e}"))?;
let groups: Vec<RbacGroup> = rows.into_iter().map(|r| RbacGroup {
id: r.id,
name: r.name,
display_name: r.display_name,
description: r.description,
is_active: r.is_active,
parent_group_id: r.parent_group_id,
created_by: r.created_by,
created_at: r.created_at,
updated_at: r.updated_at,
}).collect();
Ok(groups)
}) })
.await; .await;