Unable to Bulk Suspend/Deactivate Users in Rails Console on Discourse 3.5.0.beta5-dev

Hi everyone,

I’m facing a persistent issue trying to bulk suspend/deactivate users on my Discourse instance (version 3.5.0.beta5-dev) via the Rails console. I’m trying to exclude my admin account (hoangthai, user ID 100) and permanently suspend all other active non-admin/non-moderator users with a custom reason “Chưa gia hạn tài khoản” (Account renewal pending).

I’ve tried several approaches based on common Discourse console commands and debugging suggestions, but the accounts remain active: true even though suspended_at and suspended_till might get set.

Here’s a breakdown of what I’ve attempted and the errors/results:

My Goal: Suspend all users permanently (except hoangthai, ID 100) with the reason “Chưa gia hạn tài khoản” (Account renewal pending).

Attempts & Issues:

  1. Initial Attempt (Setting suspend_reason, suspended_at, suspended_till directly):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        user.suspend_reason = "Tài khoản tạm khóa do nghỉ phép dài hạn." # First reason attempt
        user.suspended_at = Time.current
        user.suspended_till = Time.current + 100.years
        user.save!
        puts "Khóa tài khoản: #{user.username}"
      end
    end
    
    • Result: NoMethodError: undefined method 'suspend_reason=' for an instance of User (suggested suspended_at=). This indicated suspend_reason was not the correct attribute.
  2. Second Attempt (Using UserSuspension.suspend_user as suggested for newer versions):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        UserSuspension.suspend_user(
            user,
            Time.current + 100.years,
            nil, # suspend_reason_id
            "Tài khoản tạm khóa: Nghỉ phép dài hạn.", # custom_suspend_reason
            Discourse.system_user
        )
        puts "Đã khóa tài khoản: #{user.username}"
      end
    end
    
    • Result: NameError: uninitialized constant UserSuspension (suggested UserSuspender). This indicated the class name was incorrect.
  3. Third Attempt (Corrected class name to UserSuspender):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        UserSuspender.suspend_user( # Still trying suspend_user
            user,
            Time.current + 100.years,
            nil,
            "Tài khoản tạm khóa: Nghỉ phép dài hạn.",
            Discourse.system_user
        )
        puts "Đã khóa tài khoản: #{user.username}"
      end
    end
    
    • Result: NoMethodError: undefined method 'suspend_user' for class UserSuspender (suggested UserSuspended?). This showed the method name was also incorrect.
  4. Fourth Attempt (Using UserSuspender.new(...).suspend as the service object pattern):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        begin
            UserSuspender.new(
                user,
                Discourse.system_user,
                reason: "Tài khoản tạm khóa: Nghỉ phép dài hạn.",
                suspended_till: nil # For permanent suspension
            ).suspend
            puts "Đã khóa tài khoản: #{user.username}"
        rescue => e
            puts "Lỗi khi khóa tài khoản #{user.username}: #{e.message}"
        end
      end
    end
    
    • Result: This command ran without errors in the console. It printed “Đã khóa tài khoản: [username]”.
    • However, when checking user status in the admin UI or directly querying user.active in console, users still showed active: true. Only suspended_at and suspended_till were updated (e.g., suspended_at: "2025-05-26 00:37:01.406064419 +0000", suspended_till: nil). The accounts were still able to log in.
  5. Fifth Attempt (Directly setting active = false along with suspension fields):

    admin_id_to_exclude = 100
    User.where.not(id: admin_id_to_exclude).each do |user|
      if user.active? && !user.admin? && !user.moderator?
        begin
            user.suspended_at = Time.current
            user.suspended_till = nil # Permanent
            user.custom_suspension_reason = "Chưa gia hạn tài khoản" # Custom reason
            user.active = false # Crucial part
            user.save!
            puts "Đã **khóa VĨNH VIỄN** và vô hiệu hóa tài khoản: #{user.username}"
        rescue => e
            puts "LỖI khi xử lý tài khoản #{user.username}: #{e.message}"
        end
      end
    end
    
    • Result: This command also ran without errors and printed success messages.
    • However, the outcome was the same: active: true remained, and suspended_at/suspended_till were updated, but accounts were not actually deactivated or prevented from logging in.

My Current Situation:

I can manually suspend users via the admin UI (Admin → Users → Suspend) and they get correctly suspended (e.g., as shown in your previous screenshot with “Tạm khóa bởi: hoangthai, Lý do: Chưa gia hạn tài khoản”, and they cannot log in). This confirms the core functionality works.

I suspect there’s a specific API nuance in Discourse 3.5.0.beta5-dev for programmatically suspending/deactivating users that I’m missing, or perhaps a caching issue that’s not being cleared, or a background job that needs to run.

Could anyone with experience in Discourse 3.5.x beta versions or deep Rails console knowledge offer some guidance on how to correctly achieve a bulk, permanent suspension that actually prevents user logins and sets the active status to false?

Thank you for your time and help!

1 Like