this repo has no description
1use crate::auth::validate_bearer_token; 2use crate::state::AppState; 3use axum::{ 4 Json, 5 extract::State, 6 http::{HeaderMap, StatusCode}, 7 response::{IntoResponse, Response}, 8}; 9use chrono::Utc; 10use serde::Deserialize; 11use serde_json::json; 12use tracing::{error, info}; 13 14#[derive(Deserialize)] 15#[serde(rename_all = "camelCase")] 16pub struct ConfirmChannelVerificationInput { 17 pub channel: String, 18 pub code: String, 19} 20 21pub async fn confirm_channel_verification( 22 State(state): State<AppState>, 23 headers: HeaderMap, 24 Json(input): Json<ConfirmChannelVerificationInput>, 25) -> Response { 26 let token = match crate::auth::extract_bearer_token_from_header( 27 headers.get("Authorization").and_then(|h| h.to_str().ok()), 28 ) { 29 Some(t) => t, 30 None => return ( 31 StatusCode::UNAUTHORIZED, 32 Json(json!({"error": "AuthenticationRequired", "message": "Authentication required"})), 33 ) 34 .into_response(), 35 }; 36 let user = match validate_bearer_token(&state.db, &token).await { 37 Ok(u) => u, 38 Err(_) => { 39 return ( 40 StatusCode::UNAUTHORIZED, 41 Json(json!({"error": "AuthenticationFailed", "message": "Invalid token"})), 42 ) 43 .into_response(); 44 } 45 }; 46 47 let user_id = match sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", user.did) 48 .fetch_one(&state.db) 49 .await 50 { 51 Ok(id) => id, 52 Err(_) => { 53 return ( 54 StatusCode::INTERNAL_SERVER_ERROR, 55 Json(json!({"error": "InternalError", "message": "User not found"})), 56 ) 57 .into_response(); 58 } 59 }; 60 61 let channel_str = input.channel.as_str(); 62 if !["email", "discord", "telegram", "signal"].contains(&channel_str) { 63 return ( 64 StatusCode::BAD_REQUEST, 65 Json(json!({"error": "InvalidRequest", "message": "Invalid channel"})), 66 ) 67 .into_response(); 68 } 69 70 let record = match sqlx::query!( 71 r#" 72 SELECT code, pending_identifier, expires_at FROM channel_verifications 73 WHERE user_id = $1 AND channel = $2::comms_channel 74 "#, 75 user_id, 76 channel_str as _ 77 ) 78 .fetch_optional(&state.db) 79 .await { 80 Ok(Some(r)) => r, 81 Ok(None) => return ( 82 StatusCode::BAD_REQUEST, 83 Json(json!({"error": "InvalidRequest", "message": "No pending verification found. Update notification preferences first."})), 84 ) 85 .into_response(), 86 Err(e) => return ( 87 StatusCode::INTERNAL_SERVER_ERROR, 88 Json(json!({"error": "InternalError", "message": format!("Database error: {}", e)})), 89 ) 90 .into_response(), 91 }; 92 93 let pending_identifier = 94 match record.pending_identifier { 95 Some(p) => p, 96 None => return ( 97 StatusCode::BAD_REQUEST, 98 Json(json!({"error": "InvalidRequest", "message": "No pending identifier found"})), 99 ) 100 .into_response(), 101 }; 102 103 if record.expires_at < Utc::now() { 104 return ( 105 StatusCode::BAD_REQUEST, 106 Json(json!({"error": "ExpiredToken", "message": "Verification code expired"})), 107 ) 108 .into_response(); 109 } 110 111 if record.code != input.code { 112 return ( 113 StatusCode::BAD_REQUEST, 114 Json(json!({"error": "InvalidCode", "message": "Invalid verification code"})), 115 ) 116 .into_response(); 117 } 118 119 let mut tx = match state.db.begin().await { 120 Ok(tx) => tx, 121 Err(_) => { 122 return ( 123 StatusCode::INTERNAL_SERVER_ERROR, 124 Json(json!({"error": "InternalError"})), 125 ) 126 .into_response(); 127 } 128 }; 129 130 let update_result = match channel_str { 131 "email" => sqlx::query!( 132 "UPDATE users SET email = $1, updated_at = NOW() WHERE id = $2", 133 pending_identifier, 134 user_id 135 ).execute(&mut *tx).await, 136 "discord" => sqlx::query!( 137 "UPDATE users SET discord_id = $1, discord_verified = TRUE, updated_at = NOW() WHERE id = $2", 138 pending_identifier, 139 user_id 140 ).execute(&mut *tx).await, 141 "telegram" => sqlx::query!( 142 "UPDATE users SET telegram_username = $1, telegram_verified = TRUE, updated_at = NOW() WHERE id = $2", 143 pending_identifier, 144 user_id 145 ).execute(&mut *tx).await, 146 "signal" => sqlx::query!( 147 "UPDATE users SET signal_number = $1, signal_verified = TRUE, updated_at = NOW() WHERE id = $2", 148 pending_identifier, 149 user_id 150 ).execute(&mut *tx).await, 151 _ => unreachable!(), 152 }; 153 154 if let Err(e) = update_result { 155 error!("Failed to update user channel: {:?}", e); 156 if channel_str == "email" 157 && e.as_database_error() 158 .map(|db| db.is_unique_violation()) 159 .unwrap_or(false) 160 { 161 return ( 162 StatusCode::BAD_REQUEST, 163 Json(json!({"error": "EmailTaken", "message": "Email already in use"})), 164 ) 165 .into_response(); 166 } 167 return ( 168 StatusCode::INTERNAL_SERVER_ERROR, 169 Json(json!({"error": "InternalError", "message": "Failed to update channel"})), 170 ) 171 .into_response(); 172 } 173 174 if let Err(e) = sqlx::query!( 175 "DELETE FROM channel_verifications WHERE user_id = $1 AND channel = $2::comms_channel", 176 user_id, 177 channel_str as _ 178 ) 179 .execute(&mut *tx) 180 .await 181 { 182 error!("Failed to delete verification record: {:?}", e); 183 return ( 184 StatusCode::INTERNAL_SERVER_ERROR, 185 Json(json!({"error": "InternalError"})), 186 ) 187 .into_response(); 188 } 189 190 if tx.commit().await.is_err() { 191 return ( 192 StatusCode::INTERNAL_SERVER_ERROR, 193 Json(json!({"error": "InternalError"})), 194 ) 195 .into_response(); 196 } 197 198 info!(did = %user.did, channel = %channel_str, "Channel verified successfully"); 199 200 Json(json!({"success": true})).into_response() 201}