From 40fce4bc778c7f716ad642cf17f5d2e871921020 Mon Sep 17 00:00:00 2001
From: trisua <me@trisua.com>
Date: Sat, 7 Jun 2025 21:18:28 -0400
Subject: [PATCH] add: delete uploads and polls when user is deleted

---
 CODEOWNERS                                |  3 ++-
 crates/core/src/database/auth.rs          | 10 ++++++++
 crates/core/src/database/notifications.rs |  4 +++-
 crates/core/src/database/polls.rs         | 28 +++++++++++++++++++++--
 crates/core/src/database/posts.rs         |  2 +-
 crates/core/src/database/uploads.rs       | 24 +++++++++++++++++++
 6 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/CODEOWNERS b/CODEOWNERS
index 630d045..bb5809b 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1 +1,2 @@
-* me@trisua.com
+# * me@trisua.com
+* @t
diff --git a/crates/core/src/database/auth.rs b/crates/core/src/database/auth.rs
index 1084c6e..df526d0 100644
--- a/crates/core/src/database/auth.rs
+++ b/crates/core/src/database/auth.rs
@@ -416,6 +416,16 @@ impl DataManager {
             remove_file(banner).unwrap();
         }
 
+        // delete uploads
+        for upload in self.get_uploads_by_owner_all(user.id).await? {
+            self.delete_upload(upload.id).await?;
+        }
+
+        // delete polls
+        for poll in self.get_polls_by_owner_all(user.id).await? {
+            self.delete_poll(poll.id, &user).await?;
+        }
+
         // ...
         Ok(())
     }
diff --git a/crates/core/src/database/notifications.rs b/crates/core/src/database/notifications.rs
index 562f9e7..595b0e5 100644
--- a/crates/core/src/database/notifications.rs
+++ b/crates/core/src/database/notifications.rs
@@ -129,7 +129,9 @@ impl DataManager {
         }
 
         // incr notification count
-        self.incr_user_notifications(data.owner).await.unwrap();
+        if let Err(e) = self.incr_user_notifications(data.owner).await {
+            return Err(e);
+        };
 
         // post event
         let mut con = self.2.get_con().await;
diff --git a/crates/core/src/database/polls.rs b/crates/core/src/database/polls.rs
index 4abc613..92aac8d 100644
--- a/crates/core/src/database/polls.rs
+++ b/crates/core/src/database/polls.rs
@@ -3,7 +3,7 @@ use crate::cache::Cache;
 use crate::model::communities::Poll;
 use crate::model::moderation::AuditLogEntry;
 use crate::model::{Error, Result, auth::User, permissions::FinePermission};
-use crate::{auto_method, execute, get, query_row, params};
+use crate::{auto_method, execute, get, params, query_row, query_rows};
 
 #[cfg(feature = "sqlite")]
 use rusqlite::Row;
@@ -35,6 +35,30 @@ impl DataManager {
 
     auto_method!(get_poll_by_id()@get_poll_from_row -> "SELECT * FROM polls WHERE id = $1" --name="poll" --returns=Poll --cache-key-tmpl="atto.poll:{}");
 
+    /// Get all polls by their owner.
+    ///
+    /// # Arguments
+    /// * `owner` - the ID of the owner of the polls
+    pub async fn get_polls_by_owner_all(&self, owner: usize) -> Result<Vec<Poll>> {
+        let conn = match self.connect().await {
+            Ok(c) => c,
+            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
+        };
+
+        let res = query_rows!(
+            &conn,
+            "SELECT * FROM polls WHERE owner = $1 ORDER BY created DESC",
+            &[&(owner as i64)],
+            |x| { Self::get_poll_from_row(x) }
+        );
+
+        if res.is_err() {
+            return Err(Error::GeneralNotFound("poll".to_string()));
+        }
+
+        Ok(res.unwrap())
+    }
+
     /// Create a new poll in the database.
     ///
     /// # Arguments
@@ -93,7 +117,7 @@ impl DataManager {
         Ok(data.id)
     }
 
-    pub async fn delete_poll(&self, id: usize, user: User) -> Result<()> {
+    pub async fn delete_poll(&self, id: usize, user: &User) -> Result<()> {
         let y = self.get_poll_by_id(id).await?;
 
         if user.id != y.owner {
diff --git a/crates/core/src/database/posts.rs b/crates/core/src/database/posts.rs
index 57df032..5aee1ed 100644
--- a/crates/core/src/database/posts.rs
+++ b/crates/core/src/database/posts.rs
@@ -1547,7 +1547,7 @@ impl DataManager {
 
         // remove poll
         if y.poll_id != 0 {
-            self.delete_poll(y.poll_id, user).await?;
+            self.delete_poll(y.poll_id, &user).await?;
         }
 
         // return
diff --git a/crates/core/src/database/uploads.rs b/crates/core/src/database/uploads.rs
index 2a00823..6ea8dcb 100644
--- a/crates/core/src/database/uploads.rs
+++ b/crates/core/src/database/uploads.rs
@@ -83,6 +83,30 @@ impl DataManager {
         Ok(res.unwrap())
     }
 
+    /// Get all uploads by their owner.
+    ///
+    /// # Arguments
+    /// * `owner` - the ID of the owner of the upload
+    pub async fn get_uploads_by_owner_all(&self, owner: usize) -> Result<Vec<MediaUpload>> {
+        let conn = match self.connect().await {
+            Ok(c) => c,
+            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
+        };
+
+        let res = query_rows!(
+            &conn,
+            "SELECT * FROM uploads WHERE owner = $1 ORDER BY created DESC",
+            &[&(owner as i64)],
+            |x| { Self::get_upload_from_row(x) }
+        );
+
+        if res.is_err() {
+            return Err(Error::GeneralNotFound("upload".to_string()));
+        }
+
+        Ok(res.unwrap())
+    }
+
     /// Create a new upload in the database.
     ///
     /// # Arguments