Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tootctl accounts [add|del|cull] #8642

Merged
merged 2 commits into from
Sep 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions lib/mastodon/accounts_cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,145 @@ def rotate(username = nil)
end
end

option :email, required: true
option :confirmed, type: :boolean
option :role, default: 'user'
option :reattach, type: :boolean
option :force, type: :boolean
desc 'add USERNAME', 'Create a new user'
long_desc <<-LONG_DESC
Create a new user account with a given USERNAME and an
e-mail address provided with --email.

With the --confirmed option, the confirmation e-mail will
be skipped and the account will be active straight away.

With the --role option one of "user", "admin" or "moderator"
can be supplied. Defaults to "user"

With the --reattach option, the new user will be reattached
to a given existing username of an old account. If the old
account is still in use by someone else, you can supply
the --force option to delete the old record and reattach the
username to the new account anyway.
LONG_DESC
def add(username)
account = Account.new(username: username)
password = SecureRandom.hex
user = User.new(email: options[:email], password: password, admin: options[:role] == 'admin', moderator: options[:role] == 'moderator', confirmed_at: Time.now.utc)

if options[:reattach]
account = Account.find_local(username) || Account.new(username: username)

if account.user.present? && !options[:force]
say('The chosen username is currently in use', :red)
say('Use --force to reattach it anyway and delete the other user')
return
elsif account.user.present?
account.user.destroy!
end
end

user.account = account

if user.save
if options[:confirmed]
user.confirmed_at = nil
user.confirm!
end

say('OK', :green)
say("New password: #{password}")
else
user.errors.to_h.each do |key, error|
say('Failure/Error: ', :red)
say(key)
say(' ' + error, :red)
end
end
end

desc 'del USERNAME', 'Delete a user'
long_desc <<-LONG_DESC
Remove a user account with a given USERNAME.
LONG_DESC
def del(username)
account = Account.find_local(username)

if account.nil?
say('No user with such username', :red)
return
end

say("Deleting user with #{account.statuses_count}, this might take a while...")
SuspendAccountService.new.call(account, remove_user: true)
say('OK', :green)
end

option :dry_run, type: :boolean
desc 'cull', 'Remove remote accounts that no longer exist'
long_desc <<-LONG_DESC
Query every single remote account in the database to determine
if it still exists on the origin server, and if it doesn't,
remove it from the database.

Accounts that have had confirmed activity within the last week
are excluded from the checks.

If 10 or more accounts from the same domain cannot be queried
due to a connection error (such as missing DNS records) then
the domain is considered dead, and all other accounts from it
are deleted without further querying.

With the --dry-run option, no deletes will actually be carried
out.
LONG_DESC
def cull
domain_thresholds = Hash.new { |hash, key| hash[key] = 0 }
skip_threshold = 7.days.ago
culled = 0
dead_servers = []
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''

Account.remote.where(protocol: :activitypub).partitioned.find_each do |account|
next if account.updated_at >= skip_threshold || account.last_webfingered_at >= skip_threshold

unless dead_servers.include?(account.domain)
begin
code = Request.new(:head, account.uri).perform(&:code)
rescue HTTP::ConnectionError
domain_thresholds[account.domain] += 1

if domain_thresholds[account.domain] >= 10
dead_servers << account.domain
end
rescue StandardError
next
end
end

if [404, 410].include?(code) || dead_servers.include?(account.domain)
unless options[:dry_run]
SuspendAccountService.new.call(account)
account.destroy
end

culled += 1
say('.', :green, false)
else
say('.', nil, false)
end
end

say
say("Removed #{culled} accounts (#{dead_servers.size} dead servers)#{dry_run}", :green)

unless dead_servers.empty?
say('R.I.P.:', :yellow)
dead_servers.each { |domain| say(' ' + domain) }
end
end

private

def rotate_keys_for_account(account, delay = 0)
Expand Down
1 change: 1 addition & 0 deletions lib/mastodon/cli_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@

Rails.logger = dev_null
ActiveRecord::Base.logger = dev_null
ActiveJob::Base.logger = dev_null
HttpLog.configuration.logger = dev_null
Paperclip.options[:log] = false