Files
muscl/src/server/config.rs
h7x4 8b4d549e18
All checks were successful
Build and test / check-license (push) Successful in 1m38s
Build and test / check (push) Successful in 1m51s
Build and test / build (push) Successful in 2m40s
Build and test / test (push) Successful in 4m25s
Build and test / docs (push) Successful in 6m1s
Implement denylists
2025-12-16 12:21:35 +09:00

104 lines
3.2 KiB
Rust

use std::{
fs,
path::{Path, PathBuf},
};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use sqlx::{ConnectOptions, mysql::MySqlConnectOptions};
pub const DEFAULT_PORT: u16 = 3306;
fn default_mysql_port() -> u16 {
DEFAULT_PORT
}
pub const DEFAULT_TIMEOUT: u64 = 2;
fn default_mysql_timeout() -> u64 {
DEFAULT_TIMEOUT
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename = "mysql")]
pub struct MysqlConfig {
pub socket_path: Option<PathBuf>,
pub host: Option<String>,
#[serde(default = "default_mysql_port")]
pub port: u16,
pub username: Option<String>,
pub password: Option<String>,
pub password_file: Option<PathBuf>,
#[serde(default = "default_mysql_timeout")]
pub timeout: u64,
}
impl MysqlConfig {
pub fn as_mysql_connect_options(&self) -> anyhow::Result<MySqlConnectOptions> {
let mut options = MySqlConnectOptions::new()
.database("mysql")
.log_statements(tracing::log::LevelFilter::Trace);
if let Some(username) = &self.username {
options = options.username(username);
}
if let Some(password_file) = &self.password_file {
let password = fs::read_to_string(password_file)
.with_context(|| {
format!("Failed to read MySQL password file at {:?}", password_file)
})?
.trim()
.to_owned();
options = options.password(&password);
} else if let Some(password) = &self.password {
options = options.password(password);
}
if let Some(socket_path) = &self.socket_path {
options = options.socket(socket_path);
} else if let Some(host) = &self.host {
options = options.host(host);
options = options.port(self.port);
} else {
anyhow::bail!("No MySQL host or socket path provided");
}
Ok(options)
}
pub fn log_connection_notice(&self) {
let mut display_config = self.to_owned();
display_config.password = display_config
.password
.as_ref()
.map(|_| "<REDACTED>".to_owned());
tracing::debug!(
"Connecting to MySQL server with parameters: {:#?}",
display_config
);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct AuthorizationConfig {
pub group_denylist_file: Option<PathBuf>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct ServerConfig {
pub socket_path: Option<PathBuf>,
pub authorization: AuthorizationConfig,
pub mysql: MysqlConfig,
}
impl ServerConfig {
/// Reads the server configuration from the specified path, or the default path if none is provided.
pub fn read_config_from_path(config_path: &Path) -> anyhow::Result<Self> {
tracing::debug!("Reading config file at {:?}", config_path);
fs::read_to_string(config_path)
.context(format!("Failed to read config file at {:?}", config_path))
.and_then(|c| toml::from_str(&c).context("Failed to parse config file"))
.context(format!("Failed to parse config file at {:?}", config_path))
}
}