this repo has no description
1use crate::auth::BearerAuthAdmin;
2use crate::state::AppState;
3use axum::{
4 Json,
5 extract::{Query, RawQuery, State},
6 http::StatusCode,
7 response::{IntoResponse, Response},
8};
9use serde::{Deserialize, Serialize};
10use serde_json::json;
11use tracing::error;
12
13#[derive(Deserialize)]
14pub struct GetAccountInfoParams {
15 pub did: String,
16}
17
18#[derive(Serialize)]
19#[serde(rename_all = "camelCase")]
20pub struct AccountInfo {
21 pub did: String,
22 pub handle: String,
23 pub email: Option<String>,
24 pub indexed_at: String,
25 pub invite_note: Option<String>,
26 pub invites_disabled: bool,
27 pub email_verified_at: Option<String>,
28 pub deactivated_at: Option<String>,
29}
30
31#[derive(Serialize)]
32#[serde(rename_all = "camelCase")]
33pub struct GetAccountInfosOutput {
34 pub infos: Vec<AccountInfo>,
35}
36
37pub async fn get_account_info(
38 State(state): State<AppState>,
39 _auth: BearerAuthAdmin,
40 Query(params): Query<GetAccountInfoParams>,
41) -> Response {
42 let did = params.did.trim();
43 if did.is_empty() {
44 return (
45 StatusCode::BAD_REQUEST,
46 Json(json!({"error": "InvalidRequest", "message": "did is required"})),
47 )
48 .into_response();
49 }
50 let result = sqlx::query!(
51 r#"
52 SELECT did, handle, email, created_at
53 FROM users
54 WHERE did = $1
55 "#,
56 did
57 )
58 .fetch_optional(&state.db)
59 .await;
60 match result {
61 Ok(Some(row)) => (
62 StatusCode::OK,
63 Json(AccountInfo {
64 did: row.did,
65 handle: row.handle,
66 email: row.email,
67 indexed_at: row.created_at.to_rfc3339(),
68 invite_note: None,
69 invites_disabled: false,
70 email_verified_at: None,
71 deactivated_at: None,
72 }),
73 )
74 .into_response(),
75 Ok(None) => (
76 StatusCode::NOT_FOUND,
77 Json(json!({"error": "AccountNotFound", "message": "Account not found"})),
78 )
79 .into_response(),
80 Err(e) => {
81 error!("DB error in get_account_info: {:?}", e);
82 (
83 StatusCode::INTERNAL_SERVER_ERROR,
84 Json(json!({"error": "InternalError"})),
85 )
86 .into_response()
87 }
88 }
89}
90
91fn parse_repeated_param(query: Option<&str>, key: &str) -> Vec<String> {
92 query
93 .map(|q| {
94 q.split('&')
95 .filter_map(|pair| {
96 let (k, v) = pair.split_once('=')?;
97
98 if k == key {
99 Some(urlencoding::decode(v).ok()?.into_owned())
100 } else {
101 None
102 }
103 })
104 .collect()
105 })
106 .unwrap_or_default()
107}
108
109pub async fn get_account_infos(
110 State(state): State<AppState>,
111 _auth: BearerAuthAdmin,
112 RawQuery(raw_query): RawQuery,
113) -> Response {
114 let dids = parse_repeated_param(raw_query.as_deref(), "dids");
115 if dids.is_empty() {
116 return (
117 StatusCode::BAD_REQUEST,
118 Json(json!({"error": "InvalidRequest", "message": "dids is required"})),
119 )
120 .into_response();
121 }
122 let mut infos = Vec::new();
123 for did in &dids {
124 if did.is_empty() {
125 continue;
126 }
127 let result = sqlx::query!(
128 r#"
129 SELECT did, handle, email, created_at
130 FROM users
131 WHERE did = $1
132 "#,
133 did
134 )
135 .fetch_optional(&state.db)
136 .await;
137 if let Ok(Some(row)) = result {
138 infos.push(AccountInfo {
139 did: row.did,
140 handle: row.handle,
141 email: row.email,
142 indexed_at: row.created_at.to_rfc3339(),
143 invite_note: None,
144 invites_disabled: false,
145 email_verified_at: None,
146 deactivated_at: None,
147 });
148 }
149 }
150 (StatusCode::OK, Json(GetAccountInfosOutput { infos })).into_response()
151}