feat!: universal redesign — drop Minecraft stack, single-crate architecture
- remove Java plugins (velocity/paper), dashboard, all MC-specific code
(handshake, death_code, varint, hostname-HMAC); available in history pre-v0.2
- merge crates/* into one package with src/bin/{rampart,rampart-manager,rampart-cli}
- ProtocolHandler trait + registry (no implementations yet), universal PoW kept
- XDP: universal L3/L4 filter (xdp/core/) + pluggable hook API (xdp/hooks/),
fix IPv6 saddr bug; clang build verified
- docs: bilingual knowledge base (docs/kb/: attacks x4, defense-levels,
practice x3), rewrite README/architecture for universal concept
- TODO.md v4.0: <=300-line module limit, competitor benchmark section (ref/)
- deploy/CI/docs cleanup: no MC references, new binary names
cargo build/clippy(-D warnings)/test green (55 tests)
This commit is contained in:
parent
0b53ed720b
commit
15f474486a
179 changed files with 5044 additions and 11519 deletions
143
src/config/mod.rs
Normal file
143
src/config/mod.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
//! Единый конфиг платформы Rampart.
|
||||
mod sections;
|
||||
|
||||
pub use sections::{
|
||||
BackendConfig, BanConfig, BindConfig, LimitsConfig, LoggingConfig, MetricsConfig, PowConfig, StoreConfig,
|
||||
WorkerConfig, XdpConfig,
|
||||
};
|
||||
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
pub bind: BindConfig,
|
||||
#[serde(default)]
|
||||
pub backend: BackendConfig,
|
||||
#[serde(default)]
|
||||
pub workers: WorkerConfig,
|
||||
#[serde(default)]
|
||||
pub limits: LimitsConfig,
|
||||
#[serde(default)]
|
||||
pub ban: BanConfig,
|
||||
#[serde(default)]
|
||||
pub store: StoreConfig,
|
||||
#[serde(default)]
|
||||
pub xdp: XdpConfig,
|
||||
#[serde(default)]
|
||||
pub logging: LoggingConfig,
|
||||
#[serde(default)]
|
||||
pub metrics: MetricsConfig,
|
||||
#[serde(default)]
|
||||
pub pow: PowConfig,
|
||||
#[serde(default)]
|
||||
pub whitelist: Vec<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Парсит конфиг из TOML-строки и валидирует семантику.
|
||||
///
|
||||
/// # Errors
|
||||
/// Возвращает ошибку при невалидном TOML или нарушении инвариантов
|
||||
/// (пустой список upstream-бэкендов, некорректные адреса).
|
||||
pub fn parse_str(contents: &str) -> anyhow::Result<Self> {
|
||||
let config: Config = toml::from_str(contents)?;
|
||||
config.validate()?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Читает и парсит конфиг из файла.
|
||||
///
|
||||
/// # Errors
|
||||
/// Возвращает ошибку при невозможности прочитать файл или невалидном содержимом.
|
||||
pub fn from_file(path: &str) -> anyhow::Result<Self> {
|
||||
let contents = fs::read_to_string(path)?;
|
||||
Self::parse_str(&contents)
|
||||
}
|
||||
|
||||
fn validate(&self) -> anyhow::Result<()> {
|
||||
if self.backend.upstreams.is_empty() {
|
||||
anyhow::bail!("backend.upstreams must not be empty");
|
||||
}
|
||||
for upstream in &self.backend.upstreams {
|
||||
if upstream.parse::<std::net::SocketAddr>().is_err() {
|
||||
anyhow::bail!("invalid upstream address: {upstream}");
|
||||
}
|
||||
}
|
||||
for entry in &self.whitelist {
|
||||
if entry.parse::<std::net::IpAddr>().is_err() {
|
||||
anyhow::bail!("invalid whitelist entry: {entry}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_config_uses_defaults() {
|
||||
let config = Config::parse_str("").expect("empty config should parse");
|
||||
assert_eq!(config.bind.address, "0.0.0.0");
|
||||
assert_eq!(config.bind.port, 25565);
|
||||
assert_eq!(config.backend.upstreams, vec!["127.0.0.1:25566"]);
|
||||
assert!(!config.pow.enabled);
|
||||
assert!(!config.xdp.enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_config_parses() {
|
||||
let config = Config::parse_str(
|
||||
r#"
|
||||
[bind]
|
||||
address = "10.0.0.1"
|
||||
port = 443
|
||||
|
||||
[backend]
|
||||
upstreams = ["192.168.1.10:8080", "192.168.1.11:8080"]
|
||||
|
||||
[pow]
|
||||
enabled = true
|
||||
difficulty = 6
|
||||
|
||||
whitelist = ["203.0.113.7"]
|
||||
"#,
|
||||
)
|
||||
.expect("config should parse");
|
||||
assert_eq!(config.bind.port, 443);
|
||||
assert_eq!(config.backend.upstreams.len(), 2);
|
||||
assert!(config.pow.enabled);
|
||||
assert_eq!(config.pow.difficulty, 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_upstreams_rejected() {
|
||||
let result = Config::parse_str(
|
||||
r#"
|
||||
[backend]
|
||||
upstreams = []
|
||||
"#,
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_upstream_rejected() {
|
||||
let result = Config::parse_str(
|
||||
r#"
|
||||
[backend]
|
||||
upstreams = ["not-an-addr"]
|
||||
"#,
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_whitelist_entry_rejected() {
|
||||
let result = Config::parse_str("whitelist = [\"999.999.1.1\"]");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue