122 lines
4.4 KiB
Rust
122 lines
4.4 KiB
Rust
//! The server's settings: defaults, then a TOML file (`--config server.toml`), then flags.
|
|
|
|
use std::net::SocketAddr;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use figcraft_collab_net::{Endpoint, NetLimits};
|
|
use serde::Deserialize;
|
|
|
|
use crate::store::StoreConfig;
|
|
|
|
/// The port the server listens on unless told otherwise (the app's sessions try it first too).
|
|
pub const DEFAULT_PORT: u16 = figcraft_collab_net::DEFAULT_PORT;
|
|
|
|
/// Everything the server can be told (the TOML file's keys are these names).
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
#[serde(default, deny_unknown_fields)]
|
|
pub struct Config {
|
|
/// Where to listen: `127.0.0.1:47900` (this machine only) unless told otherwise.
|
|
pub listen: String,
|
|
/// The data directory (files, links, the server key).
|
|
pub data: PathBuf,
|
|
/// How share links name this server when it isn't reached at `listen` (a reverse proxy:
|
|
/// `wss://figcraft.example.com/`).
|
|
pub public_url: Option<String>,
|
|
/// Most files kept.
|
|
pub max_files: usize,
|
|
/// Largest file (its snapshot), in MiB (a welcome carries the whole file, and messages are at
|
|
/// most 64 MiB).
|
|
pub max_file_mb: u64,
|
|
/// Most share links per file.
|
|
pub max_links: usize,
|
|
/// Most open connections.
|
|
pub max_connections: usize,
|
|
/// Most people in one file at once.
|
|
pub max_people: usize,
|
|
/// Compact a file's log into a new snapshot after this many entries…
|
|
pub compact_after: usize,
|
|
/// …or once the log is this large (MiB).
|
|
pub compact_mb: u64,
|
|
}
|
|
|
|
impl Default for Config {
|
|
fn default() -> Self {
|
|
Self {
|
|
listen: format!("127.0.0.1:{DEFAULT_PORT}"),
|
|
data: PathBuf::from("figcraft-data"),
|
|
public_url: None,
|
|
max_files: 1000,
|
|
max_file_mb: 64,
|
|
max_links: 100,
|
|
max_connections: 512,
|
|
max_people: 256,
|
|
compact_after: 1000,
|
|
compact_mb: 16,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Config {
|
|
/// Read a TOML file's settings (unknown keys are errors).
|
|
pub fn load(path: &Path) -> Result<Config, String> {
|
|
let text = std::fs::read_to_string(path).map_err(|e| format!("can't read {}: {e}", path.display()))?;
|
|
toml::from_str(&text).map_err(|e| format!("{}: {e}", path.display()))
|
|
}
|
|
|
|
/// The listen address.
|
|
pub fn addr(&self) -> Result<SocketAddr, String> {
|
|
self.listen.trim().parse().map_err(|_| format!("`listen` must be an address with a port, like 0.0.0.0:{DEFAULT_PORT}"))
|
|
}
|
|
|
|
/// The public URL, when set: a `ws://` or `wss://` address without a query.
|
|
pub fn public(&self) -> Result<Option<Endpoint>, String> {
|
|
let Some(url) = self.public_url.as_deref().map(str::trim).filter(|u| !u.is_empty()) else { return Ok(None) };
|
|
let (e, query) = Endpoint::parse(url).map_err(|e| format!("`public_url`: {e}"))?;
|
|
if !query.is_empty() {
|
|
return Err("`public_url` can't have a query (no keys or tokens in it)".into());
|
|
}
|
|
Ok(Some(e))
|
|
}
|
|
|
|
/// Check everything (before starting).
|
|
pub fn check(&self) -> Result<(), String> {
|
|
self.addr()?;
|
|
self.public()?;
|
|
for (name, v) in [
|
|
("max_files", self.max_files as u64),
|
|
("max_file_mb", self.max_file_mb),
|
|
("max_links", self.max_links as u64),
|
|
("max_connections", self.max_connections as u64),
|
|
("max_people", self.max_people as u64),
|
|
("compact_after", self.compact_after as u64),
|
|
("compact_mb", self.compact_mb),
|
|
] {
|
|
if v == 0 {
|
|
return Err(format!("`{name}` must be at least 1"));
|
|
}
|
|
}
|
|
if self.max_file_mb > 4096 || self.compact_mb > 4096 {
|
|
return Err("sizes are in MiB and at most 4096".into());
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// The store's part.
|
|
pub fn store(&self) -> StoreConfig {
|
|
let mib = |n: u64| n.saturating_mul(1 << 20);
|
|
StoreConfig {
|
|
max_files: self.max_files,
|
|
max_file_bytes: mib(self.max_file_mb),
|
|
max_links: self.max_links,
|
|
compact_after: self.compact_after,
|
|
compact_bytes: mib(self.compact_mb),
|
|
core: figcraft_collab::Limits { max_peers: self.max_people, ..figcraft_collab::Limits::default() },
|
|
}
|
|
}
|
|
|
|
/// The transport's part.
|
|
pub fn net(&self) -> NetLimits {
|
|
NetLimits { max_connections: self.max_connections, ..NetLimits::default() }
|
|
}
|
|
}
|