feat(server/client): make sure that the documents endpoint isn't broken
This commit is contained in:
parent
da4f770292
commit
96c47af2c0
|
|
@ -153,6 +153,7 @@ const DocumentsPage: React.FC = () => {
|
||||||
pagination.offset,
|
pagination.offset,
|
||||||
ocrFilter || undefined
|
ocrFilter || undefined
|
||||||
);
|
);
|
||||||
|
// Backend returns wrapped object with documents and pagination
|
||||||
setDocuments(response.data.documents || []);
|
setDocuments(response.data.documents || []);
|
||||||
setPagination(response.data.pagination || { total: 0, limit: 20, offset: 0, has_more: false });
|
setPagination(response.data.pagination || { total: 0, limit: 20, offset: 0, has_more: false });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -271,4 +271,31 @@ impl Database {
|
||||||
row.get("failed"),
|
row.get("failed"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Counts total documents for a user with role-based access control
|
||||||
|
pub async fn count_documents_by_user_with_role(&self, user_id: Uuid, user_role: UserRole) -> Result<i64> {
|
||||||
|
let mut query = QueryBuilder::<Postgres>::new("SELECT COUNT(*) as total FROM documents WHERE 1=1");
|
||||||
|
apply_role_based_filter(&mut query, user_id, user_role);
|
||||||
|
let row = query.build().fetch_one(&self.pool).await?;
|
||||||
|
Ok(row.get("total"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Counts documents for a user with role-based access control and OCR status filtering
|
||||||
|
pub async fn count_documents_by_user_with_role_and_filter(
|
||||||
|
&self,
|
||||||
|
user_id: Uuid,
|
||||||
|
user_role: UserRole,
|
||||||
|
ocr_status: Option<&str>
|
||||||
|
) -> Result<i64> {
|
||||||
|
let mut query = QueryBuilder::<Postgres>::new("SELECT COUNT(*) as total FROM documents WHERE 1=1");
|
||||||
|
apply_role_based_filter(&mut query, user_id, user_role);
|
||||||
|
|
||||||
|
if let Some(status) = ocr_status {
|
||||||
|
query.push(" AND ocr_status = ");
|
||||||
|
query.push_bind(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
let row = query.build().fetch_one(&self.pool).await?;
|
||||||
|
Ok(row.get("total"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
models::DocumentResponse,
|
models::DocumentResponse,
|
||||||
AppState,
|
AppState,
|
||||||
};
|
};
|
||||||
use super::types::{PaginationQuery, DocumentUploadResponse};
|
use super::types::{PaginationQuery, DocumentUploadResponse, PaginatedDocumentsResponse, DocumentPaginationInfo};
|
||||||
|
|
||||||
/// Upload a new document
|
/// Upload a new document
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
|
|
@ -201,7 +201,7 @@ pub async fn get_document_by_id(
|
||||||
),
|
),
|
||||||
params(PaginationQuery),
|
params(PaginationQuery),
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "List of documents", body = Vec<DocumentResponse>),
|
(status = 200, description = "Paginated list of documents", body = PaginatedDocumentsResponse),
|
||||||
(status = 401, description = "Unauthorized"),
|
(status = 401, description = "Unauthorized"),
|
||||||
(status = 500, description = "Internal server error")
|
(status = 500, description = "Internal server error")
|
||||||
)
|
)
|
||||||
|
|
@ -210,10 +210,34 @@ pub async fn list_documents(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
auth_user: AuthUser,
|
auth_user: AuthUser,
|
||||||
Query(query): Query<PaginationQuery>,
|
Query(query): Query<PaginationQuery>,
|
||||||
) -> Result<Json<Vec<DocumentResponse>>, StatusCode> {
|
) -> Result<Json<PaginatedDocumentsResponse>, StatusCode> {
|
||||||
let limit = query.limit.unwrap_or(25);
|
let limit = query.limit.unwrap_or(25);
|
||||||
let offset = query.offset.unwrap_or(0);
|
let offset = query.offset.unwrap_or(0);
|
||||||
|
|
||||||
|
// Get total count for pagination
|
||||||
|
let total_count = if let Some(ocr_status) = query.ocr_status.as_deref() {
|
||||||
|
state
|
||||||
|
.db
|
||||||
|
.count_documents_by_user_with_role_and_filter(
|
||||||
|
auth_user.user.id,
|
||||||
|
auth_user.user.role,
|
||||||
|
Some(ocr_status),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
state
|
||||||
|
.db
|
||||||
|
.count_documents_by_user_with_role(
|
||||||
|
auth_user.user.id,
|
||||||
|
auth_user.user.role,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Database error counting documents: {}", e);
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
})?;
|
||||||
|
|
||||||
let documents = if let Some(ocr_status) = query.ocr_status.as_deref() {
|
let documents = if let Some(ocr_status) = query.ocr_status.as_deref() {
|
||||||
state
|
state
|
||||||
.db
|
.db
|
||||||
|
|
@ -272,7 +296,18 @@ pub async fn list_documents(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Json(responses))
|
// Create pagination info
|
||||||
|
let pagination = DocumentPaginationInfo {
|
||||||
|
total: total_count,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
has_more: offset + limit < total_count,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Json(PaginatedDocumentsResponse {
|
||||||
|
documents: responses,
|
||||||
|
pagination,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a specific document
|
/// Delete a specific document
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,20 @@ pub struct DocumentDebugInfo {
|
||||||
pub permissions: Option<String>,
|
pub permissions: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema)]
|
||||||
|
pub struct DocumentPaginationInfo {
|
||||||
|
pub total: i64,
|
||||||
|
pub limit: i64,
|
||||||
|
pub offset: i64,
|
||||||
|
pub has_more: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema)]
|
||||||
|
pub struct PaginatedDocumentsResponse {
|
||||||
|
pub documents: Vec<crate::models::DocumentResponse>,
|
||||||
|
pub pagination: DocumentPaginationInfo,
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for PaginationQuery {
|
impl Default for PaginationQuery {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue