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(_) => return ( 53 StatusCode::INTERNAL_SERVER_ERROR, 54 Json(json!({"error": "InternalError", "message": "User not found"})), 55 ) 56 .into_response(), 57 }; 58 59 let channel_str = input.channel.as_str(); 60 if !["email", "discord", "telegram", "signal"].contains(&channel_str) { 61 return ( 62 StatusCode::BAD_REQUEST, 63 Json(json!({"error": "InvalidRequest", "message": "Invalid channel"})), 64 ) 65 .into_response(); 66 } 67 68 let record = match sqlx::query!( 69 r#" 70 SELECT code, pending_identifier, expires_at FROM channel_verifications 71 WHERE user_id = $1 AND channel = $2::notification_channel 72 "#, 73 user_id, 74 channel_str as _ 75 ) 76 .fetch_optional(&state.db) 77 .await { 78 Ok(Some(r)) => r, 79 Ok(None) => return ( 80 StatusCode::BAD_REQUEST, 81 Json(json!({"error": "InvalidRequest", "message": "No pending verification found. Update notification preferences first."})), 82 ) 83 .into_response(), 84 Err(e) => return ( 85 StatusCode::INTERNAL_SERVER_ERROR, 86 Json(json!({"error": "InternalError", "message": format!("Database error: {}", e)})), 87 ) 88 .into_response(), 89 }; 90 91 let pending_identifier = match record.pending_identifier { 92 Some(p) => p, 93 None => return ( 94 StatusCode::BAD_REQUEST, 95 Json(json!({"error": "InvalidRequest", "message": "No pending identifier found"})), 96 ) 97 .into_response(), 98 }; 99 100 if record.expires_at < Utc::now() { 101 return ( 102 StatusCode::BAD_REQUEST, 103 Json(json!({"error": "ExpiredToken", "message": "Verification code expired"})), 104 ) 105 .into_response(); 106 } 107 108 if record.code != input.code { 109 return ( 110 StatusCode::BAD_REQUEST, 111 Json(json!({"error": "InvalidCode", "message": "Invalid verification code"})), 112 ) 113 .into_response(); 114 } 115 116 let mut tx = match state.db.begin().await { 117 Ok(tx) => tx, 118 Err(_) => return ( 119 StatusCode::INTERNAL_SERVER_ERROR, 120 Json(json!({"error": "InternalError"})), 121 ) 122 .into_response(), 123 }; 124 125 let update_result = match channel_str { 126 "email" => sqlx::query!( 127 "UPDATE users SET email = $1, updated_at = NOW() WHERE id = $2", 128 pending_identifier, 129 user_id 130 ).execute(&mut *tx).await, 131 "discord" => sqlx::query!( 132 "UPDATE users SET discord_id = $1, discord_verified = TRUE, updated_at = NOW() WHERE id = $2", 133 pending_identifier, 134 user_id 135 ).execute(&mut *tx).await, 136 "telegram" => sqlx::query!( 137 "UPDATE users SET telegram_username = $1, telegram_verified = TRUE, updated_at = NOW() WHERE id = $2", 138 pending_identifier, 139 user_id 140 ).execute(&mut *tx).await, 141 "signal" => sqlx::query!( 142 "UPDATE users SET signal_number = $1, signal_verified = TRUE, updated_at = NOW() WHERE id = $2", 143 pending_identifier, 144 user_id 145 ).execute(&mut *tx).await, 146 _ => unreachable!(), 147 }; 148 149 if let Err(e) = update_result { 150 error!("Failed to update user channel: {:?}", e); 151 if channel_str == "email" && e.as_database_error().map(|db| db.is_unique_violation()).unwrap_or(false) { 152 return ( 153 StatusCode::BAD_REQUEST, 154 Json(json!({"error": "EmailTaken", "message": "Email already in use"})), 155 ) 156 .into_response(); 157 } 158 return ( 159 StatusCode::INTERNAL_SERVER_ERROR, 160 Json(json!({"error": "InternalError", "message": "Failed to update channel"})), 161 ) 162 .into_response(); 163 } 164 165 if let Err(e) = sqlx::query!( 166 "DELETE FROM channel_verifications WHERE user_id = $1 AND channel = $2::notification_channel", 167 user_id, 168 channel_str as _ 169 ) 170 .execute(&mut *tx) 171 .await { 172 error!("Failed to delete verification record: {:?}", e); 173 return ( 174 StatusCode::INTERNAL_SERVER_ERROR, 175 Json(json!({"error": "InternalError"})), 176 ) 177 .into_response(); 178 } 179 180 if let Err(_) = tx.commit().await { 181 return ( 182 StatusCode::INTERNAL_SERVER_ERROR, 183 Json(json!({"error": "InternalError"})), 184 ) 185 .into_response(); 186 } 187 188 info!(did = %user.did, channel = %channel_str, "Channel verified successfully"); 189 190 Json(json!({"success": true})).into_response() 191}