use pubky_common::timestamp::Timestamp;
use crate::{
persistence::{
files::{FileIoError, FileMetadata},
lmdb::{
tables::{
events::Event,
files::{Entry, FileLocation},
},
LmDB,
},
},
shared::webdav::EntryPath,
};
#[derive(Debug, Clone)]
pub struct EntryService {
db: LmDB,
user_disk_space_quota_bytes: Option<u64>,
}
impl EntryService {
pub fn new(db: LmDB, user_disk_space_quota_bytes: Option<u64>) -> Self {
Self {
db,
user_disk_space_quota_bytes,
}
}
pub fn write_entry(
&self,
path: &EntryPath,
metadata: &FileMetadata,
location: FileLocation,
) -> Result<Entry, FileIoError> {
let mut wtxn = self.db.env.write_txn()?;
let old_entry_size = self
.db
.tables
.entries
.get(&wtxn, path.as_str())?
.map(|bytes| Entry::deserialize(bytes).map(|entry| entry.content_length()))
.transpose()?
.unwrap_or(0);
let mut entry = Entry::new();
entry.set_content_hash(metadata.hash);
entry.set_content_length(metadata.length);
entry.set_timestamp(&metadata.modified_at);
entry.set_file_location(location);
entry.set_content_type(metadata.content_type.clone());
let entry_key = path.to_string();
self.db
.tables
.entries
.put(&mut wtxn, entry_key.as_str(), &entry.serialize())?;
let mut user = self
.db
.tables
.users
.get(&wtxn, path.pubkey())?
.ok_or(FileIoError::NotFound)?;
user.used_bytes = user
.used_bytes
.saturating_add(metadata.length as u64)
.saturating_sub(old_entry_size as u64);
if let Some(quota) = self.user_disk_space_quota_bytes {
if user.used_bytes > quota {
return Err(FileIoError::DiskSpaceQuotaExceeded);
}
}
self.db.tables.users.put(&mut wtxn, path.pubkey(), &user)?;
let url = format!("pubky://{}", entry_key);
let event = Event::put(&url);
let value = event.serialize();
self.db
.tables
.events
.put(&mut wtxn, metadata.modified_at.to_string().as_str(), &value)?;
wtxn.commit()?;
Ok(entry)
}
pub fn delete_entry(&self, path: &EntryPath) -> Result<(), FileIoError> {
let mut wtxn = self.db.env.write_txn()?;
let entry_bytes = self
.db
.tables
.entries
.get(&wtxn, path.as_str())?
.ok_or(FileIoError::NotFound)?;
let entry = Entry::deserialize(entry_bytes)?;
let mut user = self
.db
.tables
.users
.get(&wtxn, path.pubkey())?
.ok_or(FileIoError::NotFound)?;
user.used_bytes = user
.used_bytes
.saturating_sub(entry.content_length() as u64);
self.db.tables.users.put(&mut wtxn, path.pubkey(), &user)?;
self.db.tables.entries.delete(&mut wtxn, path.as_str())?;
let url = format!("pubky://{}", path.as_str());
let event = Event::delete(&url);
let value = event.serialize();
let key = Timestamp::now().to_string();
self.db.tables.events.put(&mut wtxn, &key, &value)?;
wtxn.commit()?;
Ok(())
}
}