use serde::{Deserialize, Serialize};
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use url::Url;
use super::backend::Backend;
use super::{Error, Result};
use sos_protocol::sdk::{signer::ecdsa::Address, vfs};
#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct ServerConfig {
pub storage: StorageConfig,
pub tls: Option<TlsConfig>,
pub access: Option<AccessControlConfig>,
pub cors: Option<CorsConfig>,
#[serde(skip)]
file: Option<PathBuf>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct AccessControlConfig {
pub allow: Option<HashSet<Address>>,
pub deny: Option<HashSet<Address>>,
}
impl AccessControlConfig {
pub fn is_allowed_access(&self, address: &Address) -> bool {
let has_definitions = self.allow.is_some() || self.deny.is_some();
if has_definitions {
match (&self.deny, &self.allow) {
(Some(deny), None) => {
if deny.iter().any(|a| a == address) {
return false;
}
true
}
(None, Some(allow)) => {
if allow.iter().any(|a| a == address) {
return true;
}
false
}
(Some(deny), Some(allow)) => {
if allow.iter().any(|a| a == address) {
return true;
}
if deny.iter().any(|a| a == address) {
return false;
}
false
}
_ => true,
}
} else {
true
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
pub cert: PathBuf,
pub key: PathBuf,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CorsConfig {
pub origins: Vec<Url>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct StorageConfig {
pub path: PathBuf,
}
impl Default for StorageConfig {
fn default() -> Self {
Self {
path: PathBuf::from("."),
}
}
}
impl ServerConfig {
pub async fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
if !vfs::try_exists(path.as_ref()).await? {
return Err(Error::NotFile(path.as_ref().to_path_buf()));
}
let contents = vfs::read_to_string(path.as_ref()).await?;
let mut config: ServerConfig = toml::from_str(&contents)?;
config.file = Some(path.as_ref().canonicalize()?);
let dir = config.directory();
if let Some(tls) = config.tls.as_mut() {
if tls.cert.is_relative() {
tls.cert = dir.join(&tls.cert);
}
if tls.key.is_relative() {
tls.key = dir.join(&tls.key);
}
tls.cert = tls.cert.canonicalize()?;
tls.key = tls.key.canonicalize()?;
}
Ok(config)
}
fn directory(&self) -> PathBuf {
self.file
.as_ref()
.unwrap()
.parent()
.map(|p| p.to_path_buf())
.unwrap()
}
pub async fn backend(&self) -> Result<Backend> {
let dir = self.directory();
let path = &self.storage.path;
let path = if path.is_relative() {
dir.join(path)
} else {
path.to_owned()
};
let path = path.canonicalize()?;
let mut backend = Backend::new(path);
backend.read_dir().await?;
Ok(backend)
}
}