|
| 1 | +use axum::{extract::Path, Json}; |
| 2 | +use crates_io_database::schema::{emails, users}; |
| 3 | +use diesel::{pg::Pg, prelude::*}; |
| 4 | +use diesel_async::{AsyncConnection, RunQueryDsl}; |
| 5 | +use http::request::Parts; |
| 6 | + |
| 7 | +use crate::{ |
| 8 | + app::AppState, auth::AuthCheck, models::User, sql::lower, util::errors::AppResult, |
| 9 | + views::EncodableAdminUser, |
| 10 | +}; |
| 11 | + |
| 12 | +/// Find user by login, returning the admin's view of the user. |
| 13 | +/// |
| 14 | +/// Only site admins can use this endpoint. |
| 15 | +#[utoipa::path( |
| 16 | + get, |
| 17 | + path = "/api/v1/users/{user}/admin", |
| 18 | + params( |
| 19 | + ("user" = String, Path, description = "Login name of the user"), |
| 20 | + ), |
| 21 | + tags = ["admin", "users"], |
| 22 | + responses((status = 200, description = "Successful Response")), |
| 23 | +)] |
| 24 | +pub async fn get( |
| 25 | + state: AppState, |
| 26 | + Path(user_name): Path<String>, |
| 27 | + req: Parts, |
| 28 | +) -> AppResult<Json<EncodableAdminUser>> { |
| 29 | + let mut conn = state.db_read_prefer_primary().await?; |
| 30 | + AuthCheck::only_cookie() |
| 31 | + .require_admin() |
| 32 | + .check(&req, &mut conn) |
| 33 | + .await?; |
| 34 | + |
| 35 | + get_user( |
| 36 | + |query| query.filter(lower(users::gh_login).eq(lower(user_name))), |
| 37 | + &mut conn, |
| 38 | + ) |
| 39 | + .await |
| 40 | + .map(Json) |
| 41 | +} |
| 42 | + |
| 43 | +/// A helper to get an [`EncodableAdminUser`] based on whatever filter predicate |
| 44 | +/// is provided in the callback. |
| 45 | +/// |
| 46 | +/// It would be ill advised to do anything in `filter` other than calling |
| 47 | +/// [`QueryDsl::filter`] on the given query, but I'm not the boss of you. |
| 48 | +async fn get_user<Conn, F>(filter: F, conn: &mut Conn) -> AppResult<EncodableAdminUser> |
| 49 | +where |
| 50 | + Conn: AsyncConnection<Backend = Pg>, |
| 51 | + F: FnOnce(users::BoxedQuery<'_, Pg>) -> users::BoxedQuery<'_, Pg>, |
| 52 | +{ |
| 53 | + let query = filter(users::table.into_boxed()); |
| 54 | + |
| 55 | + let (user, verified, email, verification_sent): (User, Option<bool>, Option<String>, bool) = |
| 56 | + query |
| 57 | + .left_join(emails::table) |
| 58 | + .select(( |
| 59 | + User::as_select(), |
| 60 | + emails::verified.nullable(), |
| 61 | + emails::email.nullable(), |
| 62 | + emails::token_generated_at.nullable().is_not_null(), |
| 63 | + )) |
| 64 | + .first(conn) |
| 65 | + .await?; |
| 66 | + |
| 67 | + let verified = verified.unwrap_or(false); |
| 68 | + let verification_sent = verified || verification_sent; |
| 69 | + Ok(EncodableAdminUser::from( |
| 70 | + user, |
| 71 | + email, |
| 72 | + verified, |
| 73 | + verification_sent, |
| 74 | + )) |
| 75 | +} |
0 commit comments