use axum::{ Json, extract::{Extension, State}, http::{HeaderMap, StatusCode}, response::{IntoResponse, Response}, }; use module_auth::{AuthLoginMethod, PasswordEntryError, PasswordEntryInput}; use serde_json::json; use shared_contracts::auth::{PasswordEntryRequest, PasswordEntryResponse}; use crate::{ api_response::json_success_body, auth_payload::map_auth_user_payload, auth_session::{ attach_set_cookie_header, build_refresh_session_cookie_header, create_auth_session_and_sync, record_daily_login_tracking_event_after_auth_success, }, http_error::AppError, request_context::RequestContext, session_client::resolve_session_client_context, state::AppState, tracking::{TrackingClientMarker, TrackingLoginSubject}, }; pub async fn password_entry( State(state): State, Extension(request_context): Extension, headers: HeaderMap, client_marker: Option>, Json(payload): Json, ) -> Result { state .refresh_auth_store_from_spacetime() .await .map_err(|error| { AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) .with_message(format!("刷新认证状态失败:{error}")) })?; let input = PasswordEntryInput { country_code: payload.country_code, pure_phone_number: payload.pure_phone_number, password: payload.password, }; let result = if state.config.dev_password_entry_auto_register_enabled { state .password_entry_service() .execute_with_dev_registration(input) .await } else { state.password_entry_service().execute(input).await } .map_err(map_password_entry_error)?; let session_client = resolve_session_client_context(&headers); state .sync_auth_store_tables_to_spacetime() .await .map_err(|error| { AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) .with_message(format!("同步认证状态失败:{error}")) })?; if result.created { crate::registration_reward::grant_new_user_registration_wallet_reward( &state, &request_context, &result.user.id, ) .await; } if let Err(error) = crate::external_api_keys::ensure_llm_router_account_after_auth_success( &state, &request_context, &result.user.id, ) .await { tracing::warn!( request_id = request_context.request_id(), user_id = %result.user.id, error = %error, "登录后 LLM Router 账号准备失败;不发放登录会话" ); return Err(AppError::from_status(StatusCode::SERVICE_UNAVAILABLE) .with_message("账号服务暂不可用,请稍后重试")); } let signed_session = create_auth_session_and_sync( &state, &result.user, &session_client, AuthLoginMethod::Password, ) .await?; record_daily_login_tracking_event_after_auth_success( &state, &request_context, &result.user.id, AuthLoginMethod::Password, ) .await; let tracking_login_subject = client_marker.map(|Extension(_)| TrackingLoginSubject::new(&result.user.id)); let mut headers = HeaderMap::new(); attach_set_cookie_header( &mut headers, build_refresh_session_cookie_header(&state, &signed_session.refresh_token)?, ); let mut response = ( headers, json_success_body( Some(&request_context), PasswordEntryResponse { token: signed_session.access_token, user: map_auth_user_payload(result.user), }, ), ) .into_response(); if let Some(subject) = tracking_login_subject { response.extensions_mut().insert(subject); } Ok(response) } fn map_password_entry_error(error: PasswordEntryError) -> AppError { match error { PasswordEntryError::InvalidPhoneNumber => AppError::from_status(StatusCode::BAD_REQUEST) .with_message("手机号格式不正确") .with_details(json!({ "field": "purePhoneNumber", })), PasswordEntryError::UnsupportedPhoneCountryCode => { AppError::from_status(StatusCode::BAD_REQUEST) .with_message(error.to_string()) .with_details(json!({ "field": "countryCode", })) } PasswordEntryError::InvalidPasswordLength => AppError::from_status(StatusCode::BAD_REQUEST) .with_message("密码长度需要在 6 到 128 位之间") .with_details(json!({ "field": "password", })), PasswordEntryError::InvalidPublicUserCode => AppError::from_status(StatusCode::BAD_REQUEST) .with_message("陶泥号格式不正确") .with_details(json!({ "field": "purePhoneNumber", })), PasswordEntryError::InvalidDisplayName | PasswordEntryError::InvalidAvatarDataUrl | PasswordEntryError::EmptyProfileUpdate => { AppError::from_status(StatusCode::BAD_REQUEST).with_message(error.to_string()) } PasswordEntryError::InvalidCredentials => { AppError::from_status(StatusCode::UNAUTHORIZED).with_message("手机号或密码错误") } PasswordEntryError::UserNotFound => { AppError::from_status(StatusCode::UNAUTHORIZED).with_message("手机号或密码错误") } PasswordEntryError::Store(_) | PasswordEntryError::PasswordHash(_) => { AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(error.to_string()) } } }