feat(server): pagination
parent
25627e166e
commit
e63d0b5565
|
@ -4,3 +4,9 @@ members = [
|
||||||
'libarchive',
|
'libarchive',
|
||||||
'libarchive3-sys'
|
'libarchive3-sys'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
strip = true
|
||||||
|
|
|
@ -22,9 +22,3 @@ tower-http = { version = "0.4.1", features = ["fs", "trace"] }
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||||
uuid = { version = "1.4.0", features = ["v4"] }
|
uuid = { version = "1.4.0", features = ["v4"] }
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = "fat"
|
|
||||||
codegen-units = 1
|
|
||||||
panic = "abort"
|
|
||||||
strip = true
|
|
||||||
|
|
|
@ -1,33 +1,44 @@
|
||||||
use axum::extract::{Query, State};
|
mod pagination;
|
||||||
|
|
||||||
|
use axum::extract::{Path, Query, State};
|
||||||
use axum::routing::get;
|
use axum::routing::get;
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
use sea_orm::entity::EntityTrait;
|
use sea_orm::entity::EntityTrait;
|
||||||
use sea_orm::query::QueryOrder;
|
use sea_orm::query::QueryOrder;
|
||||||
use sea_orm::PaginatorTrait;
|
use sea_orm::PaginatorTrait;
|
||||||
use serde::Deserialize;
|
|
||||||
|
use pagination::PaginatedResponse;
|
||||||
|
|
||||||
use crate::db::entities::repo;
|
use crate::db::entities::repo;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Pagination {
|
|
||||||
page: Option<u64>,
|
|
||||||
per_page: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn router() -> Router<crate::Global> {
|
pub fn router() -> Router<crate::Global> {
|
||||||
Router::new().route("/repos", get(get_repos))
|
Router::new()
|
||||||
|
.route("/repos", get(get_repos))
|
||||||
|
.route("/repos/:id", get(get_single_repo))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_repos(
|
async fn get_repos(
|
||||||
State(global): State<crate::Global>,
|
State(global): State<crate::Global>,
|
||||||
Query(params): Query<Pagination>,
|
Query(pagination): Query<pagination::Query>,
|
||||||
) -> crate::Result<Json<Vec<repo::Model>>> {
|
) -> crate::Result<Json<PaginatedResponse<repo::Model>>> {
|
||||||
let repos = repo::Entity::find()
|
let repos = repo::Entity::find()
|
||||||
.order_by_asc(repo::Column::Id)
|
.order_by_asc(repo::Column::Id)
|
||||||
.paginate(&global.db, params.per_page.unwrap_or(25))
|
.paginate(&global.db, pagination.per_page.unwrap_or(25))
|
||||||
.fetch_page(params.page.unwrap_or(0))
|
.fetch_page(pagination.page.unwrap_or(1) - 1)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Json(repos))
|
Ok(Json(pagination.res(repos)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_single_repo(
|
||||||
|
State(global): State<crate::Global>,
|
||||||
|
Path(id): Path<i32>,
|
||||||
|
) -> crate::Result<Json<repo::Model>> {
|
||||||
|
let repo = repo::Entity::find_by_id(id)
|
||||||
|
.one(&global.db)
|
||||||
|
.await?
|
||||||
|
.ok_or(axum::http::StatusCode::NOT_FOUND)?;
|
||||||
|
|
||||||
|
Ok(Json(repo))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
use axum::response::{IntoResponse, Response};
|
||||||
|
use axum::Json;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub const DEFAULT_PAGE: u64 = 0;
|
||||||
|
pub const DEFAULT_PER_PAGE: u64 = 25;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Query {
|
||||||
|
pub page: Option<u64>,
|
||||||
|
pub per_page: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PaginatedResponse<T>
|
||||||
|
where
|
||||||
|
T: for<'de> Serialize,
|
||||||
|
{
|
||||||
|
pub page: u64,
|
||||||
|
pub per_page: u64,
|
||||||
|
pub count: usize,
|
||||||
|
pub items: Vec<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Query {
|
||||||
|
pub fn res<T: for<'de> Serialize>(self, items: Vec<T>) -> PaginatedResponse<T> {
|
||||||
|
PaginatedResponse {
|
||||||
|
page: self.page.unwrap_or(DEFAULT_PAGE),
|
||||||
|
per_page: self.page.unwrap_or(DEFAULT_PER_PAGE),
|
||||||
|
count: items.len(),
|
||||||
|
items,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ pub enum ServerError {
|
||||||
Axum(axum::Error),
|
Axum(axum::Error),
|
||||||
Status(StatusCode),
|
Status(StatusCode),
|
||||||
Db(sea_orm::DbErr),
|
Db(sea_orm::DbErr),
|
||||||
|
Status(StatusCode),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ServerError {
|
impl fmt::Display for ServerError {
|
||||||
|
@ -21,6 +22,7 @@ impl fmt::Display for ServerError {
|
||||||
ServerError::Axum(err) => write!(fmt, "{}", err),
|
ServerError::Axum(err) => write!(fmt, "{}", err),
|
||||||
ServerError::Status(status) => write!(fmt, "{}", status),
|
ServerError::Status(status) => write!(fmt, "{}", status),
|
||||||
ServerError::Db(err) => write!(fmt, "{}", err),
|
ServerError::Db(err) => write!(fmt, "{}", err),
|
||||||
|
ServerError::Status(status) => write!(fmt, "{}", status),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +41,7 @@ impl IntoResponse for ServerError {
|
||||||
StatusCode::NOT_FOUND.into_response()
|
StatusCode::NOT_FOUND.into_response()
|
||||||
}
|
}
|
||||||
ServerError::Db(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
ServerError::Db(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||||
|
ServerError::Status(status) => status.into_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,3 +75,9 @@ impl From<sea_orm::DbErr> for ServerError {
|
||||||
ServerError::Db(err)
|
ServerError::Db(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<StatusCode> for ServerError {
|
||||||
|
fn from(status: StatusCode) -> Self {
|
||||||
|
ServerError::Status(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue