diff --git a/script/migrations/20251205-backfill-verified-at.rb b/script/migrations/20251205-backfill-verified-at.rb new file mode 100755 index 000000000..7cd4f0030 --- /dev/null +++ b/script/migrations/20251205-backfill-verified-at.rb @@ -0,0 +1,112 @@ +#!/usr/bin/env ruby + +require_relative "../../config/environment" + +BACKFILL_TIMESTAMP = Time.parse("2025-12-02 12:00:00 UTC") + +def collect_verified_user_ids + verified_ids = Set.new + + # Owners (they created the account) + verified_ids.merge(User.where(role: :owner).pluck(:id)) + puts "After owners: #{verified_ids.size} users" + + # Card creators + verified_ids.merge(Card.distinct.pluck(:creator_id).compact) + puts "After card creators: #{verified_ids.size} users" + + # Comment creators + verified_ids.merge(Comment.distinct.pluck(:creator_id).compact) + puts "After comment creators: #{verified_ids.size} users" + + # Board creators + verified_ids.merge(Board.distinct.pluck(:creator_id).compact) + puts "After board creators: #{verified_ids.size} users" + + # Event creators + verified_ids.merge(Event.distinct.pluck(:creator_id).compact) + puts "After event creators: #{verified_ids.size} users" + + # Assigners (not assignees - they could be assigned without logging in) + verified_ids.merge(Assignment.distinct.pluck(:assigner_id).compact) + puts "After assigners: #{verified_ids.size} users" + + # Manual closers (user_id is nil for automatic closures) + verified_ids.merge(Closure.where.not(user_id: nil).distinct.pluck(:user_id).compact) + puts "After closers: #{verified_ids.size} users" + + # Manual postponers (user_id is nil for automatic entropy postponements) + verified_ids.merge(Card::NotNow.where.not(user_id: nil).distinct.pluck(:user_id).compact) + puts "After postponers: #{verified_ids.size} users" + + # Reactors + verified_ids.merge(Reaction.distinct.pluck(:reacter_id).compact) + puts "After reactors: #{verified_ids.size} users" + + # Filter creators + verified_ids.merge(Filter.distinct.pluck(:creator_id).compact) + puts "After filter creators: #{verified_ids.size} users" + + # Pinners + verified_ids.merge(Pin.distinct.pluck(:user_id).compact) + puts "After pinners: #{verified_ids.size} users" + + # Board accessors (accessed_at is touched when viewing boards) + verified_ids.merge(Access.where.not(accessed_at: nil).distinct.pluck(:user_id).compact) + puts "After board accessors: #{verified_ids.size} users" + + # Export requesters + verified_ids.merge(Account::Export.distinct.pluck(:user_id).compact) + puts "After export requesters: #{verified_ids.size} users" + + # Push subscribers + verified_ids.merge(Push::Subscription.distinct.pluck(:user_id).compact) + puts "After push subscribers: #{verified_ids.size} users" + + # Users who completed setup (name != email) + verified_ids.merge( + User.joins(:identity) + .where.not("users.name = identities.email_address") + .pluck(:id) + ) + puts "After setup completers: #{verified_ids.size} users" + + # Users whose identity has at least one session + verified_ids.merge( + User.where(identity_id: Session.distinct.select(:identity_id)).pluck(:id) + ) + puts "After identity sessions: #{verified_ids.size} users" + + verified_ids +end + +puts "Collecting verified user IDs..." +verified_user_ids = collect_verified_user_ids + +puts "\nFiltering to unverified users only..." +users_to_update = User.where(id: verified_user_ids.to_a) + .where(verified_at: nil) + .where(active: true) + .where.not(identity_id: nil) + .where.not(role: :system) + +update_count = users_to_update.count +puts "Found #{update_count} users to backfill" + +# Report remaining unverified users (before update) +remaining_before = User.where(verified_at: nil, active: true) + .where.not(identity_id: nil) + .where.not(role: :system) + .count +remaining_after = remaining_before - update_count +puts "\nCurrently unverified active users: #{remaining_before}" +puts "After backfill, remaining unverified: #{remaining_after}" +puts "These users will need to verify on next login." + +if update_count > 0 + puts "\nBackfilling verified_at..." + updated = users_to_update.update_all(verified_at: BACKFILL_TIMESTAMP) + puts "Updated #{updated} users" +end + +puts "\nDone!"