Skip to main content

systemprompt_api/routes/oauth/client/
list.rs

1#![allow(unused_qualifications)]
2
3
4use axum::extract::{Extension, Query, State};
5use axum::http::StatusCode;
6use axum::response::{IntoResponse, Json, Response};
7use serde::Deserialize;
8use tracing::instrument;
9use validator::Validate;
10
11use super::super::responses::{bad_request, internal_error};
12use systemprompt_models::api::PaginationParams;
13use systemprompt_models::{PaginationInfo, RequestContext};
14use systemprompt_oauth::repository::OAuthRepository;
15use systemprompt_oauth::OAuthState;
16
17#[derive(Debug, Deserialize, Validate)]
18pub struct ListClientsQuery {
19    #[serde(flatten)]
20    pub pagination: PaginationParams,
21
22    #[validate(length(min = 1, max = 50))]
23    pub status: Option<String>,
24}
25
26fn init_error(e: impl std::fmt::Display) -> Response {
27    (
28        StatusCode::INTERNAL_SERVER_ERROR,
29        Json(serde_json::json!({
30            "error": "server_error",
31            "error_description": format!("Repository initialization failed: {e}")
32        })),
33    )
34        .into_response()
35}
36
37fn paginated_response<T: serde::Serialize>(items: Vec<T>, pagination: PaginationInfo) -> Response {
38    (
39        StatusCode::OK,
40        Json(serde_json::json!({
41            "data": items,
42            "meta": {
43                "pagination": pagination
44            }
45        })),
46    )
47        .into_response()
48}
49
50#[instrument(skip(state, req_ctx, query))]
51pub async fn list_clients(
52    Extension(req_ctx): Extension<RequestContext>,
53    State(state): State<OAuthState>,
54    Query(query): Query<ListClientsQuery>,
55) -> impl IntoResponse {
56    let repository = match OAuthRepository::new(state.db_pool()) {
57        Ok(r) => r,
58        Err(e) => return init_error(e),
59    };
60
61    if let Err(e) = query.validate() {
62        tracing::info!(
63            reason = "Validation error",
64            requested_by = %req_ctx.auth.user_id,
65            "OAuth clients list rejected - validation failed"
66        );
67        return bad_request(format!("Invalid query parameters: {e}"));
68    }
69
70    let page = query.pagination.page;
71    let per_page = query.pagination.per_page;
72    let offset = query.pagination.offset();
73    let limit = query.pagination.limit();
74
75    let clients_result = repository.list_clients_paginated(limit, offset).await;
76    let count_result = repository.count_clients().await;
77
78    match (clients_result, count_result) {
79        (Ok(clients), Ok(total)) => {
80            tracing::info!(
81                count = clients.len(),
82                total = total,
83                page = page,
84                per_page = per_page,
85                requested_by = %req_ctx.auth.user_id,
86                "OAuth clients listed"
87            );
88            let pagination = PaginationInfo::new(total, page, per_page);
89            let items: Vec<systemprompt_oauth::clients::api::OAuthClientResponse> =
90                clients.into_iter().map(Into::into).collect();
91            paginated_response(items, pagination)
92        },
93        (Err(e), _) | (_, Err(e)) => {
94            tracing::error!(
95                error = %e,
96                requested_by = %req_ctx.auth.user_id,
97                "OAuth clients list failed"
98            );
99            internal_error(format!("Failed to list clients: {e}"))
100        },
101    }
102}