feat: 添加默认配置文件创建功能,确保首次启动时可用

This commit is contained in:
oudecheng 2026-06-29 15:27:22 +08:00
parent 26cbe7aa2d
commit 7d11ab8067
2 changed files with 60 additions and 6 deletions

View File

@ -10,8 +10,11 @@ use std::str::FromStr;
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config { pub struct Config {
#[serde(default)]
pub providers: HashMap<String, ProviderConfig>, pub providers: HashMap<String, ProviderConfig>,
#[serde(default)]
pub models: HashMap<String, ModelConfig>, pub models: HashMap<String, ModelConfig>,
#[serde(default)]
pub agents: HashMap<String, AgentConfig>, pub agents: HashMap<String, AgentConfig>,
#[serde(default)] #[serde(default)]
pub time: TimeConfig, pub time: TimeConfig,
@ -886,9 +889,12 @@ impl Config {
tracing::info!(path = %fallback.display(), "Config loaded from fallback path"); tracing::info!(path = %fallback.display(), "Config loaded from fallback path");
fs::read_to_string(fallback)? fs::read_to_string(fallback)?
} else { } else {
return Err(Box::new(ConfigError::ConfigNotFound( // Auto-create a minimal config on first startup
path.to_string_lossy().to_string(), tracing::info!(
))); path = %path.display(),
"Config not found, auto-creating minimal config"
);
Self::create_default_config(path)?
} }
}; };
let content = resolve_env_placeholders(&content); let content = resolve_env_placeholders(&content);
@ -906,6 +912,46 @@ impl Config {
Ok(config) Ok(config)
} }
/// Create the default config file with a minimal template on first startup.
/// This ensures the gateway can start and the user can configure via web UI.
fn create_default_config(path: &Path) -> Result<String, Box<dyn std::error::Error>> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let default_config = serde_json::json!({
"providers": {
"default": {
"type": "openai",
"base_url": "https://api.openai.com/v1",
"api_key": "<YOUR_API_KEY>",
"extra_headers": {}
}
},
"models": {
"default": {
"model_id": "gpt-4o",
"temperature": 0.7,
"context_window_tokens": 128000
}
},
"agents": {
"default": {
"provider": "default",
"model": "default"
}
}
});
let content = serde_json::to_string_pretty(&default_config)?;
fs::write(path, &content)?;
tracing::info!(
path = %path.display(),
"Created default config file — please configure your API key and model"
);
Ok(content)
}
pub fn get_provider_config(&self, agent_name: &str) -> Result<LLMProviderConfig, ConfigError> { pub fn get_provider_config(&self, agent_name: &str) -> Result<LLMProviderConfig, ConfigError> {
let agent = self let agent = self
.agents .agents

View File

@ -31,7 +31,7 @@ pub mod ws;
use axum::{Router, routing}; use axum::{Router, routing};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::net::TcpListener; use tokio::net::TcpSocket;
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
@ -223,8 +223,16 @@ pub async fn run(
.with_state(state.clone()) .with_state(state.clone())
}; };
let addr = format!("{}:{}", bind_host, bind_port); let addr: std::net::SocketAddr = format!("{}:{}", bind_host, bind_port).parse()?;
let listener = TcpListener::bind(&addr).await?; let listener = {
let socket = match addr {
std::net::SocketAddr::V4(_) => TcpSocket::new_v4()?,
std::net::SocketAddr::V6(_) => TcpSocket::new_v6()?,
};
socket.set_reuseaddr(true)?;
socket.bind(addr)?;
socket.listen(1024)?
};
tracing::info!(address = %addr, "Gateway listening"); tracing::info!(address = %addr, "Gateway listening");
// Graceful shutdown / restart signal // Graceful shutdown / restart signal