feat(server-rs): 接入真实短信验证码链路
This commit is contained in:
@@ -1291,7 +1291,10 @@ mod tests {
|
||||
payload["expiresInSeconds"],
|
||||
Value::Number(serde_json::Number::from(300))
|
||||
);
|
||||
assert_eq!(payload["providerRequestId"], Value::Null);
|
||||
assert_eq!(
|
||||
payload["providerRequestId"],
|
||||
Value::String("mock-request-id".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -25,6 +25,23 @@ pub struct AppConfig {
|
||||
pub refresh_cookie_same_site: String,
|
||||
pub refresh_session_ttl_days: u32,
|
||||
pub sms_auth_enabled: bool,
|
||||
pub sms_auth_provider: String,
|
||||
pub sms_endpoint: String,
|
||||
pub sms_access_key_id: Option<String>,
|
||||
pub sms_access_key_secret: Option<String>,
|
||||
pub sms_sign_name: String,
|
||||
pub sms_template_code: String,
|
||||
pub sms_template_param_key: String,
|
||||
pub sms_country_code: String,
|
||||
pub sms_scheme_name: Option<String>,
|
||||
pub sms_code_length: u8,
|
||||
pub sms_code_type: u8,
|
||||
pub sms_valid_time_seconds: u64,
|
||||
pub sms_interval_seconds: u64,
|
||||
pub sms_duplicate_policy: u8,
|
||||
pub sms_case_auth_policy: u8,
|
||||
pub sms_return_verify_code: bool,
|
||||
pub sms_mock_verify_code: String,
|
||||
pub wechat_auth_enabled: bool,
|
||||
pub wechat_auth_provider: String,
|
||||
pub wechat_app_id: Option<String>,
|
||||
@@ -79,6 +96,23 @@ impl Default for AppConfig {
|
||||
refresh_cookie_same_site: "Lax".to_string(),
|
||||
refresh_session_ttl_days: 30,
|
||||
sms_auth_enabled: false,
|
||||
sms_auth_provider: "mock".to_string(),
|
||||
sms_endpoint: "dypnsapi.aliyuncs.com".to_string(),
|
||||
sms_access_key_id: None,
|
||||
sms_access_key_secret: None,
|
||||
sms_sign_name: "速通互联验证码".to_string(),
|
||||
sms_template_code: "100001".to_string(),
|
||||
sms_template_param_key: "code".to_string(),
|
||||
sms_country_code: "86".to_string(),
|
||||
sms_scheme_name: None,
|
||||
sms_code_length: 6,
|
||||
sms_code_type: 1,
|
||||
sms_valid_time_seconds: 300,
|
||||
sms_interval_seconds: 60,
|
||||
sms_duplicate_policy: 1,
|
||||
sms_case_auth_policy: 1,
|
||||
sms_return_verify_code: false,
|
||||
sms_mock_verify_code: "123456".to_string(),
|
||||
wechat_auth_enabled: false,
|
||||
wechat_auth_provider: "mock".to_string(),
|
||||
wechat_app_id: None,
|
||||
@@ -194,6 +228,60 @@ impl AppConfig {
|
||||
if let Some(sms_auth_enabled) = read_first_bool_env(&["SMS_AUTH_ENABLED"]) {
|
||||
config.sms_auth_enabled = sms_auth_enabled;
|
||||
}
|
||||
if let Some(sms_auth_provider) = read_first_non_empty_env(&["SMS_AUTH_PROVIDER"]) {
|
||||
config.sms_auth_provider = sms_auth_provider;
|
||||
}
|
||||
if let Some(sms_endpoint) = read_first_non_empty_env(&["ALIYUN_SMS_ENDPOINT"]) {
|
||||
config.sms_endpoint = sms_endpoint;
|
||||
}
|
||||
config.sms_access_key_id = read_first_non_empty_env(&["ALIYUN_SMS_ACCESS_KEY_ID"]);
|
||||
config.sms_access_key_secret = read_first_non_empty_env(&["ALIYUN_SMS_ACCESS_KEY_SECRET"]);
|
||||
if let Some(sms_sign_name) = read_first_non_empty_env(&["ALIYUN_SMS_SIGN_NAME"]) {
|
||||
config.sms_sign_name = sms_sign_name;
|
||||
}
|
||||
if let Some(sms_template_code) = read_first_non_empty_env(&["ALIYUN_SMS_TEMPLATE_CODE"]) {
|
||||
config.sms_template_code = sms_template_code;
|
||||
}
|
||||
if let Some(sms_template_param_key) =
|
||||
read_first_non_empty_env(&["ALIYUN_SMS_TEMPLATE_PARAM_KEY"])
|
||||
{
|
||||
config.sms_template_param_key = sms_template_param_key;
|
||||
}
|
||||
if let Some(sms_country_code) = read_first_non_empty_env(&["ALIYUN_SMS_COUNTRY_CODE"]) {
|
||||
config.sms_country_code = sms_country_code;
|
||||
}
|
||||
config.sms_scheme_name = read_first_non_empty_env(&["ALIYUN_SMS_SCHEME_NAME"]);
|
||||
if let Some(sms_code_length) = read_first_u8_env(&["ALIYUN_SMS_CODE_LENGTH"]) {
|
||||
config.sms_code_length = sms_code_length;
|
||||
}
|
||||
if let Some(sms_code_type) = read_first_u8_env(&["ALIYUN_SMS_CODE_TYPE"]) {
|
||||
config.sms_code_type = sms_code_type;
|
||||
}
|
||||
if let Some(sms_valid_time_seconds) =
|
||||
read_first_duration_seconds_env(&["ALIYUN_SMS_VALID_TIME_SECONDS"])
|
||||
{
|
||||
config.sms_valid_time_seconds = sms_valid_time_seconds;
|
||||
}
|
||||
if let Some(sms_interval_seconds) =
|
||||
read_first_duration_seconds_env(&["ALIYUN_SMS_INTERVAL_SECONDS"])
|
||||
{
|
||||
config.sms_interval_seconds = sms_interval_seconds;
|
||||
}
|
||||
if let Some(sms_duplicate_policy) = read_first_u8_env(&["ALIYUN_SMS_DUPLICATE_POLICY"]) {
|
||||
config.sms_duplicate_policy = sms_duplicate_policy;
|
||||
}
|
||||
if let Some(sms_case_auth_policy) = read_first_u8_env(&["ALIYUN_SMS_CASE_AUTH_POLICY"]) {
|
||||
config.sms_case_auth_policy = sms_case_auth_policy;
|
||||
}
|
||||
if let Some(sms_return_verify_code) =
|
||||
read_first_bool_env(&["ALIYUN_SMS_RETURN_VERIFY_CODE"])
|
||||
{
|
||||
config.sms_return_verify_code = sms_return_verify_code;
|
||||
}
|
||||
if let Some(sms_mock_verify_code) = read_first_non_empty_env(&["SMS_AUTH_MOCK_VERIFY_CODE"])
|
||||
{
|
||||
config.sms_mock_verify_code = sms_mock_verify_code;
|
||||
}
|
||||
|
||||
if let Some(wechat_auth_enabled) = read_first_bool_env(&["WECHAT_AUTH_ENABLED"]) {
|
||||
config.wechat_auth_enabled = wechat_auth_enabled;
|
||||
@@ -439,6 +527,11 @@ fn read_first_u64_env(keys: &[&str]) -> Option<u64> {
|
||||
.find_map(|key| env::var(key).ok().and_then(|value| parse_u64(&value)))
|
||||
}
|
||||
|
||||
fn read_first_u8_env(keys: &[&str]) -> Option<u8> {
|
||||
keys.iter()
|
||||
.find_map(|key| env::var(key).ok().and_then(|value| parse_u8(&value)))
|
||||
}
|
||||
|
||||
fn read_first_positive_u16_env(keys: &[&str]) -> Option<u16> {
|
||||
keys.iter().find_map(|key| {
|
||||
env::var(key)
|
||||
@@ -515,6 +608,10 @@ fn parse_u64(raw: &str) -> Option<u64> {
|
||||
raw.trim().parse::<u64>().ok()
|
||||
}
|
||||
|
||||
fn parse_u8(raw: &str) -> Option<u8> {
|
||||
raw.trim().parse::<u8>().ok()
|
||||
}
|
||||
|
||||
fn parse_positive_u16(raw: &str) -> Option<u16> {
|
||||
let value = raw.trim().parse::<u16>().ok()?;
|
||||
if value == 0 {
|
||||
|
||||
@@ -46,6 +46,7 @@ pub async fn send_phone_code(
|
||||
},
|
||||
OffsetDateTime::now_utc(),
|
||||
)
|
||||
.await
|
||||
.map_err(map_phone_auth_error)?;
|
||||
|
||||
Ok(json_success_body(
|
||||
|
||||
@@ -16,6 +16,7 @@ use module_runtime::RuntimeSnapshotRecord;
|
||||
use module_runtime::{SAVE_SNAPSHOT_VERSION, format_utc_micros};
|
||||
use platform_auth::{
|
||||
JwtConfig, JwtError, RefreshCookieConfig, RefreshCookieError, RefreshCookieSameSite,
|
||||
SmsAuthConfig, SmsAuthProvider, SmsAuthProviderKind, SmsProviderError,
|
||||
};
|
||||
use platform_llm::{LlmClient, LlmConfig, LlmError};
|
||||
use platform_oss::{OssClient, OssConfig, OssError};
|
||||
@@ -54,6 +55,7 @@ pub struct AppState {
|
||||
pub enum AppStateInitError {
|
||||
Jwt(JwtError),
|
||||
RefreshCookie(RefreshCookieError),
|
||||
SmsProvider(SmsProviderError),
|
||||
Oss(OssError),
|
||||
Llm(LlmError),
|
||||
}
|
||||
@@ -78,9 +80,30 @@ impl AppState {
|
||||
)?;
|
||||
let oss_client = build_oss_client(&config)?;
|
||||
let auth_store = InMemoryAuthStore::default();
|
||||
let sms_provider = SmsAuthProvider::new(SmsAuthConfig::new(
|
||||
SmsAuthProviderKind::parse(&config.sms_auth_provider).ok_or_else(|| {
|
||||
SmsProviderError::InvalidConfig("短信 provider 配置非法".to_string())
|
||||
})?,
|
||||
config.sms_endpoint.clone(),
|
||||
config.sms_access_key_id.clone(),
|
||||
config.sms_access_key_secret.clone(),
|
||||
config.sms_sign_name.clone(),
|
||||
config.sms_template_code.clone(),
|
||||
config.sms_template_param_key.clone(),
|
||||
config.sms_country_code.clone(),
|
||||
config.sms_scheme_name.clone(),
|
||||
config.sms_code_length,
|
||||
config.sms_code_type,
|
||||
config.sms_valid_time_seconds,
|
||||
config.sms_interval_seconds,
|
||||
config.sms_duplicate_policy,
|
||||
config.sms_case_auth_policy,
|
||||
config.sms_return_verify_code,
|
||||
config.sms_mock_verify_code.clone(),
|
||||
)?)?;
|
||||
let password_entry_service = PasswordEntryService::new(auth_store.clone());
|
||||
let auth_user_service = AuthUserService::new(auth_store.clone());
|
||||
let phone_auth_service = PhoneAuthService::new(auth_store.clone());
|
||||
let phone_auth_service = PhoneAuthService::new(auth_store.clone(), sms_provider);
|
||||
let wechat_auth_state_service =
|
||||
WechatAuthStateService::new(auth_store.clone(), config.wechat_state_ttl_minutes);
|
||||
let wechat_auth_service = WechatAuthService::new(auth_store.clone());
|
||||
@@ -331,6 +354,7 @@ impl fmt::Display for AppStateInitError {
|
||||
match self {
|
||||
Self::Jwt(error) => write!(f, "{error}"),
|
||||
Self::RefreshCookie(error) => write!(f, "{error}"),
|
||||
Self::SmsProvider(error) => write!(f, "{error}"),
|
||||
Self::Oss(error) => write!(f, "{error}"),
|
||||
Self::Llm(error) => write!(f, "{error}"),
|
||||
}
|
||||
@@ -351,6 +375,12 @@ impl From<RefreshCookieError> for AppStateInitError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SmsProviderError> for AppStateInitError {
|
||||
fn from(value: SmsProviderError) -> Self {
|
||||
Self::SmsProvider(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OssError> for AppStateInitError {
|
||||
fn from(value: OssError) -> Self {
|
||||
Self::Oss(value)
|
||||
|
||||
Reference in New Issue
Block a user