use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::Path};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum ArchitectureType {
Api,
Full,
}
impl Default for ArchitectureType {
fn default() -> Self {
Self::Api
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
pub host: String,
pub port: u16,
pub architecture: ArchitectureType,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: "0.0.0.0".to_string(),
port: 3000,
architecture: ArchitectureType::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorsConfig {
pub enabled: bool,
pub origins: Vec<String>,
pub methods: Vec<String>,
pub headers: Vec<String>,
}
impl Default for CorsConfig {
fn default() -> Self {
Self {
enabled: true,
origins: vec!["*".to_string()],
methods: vec![
"GET".to_string(),
"POST".to_string(),
"PUT".to_string(),
"DELETE".to_string(),
"PATCH".to_string(),
"OPTIONS".to_string(),
],
headers: vec!["*".to_string()],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JwtConfig {
pub enabled: bool,
pub secret: String,
pub expires_in: u64,
}
impl Default for JwtConfig {
fn default() -> Self {
Self {
enabled: false,
secret: "your-secret-key-change-this-in-production".to_string(),
expires_in: 3600,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StaticConfig {
pub enabled: bool,
pub dir: String,
pub prefix: String,
}
impl Default for StaticConfig {
fn default() -> Self {
Self {
enabled: false,
dir: "static".to_string(),
prefix: "/static".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemplateConfig {
pub enabled: bool,
pub dir: String,
pub extension: String,
}
impl Default for TemplateConfig {
fn default() -> Self {
Self {
enabled: false,
dir: "templates".to_string(),
extension: "html".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogConfig {
pub level: String,
pub requests: bool,
}
impl Default for LogConfig {
fn default() -> Self {
Self {
level: "info".to_string(),
requests: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum DatabaseType {
Postgres,
Mysql,
Sqlite,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
pub enabled: bool,
pub db_type: DatabaseType,
pub url: String,
pub max_connections: u32,
pub min_connections: u32,
pub connect_timeout: u64,
pub auto_migrate: bool,
}
impl Default for DatabaseConfig {
fn default() -> Self {
Self {
enabled: false,
db_type: DatabaseType::Postgres,
url: "postgresql://localhost/myapp".to_string(),
max_connections: 10,
min_connections: 2,
connect_timeout: 30,
auto_migrate: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QdrantConfig {
pub enabled: bool,
pub url: String,
pub api_key: Option<String>,
pub timeout: u64,
pub default_collection: String,
}
impl Default for QdrantConfig {
fn default() -> Self {
Self {
enabled: false,
url: "http://localhost:6334".to_string(),
api_key: None,
timeout: 30,
default_collection: "default".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MiddlewareConfig {
pub cors: CorsConfig,
pub jwt: JwtConfig,
pub static_files: StaticConfig,
pub templates: TemplateConfig,
pub logging: LogConfig,
pub custom: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Config {
pub server: ServerConfig,
pub middleware: MiddlewareConfig,
pub database: DatabaseConfig,
pub qdrant: QdrantConfig,
}
impl Config {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let content = std::fs::read_to_string(path.as_ref()).map_err(|e| {
Error::Config(format!("无法读取配置文件 {:?}: {}", path.as_ref(), e))
})?;
let config: Config = toml::from_str(&content).map_err(|e| {
Error::Config(format!("解析配置文件失败: {}", e))
})?;
Ok(config)
}
pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let content = toml::to_string_pretty(self).map_err(|e| {
Error::Config(format!("序列化配置失败: {}", e))
})?;
std::fs::write(path.as_ref(), content).map_err(|e| {
Error::Config(format!("写入配置文件失败: {}", e))
})?;
Ok(())
}
pub fn server_address(&self) -> String {
format!("{}:{}", self.server.host, self.server.port)
}
pub fn validate(&self) -> Result<()> {
if self.server.port == 0 {
return Err(Error::Config("端口号不能为 0".to_string()));
}
if self.middleware.templates.enabled
&& self.server.architecture == ArchitectureType::Api {
return Err(Error::Config(
"API 架构模式下不能启用模板功能".to_string()
));
}
if self.middleware.static_files.enabled {
let static_dir = Path::new(&self.middleware.static_files.dir);
if !static_dir.exists() {
return Err(Error::Config(format!(
"静态文件目录不存在: {}",
self.middleware.static_files.dir
)));
}
}
if self.middleware.templates.enabled {
let template_dir = Path::new(&self.middleware.templates.dir);
if !template_dir.exists() {
return Err(Error::Config(format!(
"模板目录不存在: {}",
self.middleware.templates.dir
)));
}
}
Ok(())
}
}