Write config changes to disk
This commit is contained in:
parent
fb18cb3c4f
commit
5ec0b5f7a5
7 changed files with 86 additions and 36 deletions
|
@ -87,6 +87,8 @@ pub enum Error {
|
|||
|
||||
#[error("Could not deserialize configuration: `{0}`")]
|
||||
ConfigDeserialization(toml::de::Error),
|
||||
#[error("Could not serialize configuration: `{0}`")]
|
||||
ConfigSerialization(toml::ser::Error),
|
||||
#[error("Could not deserialize collection")]
|
||||
IndexDeserializationError,
|
||||
#[error("Could not serialize collection")]
|
||||
|
|
|
@ -114,8 +114,32 @@ impl Manager {
|
|||
|
||||
#[cfg(test)]
|
||||
pub async fn apply(&self, config: storage::Config) -> Result<(), Error> {
|
||||
*self.config.write().await = config.try_into()?;
|
||||
// TODO persistence
|
||||
self.mutate_fallible(|c| {
|
||||
*c = config.try_into()?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn mutate<F: FnOnce(&mut Config)>(&self, op: F) -> Result<(), Error> {
|
||||
self.mutate_fallible(|c| {
|
||||
op(c);
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn mutate_fallible<F: FnOnce(&mut Config) -> Result<(), Error>>(
|
||||
&self,
|
||||
op: F,
|
||||
) -> Result<(), Error> {
|
||||
let mut config = self.config.write().await;
|
||||
op(&mut config)?;
|
||||
let serialized = toml::ser::to_string_pretty::<storage::Config>(&config.clone().into())
|
||||
.map_err(Error::ConfigSerialization)?;
|
||||
tokio::fs::write(&self.config_file_path, serialized.as_bytes())
|
||||
.await
|
||||
.map_err(|e| Error::Io(self.config_file_path.clone(), e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -125,10 +149,11 @@ impl Manager {
|
|||
Duration::from_secs(seconds)
|
||||
}
|
||||
|
||||
pub async fn set_index_sleep_duration(&self, duration: Duration) {
|
||||
let mut config = self.config.write().await;
|
||||
config.reindex_every_n_seconds = Some(duration.as_secs());
|
||||
// TODO persistence
|
||||
pub async fn set_index_sleep_duration(&self, duration: Duration) -> Result<(), Error> {
|
||||
self.mutate(|c| {
|
||||
c.reindex_every_n_seconds = Some(duration.as_secs());
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_index_album_art_pattern(&self) -> Regex {
|
||||
|
@ -137,20 +162,22 @@ impl Manager {
|
|||
pattern.unwrap_or_else(|| Regex::new("Folder.(jpeg|jpg|png)").unwrap())
|
||||
}
|
||||
|
||||
pub async fn set_index_album_art_pattern(&self, regex: Regex) {
|
||||
let mut config = self.config.write().await;
|
||||
config.album_art_pattern = Some(regex);
|
||||
// TODO persistence
|
||||
pub async fn set_index_album_art_pattern(&self, regex: Regex) -> Result<(), Error> {
|
||||
self.mutate(|c| {
|
||||
c.album_art_pattern = Some(regex);
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_ddns_update_url(&self) -> Option<http::Uri> {
|
||||
self.config.read().await.ddns_url.clone()
|
||||
}
|
||||
|
||||
pub async fn set_ddns_update_url(&self, url: http::Uri) {
|
||||
let mut config = self.config.write().await;
|
||||
config.ddns_url = Some(url);
|
||||
// TODO persistence
|
||||
pub async fn set_ddns_update_url(&self, url: http::Uri) -> Result<(), Error> {
|
||||
self.mutate(|c| {
|
||||
c.ddns_url = Some(url);
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_users(&self) -> Vec<User> {
|
||||
|
@ -169,9 +196,8 @@ impl Manager {
|
|||
password: &str,
|
||||
admin: bool,
|
||||
) -> Result<(), Error> {
|
||||
let mut config = self.config.write().await;
|
||||
config.create_user(username, password, admin)
|
||||
// TODO persistence
|
||||
self.mutate_fallible(|c| c.create_user(username, password, admin))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn login(&self, username: &str, password: &str) -> Result<auth::Token, Error> {
|
||||
|
@ -180,15 +206,13 @@ impl Manager {
|
|||
}
|
||||
|
||||
pub async fn set_is_admin(&self, username: &str, is_admin: bool) -> Result<(), Error> {
|
||||
let mut config = self.config.write().await;
|
||||
config.set_is_admin(username, is_admin)
|
||||
// TODO persistence
|
||||
self.mutate_fallible(|c| c.set_is_admin(username, is_admin))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn set_password(&self, username: &str, password: &str) -> Result<(), Error> {
|
||||
let mut config = self.config.write().await;
|
||||
config.set_password(username, password)
|
||||
// TODO persistence
|
||||
self.mutate_fallible(|c| c.set_password(username, password))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn authenticate(
|
||||
|
@ -200,10 +224,8 @@ impl Manager {
|
|||
config.authenticate(auth_token, scope, &self.auth_secret)
|
||||
}
|
||||
|
||||
pub async fn delete_user(&self, username: &str) {
|
||||
let mut config = self.config.write().await;
|
||||
config.delete_user(username);
|
||||
// TODO persistence
|
||||
pub async fn delete_user(&self, username: &str) -> Result<(), Error> {
|
||||
self.mutate(|c| c.delete_user(username)).await
|
||||
}
|
||||
|
||||
pub async fn get_mounts(&self) -> Vec<MountDir> {
|
||||
|
@ -220,15 +242,25 @@ impl Manager {
|
|||
}
|
||||
|
||||
pub async fn set_mounts(&self, mount_dirs: Vec<storage::MountDir>) -> Result<(), Error> {
|
||||
self.config.write().await.set_mounts(mount_dirs)
|
||||
// TODO persistence
|
||||
self.mutate_fallible(|c| c.set_mounts(mount_dirs)).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::app::test;
|
||||
use crate::test_name;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn blank_config_is_valid() {
|
||||
let config_path = PathBuf::from_iter(["test-data", "blank.toml"]);
|
||||
Manager::new(&config_path, auth::Secret([0; 32]))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn can_read_config() {
|
||||
let config_path = PathBuf::from_iter(["test-data", "config.toml"]);
|
||||
|
@ -257,4 +289,18 @@ mod test {
|
|||
);
|
||||
assert!(config.users[0].hashed_password.is_some());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn can_write_config() {
|
||||
let ctx = test::ContextBuilder::new(test_name!()).build().await;
|
||||
ctx.config_manager
|
||||
.create_user("Walter", "example_password", false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let manager = Manager::new(&ctx.config_manager.config_file_path, auth::Secret([0; 32]))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(manager.get_user("Walter").await.is_ok());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,10 +25,10 @@ pub struct Config {
|
|||
pub reindex_every_n_seconds: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub album_art_pattern: Option<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub mount_dirs: Vec<MountDir>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ddns_url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub users: Vec<User>,
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ mod test {
|
|||
.unwrap();
|
||||
assert!(ctx.config_manager.get_user(TEST_USERNAME).await.is_ok());
|
||||
|
||||
ctx.config_manager.delete_user(TEST_USERNAME).await;
|
||||
ctx.config_manager.delete_user(TEST_USERNAME).await.unwrap();
|
||||
assert!(ctx.config_manager.get_user(TEST_USERNAME).await.is_err());
|
||||
}
|
||||
|
||||
|
|
|
@ -126,20 +126,20 @@ async fn put_settings(
|
|||
let Ok(regex) = Regex::new(&pattern) else {
|
||||
return Err(APIError::InvalidAlbumArtPattern);
|
||||
};
|
||||
config_manager.set_index_album_art_pattern(regex).await;
|
||||
config_manager.set_index_album_art_pattern(regex).await?;
|
||||
}
|
||||
|
||||
if let Some(seconds) = new_settings.reindex_every_n_seconds {
|
||||
config_manager
|
||||
.set_index_sleep_duration(Duration::from_secs(seconds as u64))
|
||||
.await;
|
||||
.await?;
|
||||
}
|
||||
|
||||
if let Some(url_string) = new_settings.ddns_update_url {
|
||||
let Ok(uri) = http::Uri::try_from(url_string) else {
|
||||
return Err(APIError::InvalidDDNSURL);
|
||||
};
|
||||
config_manager.set_ddns_update_url(uri).await;
|
||||
config_manager.set_ddns_update_url(uri).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -239,7 +239,7 @@ async fn delete_user(
|
|||
return Err(APIError::DeletingOwnAccount);
|
||||
}
|
||||
}
|
||||
config_manager.delete_user(&name).await;
|
||||
config_manager.delete_user(&name).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,7 @@ impl From<app::Error> for APIError {
|
|||
app::Error::IndexAlbumArtPatternInvalid => APIError::InvalidAlbumArtPattern,
|
||||
|
||||
app::Error::ConfigDeserialization(_) => APIError::Internal,
|
||||
app::Error::ConfigSerialization(_) => APIError::Internal,
|
||||
app::Error::IndexDeserializationError => APIError::Internal,
|
||||
app::Error::IndexSerializationError => APIError::Internal,
|
||||
|
||||
|
|
1
test-data/blank.toml
Normal file
1
test-data/blank.toml
Normal file
|
@ -0,0 +1 @@
|
|||
|
Loading…
Add table
Reference in a new issue