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 mut parts = pair.splitn(2, '=');
97 let k = parts.next()?;
98 let v = parts.next()?;
99 if k == key {
100 Some(urlencoding::decode(v).ok()?.into_owned())
101 } else {
102 None
103 }
104 })
105 .collect()
106 })
107 .unwrap_or_default()
108}
109
110pub async fn get_account_infos(
111 State(state): State<AppState>,
112 _auth: BearerAuthAdmin,
113 RawQuery(raw_query): RawQuery,
114) -> Response {
115 let dids = parse_repeated_param(raw_query.as_deref(), "dids");
116 if dids.is_empty() {
117 return (
118 StatusCode::BAD_REQUEST,
119 Json(json!({"error": "InvalidRequest", "message": "dids is required"})),
120 )
121 .into_response();
122 }
123 let mut infos = Vec::new();
124 for did in &dids {
125 if did.is_empty() {
126 continue;
127 }
128 let result = sqlx::query!(
129 r#"
130 SELECT did, handle, email, created_at
131 FROM users
132 WHERE did = $1
133 "#,
134 did
135 )
136 .fetch_optional(&state.db)
137 .await;
138 if let Ok(Some(row)) = result {
139 infos.push(AccountInfo {
140 did: row.did,
141 handle: row.handle,
142 email: row.email,
143 indexed_at: row.created_at.to_rfc3339(),
144 invite_note: None,
145 invites_disabled: false,
146 email_verified_at: None,
147 deactivated_at: None,
148 });
149 }
150 }
151 (StatusCode::OK, Json(GetAccountInfosOutput { infos })).into_response()
152}