Skip to main content

systemprompt_api/routes/oauth/client/
list.rs

1//! OAuth client listing endpoint with pagination.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use axum::extract::{Extension, Query};
7use axum::http::StatusCode;
8use axum::response::{IntoResponse, Json, Response};
9use serde::Deserialize;
10use tracing::instrument;
11use validator::Validate;
12
13use super::super::OAuthHttpError;
14use super::super::extractors::OAuthRepo;
15use systemprompt_models::api::PaginationParams;
16use systemprompt_models::{PaginationInfo, RequestContext};
17
18#[derive(Debug, Deserialize, Validate)]
19pub struct ListClientsQuery {
20    #[serde(flatten)]
21    pub pagination: PaginationParams,
22
23    #[validate(length(min = 1, max = 50))]
24    pub status: Option<String>,
25}
26
27fn paginated_response<T: serde::Serialize>(items: &[T], pagination: &PaginationInfo) -> Response {
28    (
29        StatusCode::OK,
30        Json(serde_json::json!({
31            "data": items,
32            "meta": {
33                "pagination": pagination
34            }
35        })),
36    )
37        .into_response()
38}
39
40#[instrument(skip(repository, req_ctx, query))]
41pub async fn list_clients(
42    Extension(req_ctx): Extension<RequestContext>,
43    OAuthRepo(repository): OAuthRepo,
44    Query(query): Query<ListClientsQuery>,
45) -> Result<Response, OAuthHttpError> {
46    query
47        .validate()
48        .map_err(|e| OAuthHttpError::invalid_request(format!("Invalid query parameters: {e}")))?;
49
50    let page = query.pagination.page;
51    let per_page = query.pagination.per_page;
52    let offset = query.pagination.offset();
53    let limit = query.pagination.limit();
54
55    let clients = repository.list_clients_paginated(limit, offset).await?;
56    let total = repository.count_clients().await?;
57
58    tracing::info!(
59        count = clients.len(),
60        total = total,
61        page = page,
62        per_page = per_page,
63        requested_by = %req_ctx.auth.actor.user_id,
64        "OAuth clients listed"
65    );
66    let pagination = PaginationInfo::new(total, page, per_page);
67    let items: Vec<systemprompt_oauth::clients::api::OAuthClientResponse> =
68        clients.into_iter().map(Into::into).collect();
69    Ok(paginated_response(&items, &pagination))
70}