Skip to content

Commit

Permalink
feat: file deletion implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
insertish committed Nov 27, 2024
1 parent acc4317 commit 249749e
Show file tree
Hide file tree
Showing 15 changed files with 267 additions and 18 deletions.
28 changes: 16 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/core/config/Revolt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,4 @@ api = ""
events = ""
files = ""
proxy = ""
crond = ""
1 change: 1 addition & 0 deletions crates/core/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ pub struct Sentry {
pub events: String,
pub files: String,
pub proxy: String,
pub crond: String,
}

#[derive(Deserialize, Debug, Clone)]
Expand Down
3 changes: 3 additions & 0 deletions crates/core/database/src/models/file_hashes/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ pub trait AbstractAttachmentHashes: Sync + Send {

/// Update an attachment hash nonce value.
async fn set_attachment_hash_nonce(&self, hash: &str, nonce: &str) -> Result<()>;

/// Delete attachment hash by id.
async fn delete_attachment_hash(&self, id: &str) -> Result<()>;
}
5 changes: 5 additions & 0 deletions crates/core/database/src/models/file_hashes/ops/mongodb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ impl AbstractAttachmentHashes for MongoDb {
.map(|_| ())
.map_err(|_| create_database_error!("update_one", COL))
}

/// Delete attachment hash by id.
async fn delete_attachment_hash(&self, id: &str) -> Result<()> {
query!(self, delete_one_by_id, COL, id).map(|_| ())
}
}
12 changes: 11 additions & 1 deletion crates/core/database/src/models/file_hashes/ops/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ impl AbstractAttachmentHashes for ReferenceDb {
let hashes = self.file_hashes.lock().await;
hashes
.values()
.find(|&hash| hash.id == hash_value || hash.processed_hash == hash_value)
.cloned()
.find(|hash| hash.id == hash_value || hash.processed_hash == hash_value)
.ok_or(create_error!(NotFound))
}

Expand All @@ -38,4 +38,14 @@ impl AbstractAttachmentHashes for ReferenceDb {
Err(create_error!(NotFound))
}
}

/// Delete attachment hash by id.
async fn delete_attachment_hash(&self, id: &str) -> Result<()> {
let mut file_hashes = self.file_hashes.lock().await;
if file_hashes.remove(id).is_some() {
Ok(())
} else {
Err(create_error!(NotFound))
}
}
}
12 changes: 12 additions & 0 deletions crates/core/database/src/models/files/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ pub trait AbstractAttachments: Sync + Send {
/// Fetch an attachment by its id.
async fn fetch_attachment(&self, tag: &str, file_id: &str) -> Result<File>;

/// Fetch all deleted attachments.
async fn fetch_deleted_attachments(&self) -> Result<Vec<File>>;

/// Fetch all dangling attachments.
async fn fetch_dangling_files(&self) -> Result<Vec<File>>;

/// Count references to a given hash.
async fn count_file_hash_references(&self, hash: &str) -> Result<usize>;

/// Find an attachment by its details and mark it as used by a given parent.
async fn find_and_use_attachment(
&self,
Expand All @@ -32,4 +41,7 @@ pub trait AbstractAttachments: Sync + Send {

/// Mark multiple attachments as having been deleted.
async fn mark_attachments_as_deleted(&self, ids: &[String]) -> Result<()>;

/// Delete the attachment entry.
async fn delete_attachment(&self, id: &str) -> Result<()>;
}
54 changes: 52 additions & 2 deletions crates/core/database/src/models/files/ops/mongodb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,51 @@ impl AbstractAttachments for MongoDb {
.ok_or_else(|| create_error!(NotFound))
}

/// Fetch all deleted attachments.
async fn fetch_deleted_attachments(&self) -> Result<Vec<File>> {
query!(
self,
find,
COL,
doc! {
"deleted": true,
"reported": {
"$ne": true
}
}
)
}

/// Fetch all dangling attachments.
async fn fetch_dangling_files(&self) -> Result<Vec<File>> {
query!(
self,
find,
COL,
doc! {
"used_for.type": {
"$exists": 0
},
"deleted": {
"$ne": true
}
}
)
}

/// Count references to a given hash.
async fn count_file_hash_references(&self, hash: &str) -> Result<usize> {
query!(
self,
count_documents,
COL,
doc! {
"hash": hash
}
)
.map(|count| count as usize)
}

/// Find an attachment by its details and mark it as used by a given parent.
async fn find_and_use_attachment(
&self,
Expand Down Expand Up @@ -114,7 +159,7 @@ impl AbstractAttachments for MongoDb {
/// Mark multiple attachments as having been deleted.
async fn mark_attachments_as_deleted(&self, ids: &[String]) -> Result<()> {
self.col::<Document>(COL)
.update_one(
.update_many(
doc! {
"_id": {
"$in": ids
Expand All @@ -129,7 +174,12 @@ impl AbstractAttachments for MongoDb {
)
.await
.map(|_| ())
.map_err(|_| create_database_error!("update_one", COL))
.map_err(|_| create_database_error!("update_many", COL))
}

/// Delete the attachment entry.
async fn delete_attachment(&self, id: &str) -> Result<()> {
query!(self, delete_one_by_id, COL, id).map(|_| ())
}
}

Expand Down
45 changes: 45 additions & 0 deletions crates/core/database/src/models/files/ops/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,41 @@ impl AbstractAttachments for ReferenceDb {
}
}

/// Fetch all deleted attachments.
async fn fetch_deleted_attachments(&self) -> Result<Vec<File>> {
let files = self.files.lock().await;
Ok(files
.values()
.filter(|file| {
// file has been marked as deleted
file.deleted.is_some_and(|v| v)
// and it has not been reported
&& !file.reported.is_some_and(|v| v)
})
.cloned()
.collect())
}

/// Fetch all dangling attachments.
async fn fetch_dangling_files(&self) -> Result<Vec<File>> {
let files = self.files.lock().await;
Ok(files
.values()
.filter(|file| file.used_for.is_none() && !file.deleted.is_some_and(|v| v))
.cloned()
.collect())
}

/// Count references to a given hash.
async fn count_file_hash_references(&self, hash: &str) -> Result<usize> {
let files = self.files.lock().await;
Ok(files
.values()
.filter(|file| file.hash.as_ref().is_some_and(|h| h == hash))
.cloned()
.count())
}

/// Find an attachment by its details and mark it as used by a given parent.
async fn find_and_use_attachment(
&self,
Expand Down Expand Up @@ -96,4 +131,14 @@ impl AbstractAttachments for ReferenceDb {

Ok(())
}

/// Delete the attachment entry.
async fn delete_attachment(&self, id: &str) -> Result<()> {
let mut files = self.files.lock().await;
if files.remove(id).is_some() {
Ok(())
} else {
Err(create_error!(NotFound))
}
}
}
17 changes: 17 additions & 0 deletions crates/core/files/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,23 @@ pub async fn upload_to_s3(bucket_id: &str, path: &str, buf: &[u8]) -> Result<Str
Ok(BASE64_STANDARD.encode(nonce))
}

/// Delete a file from S3 by path
pub async fn delete_from_s3(bucket_id: &str, path: &str) -> Result<()> {
let config = config().await;
let client = create_client(config.files.s3);

report_internal_error!(
client
.delete_object()
.bucket(bucket_id)
.key(path)
.send()
.await
)?;

Ok(())
}

/// Determine size of image at temp file
pub fn image_size(f: &NamedTempFile) -> Option<(usize, usize)> {
if let Ok(size) = imagesize::size(f.path())
Expand Down
10 changes: 9 additions & 1 deletion crates/daemons/crond/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "crond"
name = "revolt-crond"
version = "0.7.19"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
Expand All @@ -9,6 +9,14 @@ description = "Revolt Daemon Service: Timed data clean up tasks"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# Utility
log = "0.4"

# Async
tokio = { version = "1" }

# Core
revolt-database = { version = "0.7.19", path = "../../core/database" }
revolt-result = { version = "0.7.19", path = "../../core/result" }
revolt-config = { version = "0.7.19", path = "../../core/config" }
revolt-files = { version = "0.7.19", path = "../../core/files" }
20 changes: 18 additions & 2 deletions crates/daemons/crond/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
fn main() {
println!("Hello, world!");
use revolt_config::configure;
use revolt_database::DatabaseInfo;
use revolt_result::Result;
use tasks::{file_deletion, prune_dangling_files};
use tokio::try_join;

pub mod tasks;

#[tokio::main]
async fn main() -> Result<()> {
configure!(crond);

let db = DatabaseInfo::Auto.connect().await.expect("database");
try_join!(
file_deletion::task(db.clone()),
prune_dangling_files::task(db)
)
.map(|_| ())
}
39 changes: 39 additions & 0 deletions crates/daemons/crond/src/tasks/file_deletion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::time::Duration;

use log::info;
use revolt_database::Database;
use revolt_files::delete_from_s3;
use revolt_result::Result;
use tokio::time::sleep;

pub async fn task(db: Database) -> Result<()> {
loop {
let files = db.fetch_deleted_attachments().await?;

for file in files {
let count = db
.count_file_hash_references(file.hash.as_ref().expect("no `hash` present"))
.await?;

// No other files reference this file on disk anymore
if count <= 1 {
let file_hash = db
.fetch_attachment_hash(file.hash.as_ref().expect("no `hash` present"))
.await?;

// Delete from S3
delete_from_s3(&file_hash.bucket_id, &file_hash.path).await?;

// Delete the hash
db.delete_attachment_hash(&file_hash.id).await?;
info!("Deleted file hash {}", file_hash.id);
}

// Delete the file
db.delete_attachment(&file.id).await?;
info!("Deleted file {}", file.id);
}

sleep(Duration::from_secs(60)).await;
}
}
2 changes: 2 additions & 0 deletions crates/daemons/crond/src/tasks/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod file_deletion;
pub mod prune_dangling_files;
Loading

0 comments on commit 249749e

Please sign in to comment.