diff --git a/README.md b/README.md index c48930960..c4649237f 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ While we don't provide docker files, other people have written very good ones. T * Run `mix deps.get` to install elixir dependencies. - * Run `mix generate_config`. This will ask you a few questions about your instance and generate a configuration file in `config/generated_config.exs`. Check that and copy it to either `config/dev.secret.exs` or `config/prod.secret.exs`. It will also create a `config/setup_db.psql`; you may want to double-check this file in case you wanted a different username, or database name than the default. Then you need to run the script as PostgreSQL superuser (i.e. `sudo su postgres -c "psql -f config/setup_db.psql"`). It will create a pleroma db user, database and will setup needed extensions that need to be set up. Postgresql super-user privileges are only needed for this step. + * Run `mix pleroma.instance gen`. This will ask you questions about your instance and generate a configuration file in `config/generated_config.exs`. Check that and copy it to either `config/dev.secret.exs` or `config/prod.secret.exs`. It will also create a `config/setup_db.psql`, which you should run as the PostgreSQL superuser (i.e., `sudo -u postgres psql -f config/setup_db.psql`). It will create the database, user, and password you gave `mix pleroma.gen.instance` earlier, as well as set up the necessary extensions in the database. PostgreSQL superuser privileges are only needed for this step. * For these next steps, the default will be to run pleroma using the dev configuration file, `config/dev.secret.exs`. To run them using the prod config file, prefix each command at the shell with `MIX_ENV=prod`. For example: `MIX_ENV=prod mix phx.server`. Documentation for the config can be found at [``config/config.md``](config/config.md) @@ -45,8 +45,7 @@ While we don't provide docker files, other people have written very good ones. T * You can check if your instance is configured correctly by running it with `mix phx.server` and checking the instance info endpoint at `/api/v1/instance`. If it shows your uri, name and email correctly, you are configured correctly. If it shows something like `localhost:4000`, your configuration is probably wrong, unless you are running a local development setup. - * The common and convenient way for adding HTTPS is by using Nginx as a reverse proxy. You can look at example Nginx configuration in `installation/pleroma.nginx`. If you need TLS/SSL certificates for HTTPS, you can look get some for free with letsencrypt: https://letsencrypt.org/ - The simplest way to obtain and install a certificate is to use [Certbot.](https://certbot.eff.org) Depending on your specific setup, certbot may be able to get a certificate and configure your web server automatically. + * The common and convenient way for adding HTTPS is by using Nginx as a reverse proxy. You can look at example Nginx configuration in `installation/pleroma.nginx`. If you need TLS/SSL certificates for HTTPS, you can look get some for free with letsencrypt: . The simplest way to obtain and install a certificate is to use [Certbot.](https://certbot.eff.org) Depending on your specific setup, certbot may be able to get a certificate and configure your web server automatically. ## Running 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 `` - - 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 be085d187..000000000 --- a/lib/mix/tasks/generate_config.ex +++ /dev/null @@ -1,51 +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) - - {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1) - - result = - EEx.eval_file( - "lib/mix/tasks/sample_config.eex", - domain: domain, - email: email, - name: name, - secret: secret, - dbpass: dbpass, - 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) - ) - - 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 `` - - 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 8dc0a04dd..000000000 --- a/lib/mix/tasks/make_moderator.ex +++ /dev/null @@ -1,38 +0,0 @@ -defmodule Mix.Tasks.SetModerator do - @moduledoc """ - Set moderator to a local user - - Usage: ``mix set_moderator `` - - Example: ``mix set_moderator lain`` - """ - - use Mix.Task - import Ecto.Changeset - 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_cng = User.Info.admin_api_update(user.info, %{is_moderator: !!moderator}) - - user_cng = - Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) - - {:ok, user} = User.update_and_set_cache(user_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/migrate_local_uploads.ex b/lib/mix/tasks/migrate_local_uploads.ex deleted file mode 100644 index 8f9e210c0..000000000 --- a/lib/mix/tasks/migrate_local_uploads.ex +++ /dev/null @@ -1,97 +0,0 @@ -defmodule Mix.Tasks.MigrateLocalUploads do - use Mix.Task - import Mix.Ecto - alias Pleroma.{Upload, Uploaders.Local, Uploaders.S3} - 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) - - local_path = Pleroma.Config.get!([Local, :uploads]) - uploader = Module.concat(Pleroma.Uploaders, target_uploader) - - unless Code.ensure_loaded?(uploader) do - raise("The uploader #{inspect(uploader)} is not an existing/loaded module.") - end - - target_enabled? = Pleroma.Config.get([Upload, :uploader]) == uploader - - unless target_enabled? do - Pleroma.Config.put([Upload, :uploader], uploader) - end - - Logger.info("Migrating files from local #{local_path} to #{to_string(uploader)}") - - if delete? do - Logger.warn( - "Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)" - ) - - :timer.sleep(:timer.seconds(5)) - end - - uploads = - File.ls!(local_path) - |> Enum.map(fn id -> - root_path = Path.join(local_path, id) - - cond do - File.dir?(root_path) -> - files = for file <- File.ls!(root_path), do: {id, file, Path.join([root_path, file])} - - case List.first(files) do - {id, file, path} -> - {%Pleroma.Upload{id: id, name: file, path: id <> "/" <> file, tempfile: path}, - root_path} - - _ -> - nil - end - - File.exists?(root_path) -> - file = Path.basename(id) - [hash, ext] = String.split(id, ".") - {%Pleroma.Upload{id: hash, name: file, path: file, tempfile: root_path}, root_path} - - true -> - nil - end - end) - |> Enum.filter(& &1) - - total_count = length(uploads) - Logger.info("Found #{total_count} uploads") - - uploads - |> Task.async_stream( - fn {upload, root_path} -> - case Upload.store(upload, uploader: uploader, filters: [], size_limit: nil) do - {:ok, _} -> - if delete?, do: File.rm_rf!(root_path) - Logger.debug("uploaded: #{inspect(upload.path)} #{inspect(upload)}") - :ok - - error -> - Logger.error("failed to upload #{inspect(upload.path)}: #{inspect(error)}") - end - end, - timeout: 150_000 - ) - |> Stream.chunk_every(@log_every) - |> Enum.reduce(0, fn done, count -> - count = count + length(done) - Logger.info("Uploaded #{count}/#{total_count} files") - count - end) - - Logger.info("Done!") - end - - def run(_) do - Logger.error("Usage: migrate_local_uploads S3|Swift [--delete]") - end -end diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex new file mode 100644 index 000000000..c66322707 --- /dev/null +++ b/lib/mix/tasks/pleroma/instance.ex @@ -0,0 +1,157 @@ +defmodule Mix.Tasks.Pleroma.Instance do + use Mix.Task + alias Pleroma.{Repo, User} + 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 = + Common.get_option( + options, + :domain, + "What domain will your instance use? (e.g pleroma.soykaf.com)" + ) + + 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, + 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..4aea52732 --- /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 unfollow `` + + Example: ``mix pleroma.relay follow https://example.org/relay`` + + ## Unfollow a remote relay + + ``mix pleroma.relay unfollow `` + + 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.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/sample_config.eex b/lib/mix/tasks/pleroma/sample_config.eex similarity index 89% rename from lib/mix/tasks/sample_config.eex rename to lib/mix/tasks/pleroma/sample_config.eex index 47b6be729..0cd572797 100644 --- a/lib/mix/tasks/sample_config.eex +++ b/lib/mix/tasks/pleroma/sample_config.eex @@ -1,3 +1,8 @@ +# 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, @@ -16,13 +21,12 @@ 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 diff --git a/lib/mix/tasks/sample_psql.eex b/lib/mix/tasks/pleroma/sample_psql.eex similarity index 100% rename from lib/mix/tasks/sample_psql.eex rename to lib/mix/tasks/pleroma/sample_psql.eex diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex new file mode 100644 index 000000000..12b5af774 --- /dev/null +++ b/lib/mix/tasks/pleroma/user.ex @@ -0,0 +1,289 @@ +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 + + ## 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 + } + |> IO.inspect() + + 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 + User.deactivate(user, !user.info["deactivated"]) + Mix.shell().info("Activation status of #{nickname}: #{user.info["deactivated"]}") + 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 + case Keyword.get(options, :moderator) do + nil -> nil + value -> set_moderator(user, value) + end + + case Keyword.get(options, :locked) do + nil -> nil + value -> set_locked(user, value) + end + + case Keyword.get(options, :admin) do + nil -> nil + value -> set_admin(user, value) + end + else + _ -> + Mix.shell().error("No local user #{nickname}") + 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}") + 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_moderator}") + 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}") + 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 +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 `` - - 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 `` - - 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 `` - - 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 `` - - 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 ac26516f1..000000000 --- a/lib/mix/tasks/set_admin.ex +++ /dev/null @@ -1,34 +0,0 @@ -defmodule Mix.Tasks.SetAdmin do - use Mix.Task - import Ecto.Changeset - alias Pleroma.User - - @doc """ - Sets admin status - Usage: set_admin nickname [true|false] - """ - def run([nickname | rest]) do - Application.ensure_all_started(:pleroma) - - admin = - case rest do - [admin] -> admin == "true" - _ -> true - end - - with %User{local: true} = user <- User.get_by_nickname(nickname) do - info_cng = User.Info.admin_api_update(user.info, %{is_admin: !!admin}) - - user_cng = - Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) - - {:ok, user} = User.update_and_set_cache(user_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 e93a63505..000000000 --- a/lib/mix/tasks/set_locked.ex +++ /dev/null @@ -1,40 +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 `` - - Example: ``mix set_locked lain`` - """ - - use Mix.Task - import Ecto.Changeset - alias Pleroma.{Repo, User} - - def run([nickname | rest]) do - Application.ensure_all_started(:pleroma) - - locked = - case rest do - [locked] -> locked == "true" - _ -> true - end - - with %User{local: true} = user <- User.get_by_nickname(nickname) do - info_cng = User.Info.profile_update(user.info, %{locked: !!locked}) - - user_cng = - Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) - - {:ok, user} = User.update_and_set_cache(user_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 `` - - 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