diff --git a/src/admin/room/commands.rs b/src/admin/room/commands.rs index 2a4e60ac..5e25ec7a 100644 --- a/src/admin/room/commands.rs +++ b/src/admin/room/commands.rs @@ -96,23 +96,32 @@ pub(crate) enum RoomTargetOption { } #[admin_command] -pub(super) async fn purge_all_sync_tokens( +pub(super) async fn purge_empty_room_tokens( &self, + yes: bool, target_option: Option, - execute: bool, + dry_run: bool, ) -> Result { use conduwuit::{debug, info}; - let mode = if !execute { "Simulating" } else { "Starting" }; + if !yes && !dry_run { + return Err!( + "Please confirm this operation with --yes as it may delete tokens from many rooms, \ + or use --dry-run to simulate" + ); + } + + let mode = if dry_run { "Simulating" } else { "Starting" }; // strictly, we should check if these reach the max value after the loop and // warn the user that the count is too large - let mut total_rooms_checked: usize = 0; + let mut total_rooms_processed: usize = 0; + let mut empty_rooms_processed: u32 = 0; let mut total_tokens_deleted: usize = 0; let mut error_count: u32 = 0; - let mut skipped_rooms: usize = 0; + let mut skipped_rooms: u32 = 0; - info!("{} purge of sync tokens", mode); + info!("{} purge of sync tokens for rooms with no local users", mode); // Get all rooms in the server let all_rooms = self @@ -160,86 +169,102 @@ pub(super) async fn purge_all_sync_tokens( // Process each room for room_id in rooms { - total_rooms_checked = total_rooms_checked.saturating_add(1); + total_rooms_processed = total_rooms_processed.saturating_add(1); - // Log progress periodically - if total_rooms_checked % 100 == 0 || total_rooms_checked == total_rooms { - info!( - "Progress: {}/{} rooms checked, {} tokens {}", - total_rooms_checked, - total_rooms, - total_tokens_deleted, - if !execute { "would be deleted" } else { "deleted" } + // Count local users in this room + let local_users_count = self + .services + .rooms + .state_cache + .local_users_in_room(room_id) + .count() + .await; + + // Only process rooms with no local users + if local_users_count == 0 { + empty_rooms_processed = empty_rooms_processed.saturating_add(1); + + // In dry run mode, just count what would be deleted, don't actually delete + debug!( + "Room {} has no local users, {}", + room_id, + if dry_run { + "would purge sync tokens" + } else { + "purging sync tokens" + } ); - } - // In dry run mode, just count what would be deleted, don't actually delete - debug!( - "Room {} has no local users, {}", - room_id, - if !execute { - "would purge sync tokens" - } else { - "purging sync tokens" - } - ); - - if !execute { - // For dry run mode, count tokens without deleting - match self.services.rooms.user.count_room_tokens(room_id).await { - | Ok(count) => - if count > 0 { - debug!("Would delete {} sync tokens for room {}", count, room_id); - total_tokens_deleted = total_tokens_deleted.saturating_add(count); - } else { - debug!("No sync tokens found for room {}", room_id); + if dry_run { + // For dry run mode, count tokens without deleting + match self.services.rooms.user.count_room_tokens(room_id).await { + | Ok(count) => + if count > 0 { + debug!("Would delete {} sync tokens for room {}", count, room_id); + total_tokens_deleted = total_tokens_deleted.saturating_add(count); + } else { + debug!("No sync tokens found for room {}", room_id); + }, + | Err(e) => { + debug!("Error counting sync tokens for room {}: {:?}", room_id, e); + error_count = error_count.saturating_add(1); }, - | Err(e) => { - debug!("Error counting sync tokens for room {}: {:?}", room_id, e); - error_count = error_count.saturating_add(1); - }, + } + } else { + // Real deletion mode + match self.services.rooms.user.delete_room_tokens(room_id).await { + | Ok(count) => + if count > 0 { + debug!("Deleted {} sync tokens for room {}", count, room_id); + total_tokens_deleted = total_tokens_deleted.saturating_add(count); + } else { + debug!("No sync tokens found for room {}", room_id); + }, + | Err(e) => { + debug!("Error purging sync tokens for room {}: {:?}", room_id, e); + error_count = error_count.saturating_add(1); + }, + } } } else { - // Real deletion mode - match self.services.rooms.user.delete_room_tokens(room_id).await { - | Ok(count) => - if count > 0 { - debug!("Deleted {} sync tokens for room {}", count, room_id); - total_tokens_deleted = total_tokens_deleted.saturating_add(count); - } else { - debug!("No sync tokens found for room {}", room_id); - }, - | Err(e) => { - debug!("Error purging sync tokens for room {}: {:?}", room_id, e); - error_count = error_count.saturating_add(1); - }, - } + debug!("Room {} has {} local users, skipping", room_id, local_users_count); + } + + // Log progress periodically + if total_rooms_processed % 100 == 0 || total_rooms_processed == total_rooms { + info!( + "Progress: {}/{} rooms processed, {} empty rooms found, {} tokens {}", + total_rooms_processed, + total_rooms, + empty_rooms_processed, + total_tokens_deleted, + if dry_run { "would be deleted" } else { "deleted" } + ); } } - let action = if !execute { "would be deleted" } else { "deleted" }; + let action = if dry_run { "would be deleted" } else { "deleted" }; info!( - "Finished {}: checked {} rooms out of {} total, {} tokens {}, errors: {}", - if !execute { + "Finished {}: processed {} empty rooms out of {} total, {} tokens {}, errors: {}", + if dry_run { "purge simulation" } else { "purging sync tokens" }, - total_rooms_checked, + empty_rooms_processed, total_rooms, total_tokens_deleted, action, error_count ); + let mode_msg = if dry_run { "DRY RUN: " } else { "" }; self.write_str(&format!( - "Finished {}: checked {} rooms out of {} total, {} tokens {}, errors: {}", - if !execute { "simulation" } else { "purging sync tokens" }, - total_rooms_checked, - total_rooms, - total_tokens_deleted, - action, - error_count + "{}Successfully processed {empty_rooms_processed} empty rooms (out of {total_rooms} \ + total rooms), {total_tokens_deleted} tokens {}. Skipped {skipped_rooms} rooms based on \ + filters. Failed for {error_count} rooms.", + mode_msg, + if dry_run { "would be deleted" } else { "deleted" } )) .await } diff --git a/src/admin/room/mod.rs b/src/admin/room/mod.rs index fe44dec4..1b4650c3 100644 --- a/src/admin/room/mod.rs +++ b/src/admin/room/mod.rs @@ -68,14 +68,17 @@ pub(super) enum RoomCommand { /// - Delete sync tokens for all rooms that have no local users /// /// By default, processes all empty rooms. - PurgeAllSyncTokens { + PurgeEmptyRoomTokens { + /// Confirm you want to delete tokens from potentially many rooms + #[arg(long)] + yes: bool, + /// Target specific room types #[arg(long, value_enum)] target_option: Option, - /// Execute token deletions. Otherwise, - /// Performs a dry run without actually deleting any tokens + /// Perform a dry run without actually deleting any tokens #[arg(long)] - execute: bool, + dry_run: bool, }, } diff --git a/src/build_metadata/build.rs b/src/build_metadata/build.rs index bf84d508..bfdf20b1 100644 --- a/src/build_metadata/build.rs +++ b/src/build_metadata/build.rs @@ -79,12 +79,12 @@ fn main() { // --- Rerun Triggers --- // TODO: The git rerun triggers seem to always run - // // Rerun if the git HEAD changes - // println!("cargo:rerun-if-changed=.git/HEAD"); - // // Rerun if the ref pointed to by HEAD changes (e.g., new commit on branch) - // if let Some(ref_path) = run_git_command(&["symbolic-ref", "--quiet", "HEAD"]) - // { println!("cargo:rerun-if-changed=.git/{ref_path}"); - // } + // Rerun if the git HEAD changes + println!("cargo:rerun-if-changed=.git/HEAD"); + // Rerun if the ref pointed to by HEAD changes (e.g., new commit on branch) + if let Some(ref_path) = run_git_command(&["symbolic-ref", "--quiet", "HEAD"]) { + println!("cargo:rerun-if-changed=.git/{ref_path}"); + } println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH"); println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH_SHORT");