mirror of https://github.com/buster-so/buster.git
list endpoints for chats and logs
This commit is contained in:
parent
fbfea49253
commit
f6b42ec9b7
|
@ -10,7 +10,7 @@ use uuid::Uuid;
|
|||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ListChatsRequest {
|
||||
pub page_token: Option<String>,
|
||||
pub page: Option<i32>,
|
||||
pub page_size: i32,
|
||||
pub admin_view: bool,
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ pub struct ChatListItem {
|
|||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct PaginationInfo {
|
||||
pub has_more: bool,
|
||||
pub next_page_token: Option<String>,
|
||||
pub next_page: Option<i32>,
|
||||
pub total_items: i32, // Number of items in current page
|
||||
}
|
||||
|
||||
|
@ -58,14 +58,14 @@ struct ChatWithUser {
|
|||
/// List chats with pagination support
|
||||
///
|
||||
/// This function efficiently retrieves a list of chats with their associated user information.
|
||||
/// It supports pagination using cursor-based pagination (page_token) and limits results using page_size.
|
||||
/// It supports pagination using page number and limits results using page_size.
|
||||
/// If admin_view is true and the user has admin privileges, it shows all chats; otherwise, only the user's chats.
|
||||
///
|
||||
/// Returns a list of chat items with user information and pagination details.
|
||||
pub async fn list_chats_handler(
|
||||
request: ListChatsRequest,
|
||||
user_id: &Uuid,
|
||||
) -> Result<ListChatsResponse> {
|
||||
) -> Result<Vec<ChatListItem>> {
|
||||
use database::schema::{chats, users};
|
||||
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
|
@ -81,19 +81,14 @@ pub async fn list_chats_handler(
|
|||
query = query.filter(chats::created_by.eq(user_id));
|
||||
}
|
||||
|
||||
// Add cursor-based pagination if page_token is provided
|
||||
if let Some(token) = request.page_token {
|
||||
// Parse the RFC3339 timestamp directly
|
||||
let cursor_dt = DateTime::parse_from_rfc3339(&token)
|
||||
.map_err(|_| anyhow!("Invalid timestamp in page token"))?
|
||||
.with_timezone(&Utc);
|
||||
|
||||
query = query.filter(chats::created_at.lt(cursor_dt));
|
||||
}
|
||||
// Calculate offset based on page number
|
||||
let page = request.page.unwrap_or(1);
|
||||
let offset = (page - 1) * request.page_size;
|
||||
|
||||
// Order by creation date descending and limit results
|
||||
// Order by creation date descending and apply pagination
|
||||
query = query
|
||||
.order_by(chats::created_at.desc())
|
||||
.offset(offset as i64)
|
||||
.limit((request.page_size + 1) as i64);
|
||||
|
||||
// Execute query and select required fields
|
||||
|
@ -139,17 +134,9 @@ pub async fn list_chats_handler(
|
|||
// Create pagination info
|
||||
let pagination = PaginationInfo {
|
||||
has_more,
|
||||
next_page_token: if has_more {
|
||||
// Just use the RFC3339 timestamp directly as the token
|
||||
items.last().map(|last_item| last_item.created_at.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
next_page: if has_more { Some(page + 1) } else { None },
|
||||
total_items: items.len() as i32,
|
||||
};
|
||||
|
||||
Ok(ListChatsResponse {
|
||||
items,
|
||||
pagination,
|
||||
})
|
||||
}
|
||||
Ok(items)
|
||||
}
|
|
@ -2,6 +2,7 @@ pub mod chats;
|
|||
pub mod collections;
|
||||
pub mod favorites;
|
||||
pub mod files;
|
||||
pub mod logs;
|
||||
pub mod messages;
|
||||
pub mod metrics;
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use database::pool::get_pg_pool;
|
||||
use diesel::prelude::*;
|
||||
use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ListLogsRequest {
|
||||
pub page: Option<i32>,
|
||||
pub page_size: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct LogListItem {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub updated_at: String,
|
||||
pub created_at: String,
|
||||
pub created_by: String,
|
||||
pub created_by_id: String,
|
||||
pub created_by_name: String,
|
||||
pub created_by_avatar: Option<String>,
|
||||
pub last_edited: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct PaginationInfo {
|
||||
pub has_more: bool,
|
||||
pub next_page: Option<i32>,
|
||||
pub total_items: i32, // Number of items in current page
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ListLogsResponse {
|
||||
pub items: Vec<LogListItem>,
|
||||
pub pagination: PaginationInfo,
|
||||
}
|
||||
|
||||
#[derive(Queryable)]
|
||||
struct ChatWithUser {
|
||||
// Chat fields
|
||||
pub id: Uuid,
|
||||
pub title: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub created_by: Uuid,
|
||||
// User fields
|
||||
pub user_name: Option<String>,
|
||||
pub user_attributes: Value,
|
||||
}
|
||||
|
||||
/// List logs with pagination support
|
||||
///
|
||||
/// This function efficiently retrieves a list of chats (logs) with their associated user information.
|
||||
/// It supports pagination using page number and limits results using page_size.
|
||||
/// Unlike the regular chats endpoint, logs are not restricted to the user and are visible to everyone.
|
||||
///
|
||||
/// Returns a list of log items with user information and pagination details.
|
||||
pub async fn list_logs_handler(
|
||||
request: ListLogsRequest,
|
||||
) -> Result<Vec<LogListItem>, anyhow::Error> {
|
||||
use database::schema::{chats, users};
|
||||
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
|
||||
// Start building the query
|
||||
let mut query = chats::table
|
||||
.inner_join(users::table.on(chats::created_by.eq(users::id)))
|
||||
.filter(chats::deleted_at.is_null())
|
||||
.into_boxed();
|
||||
|
||||
// Calculate offset based on page number
|
||||
let page = request.page.unwrap_or(1);
|
||||
let offset = (page - 1) * request.page_size;
|
||||
|
||||
// Order by creation date descending and apply pagination
|
||||
query = query
|
||||
.order_by(chats::created_at.desc())
|
||||
.offset(offset as i64)
|
||||
.limit((request.page_size + 1) as i64);
|
||||
|
||||
// Execute query and select required fields
|
||||
let results: Vec<ChatWithUser> = query
|
||||
.select((
|
||||
chats::id,
|
||||
chats::title,
|
||||
chats::created_at,
|
||||
chats::updated_at,
|
||||
chats::created_by,
|
||||
users::name.nullable(),
|
||||
users::attributes,
|
||||
))
|
||||
.load::<ChatWithUser>(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Check if there are more results and prepare pagination info
|
||||
let has_more = results.len() > request.page_size as usize;
|
||||
let items: Vec<LogListItem> = results
|
||||
.into_iter()
|
||||
.take(request.page_size as usize)
|
||||
.map(|chat| {
|
||||
let created_by_avatar = chat.user_attributes
|
||||
.get("avatar")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from);
|
||||
|
||||
LogListItem {
|
||||
id: chat.id.to_string(),
|
||||
title: chat.title,
|
||||
created_at: chat.created_at.to_rfc3339(),
|
||||
updated_at: chat.updated_at.to_rfc3339(),
|
||||
created_by: chat.created_by.to_string(),
|
||||
created_by_id: chat.created_by.to_string(),
|
||||
created_by_name: chat.user_name.unwrap_or_else(|| "Unknown".to_string()),
|
||||
created_by_avatar,
|
||||
last_edited: chat.updated_at.to_rfc3339(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Create pagination info
|
||||
let pagination = PaginationInfo {
|
||||
has_more,
|
||||
next_page: if has_more { Some(page + 1) } else { None },
|
||||
total_items: items.len() as i32,
|
||||
};
|
||||
|
||||
Ok(items)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub mod list_logs_handler;
|
||||
|
||||
pub use list_logs_handler::*;
|
|
@ -1,16 +1,16 @@
|
|||
use axum::{extract::Query, http::StatusCode, Extension};
|
||||
use database::models::User;
|
||||
use handlers::chats::list_chats_handler::{
|
||||
list_chats_handler, ListChatsRequest, ListChatsResponse,
|
||||
list_chats_handler, ChatListItem, ListChatsRequest, ListChatsResponse,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use middleware::AuthenticatedUser;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::routes::rest::ApiResponse;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ListChatsQuery {
|
||||
pub page_token: Option<String>,
|
||||
pub page: Option<i32>,
|
||||
#[serde(default = "default_page_size")]
|
||||
pub page_size: i32,
|
||||
#[serde(default)]
|
||||
|
@ -24,9 +24,9 @@ fn default_page_size() -> i32 {
|
|||
pub async fn list_chats_route(
|
||||
Extension(user): Extension<AuthenticatedUser>,
|
||||
Query(query): Query<ListChatsQuery>,
|
||||
) -> Result<ApiResponse<ListChatsResponse>, (StatusCode, &'static str)> {
|
||||
) -> Result<ApiResponse<Vec<ChatListItem>>, (StatusCode, &'static str)> {
|
||||
let request = ListChatsRequest {
|
||||
page_token: query.page_token,
|
||||
page: query.page,
|
||||
page_size: query.page_size,
|
||||
admin_view: query.admin_view,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
use axum::{extract::Query, http::StatusCode, Extension};
|
||||
use handlers::logs::list_logs_handler::{
|
||||
list_logs_handler, ListLogsRequest, ListLogsResponse, LogListItem,
|
||||
};
|
||||
use middleware::AuthenticatedUser;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::routes::rest::ApiResponse;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ListLogsQuery {
|
||||
pub page: Option<i32>,
|
||||
#[serde(default = "default_page_size")]
|
||||
pub page_size: i32,
|
||||
}
|
||||
|
||||
fn default_page_size() -> i32 {
|
||||
20 // Default to 20 items per page
|
||||
}
|
||||
|
||||
pub async fn list_logs_route(
|
||||
Extension(user): Extension<AuthenticatedUser>,
|
||||
Query(query): Query<ListLogsQuery>,
|
||||
) -> Result<ApiResponse<Vec<LogListItem>>, (StatusCode, &'static str)> {
|
||||
let request = ListLogsRequest {
|
||||
page: query.page,
|
||||
page_size: query.page_size,
|
||||
};
|
||||
|
||||
match list_logs_handler(request).await {
|
||||
Ok(response) => Ok(ApiResponse::JsonData(response)),
|
||||
Err(e) => {
|
||||
tracing::error!("Error listing logs: {}", e);
|
||||
Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to list logs"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
use axum::{routing::get, Router};
|
||||
|
||||
mod list_logs;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new().route("/", get(list_logs::list_logs_route))
|
||||
}
|
|
@ -5,6 +5,7 @@ mod dashboards;
|
|||
mod data_sources;
|
||||
mod dataset_groups;
|
||||
mod datasets;
|
||||
mod logs;
|
||||
mod messages;
|
||||
mod metrics;
|
||||
mod organizations;
|
||||
|
@ -33,6 +34,7 @@ pub fn router() -> Router {
|
|||
.nest("/dashboards", dashboards::router())
|
||||
.nest("/users", users::router())
|
||||
.nest("/collections", collections::router())
|
||||
.nest("/logs", logs::router())
|
||||
.route_layer(axum_middleware::from_fn(auth)),
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue