diff options
Diffstat (limited to 'lib/mix')
20 files changed, 572 insertions, 421 deletions
| diff --git a/lib/mix/tasks/deactivate_user.ex b/lib/mix/tasks/deactivate_user.ex deleted file mode 100644 index e71ed1ec0..000000000 --- a/lib/mix/tasks/deactivate_user.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Mix.Tasks.DeactivateUser do -  use Mix.Task -  alias Pleroma.User - -  @moduledoc """ -  Deactivates a user (local or remote) - -  Usage: ``mix deactivate_user <nickname>`` - -  Example: ``mix deactivate_user lain`` -  """ -  def run([nickname]) do -    Mix.Task.run("app.start") - -    with user <- User.get_by_nickname(nickname) do -      User.deactivate(user) -    end -  end -end diff --git a/lib/mix/tasks/generate_config.ex b/lib/mix/tasks/generate_config.ex deleted file mode 100644 index e3cbbf131..000000000 --- a/lib/mix/tasks/generate_config.ex +++ /dev/null @@ -1,47 +0,0 @@ -defmodule Mix.Tasks.GenerateConfig do -  use Mix.Task - -  @moduledoc """ -  Generate a new config - -  ## Usage -  ``mix generate_config`` - -  This mix task is interactive, and will overwrite the config present at ``config/generated_config.exs``. -  """ - -  def run(_) do -    IO.puts("Answer a few questions to generate a new config\n") -    IO.puts("--- THIS WILL OVERWRITE YOUR config/generated_config.exs! ---\n") -    domain = IO.gets("What is your domain name? (e.g. pleroma.soykaf.com): ") |> String.trim() -    name = IO.gets("What is the name of your instance? (e.g. Pleroma/Soykaf): ") |> String.trim() -    email = IO.gets("What's your admin email address: ") |> String.trim() - -    secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) -    dbpass = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) - -    resultSql = EEx.eval_file("lib/mix/tasks/sample_psql.eex", dbpass: dbpass) - -    result = -      EEx.eval_file( -        "lib/mix/tasks/sample_config.eex", -        domain: domain, -        email: email, -        name: name, -        secret: secret, -        dbpass: dbpass -      ) - -    IO.puts( -      "\nWriting config to config/generated_config.exs.\n\nCheck it and configure your database, then copy it to either config/dev.secret.exs or config/prod.secret.exs" -    ) - -    File.write("config/generated_config.exs", result) - -    IO.puts( -      "\nWriting setup_db.psql, please run it as postgre superuser, i.e.: sudo su postgres -c 'psql -f config/setup_db.psql'" -    ) - -    File.write("config/setup_db.psql", resultSql) -  end -end diff --git a/lib/mix/tasks/generate_invite_token.ex b/lib/mix/tasks/generate_invite_token.ex deleted file mode 100644 index 418ef3790..000000000 --- a/lib/mix/tasks/generate_invite_token.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Mix.Tasks.GenerateInviteToken do -  use Mix.Task - -  @moduledoc """ -  Generates invite token - -  This is in the form of a URL to be used by the Invited user to register themselves. - -  ## Usage -  ``mix generate_invite_token`` -  """ -  def run([]) do -    Mix.Task.run("app.start") - -    with {:ok, token} <- Pleroma.UserInviteToken.create_token() do -      IO.puts("Generated user invite token") - -      IO.puts( -        "Url: #{ -          Pleroma.Web.Router.Helpers.redirect_url( -            Pleroma.Web.Endpoint, -            :registration_page, -            token.token -          ) -        }" -      ) -    else -      _ -> -        IO.puts("Error creating token") -    end -  end -end diff --git a/lib/mix/tasks/generate_password_reset.ex b/lib/mix/tasks/generate_password_reset.ex deleted file mode 100644 index f7f4c4f59..000000000 --- a/lib/mix/tasks/generate_password_reset.ex +++ /dev/null @@ -1,33 +0,0 @@ -defmodule Mix.Tasks.GeneratePasswordReset do -  use Mix.Task -  alias Pleroma.User - -  @moduledoc """ -  Generate password reset link for user - -  Usage: ``mix generate_password_reset <nickname>`` - -  Example: ``mix generate_password_reset lain`` -  """ -  def run([nickname]) do -    Mix.Task.run("app.start") - -    with %User{local: true} = user <- User.get_by_nickname(nickname), -         {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do -      IO.puts("Generated password reset token for #{user.nickname}") - -      IO.puts( -        "Url: #{ -          Pleroma.Web.Router.Helpers.util_url( -            Pleroma.Web.Endpoint, -            :show_password_reset, -            token.token -          ) -        }" -      ) -    else -      _ -> -        IO.puts("No local user #{nickname}") -    end -  end -end diff --git a/lib/mix/tasks/make_moderator.ex b/lib/mix/tasks/make_moderator.ex deleted file mode 100644 index 15586dc30..000000000 --- a/lib/mix/tasks/make_moderator.ex +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Mix.Tasks.SetModerator do -  @moduledoc """ -  Set moderator to a local user - -  Usage: ``mix set_moderator <nickname>`` - -  Example: ``mix set_moderator lain`` -  """ - -  use Mix.Task -  import Mix.Ecto -  alias Pleroma.{Repo, User} - -  def run([nickname | rest]) do -    Application.ensure_all_started(:pleroma) - -    moderator = -      case rest do -        [moderator] -> moderator == "true" -        _ -> true -      end - -    with %User{local: true} = user <- User.get_by_nickname(nickname) do -      info = -        user.info -        |> Map.put("is_moderator", !!moderator) - -      cng = User.info_changeset(user, %{info: info}) -      {:ok, user} = User.update_and_set_cache(cng) - -      IO.puts("Moderator status of #{nickname}: #{user.info["is_moderator"]}") -    else -      _ -> -        IO.puts("No local user #{nickname}") -    end -  end -end diff --git a/lib/mix/tasks/pleroma/common.ex b/lib/mix/tasks/pleroma/common.ex new file mode 100644 index 000000000..36432c291 --- /dev/null +++ b/lib/mix/tasks/pleroma/common.ex @@ -0,0 +1,24 @@ +defmodule Mix.Tasks.Pleroma.Common do +  @doc "Common functions to be reused in mix tasks" +  def start_pleroma do +    Mix.Task.run("app.start") +  end + +  def get_option(options, opt, prompt, defval \\ nil, defname \\ nil) do +    Keyword.get(options, opt) || +      case Mix.shell().prompt("#{prompt} [#{defname || defval}]") do +        "\n" -> +          case defval do +            nil -> get_option(options, opt, prompt, defval) +            defval -> defval +          end + +        opt -> +          opt |> String.trim() +      end +  end + +  def escape_sh_path(path) do +    ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') +  end +end diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex new file mode 100644 index 000000000..02e1ce27d --- /dev/null +++ b/lib/mix/tasks/pleroma/instance.ex @@ -0,0 +1,160 @@ +defmodule Mix.Tasks.Pleroma.Instance do +  use Mix.Task +  alias Mix.Tasks.Pleroma.Common + +  @shortdoc "Manages Pleroma instance" +  @moduledoc """ +  Manages Pleroma instance. + +  ## Generate a new instance config. + +    mix pleroma.instance gen [OPTION...] + +  If any options are left unspecified, you will be prompted interactively + +  ## Options + +  - `-f`, `--force` - overwrite any output files +  - `-o PATH`, `--output PATH` - the output file for the generated configuration +  - `--output-psql PATH` - the output file for the generated PostgreSQL setup +  - `--domain DOMAIN` - the domain of your instance +  - `--instance-name INSTANCE_NAME` - the name of your instance +  - `--admin-email ADMIN_EMAIL` - the email address of the instance admin +  - `--dbhost HOSTNAME` - the hostname of the PostgreSQL database to use +  - `--dbname DBNAME` - the name of the database to use +  - `--dbuser DBUSER` - the user (aka role) to use for the database connection +  - `--dbpass DBPASS` - the password to use for the database connection +  """ + +  def run(["gen" | rest]) do +    {options, [], []} = +      OptionParser.parse( +        rest, +        strict: [ +          force: :boolean, +          output: :string, +          output_psql: :string, +          domain: :string, +          instance_name: :string, +          admin_email: :string, +          dbhost: :string, +          dbname: :string, +          dbuser: :string, +          dbpass: :string +        ], +        aliases: [ +          o: :output, +          f: :force +        ] +      ) + +    paths = +      [config_path, psql_path] = [ +        Keyword.get(options, :output, "config/generated_config.exs"), +        Keyword.get(options, :output_psql, "config/setup_db.psql") +      ] + +    will_overwrite = Enum.filter(paths, &File.exists?/1) +    proceed? = Enum.empty?(will_overwrite) or Keyword.get(options, :force, false) + +    unless not proceed? do +      [domain, port | _] = +        String.split( +          Common.get_option( +            options, +            :domain, +            "What domain will your instance use? (e.g pleroma.soykaf.com)" +          ), +          ":" +        ) ++ [443] + +      name = +        Common.get_option( +          options, +          :name, +          "What is the name of your instance? (e.g. Pleroma/Soykaf)" +        ) + +      email = Common.get_option(options, :admin_email, "What is your admin email address?") + +      dbhost = +        Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost") + +      dbname = +        Common.get_option(options, :dbname, "What is the name of your database?", "pleroma_dev") + +      dbuser = +        Common.get_option( +          options, +          :dbuser, +          "What is the user used to connect to your database?", +          "pleroma" +        ) + +      dbpass = +        Common.get_option( +          options, +          :dbpass, +          "What is the password used to connect to your database?", +          :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64), +          "autogenerated" +        ) + +      secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) +      {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1) + +      result_config = +        EEx.eval_file( +          "sample_config.eex" |> Path.expand(__DIR__), +          domain: domain, +          port: port, +          email: email, +          name: name, +          dbhost: dbhost, +          dbname: dbname, +          dbuser: dbuser, +          dbpass: dbpass, +          version: Pleroma.Mixfile.project() |> Keyword.get(:version), +          secret: secret, +          web_push_public_key: Base.url_encode64(web_push_public_key, padding: false), +          web_push_private_key: Base.url_encode64(web_push_private_key, padding: false) +        ) + +      result_psql = +        EEx.eval_file( +          "sample_psql.eex" |> Path.expand(__DIR__), +          dbname: dbname, +          dbuser: dbuser, +          dbpass: dbpass +        ) + +      Mix.shell().info( +        "Writing config to #{config_path}. You should rename it to config/prod.secret.exs or config/dev.secret.exs." +      ) + +      File.write(config_path, result_config) +      Mix.shell().info("Writing #{psql_path}.") +      File.write(psql_path, result_psql) + +      Mix.shell().info( +        "\n" <> +          """ +          To get started: +          1. Verify the contents of the generated files. +          2. Run `sudo -u postgres psql -f #{Common.escape_sh_path(psql_path)}`. +          """ <> +          if config_path in ["config/dev.secret.exs", "config/prod.secret.exs"] do +            "" +          else +            "3. Run `mv #{Common.escape_sh_path(config_path)} 'config/prod.secret.exs'`." +          end +      ) +    else +      Mix.shell().error( +        "The task would have overwritten the following files:\n" <> +          (Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <> +          "Rerun with `--force` to overwrite them." +      ) +    end +  end +end diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex new file mode 100644 index 000000000..03586d6c3 --- /dev/null +++ b/lib/mix/tasks/pleroma/relay.ex @@ -0,0 +1,43 @@ +defmodule Mix.Tasks.Pleroma.Relay do +  use Mix.Task +  alias Pleroma.Web.ActivityPub.Relay +  alias Mix.Tasks.Pleroma.Common + +  @shortdoc "Manages remote relays" +  @moduledoc """ +  Manages remote relays + +  ## Follow a remote relay + +  ``mix pleroma.relay follow <relay_url>`` + +  Example: ``mix pleroma.relay follow  https://example.org/relay`` + +  ## Unfollow a remote relay + +  ``mix pleroma.relay unfollow <relay_url>`` + +  Example: ``mix pleroma.relay unfollow https://example.org/relay`` +  """ +  def run(["follow", target]) do +    Common.start_pleroma() + +    with {:ok, _activity} <- Relay.follow(target) do +      # put this task to sleep to allow the genserver to push out the messages +      :timer.sleep(500) +    else +      {:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}") +    end +  end + +  def run(["unfollow", target]) do +    Common.start_pleroma() + +    with {:ok, _activity} <- Relay.unfollow(target) do +      # put this task to sleep to allow the genserver to push out the messages +      :timer.sleep(500) +    else +      {:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}") +    end +  end +end diff --git a/lib/mix/tasks/sample_config.eex b/lib/mix/tasks/pleroma/sample_config.eex index 462c34636..740b9f8d1 100644 --- a/lib/mix/tasks/sample_config.eex +++ b/lib/mix/tasks/pleroma/sample_config.eex @@ -1,7 +1,12 @@ +# Pleroma instance configuration + +# NOTE: This file should not be committed to a repo or otherwise made public +# without removing sensitive information. +  use Mix.Config  config :pleroma, Pleroma.Web.Endpoint, -   url: [host: "<%= domain %>", scheme: "https", port: 443], +   url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],     secret_key_base: "<%= secret %>"  config :pleroma, :instance, @@ -16,15 +21,20 @@ config :pleroma, :media_proxy,    redirect_on_failure: true    #base_url: "https://cache.pleroma.social" -# Configure your database  config :pleroma, Pleroma.Repo,    adapter: Ecto.Adapters.Postgres, -  username: "pleroma", +  username: "<%= dbuser %>",    password: "<%= dbpass %>", -  database: "pleroma_dev", -  hostname: "localhost", +  database: "<%= dbname %>", +  hostname: "<%= dbhost %>",    pool_size: 10 +# Configure web push notifications +config :web_push_encryption, :vapid_details, +  subject: "mailto:<%= email %>", +  public_key: "<%= web_push_public_key %>", +  private_key: "<%= web_push_private_key %>" +  # Enable Strict-Transport-Security once SSL is working:  # config :pleroma, :http_security,  #   sts: true @@ -50,9 +60,9 @@ config :pleroma, Pleroma.Repo,  # Configure Openstack Swift support if desired. -#  -# Many openstack deployments are different, so config is left very open with  -# no assumptions made on which provider you're using. This should allow very  +# +# Many openstack deployments are different, so config is left very open with +# no assumptions made on which provider you're using. This should allow very  # wide support without needing separate handlers for OVH, Rackspace, etc.  #  # config :pleroma, Pleroma.Uploaders.Swift, diff --git a/lib/mix/tasks/sample_psql.eex b/lib/mix/tasks/pleroma/sample_psql.eex index c89b34ef2..f0ac05e57 100644 --- a/lib/mix/tasks/sample_psql.eex +++ b/lib/mix/tasks/pleroma/sample_psql.eex @@ -1,6 +1,6 @@ -CREATE USER pleroma WITH ENCRYPTED PASSWORD '<%= dbpass %>'; -CREATE DATABASE pleroma_dev OWNER pleroma; -\c pleroma_dev; +CREATE USER <%= dbuser %> WITH ENCRYPTED PASSWORD '<%= dbpass %>'; +CREATE DATABASE <%= dbname %> OWNER <%= dbuser %>; +\c <%= dbname %>;  --Extensions made by ecto.migrate that need superuser access  CREATE EXTENSION IF NOT EXISTS citext;  CREATE EXTENSION IF NOT EXISTS pg_trgm; diff --git a/lib/mix/tasks/migrate_local_uploads.ex b/lib/mix/tasks/pleroma/uploads.ex index 8f9e210c0..63299b2ae 100644 --- a/lib/mix/tasks/migrate_local_uploads.ex +++ b/lib/mix/tasks/pleroma/uploads.ex @@ -1,16 +1,26 @@ -defmodule Mix.Tasks.MigrateLocalUploads do +defmodule Mix.Tasks.Pleroma.Uploads do    use Mix.Task -  import Mix.Ecto -  alias Pleroma.{Upload, Uploaders.Local, Uploaders.S3} +  alias Pleroma.{Upload, Uploaders.Local} +  alias Mix.Tasks.Pleroma.Common    require Logger    @log_every 50 -  @shortdoc "Migrate uploads from local to remote storage" -  def run([target_uploader | args]) do -    delete? = Enum.member?(args, "--delete") -    Application.ensure_all_started(:pleroma) +  @shortdoc "Migrates uploads from local to remote storage" +  @moduledoc """ +   Manages uploads + +   ## Migrate uploads from local to remote storage +       mix pleroma.uploads migrate_local TARGET_UPLOADER [OPTIONS...] +   Options: +   - `--delete` - delete local uploads after migrating them to the target uploader + +   A list of avalible uploaders can be seen in config.exs +  """ +  def run(["migrate_local", target_uploader | args]) do +    delete? = Enum.member?(args, "--delete") +    Common.start_pleroma()      local_path = Pleroma.Config.get!([Local, :uploads])      uploader = Module.concat(Pleroma.Uploaders, target_uploader) @@ -24,10 +34,10 @@ defmodule Mix.Tasks.MigrateLocalUploads do        Pleroma.Config.put([Upload, :uploader], uploader)      end -    Logger.info("Migrating files from local #{local_path} to #{to_string(uploader)}") +    Mix.shell().info("Migrating files from local #{local_path} to #{to_string(uploader)}")      if delete? do -      Logger.warn( +      Mix.shell().info(          "Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)"        ) @@ -54,7 +64,7 @@ defmodule Mix.Tasks.MigrateLocalUploads do            File.exists?(root_path) ->              file = Path.basename(id) -            [hash, ext] = String.split(id, ".") +            hash = Path.rootname(id)              {%Pleroma.Upload{id: hash, name: file, path: file, tempfile: root_path}, root_path}            true -> @@ -64,7 +74,7 @@ defmodule Mix.Tasks.MigrateLocalUploads do        |> Enum.filter(& &1)      total_count = length(uploads) -    Logger.info("Found #{total_count} uploads") +    Mix.shell().info("Found #{total_count} uploads")      uploads      |> Task.async_stream( @@ -76,7 +86,7 @@ defmodule Mix.Tasks.MigrateLocalUploads do              :ok            error -> -            Logger.error("failed to upload #{inspect(upload.path)}: #{inspect(error)}") +            Mix.shell().error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")          end        end,        timeout: 150_000 @@ -84,14 +94,10 @@ defmodule Mix.Tasks.MigrateLocalUploads do      |> Stream.chunk_every(@log_every)      |> Enum.reduce(0, fn done, count ->        count = count + length(done) -      Logger.info("Uploaded #{count}/#{total_count} files") +      Mix.shell().info("Uploaded #{count}/#{total_count} files")        count      end) -    Logger.info("Done!") -  end - -  def run(_) do -    Logger.error("Usage: migrate_local_uploads S3|Swift [--delete]") +    Mix.shell().info("Done!")    end  end diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex new file mode 100644 index 000000000..3d30e3a81 --- /dev/null +++ b/lib/mix/tasks/pleroma/user.ex @@ -0,0 +1,300 @@ +defmodule Mix.Tasks.Pleroma.User do +  use Mix.Task +  import Ecto.Changeset +  alias Pleroma.{Repo, User} +  alias Mix.Tasks.Pleroma.Common + +  @shortdoc "Manages Pleroma users" +  @moduledoc """ +  Manages Pleroma users. + +  ## Create a new user. + +      mix pleroma.user new NICKNAME EMAIL [OPTION...] + +  Options: +  - `--name NAME` - the user's name (i.e., "Lain Iwakura") +  - `--bio BIO` - the user's bio +  - `--password PASSWORD` - the user's password +  - `--moderator`/`--no-moderator` - whether the user is a moderator +  - `--admin`/`--no-admin` - whether the user is an admin + +  ## Generate an invite link. + +      mix pleroma.user invite + +  ## Delete the user's account. + +      mix pleroma.user rm NICKNAME + +  ## Deactivate or activate the user's account. + +      mix pleroma.user toggle_activated NICKNAME + +  ## Unsubscribe local users from user's account and deactivate it + +      mix pleroma.user unsubscribe NICKNAME + +  ## Create a password reset link. + +      mix pleroma.user reset_password NICKNAME + +  ## Set the value of the given user's settings. + +      mix pleroma.user set NICKNAME [OPTION...] + +  Options: +  - `--locked`/`--no-locked` - whether the user's account is locked +  - `--moderator`/`--no-moderator` - whether the user is a moderator +  - `--admin`/`--no-admin` - whether the user is an admin +  """ +  def run(["new", nickname, email | rest]) do +    {options, [], []} = +      OptionParser.parse( +        rest, +        strict: [ +          name: :string, +          bio: :string, +          password: :string, +          moderator: :boolean, +          admin: :boolean +        ] +      ) + +    name = Keyword.get(options, :name, nickname) +    bio = Keyword.get(options, :bio, "") + +    {password, generated_password?} = +      case Keyword.get(options, :password) do +        nil -> +          {:crypto.strong_rand_bytes(16) |> Base.encode64(), true} + +        password -> +          {password, false} +      end + +    moderator? = Keyword.get(options, :moderator, false) +    admin? = Keyword.get(options, :admin, false) + +    Mix.shell().info(""" +    A user will be created with the following information: +      - nickname: #{nickname} +      - email: #{email} +      - password: #{ +      if(generated_password?, do: "[generated; a reset link will be created]", else: password) +    } +      - name: #{name} +      - bio: #{bio} +      - moderator: #{if(moderator?, do: "true", else: "false")} +      - admin: #{if(admin?, do: "true", else: "false")} +    """) + +    proceed? = Mix.shell().yes?("Continue?") + +    unless not proceed? do +      Common.start_pleroma() + +      params = %{ +        nickname: nickname, +        email: email, +        password: password, +        password_confirmation: password, +        name: name, +        bio: bio +      } + +      user = User.register_changeset(%User{}, params) +      Repo.insert!(user) + +      Mix.shell().info("User #{nickname} created") + +      if moderator? do +        run(["set", nickname, "--moderator"]) +      end + +      if admin? do +        run(["set", nickname, "--admin"]) +      end + +      if generated_password? do +        run(["reset_password", nickname]) +      end +    else +      Mix.shell().info("User will not be created.") +    end +  end + +  def run(["rm", nickname]) do +    Common.start_pleroma() + +    with %User{local: true} = user <- User.get_by_nickname(nickname) do +      User.delete(user) +      Mix.shell().info("User #{nickname} deleted.") +    else +      _ -> +        Mix.shell().error("No local user #{nickname}") +    end +  end + +  def run(["toggle_activated", nickname]) do +    Common.start_pleroma() + +    with %User{} = user <- User.get_by_nickname(nickname) do +      {:ok, user} = User.deactivate(user, !user.info.deactivated) + +      Mix.shell().info( +        "Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated" +      ) +    else +      _ -> +        Mix.shell().error("No user #{nickname}") +    end +  end + +  def run(["reset_password", nickname]) do +    Common.start_pleroma() + +    with %User{local: true} = user <- User.get_by_nickname(nickname), +         {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do +      Mix.shell().info("Generated password reset token for #{user.nickname}") + +      IO.puts( +        "URL: #{ +          Pleroma.Web.Router.Helpers.util_url( +            Pleroma.Web.Endpoint, +            :show_password_reset, +            token.token +          ) +        }" +      ) +    else +      _ -> +        Mix.shell().error("No local user #{nickname}") +    end +  end + +  def run(["unsubscribe", nickname]) do +    Common.start_pleroma() + +    with %User{} = user <- User.get_by_nickname(nickname) do +      Mix.shell().info("Deactivating #{user.nickname}") +      User.deactivate(user) + +      {:ok, friends} = User.get_friends(user) + +      Enum.each(friends, fn friend -> +        user = Repo.get(User, user.id) + +        Mix.shell().info("Unsubscribing #{friend.nickname} from #{user.nickname}") +        User.unfollow(user, friend) +      end) + +      :timer.sleep(500) + +      user = Repo.get(User, user.id) + +      if length(user.following) == 0 do +        Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}") +      end +    else +      _ -> +        Mix.shell().error("No user #{nickname}") +    end +  end + +  def run(["set", nickname | rest]) do +    Common.start_pleroma() + +    {options, [], []} = +      OptionParser.parse( +        rest, +        strict: [ +          moderator: :boolean, +          admin: :boolean, +          locked: :boolean +        ] +      ) + +    with %User{local: true} = user <- User.get_by_nickname(nickname) do +      user = +        case Keyword.get(options, :moderator) do +          nil -> user +          value -> set_moderator(user, value) +        end + +      user = +        case Keyword.get(options, :locked) do +          nil -> user +          value -> set_locked(user, value) +        end + +      _user = +        case Keyword.get(options, :admin) do +          nil -> user +          value -> set_admin(user, value) +        end +    else +      _ -> +        Mix.shell().error("No local user #{nickname}") +    end +  end + +  def run(["invite"]) do +    Common.start_pleroma() + +    with {:ok, token} <- Pleroma.UserInviteToken.create_token() do +      Mix.shell().info("Generated user invite token") + +      url = +        Pleroma.Web.Router.Helpers.redirect_url( +          Pleroma.Web.Endpoint, +          :registration_page, +          token.token +        ) + +      IO.puts(url) +    else +      _ -> +        Mix.shell().error("Could not create invite token.") +    end +  end + +  defp set_moderator(user, value) do +    info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value}) + +    user_cng = +      Ecto.Changeset.change(user) +      |> put_embed(:info, info_cng) + +    {:ok, user} = User.update_and_set_cache(user_cng) + +    Mix.shell().info("Moderator status of #{user.nickname}: #{user.info.is_moderator}") +    user +  end + +  defp set_admin(user, value) do +    info_cng = User.Info.admin_api_update(user.info, %{is_admin: value}) + +    user_cng = +      Ecto.Changeset.change(user) +      |> put_embed(:info, info_cng) + +    {:ok, user} = User.update_and_set_cache(user_cng) + +    Mix.shell().info("Admin status of #{user.nickname}: #{user.info.is_admin}") +    user +  end + +  defp set_locked(user, value) do +    info_cng = User.Info.user_upgrade(user.info, %{locked: value}) + +    user_cng = +      Ecto.Changeset.change(user) +      |> put_embed(:info, info_cng) + +    {:ok, user} = User.update_and_set_cache(user_cng) + +    Mix.shell().info("Locked status of #{user.nickname}: #{user.info.locked}") +    user +  end +end diff --git a/lib/mix/tasks/reactivate_user.ex b/lib/mix/tasks/reactivate_user.ex deleted file mode 100644 index a30d3ac8b..000000000 --- a/lib/mix/tasks/reactivate_user.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Mix.Tasks.ReactivateUser do -  use Mix.Task -  alias Pleroma.User - -  @moduledoc """ -  Reactivate a user - -  Usage: ``mix reactivate_user <nickname>`` - -  Example: ``mix reactivate_user lain`` -  """ -  def run([nickname]) do -    Mix.Task.run("app.start") - -    with user <- User.get_by_nickname(nickname) do -      User.deactivate(user, false) -    end -  end -end diff --git a/lib/mix/tasks/register_user.ex b/lib/mix/tasks/register_user.ex deleted file mode 100644 index 1f5321093..000000000 --- a/lib/mix/tasks/register_user.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule Mix.Tasks.RegisterUser do -  @moduledoc """ -  Manually register a local user - -  Usage: ``mix register_user <name> <nickname> <email> <bio> <password>`` - -  Example: ``mix register_user 仮面の告白 lain lain@example.org "blushy-crushy fediverse idol + pleroma dev" pleaseDontHeckLain`` -  """ - -  use Mix.Task -  alias Pleroma.{Repo, User} - -  @shortdoc "Register user" -  def run([name, nickname, email, bio, password]) do -    Mix.Task.run("app.start") - -    params = %{ -      name: name, -      nickname: nickname, -      email: email, -      password: password, -      password_confirmation: password, -      bio: bio -    } - -    user = User.register_changeset(%User{}, params) - -    Repo.insert!(user) -  end -end diff --git a/lib/mix/tasks/relay_follow.ex b/lib/mix/tasks/relay_follow.ex deleted file mode 100644 index 85b1c024d..000000000 --- a/lib/mix/tasks/relay_follow.ex +++ /dev/null @@ -1,24 +0,0 @@ -defmodule Mix.Tasks.RelayFollow do -  use Mix.Task -  require Logger -  alias Pleroma.Web.ActivityPub.Relay - -  @shortdoc "Follows a remote relay" -  @moduledoc """ -  Follows a remote relay - -  Usage: ``mix relay_follow <relay_url>`` - -  Example: ``mix relay_follow https://example.org/relay`` -  """ -  def run([target]) do -    Mix.Task.run("app.start") - -    with {:ok, activity} <- Relay.follow(target) do -      # put this task to sleep to allow the genserver to push out the messages -      :timer.sleep(500) -    else -      {:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}") -    end -  end -end diff --git a/lib/mix/tasks/relay_unfollow.ex b/lib/mix/tasks/relay_unfollow.ex deleted file mode 100644 index 237fb771c..000000000 --- a/lib/mix/tasks/relay_unfollow.ex +++ /dev/null @@ -1,23 +0,0 @@ -defmodule Mix.Tasks.RelayUnfollow do -  use Mix.Task -  require Logger -  alias Pleroma.Web.ActivityPub.Relay - -  @moduledoc """ -  Unfollows a remote relay - -  Usage: ``mix relay_follow <relay_url>`` - -  Example: ``mix relay_follow https://example.org/relay`` -  """ -  def run([target]) do -    Mix.Task.run("app.start") - -    with {:ok, activity} <- Relay.follow(target) do -      # put this task to sleep to allow the genserver to push out the messages -      :timer.sleep(500) -    else -      {:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}") -    end -  end -end diff --git a/lib/mix/tasks/rm_user.ex b/lib/mix/tasks/rm_user.ex deleted file mode 100644 index 50463046c..000000000 --- a/lib/mix/tasks/rm_user.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Mix.Tasks.RmUser do -  use Mix.Task -  alias Pleroma.User - -  @moduledoc """ -  Permanently deletes a user - -  Usage: ``mix rm_user [nickname]`` - -  Example: ``mix rm_user lain`` -  """ -  def run([nickname]) do -    Mix.Task.run("app.start") - -    with %User{local: true} = user <- User.get_by_nickname(nickname) do -      {:ok, _} = User.delete(user) -    end -  end -end diff --git a/lib/mix/tasks/set_admin.ex b/lib/mix/tasks/set_admin.ex deleted file mode 100644 index d5ccf261b..000000000 --- a/lib/mix/tasks/set_admin.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Mix.Tasks.SetAdmin do -  use Mix.Task -  alias Pleroma.User - -  @doc """ -  Sets admin status -  Usage: set_admin nickname [true|false] -  """ -  def run([nickname | rest]) do -    Application.ensure_all_started(:pleroma) - -    status = -      case rest do -        [status] -> status == "true" -        _ -> true -      end - -    with %User{local: true} = user <- User.get_by_nickname(nickname) do -      info = -        user.info -        |> Map.put("is_admin", !!status) - -      cng = User.info_changeset(user, %{info: info}) -      {:ok, user} = User.update_and_set_cache(cng) - -      IO.puts("Admin status of #{nickname}: #{user.info["is_admin"]}") -    else -      _ -> -        IO.puts("No local user #{nickname}") -    end -  end -end diff --git a/lib/mix/tasks/set_locked.ex b/lib/mix/tasks/set_locked.ex deleted file mode 100644 index a154595ca..000000000 --- a/lib/mix/tasks/set_locked.ex +++ /dev/null @@ -1,39 +0,0 @@ -defmodule Mix.Tasks.SetLocked do -  @moduledoc """ -  Lock a local user - -  The local user will then have to manually accept/reject followers. This can also be done by the user into their settings. - -  Usage: ``mix set_locked <username>`` - -  Example: ``mix set_locked lain`` -  """ - -  use Mix.Task -  import Mix.Ecto -  alias Pleroma.{Repo, User} - -  def run([nickname | rest]) do -    ensure_started(Repo, []) - -    locked = -      case rest do -        [locked] -> locked == "true" -        _ -> true -      end - -    with %User{local: true} = user <- User.get_by_nickname(nickname) do -      info = -        user.info -        |> Map.put("locked", !!locked) - -      cng = User.info_changeset(user, %{info: info}) -      user = Repo.update!(cng) - -      IO.puts("locked status of #{nickname}: #{user.info["locked"]}") -    else -      _ -> -        IO.puts("No local user #{nickname}") -    end -  end -end diff --git a/lib/mix/tasks/unsubscribe_user.ex b/lib/mix/tasks/unsubscribe_user.ex deleted file mode 100644 index 62ea61a5c..000000000 --- a/lib/mix/tasks/unsubscribe_user.ex +++ /dev/null @@ -1,38 +0,0 @@ -defmodule Mix.Tasks.UnsubscribeUser do -  use Mix.Task -  alias Pleroma.{User, Repo} -  require Logger - -  @moduledoc """ -  Deactivate and Unsubscribe local users from a user - -  Usage: ``mix unsubscribe_user <nickname>`` - -  Example: ``mix unsubscribe_user lain`` -  """ -  def run([nickname]) do -    Mix.Task.run("app.start") - -    with %User{} = user <- User.get_by_nickname(nickname) do -      Logger.info("Deactivating #{user.nickname}") -      User.deactivate(user) - -      {:ok, friends} = User.get_friends(user) - -      Enum.each(friends, fn friend -> -        user = Repo.get(User, user.id) - -        Logger.info("Unsubscribing #{friend.nickname} from #{user.nickname}") -        User.unfollow(user, friend) -      end) - -      :timer.sleep(500) - -      user = Repo.get(User, user.id) - -      if length(user.following) == 0 do -        Logger.info("Successfully unsubscribed all followers from #{user.nickname}") -      end -    end -  end -end | 
