use std::sync::Arc;
use pagedb::{Db, PagedbError, RealmId};
use crate::error::LiteError;
use crate::storage::encryption::Encryption;
use crate::storage::pagedb_storage::types::{PagedbStorage, lite_open_options};
fn validate_opfs_db_name(name: &str) -> Result<(), LiteError> {
if name.is_empty()
|| name == "."
|| name == ".."
|| name.contains('/')
|| name.contains('\\')
|| name.contains('\0')
{
return Err(LiteError::BadRequest {
detail: format!(
"invalid OPFS database name {name:?}: must be a non-empty single path \
segment without '/', '\\', or NUL and not '.' or '..'"
),
});
}
Ok(())
}
impl PagedbStorage<pagedb::vfs::opfs::OpfsVfs> {
pub async fn open_opfs(
db_name: &str,
worker_url: &str,
encryption: Encryption,
) -> Result<Self, LiteError> {
validate_opfs_db_name(db_name)?;
let realm = RealmId::new([0u8; 16]);
let vfs = pagedb::vfs::opfs::OpfsVfs::with_root(worker_url, db_name).map_err(|e| {
LiteError::WorkerFailed {
detail: format!("failed to spawn OPFS worker at '{worker_url}': {e}"),
}
})?;
let kek = crate::storage::encryption::resolve_kek_opfs(&encryption, &vfs.clone()).await?;
let db = Db::open(vfs, kek, 4096, realm, lite_open_options())
.await
.map_err(|e| match e {
PagedbError::Corruption(_) | PagedbError::ChecksumFailure => LiteError::Corrupted {
detail: format!(
"OPFS database is corrupted and has been left untouched. Recovery \
is the caller's decision: re-sync from Origin into a new database \
name, or delete the OPFS directory to start empty. Original \
error: {e}"
),
},
other => LiteError::from(other),
})?;
Ok(Self { db: Arc::new(db) })
}
}