use serde::{Deserialize, Serialize}; pub const AUTH_LOGIN_METHOD_PASSWORD: &str = "password"; pub const AUTH_LOGIN_METHOD_PHONE: &str = "phone"; pub const AUTH_LOGIN_METHOD_WECHAT: &str = "wechat"; pub const AUTH_BINDING_STATUS_ACTIVE: &str = "active"; pub const AUTH_BINDING_STATUS_PENDING_BIND_PHONE: &str = "pending_bind_phone"; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AuthLoginOptionsResponse { pub available_login_methods: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AuthUserPayload { pub id: String, pub public_user_code: String, pub username: String, pub display_name: String, pub phone_number_masked: Option, pub login_method: String, pub binding_status: String, pub wechat_bound: bool, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PublicUserSummaryPayload { pub id: String, pub public_user_code: String, pub display_name: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PublicUserSearchResponse { pub user: PublicUserSummaryPayload, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PasswordEntryRequest { pub username: String, pub password: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PasswordEntryResponse { pub token: String, pub user: AuthUserPayload, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PasswordChangeRequest { pub current_password: Option, pub new_password: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PasswordChangeResponse { pub user: AuthUserPayload, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PasswordResetRequest { pub phone: String, pub code: String, pub new_password: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PasswordResetResponse { pub token: String, pub user: AuthUserPayload, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AuthMeResponse { pub user: AuthUserPayload, pub available_login_methods: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AuthSessionsResponse { pub sessions: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AuthSessionSummaryPayload { pub session_id: String, pub client_type: String, pub client_runtime: String, pub client_platform: String, pub client_label: String, pub device_display_name: String, pub mini_program_app_id: Option, pub mini_program_env: Option, pub user_agent: Option, pub ip_masked: Option, pub is_current: bool, pub created_at: String, pub last_seen_at: String, pub expires_at: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct RefreshSessionResponse { pub token: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct LogoutResponse { pub ok: bool, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct LogoutAllResponse { pub ok: bool, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PhoneSendCodeRequest { pub phone: String, pub scene: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PhoneSendCodeResponse { pub ok: bool, pub cooldown_seconds: u64, pub expires_in_seconds: u64, pub provider_request_id: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PhoneLoginRequest { pub phone: String, pub code: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PhoneLoginResponse { pub token: String, pub user: AuthUserPayload, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct WechatStartQuery { pub redirect_path: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct WechatStartResponse { pub authorization_url: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct WechatCallbackQuery { pub state: Option, pub code: Option, pub mock_code: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct WechatBindPhoneRequest { pub phone: String, pub code: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct WechatBindPhoneResponse { pub token: String, pub user: AuthUserPayload, } pub fn build_available_login_methods( sms_auth_enabled: bool, wechat_auth_enabled: bool, ) -> Vec { let mut methods = vec![AUTH_LOGIN_METHOD_PASSWORD.to_string()]; if sms_auth_enabled { methods.push(AUTH_LOGIN_METHOD_PHONE.to_string()); } if wechat_auth_enabled { methods.push(AUTH_LOGIN_METHOD_WECHAT.to_string()); } methods } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn available_login_methods_keep_phone_then_wechat_order() { let methods = build_available_login_methods(true, true); assert_eq!( methods, vec![ AUTH_LOGIN_METHOD_PASSWORD.to_string(), AUTH_LOGIN_METHOD_PHONE.to_string(), AUTH_LOGIN_METHOD_WECHAT.to_string() ] ); } #[test] fn password_entry_request_uses_camel_case_fields() { let payload = serde_json::to_value(PasswordEntryRequest { username: "guest_001".to_string(), password: "secret123".to_string(), }) .expect("payload should serialize"); assert_eq!( payload, json!({ "username": "guest_001", "password": "secret123" }) ); } #[test] fn wechat_callback_query_keeps_provider_compatible_field_names() { let payload = serde_json::to_value(WechatCallbackQuery { state: Some("state-1".to_string()), code: Some("code-1".to_string()), mock_code: Some("mock-1".to_string()), }) .expect("payload should serialize"); assert_eq!( payload, json!({ "state": "state-1", "code": "code-1", "mock_code": "mock-1" }) ); } }