fix: Properly load/save DB and stop creating dummy users

This commit is contained in:
selfhoster selfhoster 2026-09-20 13:47:46 +02:00
commit 1e0b9ac166
4 changed files with 37 additions and 43 deletions

View file

@ -1,5 +1,4 @@
use camino::{Utf8Path, Utf8PathBuf};
use serde::{Deserialize, Serialize};
use serde_json::Error as JsonError;
use std::fmt;
@ -35,7 +34,7 @@ impl fmt::Display for FilesystemDatabaseError {
impl std::error::Error for FilesystemDatabaseError {}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default)]
pub struct FilesystemDatabase {
// TODO: Once we have common validation steps in place across DB backends, we can reduce cloning.
// For now, we clone the DB on every write operation, and update it when saving to disk
@ -59,10 +58,14 @@ impl FilesystemDatabase {
path: path.to_path_buf(),
};
db.save().await?;
tracing::info!("Initializing empty database in file {path}. Checking permissions...");
db.save_self().await?;
tracing::info!("Database successfully created");
return Ok(Database::new(db));
}
tracing::info!("Loading database from file {path}");
let s = tokio::fs::read(path)
.await
.map_err(|e| Box::new(FilesystemDatabaseError::ReadFileIO(path.to_path_buf(), e)))?;
@ -74,13 +77,29 @@ impl FilesystemDatabase {
}))
}
pub async fn save(&mut self) -> Result<(), BoxedError> {
let s = serde_json::to_string(&self)
/// Attempts to save a new state of the DB, effectively switching to the new state
/// only if the save is successful.
pub async fn save(&mut self, new_db: MemoryDatabase) -> Result<(), BoxedError> {
self.save_inner(&new_db).await?;
self.inner = new_db;
Ok(())
}
/// Saves an in-memory DB to disk. Cannot be used directly to observe exclusive access.
async fn save_inner(&self, inner: &MemoryDatabase) -> Result<(), BoxedError> {
let s = serde_json::to_string(inner)
.map_err(|e| Box::new(FilesystemDatabaseError::WriteFileJson(self.path.clone(), e)))?;
Ok(tokio::fs::write(&self.path, &s)
.await
.map_err(|e| Box::new(FilesystemDatabaseError::WriteFileIO(self.path.clone(), e)))?)
}
/// Save the database without switching to a new state.
///
/// Used when initializing the DB, to check for permissions.
pub async fn save_self(&mut self) -> Result<(), BoxedError> {
self.save_inner(&self.inner).await
}
}
#[async_trait::async_trait]
@ -95,9 +114,7 @@ impl DatabaseInterface for FilesystemDatabase {
return Ok(Err(e));
}
self.save().await?;
self.inner = new_db;
self.save(new_db).await?;
Ok(Ok(()))
}
@ -119,9 +136,7 @@ impl DatabaseInterface for FilesystemDatabase {
return Ok(Err(e));
}
self.save().await?;
self.inner = new_db;
self.save(new_db).await?;
Ok(Ok(()))
}
@ -136,9 +151,7 @@ impl DatabaseInterface for FilesystemDatabase {
return Ok(Err(e));
}
self.save().await?;
self.inner = new_db;
self.save(new_db).await?;
Ok(Ok(()))
}