Move more tasks to tootctl (#8675)
* Move more tasks to tootctl - tootctl feeds build - tootctl feeds clear - tootctl accounts refresh Clean up exit codes and help messages * Move user modifying to tootctl * Improve user modification through CLI, rename commands add -> create mod -> modify del -> delete To remove ambiguity * Fix code style issues * Fix not being able to unset admin/mod role
This commit is contained in:
parent
1d8b693d31
commit
6a3f9b7e53
7 changed files with 205 additions and 387 deletions
|
@ -40,6 +40,7 @@ module Mastodon
|
|||
say('OK', :green)
|
||||
else
|
||||
say('No account(s) given', :red)
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -48,7 +49,7 @@ module Mastodon
|
|||
option :role, default: 'user'
|
||||
option :reattach, type: :boolean
|
||||
option :force, type: :boolean
|
||||
desc 'add USERNAME', 'Create a new user'
|
||||
desc 'create 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.
|
||||
|
@ -65,7 +66,7 @@ module Mastodon
|
|||
the --force option to delete the old record and reattach the
|
||||
username to the new account anyway.
|
||||
LONG_DESC
|
||||
def add(username)
|
||||
def create(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)
|
||||
|
@ -98,19 +99,75 @@ module Mastodon
|
|||
say(key)
|
||||
say(' ' + error, :red)
|
||||
end
|
||||
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'del USERNAME', 'Delete a user'
|
||||
option :role
|
||||
option :email
|
||||
option :confirm, type: :boolean
|
||||
option :enable, type: :boolean
|
||||
option :disable, type: :boolean
|
||||
option :disable_2fa, type: :boolean
|
||||
desc 'modify USERNAME', 'Modify a user'
|
||||
long_desc <<-LONG_DESC
|
||||
Modify a user account.
|
||||
|
||||
With the --role option, update the user's role to one of "user",
|
||||
"moderator" or "admin".
|
||||
|
||||
With the --email option, update the user's e-mail address. With
|
||||
the --confirm option, mark the user's e-mail as confirmed.
|
||||
|
||||
With the --disable option, lock the user out of their account. The
|
||||
--enable option is the opposite.
|
||||
|
||||
With the --disable-2fa option, the two-factor authentication
|
||||
requirement for the user can be removed.
|
||||
LONG_DESC
|
||||
def modify(username)
|
||||
user = Account.find_local(username)&.user
|
||||
|
||||
if user.nil?
|
||||
say('No user with such username', :red)
|
||||
exit(1)
|
||||
end
|
||||
|
||||
if options[:role]
|
||||
user.admin = options[:role] == 'admin'
|
||||
user.moderator = options[:role] == 'moderator'
|
||||
end
|
||||
|
||||
user.email = options[:email] if options[:email]
|
||||
user.disabled = false if options[:enable]
|
||||
user.disabled = true if options[:disable]
|
||||
user.otp_required_for_login = false if options[:disable_2fa]
|
||||
user.confirm if options[:confirm]
|
||||
|
||||
if user.save
|
||||
say('OK', :green)
|
||||
else
|
||||
user.errors.to_h.each do |key, error|
|
||||
say('Failure/Error: ', :red)
|
||||
say(key)
|
||||
say(' ' + error, :red)
|
||||
end
|
||||
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'delete USERNAME', 'Delete a user'
|
||||
long_desc <<-LONG_DESC
|
||||
Remove a user account with a given USERNAME.
|
||||
LONG_DESC
|
||||
def del(username)
|
||||
def delete(username)
|
||||
account = Account.find_local(username)
|
||||
|
||||
if account.nil?
|
||||
say('No user with such username', :red)
|
||||
return
|
||||
exit(1)
|
||||
end
|
||||
|
||||
say("Deleting user with #{account.statuses_count}, this might take a while...")
|
||||
|
@ -182,9 +239,56 @@ module Mastodon
|
|||
end
|
||||
end
|
||||
|
||||
option :all, type: :boolean
|
||||
option :domain
|
||||
desc 'refresh [USERNAME]', 'Fetch remote user data and files'
|
||||
long_desc <<-LONG_DESC
|
||||
Fetch remote user data and files for one or multiple accounts.
|
||||
|
||||
With the --all option, all remote accounts will be processed.
|
||||
Through the --domain option, this can be narrowed down to a
|
||||
specific domain only. Otherwise, a single remote account must
|
||||
be specified with USERNAME.
|
||||
|
||||
All processing is done in the background through Sidekiq.
|
||||
LONG_DESC
|
||||
def refresh(username = nil)
|
||||
if options[:domain] || options[:all]
|
||||
queued = 0
|
||||
scope = Account.remote
|
||||
scope = scope.where(domain: options[:domain]) if options[:domain]
|
||||
|
||||
scope.select(:id).reorder(nil).find_in_batches do |accounts|
|
||||
Maintenance::RedownloadAccountMediaWorker.push_bulk(accounts.map(&:id))
|
||||
queued += accounts.size
|
||||
end
|
||||
|
||||
say("Scheduled refreshment of #{queued} accounts", :green, true)
|
||||
elsif username.present?
|
||||
username, domain = username.split('@')
|
||||
account = Account.find_remote(username, domain)
|
||||
|
||||
if account.nil?
|
||||
say('No such account', :red)
|
||||
exit(1)
|
||||
end
|
||||
|
||||
Maintenance::RedownloadAccountMediaWorker.perform_async(account.id)
|
||||
say('OK', :green)
|
||||
else
|
||||
say('No account(s) given', :red)
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rotate_keys_for_account(account, delay = 0)
|
||||
if account.nil?
|
||||
say('No such account', :red)
|
||||
exit(1)
|
||||
end
|
||||
|
||||
old_key = account.private_key
|
||||
new_key = OpenSSL::PKey::RSA.new(2048).to_pem
|
||||
account.update(private_key: new_key)
|
||||
|
|
|
@ -5,8 +5,6 @@ require_relative '../../config/boot'
|
|||
require_relative '../../config/environment'
|
||||
require_relative 'cli_helper'
|
||||
|
||||
# rubocop:disable Rails/Output
|
||||
|
||||
module Mastodon
|
||||
class EmojiCLI < Thor
|
||||
option :prefix
|
||||
|
@ -77,5 +75,3 @@ module Mastodon
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Rails/Output
|
||||
|
|
81
lib/mastodon/feeds_cli.rb
Normal file
81
lib/mastodon/feeds_cli.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../config/boot'
|
||||
require_relative '../../config/environment'
|
||||
require_relative 'cli_helper'
|
||||
|
||||
module Mastodon
|
||||
class FeedsCLI < Thor
|
||||
option :all, type: :boolean, default: false
|
||||
option :background, type: :boolean, default: false
|
||||
option :dry_run, type: :boolean, default: false
|
||||
option :verbose, type: :boolean, default: false
|
||||
desc 'build [USERNAME]', 'Build home and list feeds for one or all users'
|
||||
long_desc <<-LONG_DESC
|
||||
Build home and list feeds that are stored in Redis from the database.
|
||||
|
||||
With the --all option, all active users will be processed.
|
||||
Otherwise, a single user specified by USERNAME.
|
||||
|
||||
With the --background option, regeneration will be queued into Sidekiq,
|
||||
and the command will exit as soon as possible.
|
||||
|
||||
With the --dry-run option, no work will be done.
|
||||
|
||||
With the --verbose option, when accounts are processed sequentially in the
|
||||
foreground, the IDs of the accounts will be printed.
|
||||
LONG_DESC
|
||||
def build(username = nil)
|
||||
dry_run = options[:dry_run] ? '(DRY RUN)' : ''
|
||||
|
||||
if options[:all] || username.nil?
|
||||
processed = 0
|
||||
queued = 0
|
||||
|
||||
User.active.select(:id, :account_id).reorder(nil).find_in_batches do |users|
|
||||
if options[:background]
|
||||
RegenerationWorker.push_bulk(users.map(&:account_id)) unless options[:dry_run]
|
||||
queued += users.size
|
||||
else
|
||||
users.each do |user|
|
||||
RegenerationWorker.new.perform(user.account_id) unless options[:dry_run]
|
||||
options[:verbose] ? say(user.account_id) : say('.', :green, false)
|
||||
processed += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if options[:background]
|
||||
say("Scheduled feed regeneration for #{queued} accounts #{dry_run}", :green, true)
|
||||
else
|
||||
say
|
||||
say("Regenerated feeds for #{processed} accounts #{dry_run}", :green, true)
|
||||
end
|
||||
elsif username.present?
|
||||
account = Account.find_local(username)
|
||||
|
||||
if options[:background]
|
||||
RegenerationWorker.perform_async(account.id) unless options[:dry_run]
|
||||
else
|
||||
RegenerationWorker.new.perform(account.id) unless options[:dry_run]
|
||||
end
|
||||
|
||||
say("OK #{dry_run}", :green, true)
|
||||
else
|
||||
say('No account(s) given', :red)
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
desc 'clear', 'Remove all home and list feeds from Redis'
|
||||
def clear
|
||||
keys = Redis.current.keys('feed:*')
|
||||
|
||||
Redis.current.pipelined do
|
||||
keys.each { |key| Redis.current.del(key) }
|
||||
end
|
||||
|
||||
say('OK', :green)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,8 +4,6 @@ require_relative '../../config/boot'
|
|||
require_relative '../../config/environment'
|
||||
require_relative 'cli_helper'
|
||||
|
||||
# rubocop:disable Rails/Output
|
||||
|
||||
module Mastodon
|
||||
class MediaCLI < Thor
|
||||
option :days, type: :numeric, default: 7
|
||||
|
@ -25,9 +23,10 @@ module Mastodon
|
|||
it may impact other operations of the Mastodon server, and it may overload
|
||||
the underlying file storage.
|
||||
|
||||
With the --verbose option, output deleting file ID to console (only when --background false).
|
||||
With the --dry-run option, no work will be done.
|
||||
|
||||
With the --dry-run option, output the number of files to delete without deleting.
|
||||
With the --verbose option, when media attachments are processed sequentially in the
|
||||
foreground, the IDs of the media attachments will be printed.
|
||||
DESC
|
||||
def remove
|
||||
time_ago = options[:days].days.ago
|
||||
|
@ -53,12 +52,10 @@ module Mastodon
|
|||
say
|
||||
|
||||
if options[:background]
|
||||
say("Scheduled the deletion of #{queued} media attachments #{dry_run}.", :green)
|
||||
say("Scheduled the deletion of #{queued} media attachments #{dry_run}", :green, true)
|
||||
else
|
||||
say("Removed #{processed} media attachments #{dry_run}.", :green)
|
||||
say("Removed #{processed} media attachments #{dry_run}", :green, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Rails/Output
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue