feat: add WeChat session authentication

This commit is contained in:
2026-07-20 14:47:16 +08:00
parent 2ada52f35a
commit 67d29d3ca0
26 changed files with 1289 additions and 130 deletions

View File

@@ -1,12 +1,12 @@
use std::{env, net::IpAddr, path::PathBuf};
#[derive(Debug, Clone)]
#[derive(Clone)]
pub enum SpeechProviderConfig {
Local { api_url: String },
Tencent(TencentSpeechConfig),
}
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct TencentSpeechConfig {
pub app_id: u64,
pub secret_id: String,
@@ -14,7 +14,7 @@ pub struct TencentSpeechConfig {
pub engine_type: String,
}
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct SpeechConfig {
pub provider: SpeechProviderConfig,
pub request_timeout_seconds: u64,
@@ -22,14 +22,28 @@ pub struct SpeechConfig {
pub job_lease_seconds: i64,
}
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct SummaryConfig {
pub api_url: String,
pub api_key: Option<String>,
pub model: String,
}
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct WechatAuthConfig {
pub app_id: String,
pub app_secret: String,
pub code2session_url: String,
}
#[derive(Clone)]
pub struct AuthConfig {
pub wechat: Option<WechatAuthConfig>,
pub session_ttl_days: i64,
pub allow_development_user_header: bool,
}
#[derive(Clone)]
pub struct Config {
pub host: IpAddr,
pub port: u16,
@@ -39,6 +53,7 @@ pub struct Config {
pub audio_storage_dir: PathBuf,
pub speech: Option<SpeechConfig>,
pub summary: Option<SummaryConfig>,
pub auth: AuthConfig,
}
impl Config {
@@ -58,6 +73,7 @@ impl Config {
let speech = speech_config()?;
let summary = summary_config()?;
let auth = auth_config()?;
Ok(Self {
host,
@@ -74,10 +90,41 @@ impl Config {
.unwrap_or_else(|_| PathBuf::from("./data/audio")),
speech,
summary,
auth,
})
}
}
fn auth_config() -> Result<AuthConfig, String> {
let app_id = optional_env("WECHAT_APP_ID");
let app_secret = optional_env("WECHAT_APP_SECRET");
let wechat = match (app_id, app_secret) {
(Some(app_id), Some(app_secret)) => Some(WechatAuthConfig {
app_id,
app_secret,
code2session_url: optional_env("WECHAT_CODE2SESSION_URL")
.unwrap_or_else(|| "https://api.weixin.qq.com/sns/jscode2session".to_owned()),
}),
(None, None) => None,
_ => {
return Err(
"WECHAT_APP_ID and WECHAT_APP_SECRET must be configured together".to_owned(),
);
}
};
let session_ttl_days = parse_env("AUTH_SESSION_TTL_DAYS", 30_i64)?;
if !(1..=365).contains(&session_ttl_days) {
return Err("AUTH_SESSION_TTL_DAYS must be between 1 and 365".to_owned());
}
let allow_development_user_header = parse_bool_env("ALLOW_DEVELOPMENT_USER_HEADER", false)?;
Ok(AuthConfig {
wechat,
session_ttl_days,
allow_development_user_header,
})
}
fn speech_config() -> Result<Option<SpeechConfig>, String> {
let provider = optional_env("ASR_PROVIDER").unwrap_or_else(|| "local".to_owned());
if matches!(provider.as_str(), "disabled" | "none") {
@@ -165,3 +212,14 @@ where
|value| value.parse().map_err(|_| format!("{name} is invalid")),
)
}
fn parse_bool_env(name: &str, default: bool) -> Result<bool, String> {
let Some(value) = optional_env(name) else {
return Ok(default);
};
match value.to_ascii_lowercase().as_str() {
"1" | "true" | "yes" | "on" => Ok(true),
"0" | "false" | "no" | "off" => Ok(false),
_ => Err(format!("{name} must be true or false")),
}
}