use std::{env, sync::Arc};
use neocrates::rediscache::{RedisConfig, RedisPool};
use neocrates::sms::sms_service::{
AliyunSmsConfig, SmsConfig, SmsProviderConfig, SmsService, TencentSmsConfig,
};
use neocrates::sms::tencent::Region;
fn must_get_env(key: &str) -> String {
env::var(key).unwrap_or_else(|_| {
eprintln!("Missing required env var: {}", key);
std::process::exit(1);
})
}
fn parse_region(s: &str) -> Region {
match s {
"ap-beijing" => Region::Beijing,
"ap-nanjing" => Region::Nanjing,
"ap-guangzhou" => Region::Guangzhou,
other => Region::Other(other.to_string()),
}
}
#[neocrates::tokio::main]
async fn main() {
let redis_url = env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
println!("Connecting to Redis at: {}", redis_url);
let redis_config = RedisConfig {
url: redis_url,
max_size: 10,
min_idle: Some(1),
connection_timeout: std::time::Duration::from_secs(5),
idle_timeout: Some(std::time::Duration::from_secs(600)),
max_lifetime: Some(std::time::Duration::from_secs(3600)),
};
let redis_pool = match RedisPool::new(redis_config).await {
Ok(pool) => Arc::new(pool),
Err(e) => {
eprintln!("Failed to connect to Redis: {}", e);
eprintln!("Please make sure Redis is running. Example: redis-server");
std::process::exit(1);
}
};
let provider = env::var("SMS_PROVIDER").unwrap_or_else(|_| "aliyun".to_string());
let debug = env::var("SMS_DEBUG")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false);
let sms_config = match provider.as_str() {
"aliyun" => {
let aliyun = AliyunSmsConfig {
access_key_id: must_get_env("ALIYUN_SMS_ACCESS_KEY_ID"),
access_key_secret: must_get_env("ALIYUN_SMS_ACCESS_KEY_SECRET"),
sign_name: must_get_env("ALIYUN_SMS_SIGN_NAME"),
template_code: must_get_env("ALIYUN_SMS_TEMPLATE_CODE"),
};
SmsConfig {
debug,
provider: SmsProviderConfig::Aliyun(aliyun),
}
}
"tencent" => {
let region_str =
env::var("TENCENT_SMS_REGION").unwrap_or_else(|_| "ap-beijing".to_string());
let tencent = TencentSmsConfig {
secret_id: must_get_env("TENCENT_SMS_SECRET_ID"),
secret_key: must_get_env("TENCENT_SMS_SECRET_KEY"),
sms_app_id: must_get_env("TENCENT_SMS_APP_ID"),
region: parse_region(®ion_str),
sign_name: must_get_env("TENCENT_SMS_SIGN_NAME"),
template_id: must_get_env("TENCENT_SMS_TEMPLATE_ID"),
};
SmsConfig {
debug,
provider: SmsProviderConfig::Tencent(tencent),
}
}
other => {
eprintln!(
"Unsupported SMS_PROVIDER: {} (expected: aliyun|tencent)",
other
);
std::process::exit(1);
}
};
let sms_config = Arc::new(sms_config);
let redis_key_prefix = "captcha:sms:";
let mobile_regex = regex::Regex::new(r"^1\d{10}$").expect("invalid mobile regex");
let mobile = env::var("MOBILE").unwrap_or_else(|_| "13800138000".to_string());
println!("\n=== SMS captcha demo ===");
println!("provider: {}", provider);
println!("debug: {}", debug);
println!("mobile: {}", mobile);
println!("redis_key_prefix: {}", redis_key_prefix);
let send_res = SmsService::send_captcha(
&sms_config,
&redis_pool,
&mobile,
redis_key_prefix,
&mobile_regex,
)
.await;
match send_res {
Ok(_) => println!("send_captcha: OK (code stored in Redis)"),
Err(e) => {
eprintln!("send_captcha: FAILED: {}", e);
eprintln!("If you are testing locally, consider setting SMS_DEBUG=true.");
std::process::exit(1);
}
}
let stored = SmsService::get_captcha_code(&redis_pool, &mobile, redis_key_prefix)
.await
.unwrap_or(None);
let Some(code) = stored else {
eprintln!("No captcha found in Redis (unexpected).");
std::process::exit(1);
};
println!("(demo) captcha read from Redis: {}", code);
let valid_res =
SmsService::valid_auth_captcha(&redis_pool, &mobile, &code, redis_key_prefix, true).await;
match valid_res {
Ok(_) => println!("valid_auth_captcha: OK (deleted from Redis)"),
Err(e) => {
eprintln!("valid_auth_captcha: FAILED: {}", e);
std::process::exit(1);
}
}
let valid_again =
SmsService::valid_auth_captcha(&redis_pool, &mobile, &code, redis_key_prefix, true).await;
println!(
"valid_auth_captcha (again, expected to fail): {}",
match valid_again {
Ok(_) => "unexpected OK".to_string(),
Err(e) => format!("error: {}", e),
}
);
println!("\nDone.");
}