diff options
Diffstat (limited to 'lib/mix/tasks')
-rw-r--r-- | lib/mix/tasks/deactivate_user.ex | 19 | ||||
-rw-r--r-- | lib/mix/tasks/generate_config.ex | 47 | ||||
-rw-r--r-- | lib/mix/tasks/generate_invite_token.ex | 32 | ||||
-rw-r--r-- | lib/mix/tasks/generate_password_reset.ex | 33 | ||||
-rw-r--r-- | lib/mix/tasks/make_moderator.ex | 37 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/common.ex | 28 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/instance.ex | 164 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/relay.ex | 47 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/sample_config.eex (renamed from lib/mix/tasks/sample_config.eex) | 26 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/sample_psql.eex | 7 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/uploads.ex | 107 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/user.ex | 304 | ||||
-rw-r--r-- | lib/mix/tasks/reactivate_user.ex | 19 | ||||
-rw-r--r-- | lib/mix/tasks/register_user.ex | 30 | ||||
-rw-r--r-- | lib/mix/tasks/relay_follow.ex | 24 | ||||
-rw-r--r-- | lib/mix/tasks/relay_unfollow.ex | 23 | ||||
-rw-r--r-- | lib/mix/tasks/rm_user.ex | 19 | ||||
-rw-r--r-- | lib/mix/tasks/sample_psql.eex | 6 | ||||
-rw-r--r-- | lib/mix/tasks/set_admin.ex | 32 | ||||
-rw-r--r-- | lib/mix/tasks/set_locked.ex | 39 | ||||
-rw-r--r-- | lib/mix/tasks/unsubscribe_user.ex | 38 |
21 files changed, 675 insertions, 406 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..48c0c1346 --- /dev/null +++ b/lib/mix/tasks/pleroma/common.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +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..1ef40671c --- /dev/null +++ b/lib/mix/tasks/pleroma/instance.ex @@ -0,0 +1,164 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +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..cbe23f82e --- /dev/null +++ b/lib/mix/tasks/pleroma/relay.ex @@ -0,0 +1,47 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +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/pleroma/sample_psql.eex b/lib/mix/tasks/pleroma/sample_psql.eex new file mode 100644 index 000000000..f0ac05e57 --- /dev/null +++ b/lib/mix/tasks/pleroma/sample_psql.eex @@ -0,0 +1,7 @@ +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; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; diff --git a/lib/mix/tasks/pleroma/uploads.ex b/lib/mix/tasks/pleroma/uploads.ex new file mode 100644 index 000000000..f0eb13e1a --- /dev/null +++ b/lib/mix/tasks/pleroma/uploads.ex @@ -0,0 +1,107 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.Uploads do + use Mix.Task + alias Pleroma.{Upload, Uploaders.Local} + alias Mix.Tasks.Pleroma.Common + require Logger + + @log_every 50 + + @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) + + 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 + + Mix.shell().info("Migrating files from local #{local_path} to #{to_string(uploader)}") + + if delete? do + Mix.shell().info( + "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 = Path.rootname(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) + Mix.shell().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 -> + Mix.shell().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) + Mix.shell().info("Uploaded #{count}/#{total_count} files") + count + end) + + 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..217a52fdd --- /dev/null +++ b/lib/mix/tasks/pleroma/user.ex @@ -0,0 +1,304 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +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 + } + + changeset = User.register_changeset(%User{}, params, confirmed: true) + {:ok, _user} = User.register(changeset) + + 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/sample_psql.eex b/lib/mix/tasks/sample_psql.eex deleted file mode 100644 index b6f57948b..000000000 --- a/lib/mix/tasks/sample_psql.eex +++ /dev/null @@ -1,6 +0,0 @@ -CREATE USER pleroma WITH ENCRYPTED PASSWORD '<%= dbpass %>'; -CREATE DATABASE pleroma_dev OWNER pleroma; -\c pleroma_dev; ---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/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 |