Skip to content
Clone
b453d5d
132 lines 4.1 KB Rust
use serde::Deserialize;
use serde::de::Deserializer;
use std::path::Path;
use std::time::Duration;
/// Windows 上 `/opt/gitdata/repos` 会退化成盘符相对路径(没有盘符前缀),git 读得到
/// refs 却读不到 object,表现为 `fatal: not a tree object`。这里统一锚定成带盘符的
/// 绝对路径;POSIX 平台本来就是绝对路径,保持原样。
pub fn anchor_local_path(raw: &str) -> String {
let trimmed = raw.trim();
if trimmed.is_empty() {
return raw.to_string();
}
let path = Path::new(trimmed);
if path.is_absolute() {
return trimmed.to_string();
}
match std::path::absolute(path) {
Ok(anchored) => anchored.to_string_lossy().into_owned(),
Err(_) => trimmed.to_string(),
}
}
fn deserialize_anchored_path<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer).map(|value| anchor_local_path(&value))
}
mod duration_secs {
use serde::{Deserialize, Deserializer};
use std::time::Duration;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
u64::deserialize(deserializer).map(Duration::from_secs)
}
}
mod opt_duration_secs {
use serde::{Deserialize, Deserializer};
use std::time::Duration;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
Option::<u64>::deserialize(deserializer).map(|o| o.map(Duration::from_secs))
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct GitConfig {
#[serde(deserialize_with = "deserialize_anchored_path")]
pub repo_root: String,
#[serde(deserialize_with = "deserialize_anchored_path")]
pub objects_root: String,
pub max_blob_size: u64,
pub git_command_timeout_secs: u64,
pub git_rpc_timeout_secs: u64,
pub ssh: GitSshConfig,
pub base_url: String,
pub http_clone_base_url: String,
pub ssh_clone_host: String,
pub ssh_clone_port: u16,
pub ssh_clone_user: String,
pub graphql_complexity_limit: u64,
pub graphql_introspection: bool,
pub kenshell_endpoint: String,
pub kenshell_rpc_key: String,
pub kenshell_timeout_secs: u64,
}
impl Default for GitConfig {
fn default() -> Self {
Self {
repo_root: anchor_local_path("/opt/gitdata/repos"),
objects_root: anchor_local_path("/opt/gitdata/objects"),
max_blob_size: 100 * 1024 * 1024,
git_command_timeout_secs: 300,
git_rpc_timeout_secs: 3600,
ssh: GitSshConfig::default(),
base_url: "http://localhost:3000".to_string(),
http_clone_base_url: String::new(),
ssh_clone_host: String::new(),
ssh_clone_port: 0,
ssh_clone_user: "git".to_string(),
graphql_complexity_limit: 1000,
graphql_introspection: false,
kenshell_endpoint: "http://127.0.0.1:50091".to_string(),
kenshell_rpc_key: String::new(),
kenshell_timeout_secs: 3600,
}
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct GitSshConfig {
pub bind_addr: String,
pub port: u16,
pub host_key_path: String,
pub banner: Option<String>,
pub max_sessions: usize,
#[serde(deserialize_with = "duration_secs::deserialize")]
pub auth_rejection_time: Duration,
#[serde(deserialize_with = "opt_duration_secs::deserialize")]
pub inactivity_timeout: Option<Duration>,
#[serde(deserialize_with = "duration_secs::deserialize")]
pub keepalive_interval: Duration,
pub keepalive_max: u32,
}
impl Default for GitSshConfig {
fn default() -> Self {
Self {
bind_addr: "0.0.0.0".to_string(),
port: 22,
host_key_path: "/opt/gitdata/ssh_host_ed25519".to_string(),
banner: None,
max_sessions: 64,
auth_rejection_time: Duration::from_secs(2),
inactivity_timeout: Some(Duration::from_secs(300)),
keepalive_interval: Duration::from_secs(30),
keepalive_max: 3,
}
}
}