pagnation

This commit is contained in:
Mateusz Gruszczyński
2026-08-02 14:26:32 +02:00
parent 464660a274
commit f71ebefa82
7 changed files with 290 additions and 20 deletions
+74 -11
View File
@@ -16,7 +16,7 @@ use argon2::{
};
use axum::{
Json,
extract::{Path as AxumPath, State},
extract::{Path as AxumPath, Query, State},
http::{HeaderMap, StatusCode, header},
response::{IntoResponse, Response},
};
@@ -116,6 +116,8 @@ pub struct ResourceActionRequest {
}
#[derive(Serialize)]
pub struct ResourceItem {
#[serde(default)]
kind: String,
slug: String,
title: String,
protected: i64,
@@ -201,6 +203,7 @@ impl<'r> sqlx::FromRow<'r, AnyRow> for User {
impl<'r> sqlx::FromRow<'r, AnyRow> for ResourceItem {
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
Ok(Self {
kind: String::new(),
slug: crate::row_decode::text(row, "slug")?,
title: crate::row_decode::text(row, "title")?,
protected: row.try_get("protected")?,
@@ -257,8 +260,32 @@ pub struct RevokeShareLinkRequest {
#[derive(Serialize)]
pub struct ResourceList {
workspaces: Vec<ResourceItem>,
pads: Vec<ResourceItem>,
items: Vec<ResourceItem>,
pagination: PaginationMeta,
}
#[derive(Debug, Deserialize)]
pub struct ResourceListQuery {
#[serde(default)]
q: String,
#[serde(default = "default_page")]
page: usize,
#[serde(default = "default_per_page")]
per_page: usize,
}
#[derive(Debug, Serialize)]
pub struct PaginationMeta {
page: usize,
per_page: usize,
total: usize,
total_pages: usize,
}
fn default_page() -> usize { 1 }
fn default_per_page() -> usize { 25 }
fn normalized_per_page(value: usize) -> usize {
match value { 25 | 50 | 100 => value, _ => 25 }
}
#[derive(Serialize)]
pub struct SessionResponse {
@@ -1142,6 +1169,7 @@ async fn send_account_action(
pub async fn resources(
State(state): State<SharedState>,
headers: HeaderMap,
Query(query): Query<ResourceListQuery>,
) -> Result<Json<ResourceList>, AuthError> {
let user = require_user(&state, &headers).await?;
let workspaces = sqlx::query_as::<_, ResourceItem>(queries::get(
@@ -1153,14 +1181,49 @@ pub async fn resources(
.fetch_all(state.db.pool())
.await
.map_err(AuthError::database)?;
let pads =
sqlx::query_as::<_, ResourceItem>(queries::get(state.db.kind(), queries::USER_LIST_PADS))
.bind(user.id)
.bind(user.id)
.fetch_all(state.db.pool())
.await
.map_err(AuthError::database)?;
Ok(Json(ResourceList { workspaces, pads }))
let pads = sqlx::query_as::<_, ResourceItem>(
queries::get(state.db.kind(), queries::USER_LIST_PADS),
)
.bind(user.id)
.bind(user.id)
.fetch_all(state.db.pool())
.await
.map_err(AuthError::database)?;
let search = query.q.trim().to_lowercase();
let mut items = workspaces
.into_iter()
.map(|item| (item, "workspace"))
.chain(pads.into_iter().map(|item| (item, "pad")))
.filter(|(item, kind)| {
search.is_empty()
|| item.title.to_lowercase().contains(&search)
|| item.slug.to_lowercase().contains(&search)
|| kind.contains(&search)
})
.collect::<Vec<_>>();
items.sort_by(|(left, _), (right, _)| right.updated_at.cmp(&left.updated_at));
let page = query.page.max(1);
let per_page = normalized_per_page(query.per_page);
let total = items.len();
let total_pages = ((total + per_page - 1) / per_page).max(1);
let page = page.min(total_pages);
let start = (page - 1) * per_page;
let items = items
.into_iter()
.skip(start)
.take(per_page)
.map(|(mut item, kind)| {
item.kind = kind.to_string();
item
})
.collect();
Ok(Json(ResourceList {
items,
pagination: PaginationMeta { page, per_page, total, total_pages },
}))
}
pub async fn update_resource(