From 8a1dc0de92c8c99bd6216281355be829cbcfb21f Mon Sep 17 00:00:00 2001 From: Jorty Date: Thu, 28 Jun 2018 20:24:51 -0400 Subject: [PATCH 01/25] Refactor Mix tasks 1. Move Mix tasks into a `pleroma` namespace, to avoid collisions with dependent packages. 2. Rename and condense tasks into two `pleroma.user` and `pleroma.gen.instance` tasks for consistency with Hex and Phoenix. 3. Add additional functionality to the tasks to make them more user-friendly. Arguments with sensible defaults were demoted to flags and in the interactive `generate_config` (renamed to `pleroma.gen.instance`), flags were added to allow non-interactive use, though interactive use remains the primary interface. That task also now prompts the user for database parameters. 4. Documentation has been added to both tasks such that `mix help` now shows useful information. 5. Finally, use of IO.puts in tasks has been replaced with Mix.shell() equivalents to make the behavior more consistent with Mix tasks in other packages, and such that variables like MIX_QUIET are respected. The only exception is in `mix pleroma.user reset_password`, wherein the URL must always be printed regardless of the value of MIX_QUIET since that's its entire purpose. --- lib/mix/tasks/deactivate_user.ex | 13 -- lib/mix/tasks/fix_ap_users.ex | 28 --- lib/mix/tasks/generate_config.ex | 39 ---- lib/mix/tasks/generate_password_reset.ex | 27 --- lib/mix/tasks/make_moderator.ex | 30 --- lib/mix/tasks/pleroma/gen_instance.ex | 161 ++++++++++++++ lib/mix/tasks/{ => pleroma}/sample_config.eex | 12 +- lib/mix/tasks/pleroma/sample_psql.eex | 9 + lib/mix/tasks/pleroma/user.ex | 207 ++++++++++++++++++ lib/mix/tasks/register_user.ex | 22 -- lib/mix/tasks/rm_user.ex | 13 -- lib/mix/tasks/sample_psql.eex | 9 - lib/mix/tasks/set_locked.ex | 30 --- 13 files changed, 385 insertions(+), 215 deletions(-) delete mode 100644 lib/mix/tasks/deactivate_user.ex delete mode 100644 lib/mix/tasks/fix_ap_users.ex delete mode 100644 lib/mix/tasks/generate_config.ex delete mode 100644 lib/mix/tasks/generate_password_reset.ex delete mode 100644 lib/mix/tasks/make_moderator.ex create mode 100644 lib/mix/tasks/pleroma/gen_instance.ex rename lib/mix/tasks/{ => pleroma}/sample_config.eex (68%) create mode 100644 lib/mix/tasks/pleroma/sample_psql.eex create mode 100644 lib/mix/tasks/pleroma/user.ex delete mode 100644 lib/mix/tasks/register_user.ex delete mode 100644 lib/mix/tasks/rm_user.ex delete mode 100644 lib/mix/tasks/sample_psql.eex delete mode 100644 lib/mix/tasks/set_locked.ex diff --git a/lib/mix/tasks/deactivate_user.ex b/lib/mix/tasks/deactivate_user.ex deleted file mode 100644 index 96b3db6e4..000000000 --- a/lib/mix/tasks/deactivate_user.ex +++ /dev/null @@ -1,13 +0,0 @@ -defmodule Mix.Tasks.DeactivateUser do - use Mix.Task - alias Pleroma.User - - @shortdoc "Toggle deactivation status for a user" - 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/fix_ap_users.ex b/lib/mix/tasks/fix_ap_users.ex deleted file mode 100644 index 7e970850e..000000000 --- a/lib/mix/tasks/fix_ap_users.ex +++ /dev/null @@ -1,28 +0,0 @@ -defmodule Mix.Tasks.FixApUsers do - use Mix.Task - import Ecto.Query - alias Pleroma.{Repo, User} - - @shortdoc "Grab all ap users again" - def run([]) do - Mix.Task.run("app.start") - - q = - from( - u in User, - where: fragment("? @> ?", u.info, ^%{"ap_enabled" => true}), - where: u.local == false - ) - - users = Repo.all(q) - - Enum.each(users, fn user -> - try do - IO.puts("Fetching #{user.nickname}") - Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(user.ap_id, false) - rescue - e -> IO.inspect(e) - end - end) - end -end diff --git a/lib/mix/tasks/generate_config.ex b/lib/mix/tasks/generate_config.ex deleted file mode 100644 index 70a110561..000000000 --- a/lib/mix/tasks/generate_config.ex +++ /dev/null @@ -1,39 +0,0 @@ -defmodule Mix.Tasks.GenerateConfig do - use Mix.Task - - @shortdoc "Generates a new config" - 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_password_reset.ex b/lib/mix/tasks/generate_password_reset.ex deleted file mode 100644 index 6bf640150..000000000 --- a/lib/mix/tasks/generate_password_reset.ex +++ /dev/null @@ -1,27 +0,0 @@ -defmodule Mix.Tasks.GeneratePasswordReset do - use Mix.Task - alias Pleroma.User - - @shortdoc "Generate password reset link for user" - 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 a454a958e..000000000 --- a/lib/mix/tasks/make_moderator.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule Mix.Tasks.SetModerator do - use Mix.Task - import Mix.Ecto - alias Pleroma.{Repo, User} - - @shortdoc "Set moderator status" - 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/gen_instance.ex b/lib/mix/tasks/pleroma/gen_instance.ex new file mode 100644 index 000000000..94f2220b1 --- /dev/null +++ b/lib/mix/tasks/pleroma/gen_instance.ex @@ -0,0 +1,161 @@ +defmodule Mix.Tasks.Pleroma.Gen.Instance do + use Mix.Task + + @shortdoc "Generates the configuration for a new instance" + @moduledoc """ + Generates the configuration for a new instance. + + If any options are left unspecified, you will be prompted interactively. This + means the simplest invocation would be + + mix pleroma.gen.instance + + ## 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(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 = + Keyword.get(options, :domain) || + Mix.shell().prompt("What domain will your instance use? (e.g. pleroma.soykaf.com)") + |> String.trim() + + name = + Keyword.get(options, :name) || + Mix.shell().prompt("What is the name of your instance? (e.g. Pleroma/Soykaf)") + |> String.trim() + + email = + Keyword.get(options, :admin_email) || + Mix.shell().prompt("What is your admin email address?") + |> String.trim() + + dbhost = + Keyword.get(options, :dbhost) || + case Mix.shell().prompt("What is the hostname of your database? [localhost]") do + "\n" -> "localhost" + dbhost -> dbhost |> String.trim() + end + + dbname = + Keyword.get(options, :dbname) || + case Mix.shell().prompt("What is the name of your database? [pleroma_dev]") do + "\n" -> "pleroma_dev" + dbname -> dbname |> String.trim() + end + + dbuser = + Keyword.get(options, :dbuser) || + case Mix.shell().prompt("What is the user used to connect to your database? [pleroma]") do + "\n" -> "pleroma" + dbuser -> dbuser |> String.trim() + end + + dbpass = + Keyword.get(options, :dbpass) || + case Mix.shell().prompt( + "What is the password used to connect to your database? [autogenerated]" + ) do + "\n" -> :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) + dbpass -> dbpass |> String.trim() + end + + secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) + + 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 + ) + + 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 #{escape_sh_path(psql_path)}`. + """ <> + if config_path in ["config/dev.secret.exs", "config/prod.secret.exs"] do + "" + else + "3. Run `mv #{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 + + defp escape_sh_path(path) do + ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') + end +end diff --git a/lib/mix/tasks/sample_config.eex b/lib/mix/tasks/pleroma/sample_config.eex similarity index 68% rename from lib/mix/tasks/sample_config.eex rename to lib/mix/tasks/pleroma/sample_config.eex index 6db36fa09..066939981 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,11 +21,10 @@ 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 diff --git a/lib/mix/tasks/pleroma/sample_psql.eex b/lib/mix/tasks/pleroma/sample_psql.eex new file mode 100644 index 000000000..66f76752f --- /dev/null +++ b/lib/mix/tasks/pleroma/sample_psql.eex @@ -0,0 +1,9 @@ +CREATE USER <%= dbuser %> WITH ENCRYPTED PASSWORD '<%= dbpass %>' CREATEDB; +-- in case someone runs this second time accidentally +ALTER USER <%= dbuser %> WITH ENCRYPTED PASSWORD '<%= dbpass %>' CREATEDB; +CREATE DATABASE <%= dbname %>; +ALTER DATABASE <%= dbname %> OWNER TO <%= 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/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex new file mode 100644 index 000000000..c20fecaa1 --- /dev/null +++ b/lib/mix/tasks/pleroma/user.ex @@ -0,0 +1,207 @@ +defmodule Mix.Tasks.Pleroma.User do + use Mix.Task + alias Pleroma.{Repo, User} + + @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 + + ## 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 + """ + + def run(["new", nickname, email | rest]) do + {options, [], []} = + OptionParser.parse( + rest, + strict: [ + name: :string, + bio: :string, + password: :string, + moderator: :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) + + 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")} + """) + + proceed? = Mix.shell().yes?("Continue?") + + unless not proceed? do + Mix.Task.run("app.start") + + 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 generated_password? do + run(["reset_password", nickname]) + end + else + Mix.shell().info("User will not be created.") + end + end + + def run(["rm", nickname]) do + Mix.Task.run("app.start") + + with %User{local: true} = user <- User.get_by_nickname(nickname) do + User.delete(user) + end + + Mix.shell().info("User #{nickname} deleted.") + end + + def run(["toggle_activated", nickname]) do + Mix.Task.run("app.start") + + with user <- User.get_by_nickname(nickname) do + User.deactivate(user) + end + end + + def run(["reset_password", 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 + 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(["set", nickname | rest]) do + {options, [], []} = + OptionParser.parse( + rest, + strict: [ + moderator: :boolean, + locked: :boolean + ] + ) + + case Keyword.get(options, :moderator) do + nil -> nil + value -> set_moderator(nickname, value) + end + + case Keyword.get(options, :locked) do + nil -> nil + value -> set_locked(nickname, value) + end + end + + defp set_moderator(nickname, value) do + Application.ensure_all_started(:pleroma) + + with %User{local: true} = user <- User.get_by_nickname(nickname) do + info = + user.info + |> Map.put("is_moderator", value) + + cng = User.info_changeset(user, %{info: info}) + {:ok, user} = User.update_and_set_cache(cng) + + Mix.shell().info("Moderator status of #{nickname}: #{user.info["is_moderator"]}") + else + _ -> + Mix.shell().error("No local user #{nickname}") + end + end + + defp set_locked(nickname, value) do + Mix.Ecto.ensure_started(Repo, []) + + with %User{local: true} = user <- User.get_by_nickname(nickname) do + info = + user.info + |> Map.put("locked", value) + + 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/register_user.ex b/lib/mix/tasks/register_user.ex deleted file mode 100644 index e74721c49..000000000 --- a/lib/mix/tasks/register_user.ex +++ /dev/null @@ -1,22 +0,0 @@ -defmodule Mix.Tasks.RegisterUser do - 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/rm_user.ex b/lib/mix/tasks/rm_user.ex deleted file mode 100644 index 27521b745..000000000 --- a/lib/mix/tasks/rm_user.ex +++ /dev/null @@ -1,13 +0,0 @@ -defmodule Mix.Tasks.RmUser do - use Mix.Task - alias Pleroma.User - - @shortdoc "Permanently delete a user" - def run([nickname]) do - Mix.Task.run("app.start") - - with %User{local: true} = user <- User.get_by_nickname(nickname) do - 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 bc22f166c..000000000 --- a/lib/mix/tasks/sample_psql.eex +++ /dev/null @@ -1,9 +0,0 @@ -CREATE USER pleroma WITH ENCRYPTED PASSWORD '<%= dbpass %>' CREATEDB; --- in case someone runs this second time accidentally -ALTER USER pleroma WITH ENCRYPTED PASSWORD '<%= dbpass %>' CREATEDB; -CREATE DATABASE pleroma_dev; -ALTER DATABASE pleroma_dev OWNER TO 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_locked.ex b/lib/mix/tasks/set_locked.ex deleted file mode 100644 index 2b3b18b09..000000000 --- a/lib/mix/tasks/set_locked.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule Mix.Tasks.SetLocked do - use Mix.Task - import Mix.Ecto - alias Pleroma.{Repo, User} - - @shortdoc "Set locked status" - 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 From 76ed9ca1e93dfada63f50d6ba22491ef8d5d253c Mon Sep 17 00:00:00 2001 From: Jorty Date: Thu, 28 Jun 2018 20:46:10 -0400 Subject: [PATCH 02/25] Update README.md for refactored Mix tasks --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3523c9a92..5a20bf553 100644 --- a/README.md +++ b/README.md @@ -34,16 +34,15 @@ 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.gen.instance`. 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`. + * 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`. You can also simply run `export MIX_ENV=prod` which will set this variable for the rest of the shell session. * Run `mix ecto.migrate` to run the database migrations. You will have to do this again after certain updates. * 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. * [Not tested with system reboot yet!] You'll also want to set up Pleroma to be run as a systemd service. Example .service file can be found in `installation/pleroma.service` you can put it in `/etc/systemd/system/`. From ba6e3eba33f16bdd2fede086d5fb5c86201cb57b Mon Sep 17 00:00:00 2001 From: Jorty Date: Thu, 23 Aug 2018 12:37:20 -0400 Subject: [PATCH 03/25] Move invite task to pleroma namespace Some other minor changes were made to make it consistent with the behavior of other tasks both within Pleroma and the conventions set by dependencies such as Phoenix. Namely, the task is named `gen.invite` and `IO.puts` has been replaced with references to `Mix.shell()` where appropriate. --- lib/mix/tasks/generate_invite_token.ex | 25 ------------------------- lib/mix/tasks/pleroma/gen_invite.ex | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 25 deletions(-) delete mode 100644 lib/mix/tasks/generate_invite_token.ex create mode 100644 lib/mix/tasks/pleroma/gen_invite.ex diff --git a/lib/mix/tasks/generate_invite_token.ex b/lib/mix/tasks/generate_invite_token.ex deleted file mode 100644 index c4daa9a6c..000000000 --- a/lib/mix/tasks/generate_invite_token.ex +++ /dev/null @@ -1,25 +0,0 @@ -defmodule Mix.Tasks.GenerateInviteToken do - use Mix.Task - - @shortdoc "Generate invite token for user" - 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/pleroma/gen_invite.ex b/lib/mix/tasks/pleroma/gen_invite.ex new file mode 100644 index 000000000..0aa028f1e --- /dev/null +++ b/lib/mix/tasks/pleroma/gen_invite.ex @@ -0,0 +1,24 @@ +defmodule Mix.Tasks.Pleroma.Gen.Invite do + use Mix.Task + + @shortdoc "Generates a user invite token" + def run([]) do + Mix.Task.run("app.start") + + 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: #{url}") + else + _ -> + Mix.shell().error("Could not create invite token.") + end + end +end From 5f91d6b8592f601553355e2c94832c90afe17d66 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 1 Dec 2018 18:33:53 +0300 Subject: [PATCH 04/25] Fix toggle_deactivated to reactivate a deactivated user --- lib/mix/tasks/pleroma/user.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index c20fecaa1..c7c69ed01 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -122,7 +122,7 @@ def run(["toggle_activated", nickname]) do Mix.Task.run("app.start") with user <- User.get_by_nickname(nickname) do - User.deactivate(user) + User.deactivate(user, !user.info["deactivated"]) end end From 6f174cbb7100bc5ae6a0eba4e7e926704ee11dc3 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 1 Dec 2018 18:34:26 +0300 Subject: [PATCH 05/25] Delete reactivate user task --- lib/mix/tasks/reactivate_user.ex | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 lib/mix/tasks/reactivate_user.ex 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 From ae82852330105edb681d131fd33cf35557c8614c Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 1 Dec 2018 18:55:52 +0300 Subject: [PATCH 06/25] Move set_admin task to lib/mix/tasks/pleroma/user.ex --- lib/mix/tasks/pleroma/user.ex | 26 ++++++++++++++++++++++++++ lib/mix/tasks/set_admin.ex | 32 -------------------------------- 2 files changed, 26 insertions(+), 32 deletions(-) delete mode 100644 lib/mix/tasks/set_admin.ex diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index c7c69ed01..9aa569b2a 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -15,6 +15,7 @@ defmodule Mix.Tasks.Pleroma.User do - `--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 ## Delete the user's account. @@ -35,6 +36,7 @@ defmodule Mix.Tasks.Pleroma.User do 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 @@ -154,6 +156,7 @@ def run(["set", nickname | rest]) do rest, strict: [ moderator: :boolean, + admin: :boolean, locked: :boolean ] ) @@ -167,6 +170,11 @@ def run(["set", nickname | rest]) do nil -> nil value -> set_locked(nickname, value) end + + case Keyword.get(options, :admin) do + nil -> nil + value -> set_admin(nickname, value) + end end defp set_moderator(nickname, value) do @@ -187,6 +195,24 @@ defp set_moderator(nickname, value) do end end + defp set_admin(nickname, value) do + Application.ensure_all_started(:pleroma) + + with %User{local: true} = user <- User.get_by_nickname(nickname) do + info = + user.info + |> Map.put("is_admin", value) + + cng = User.info_changeset(user, %{info: info}) + {:ok, user} = User.update_and_set_cache(cng) + + Mix.shell().info("Admin status of #{nickname}: #{user.info["is_admin"]}") + else + _ -> + Mix.shell().error("No local user #{nickname}") + end + end + defp set_locked(nickname, value) do Mix.Ecto.ensure_started(Repo, []) 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 From a8ef6b1190aff949140e0c79603d833668647a31 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Sun, 2 Dec 2018 09:36:31 +0100 Subject: [PATCH 07/25] Add admin option to pleroma.user new. Add user existence checking to toggle_activated --- lib/mix/tasks/pleroma/user.ex | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 9aa569b2a..40d920866 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -47,7 +47,8 @@ def run(["new", nickname, email | rest]) do name: :string, bio: :string, password: :string, - moderator: :boolean + moderator: :boolean, + admin: :boolean ] ) @@ -64,6 +65,7 @@ def run(["new", nickname, email | rest]) do 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: @@ -75,6 +77,7 @@ def run(["new", nickname, email | rest]) do - name: #{name} - bio: #{bio} - moderator: #{if(moderator?, do: "true", else: "false")} + - admin: #{if(admin?, do: "true", else: "false")} """) proceed? = Mix.shell().yes?("Continue?") @@ -102,9 +105,14 @@ def run(["new", nickname, email | rest]) 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 @@ -115,16 +123,22 @@ def run(["rm", nickname]) do 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 - - Mix.shell().info("User #{nickname} deleted.") end def run(["toggle_activated", nickname]) do Mix.Task.run("app.start") - with user <- User.get_by_nickname(nickname) do + with %User{local: true} = 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 local user #{nickname}") end end From 31b3ac05accd3bc56f6663fad554ad67baf0c549 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Sun, 2 Dec 2018 10:01:17 +0100 Subject: [PATCH 08/25] Lint fix --- lib/mix/tasks/pleroma/user.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 40d920866..d7a6be844 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -112,7 +112,6 @@ def run(["new", nickname, email | rest]) do if generated_password? do run(["reset_password", nickname]) end - else Mix.shell().info("User will not be created.") end From 7983b0bdfe40c88d159af2f0562d96e8812c31e3 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Sun, 2 Dec 2018 18:05:59 +0100 Subject: [PATCH 09/25] Move unsubscribe user task to pleroma/user.ex. Delete unsubscribe_user.ex. Fix pleroma.user toggle_activated to work not only on local users. --- lib/mix/tasks/pleroma/user.ex | 33 +++++++++++++++++++++++++-- lib/mix/tasks/unsubscribe_user.ex | 38 ------------------------------- 2 files changed, 31 insertions(+), 40 deletions(-) delete mode 100644 lib/mix/tasks/unsubscribe_user.ex diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index d7a6be844..d7e172994 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -132,12 +132,12 @@ def run(["rm", nickname]) do def run(["toggle_activated", nickname]) do Mix.Task.run("app.start") - with %User{local: true} = user <- User.get_by_nickname(nickname) do + 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 local user #{nickname}") + Mix.shell().error("No user #{nickname}") end end @@ -163,6 +163,35 @@ def run(["reset_password", nickname]) do end end + def run(["unsubscribe", nickname]) do + Mix.Task.run("app.start") + + 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 {options, [], []} = OptionParser.parse( 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 From faf1f2b304d8b2a7e26856e767002f3fcf46d67c Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Sun, 2 Dec 2018 19:18:06 +0100 Subject: [PATCH 10/25] Move gen.instance to instance.ex --- .../pleroma/{gen_instance.ex => instance.ex} | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) rename lib/mix/tasks/pleroma/{gen_instance.ex => instance.ex} (94%) diff --git a/lib/mix/tasks/pleroma/gen_instance.ex b/lib/mix/tasks/pleroma/instance.ex similarity index 94% rename from lib/mix/tasks/pleroma/gen_instance.ex rename to lib/mix/tasks/pleroma/instance.ex index 94f2220b1..639893985 100644 --- a/lib/mix/tasks/pleroma/gen_instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -1,14 +1,16 @@ -defmodule Mix.Tasks.Pleroma.Gen.Instance do +defmodule Mix.Tasks.Pleroma.Instance do use Mix.Task + alias Pleroma.{Repo, User} - @shortdoc "Generates the configuration for a new instance" + @shortdoc "Manages Pleroma instance" @moduledoc """ - Generates the configuration for a new instance. + Manages Pleroma instance. - If any options are left unspecified, you will be prompted interactively. This - means the simplest invocation would be + ## Generate a new instance. + + mix pleroma.instance new [OPTION...] - mix pleroma.gen.instance + If any options are left unspecified, you will be prompted interactively ## Options @@ -24,7 +26,7 @@ defmodule Mix.Tasks.Pleroma.Gen.Instance do - `--dbpass DBPASS` - the password to use for the database connection """ - def run(rest) do + def run(["new" | rest]) do {options, [], []} = OptionParser.parse( rest, @@ -159,3 +161,4 @@ defp escape_sh_path(path) do ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') end end + From cbe22deb510317bca811cc2df30d5cd7e892e4b5 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Sun, 2 Dec 2018 19:20:50 +0100 Subject: [PATCH 11/25] Lint fix --- lib/mix/tasks/pleroma/instance.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 639893985..8c728625a 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -7,7 +7,7 @@ defmodule Mix.Tasks.Pleroma.Instance do Manages Pleroma instance. ## Generate a new instance. - + mix pleroma.instance new [OPTION...] If any options are left unspecified, you will be prompted interactively @@ -161,4 +161,3 @@ defp escape_sh_path(path) do ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') end end - From d924b6cd3da7ad96c522f9de45eb8f7453e0f53a Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Sun, 2 Dec 2018 20:04:33 +0100 Subject: [PATCH 12/25] Refactor copypasta to a private function in instance.ex --- lib/mix/tasks/pleroma/instance.ex | 70 ++++++++++++++++--------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 8c728625a..b3e0f99df 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -59,49 +59,37 @@ def run(["new" | rest]) do unless not proceed? do domain = - Keyword.get(options, :domain) || - Mix.shell().prompt("What domain will your instance use? (e.g. pleroma.soykaf.com)") - |> String.trim() + get_option( + options, + :domain, + "What domain will your instance use? (e.g pleroma.soykaf.com)" + ) name = - Keyword.get(options, :name) || - Mix.shell().prompt("What is the name of your instance? (e.g. Pleroma/Soykaf)") - |> String.trim() + get_option(options, :name, "What is the name of your instance? (e.g. Pleroma/Soykaf)") - email = - Keyword.get(options, :admin_email) || - Mix.shell().prompt("What is your admin email address?") - |> String.trim() + email = get_option(options, :admin_email, "What is your admin email address?") - dbhost = - Keyword.get(options, :dbhost) || - case Mix.shell().prompt("What is the hostname of your database? [localhost]") do - "\n" -> "localhost" - dbhost -> dbhost |> String.trim() - end + dbhost = get_option(options, :dbhost, "What is the hostname of your database?", "localhost") - dbname = - Keyword.get(options, :dbname) || - case Mix.shell().prompt("What is the name of your database? [pleroma_dev]") do - "\n" -> "pleroma_dev" - dbname -> dbname |> String.trim() - end + dbname = get_option(options, :dbname, "What is the name of your database?", "pleroma_dev") dbuser = - Keyword.get(options, :dbuser) || - case Mix.shell().prompt("What is the user used to connect to your database? [pleroma]") do - "\n" -> "pleroma" - dbuser -> dbuser |> String.trim() - end + get_option( + options, + :dbuser, + "What is the user used to connect to your database?", + "pleroma" + ) dbpass = - Keyword.get(options, :dbpass) || - case Mix.shell().prompt( - "What is the password used to connect to your database? [autogenerated]" - ) do - "\n" -> :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) - dbpass -> dbpass |> String.trim() - end + 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) @@ -160,4 +148,18 @@ def run(["new" | rest]) do defp escape_sh_path(path) do ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') end + + defp get_option(options, opt, prompt, def \\ nil, defname \\ nil) do + Keyword.get(options, opt) || + case Mix.shell().prompt("#{prompt} [#{defname || def}]") do + "\n" -> + case def do + nil -> get_option(options, opt, prompt, def) + def -> def + end + + opt -> + opt |> String.trim() + end + end end From 57c71f846880083d8a78247f433713963c284ab9 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Sun, 2 Dec 2018 20:26:15 +0100 Subject: [PATCH 13/25] Move generate_invite to user.ex --- lib/mix/tasks/pleroma/gen_invite.ex | 24 ------------------------ lib/mix/tasks/pleroma/instance.ex | 3 ++- lib/mix/tasks/pleroma/user.ex | 24 ++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 25 deletions(-) delete mode 100644 lib/mix/tasks/pleroma/gen_invite.ex diff --git a/lib/mix/tasks/pleroma/gen_invite.ex b/lib/mix/tasks/pleroma/gen_invite.ex deleted file mode 100644 index 0aa028f1e..000000000 --- a/lib/mix/tasks/pleroma/gen_invite.ex +++ /dev/null @@ -1,24 +0,0 @@ -defmodule Mix.Tasks.Pleroma.Gen.Invite do - use Mix.Task - - @shortdoc "Generates a user invite token" - def run([]) do - Mix.Task.run("app.start") - - 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: #{url}") - else - _ -> - Mix.shell().error("Could not create invite token.") - end - end -end diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index b3e0f99df..05653c238 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -6,7 +6,7 @@ defmodule Mix.Tasks.Pleroma.Instance do @moduledoc """ Manages Pleroma instance. - ## Generate a new instance. + ## Generate a new instance config. mix pleroma.instance new [OPTION...] @@ -145,6 +145,7 @@ def run(["new" | rest]) do end end + defp escape_sh_path(path) do ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') end diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index d7e172994..c23c5cdf3 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -16,6 +16,10 @@ defmodule Mix.Tasks.Pleroma.User do - `--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. @@ -255,6 +259,26 @@ defp set_admin(nickname, value) do end end + def run(["invite"]) do + Mix.Task.run("app.start") + + 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_locked(nickname, value) do Mix.Ecto.ensure_started(Repo, []) From 03b2d1016d25ab2b17a5a511249b4df59c4743fd Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Sun, 2 Dec 2018 20:27:49 +0100 Subject: [PATCH 14/25] F O R M A T I N G --- lib/mix/tasks/pleroma/instance.ex | 1 - lib/mix/tasks/pleroma/user.ex | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 05653c238..6a85880bf 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -145,7 +145,6 @@ def run(["new" | rest]) do end end - defp escape_sh_path(path) do ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') end diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index c23c5cdf3..39825762d 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -16,7 +16,7 @@ defmodule Mix.Tasks.Pleroma.User do - `--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 @@ -276,9 +276,9 @@ def run(["invite"]) do else _ -> Mix.shell().error("Could not create invite token.") + end end - end defp set_locked(nickname, value) do Mix.Ecto.ensure_started(Repo, []) From 6396f1b58f69b727e801df4b52a32c47da08e517 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Tue, 4 Dec 2018 19:00:45 +0100 Subject: [PATCH 15/25] change new to gen in instance.ex . Refactor user.ex --- lib/mix/tasks/pleroma/instance.ex | 4 +- lib/mix/tasks/pleroma/user.ex | 98 +++++++++++++------------------ 2 files changed, 44 insertions(+), 58 deletions(-) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 6a85880bf..eb578644d 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -8,7 +8,7 @@ defmodule Mix.Tasks.Pleroma.Instance do ## Generate a new instance config. - mix pleroma.instance new [OPTION...] + mix pleroma.instance gen [OPTION...] If any options are left unspecified, you will be prompted interactively @@ -26,7 +26,7 @@ defmodule Mix.Tasks.Pleroma.Instance do - `--dbpass DBPASS` - the password to use for the database connection """ - def run(["new" | rest]) do + def run(["gen" | rest]) do {options, [], []} = OptionParser.parse( rest, diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 39825762d..68c6bd2b6 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -197,6 +197,8 @@ def run(["unsubscribe", nickname]) do end def run(["set", nickname | rest]) do + Application.ensure_all_started(:pleroma) + {options, [], []} = OptionParser.parse( rest, @@ -207,56 +209,58 @@ def run(["set", nickname | rest]) do ] ) - case Keyword.get(options, :moderator) do - nil -> nil - value -> set_moderator(nickname, value) - end - - case Keyword.get(options, :locked) do - nil -> nil - value -> set_locked(nickname, value) - end - - case Keyword.get(options, :admin) do - nil -> nil - value -> set_admin(nickname, value) - end - end - - defp set_moderator(nickname, value) do - Application.ensure_all_started(:pleroma) - with %User{local: true} = user <- User.get_by_nickname(nickname) do - info = - user.info - |> Map.put("is_moderator", value) + case Keyword.get(options, :moderator) do + nil -> nil + value -> set_moderator(user, value) + end - cng = User.info_changeset(user, %{info: info}) - {:ok, user} = User.update_and_set_cache(cng) + case Keyword.get(options, :locked) do + nil -> nil + value -> set_locked(user, value) + end - Mix.shell().info("Moderator status of #{nickname}: #{user.info["is_moderator"]}") + 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_admin(nickname, value) do - Application.ensure_all_started(:pleroma) + defp set_moderator(user, value) do + info = + user.info + |> Map.put("is_moderator", value) - with %User{local: true} = user <- User.get_by_nickname(nickname) do - info = - user.info - |> Map.put("is_admin", value) + cng = User.info_changeset(user, %{info: info}) + {:ok, user} = User.update_and_set_cache(cng) - cng = User.info_changeset(user, %{info: info}) - {:ok, user} = User.update_and_set_cache(cng) + Mix.shell().info("Moderator status of #{user.nickname}: #{user.info["is_moderator"]}") + end - Mix.shell().info("Admin status of #{nickname}: #{user.info["is_admin"]}") - else - _ -> - Mix.shell().error("No local user #{nickname}") - end + defp set_admin(user, value) do + info = + user.info + |> Map.put("is_admin", value) + + cng = User.info_changeset(user, %{info: info}) + {:ok, user} = User.update_and_set_cache(cng) + + Mix.shell().info("Admin status of #{user.nickname}: #{user.info["is_admin"]}") + end + + defp set_locked(user, value) do + info = + user.info + |> Map.put("locked", value) + + cng = User.info_changeset(user, %{info: info}) + user = Repo.update!(cng) + + IO.puts("Locked status of #{user.nickname}: #{user.info["locked"]}") end def run(["invite"]) do @@ -278,22 +282,4 @@ def run(["invite"]) do Mix.shell().error("Could not create invite token.") end end - - defp set_locked(nickname, value) do - Mix.Ecto.ensure_started(Repo, []) - - with %User{local: true} = user <- User.get_by_nickname(nickname) do - info = - user.info - |> Map.put("locked", value) - - 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 From e8ba579efe7d9ca4be32c5aacf62be0de1ec09df Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Wed, 5 Dec 2018 17:58:26 +0100 Subject: [PATCH 16/25] Switch from User.info_changeset because it is deprecated --- lib/mix/tasks/pleroma/user.ex | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 68c6bd2b6..14ba60bf4 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -1,5 +1,6 @@ defmodule Mix.Tasks.Pleroma.User do use Mix.Task + import Ecto.Changeset alias Pleroma.{Repo, User} @shortdoc "Manages Pleroma users" @@ -235,10 +236,14 @@ defp set_moderator(user, value) do user.info |> Map.put("is_moderator", value) - cng = User.info_changeset(user, %{info: info}) - {:ok, user} = User.update_and_set_cache(cng) + info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value}) + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) - Mix.shell().info("Moderator status of #{user.nickname}: #{user.info["is_moderator"]}") + {: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 @@ -246,10 +251,14 @@ defp set_admin(user, value) do user.info |> Map.put("is_admin", value) - cng = User.info_changeset(user, %{info: info}) - {:ok, user} = User.update_and_set_cache(cng) + info_cng = User.Info.admin_api_update(user.info, %{is_admin: value}) + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) - Mix.shell().info("Admin status of #{user.nickname}: #{user.info["is_admin"]}") + {: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 @@ -257,10 +266,14 @@ defp set_locked(user, value) do user.info |> Map.put("locked", value) - cng = User.info_changeset(user, %{info: info}) - user = Repo.update!(cng) + info_cng = User.Info.user_upgrade(user.info, %{locked: value}) + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) - IO.puts("Locked status of #{user.nickname}: #{user.info["locked"]}") + {: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 From facfd03bc1f9ec2464fb0b78ece2c7d3162521db Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Wed, 5 Dec 2018 18:11:59 +0100 Subject: [PATCH 17/25] Move relay tasks to relay.ex --- lib/mix/tasks/pleroma/relay.ex | 42 +++++++++++++++++++++++++++++++++ lib/mix/tasks/relay_follow.ex | 24 ------------------- lib/mix/tasks/relay_unfollow.ex | 23 ------------------ 3 files changed, 42 insertions(+), 47 deletions(-) create mode 100644 lib/mix/tasks/pleroma/relay.ex delete mode 100644 lib/mix/tasks/relay_follow.ex delete mode 100644 lib/mix/tasks/relay_unfollow.ex diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex new file mode 100644 index 000000000..aa0232a32 --- /dev/null +++ b/lib/mix/tasks/pleroma/relay.ex @@ -0,0 +1,42 @@ +defmodule Mix.Tasks.Pleroma.Relay do + use Mix.Task + alias Pleroma.Web.ActivityPub.Relay + + @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 + 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 + + def run(["unfollow", 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_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 From ffec96d8ccee6d51e3806d209ae176fb2c589beb Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Wed, 5 Dec 2018 19:05:37 +0100 Subject: [PATCH 18/25] Everything should use Mix.Task.Run --- lib/mix/tasks/pleroma/user.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 14ba60bf4..e2b9b6236 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -198,7 +198,7 @@ def run(["unsubscribe", nickname]) do end def run(["set", nickname | rest]) do - Application.ensure_all_started(:pleroma) + Mix.Task.run("app.start") {options, [], []} = OptionParser.parse( From dfc9c08796055c723fac11dd13eea90d340fa0ae Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Wed, 5 Dec 2018 19:12:23 +0100 Subject: [PATCH 19/25] formating --- lib/mix/tasks/pleroma/relay.ex | 4 ++-- lib/mix/tasks/pleroma/user.ex | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index aa0232a32..eaaded5cb 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -11,9 +11,9 @@ defmodule Mix.Tasks.Pleroma.Relay do ``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`` diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index e2b9b6236..aa023aae8 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -237,9 +237,10 @@ defp set_moderator(user, value) do |> Map.put("is_moderator", value) info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value}) - user_cng = - Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) + + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) {:ok, user} = User.update_and_set_cache(user_cng) @@ -252,9 +253,10 @@ defp set_admin(user, value) do |> Map.put("is_admin", value) info_cng = User.Info.admin_api_update(user.info, %{is_admin: value}) - user_cng = - Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) + + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) {:ok, user} = User.update_and_set_cache(user_cng) @@ -267,9 +269,10 @@ defp set_locked(user, value) do |> Map.put("locked", value) info_cng = User.Info.user_upgrade(user.info, %{locked: value}) - user_cng = - Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) + + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) {:ok, user} = User.update_and_set_cache(user_cng) From 3a84511df10b647f08d5ec2df789663b60646c02 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Thu, 6 Dec 2018 17:35:33 +0100 Subject: [PATCH 20/25] remove migrate_local_uploads.ex --- lib/mix/tasks/migrate_local_uploads.ex | 97 -------------------------- 1 file changed, 97 deletions(-) delete mode 100644 lib/mix/tasks/migrate_local_uploads.ex 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 From 4a2a7ce636ac4616ec71665b4af4ee0ab884ac15 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Thu, 6 Dec 2018 18:00:24 +0100 Subject: [PATCH 21/25] Refactor common functions to common.ex --- lib/mix/tasks/pleroma/instance.ex | 34 +++++++++---------------------- lib/mix/tasks/pleroma/relay.ex | 6 +++--- lib/mix/tasks/pleroma/user.ex | 16 +++++++-------- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index eb578644d..88fc3ba75 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -1,6 +1,7 @@ defmodule Mix.Tasks.Pleroma.Instance do use Mix.Task alias Pleroma.{Repo, User} + alias Mix.Tasks.Pleroma.Common @shortdoc "Manages Pleroma instance" @moduledoc """ @@ -59,23 +60,23 @@ def run(["gen" | rest]) do unless not proceed? do domain = - get_option( + Common.get_option( options, :domain, "What domain will your instance use? (e.g pleroma.soykaf.com)" ) name = - get_option(options, :name, "What is the name of your instance? (e.g. Pleroma/Soykaf)") + Common.get_option(options, :name, "What is the name of your instance? (e.g. Pleroma/Soykaf)") - email = get_option(options, :admin_email, "What is your admin email address?") + email = Common.get_option(options, :admin_email, "What is your admin email address?") - dbhost = get_option(options, :dbhost, "What is the hostname of your database?", "localhost") + dbhost = Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost") - dbname = get_option(options, :dbname, "What is the name of your database?", "pleroma_dev") + dbname = Common.get_option(options, :dbname, "What is the name of your database?", "pleroma_dev") dbuser = - get_option( + Common.get_option( options, :dbuser, "What is the user used to connect to your database?", @@ -83,7 +84,7 @@ def run(["gen" | rest]) do ) dbpass = - get_option( + Common.get_option( options, :dbpass, "What is the password used to connect to your database?", @@ -128,12 +129,12 @@ def run(["gen" | rest]) do """ To get started: 1. Verify the contents of the generated files. - 2. Run `sudo -u postgres psql -f #{escape_sh_path(psql_path)}`. + 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 #{escape_sh_path(config_path)} 'config/prod.secret.exs'`." + "3. Run `mv #{Common.escape_sh_path(config_path)} 'config/prod.secret.exs'`." end ) else @@ -145,21 +146,6 @@ def run(["gen" | rest]) do end end - defp escape_sh_path(path) do - ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') - end - defp get_option(options, opt, prompt, def \\ nil, defname \\ nil) do - Keyword.get(options, opt) || - case Mix.shell().prompt("#{prompt} [#{defname || def}]") do - "\n" -> - case def do - nil -> get_option(options, opt, prompt, def) - def -> def - end - opt -> - opt |> String.trim() - end - end end diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index eaaded5cb..828c7cd3d 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -1,6 +1,7 @@ defmodule Mix.Tasks.Pleroma.Relay do use Mix.Task alias Pleroma.Web.ActivityPub.Relay + alias Mix.Tasks.Pleroma.Common @shortdoc "Manages remote relays" @moduledoc """ @@ -19,8 +20,7 @@ defmodule Mix.Tasks.Pleroma.Relay do Example: ``mix pleroma.relay unfollow https://example.org/relay`` """ def run(["follow", target]) do - Mix.Task.run("app.start") - + 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) @@ -30,7 +30,7 @@ def run(["follow", target]) do end def run(["unfollow", target]) do - Mix.Task.run("app.start") + Common.start_pleroma with {:ok, activity} <- Relay.follow(target) do # put this task to sleep to allow the genserver to push out the messages diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index aa023aae8..82f351456 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -2,6 +2,7 @@ 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 """ @@ -43,7 +44,6 @@ defmodule Mix.Tasks.Pleroma.User do - `--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( @@ -88,7 +88,7 @@ def run(["new", nickname, email | rest]) do proceed? = Mix.shell().yes?("Continue?") unless not proceed? do - Mix.Task.run("app.start") + Common.start_pleroma() params = %{ @@ -123,7 +123,7 @@ def run(["new", nickname, email | rest]) do end def run(["rm", nickname]) do - Mix.Task.run("app.start") + Common.start_pleroma() with %User{local: true} = user <- User.get_by_nickname(nickname) do User.delete(user) @@ -135,7 +135,7 @@ def run(["rm", nickname]) do end def run(["toggle_activated", nickname]) do - Mix.Task.run("app.start") + Common.start_pleroma() with %User{} = user <- User.get_by_nickname(nickname) do User.deactivate(user, !user.info["deactivated"]) @@ -147,7 +147,7 @@ def run(["toggle_activated", nickname]) do end def run(["reset_password", nickname]) do - Mix.Task.run("app.start") + Common.start_pleroma() with %User{local: true} = user <- User.get_by_nickname(nickname), {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do @@ -169,7 +169,7 @@ def run(["reset_password", nickname]) do end def run(["unsubscribe", nickname]) do - Mix.Task.run("app.start") + Common.start_pleroma() with %User{} = user <- User.get_by_nickname(nickname) do Mix.shell().info("Deactivating #{user.nickname}") @@ -198,7 +198,7 @@ def run(["unsubscribe", nickname]) do end def run(["set", nickname | rest]) do - Mix.Task.run("app.start") + Common.start_pleroma() {options, [], []} = OptionParser.parse( @@ -280,7 +280,7 @@ defp set_locked(user, value) do end def run(["invite"]) do - Mix.Task.run("app.start") + Common.start_pleroma() with {:ok, token} <- Pleroma.UserInviteToken.create_token() do Mix.shell().info("Generated user invite token") From ca7b46fb3ba576fb7e67eba02654e6df9299392a Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Thu, 6 Dec 2018 18:01:28 +0100 Subject: [PATCH 22/25] Refactor common functions to common.ex --- lib/mix/tasks/pleroma/instance.ex | 15 +++++++++------ lib/mix/tasks/pleroma/relay.ex | 5 +++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 88fc3ba75..e0ebb3f5e 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -67,13 +67,19 @@ def run(["gen" | rest]) do ) name = - Common.get_option(options, :name, "What is the name of your instance? (e.g. Pleroma/Soykaf)") + 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") + 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") + dbname = + Common.get_option(options, :dbname, "What is the name of your database?", "pleroma_dev") dbuser = Common.get_option( @@ -145,7 +151,4 @@ def run(["gen" | rest]) do ) end end - - - end diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index 828c7cd3d..4aea52732 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -20,7 +20,8 @@ defmodule Mix.Tasks.Pleroma.Relay do Example: ``mix pleroma.relay unfollow https://example.org/relay`` """ def run(["follow", target]) do - Common.start_pleroma + 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) @@ -30,7 +31,7 @@ def run(["follow", target]) do end def run(["unfollow", target]) do - Common.start_pleroma + Common.start_pleroma() with {:ok, activity} <- Relay.follow(target) do # put this task to sleep to allow the genserver to push out the messages From 66313cda02d8c335a2c24c0b7ad64ba6cebc4df8 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Thu, 6 Dec 2018 18:16:51 +0100 Subject: [PATCH 23/25] Update instance.ex for web push --- lib/mix/tasks/pleroma/instance.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index e0ebb3f5e..c66322707 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -99,6 +99,7 @@ def run(["gen" | rest]) do ) 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( @@ -111,7 +112,9 @@ def run(["gen" | rest]) do dbuser: dbuser, dbpass: dbpass, version: Pleroma.Mixfile.project() |> Keyword.get(:version), - secret: secret + 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 = From 71d5cf9ed86089bb2e5b280c462759590307b94d Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Thu, 6 Dec 2018 18:25:39 +0100 Subject: [PATCH 24/25] Remove unused vars from user.ex --- lib/mix/tasks/pleroma/user.ex | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 82f351456..12b5af774 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -232,10 +232,6 @@ def run(["set", nickname | rest]) do end defp set_moderator(user, value) do - info = - user.info - |> Map.put("is_moderator", value) - info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value}) user_cng = @@ -248,10 +244,6 @@ defp set_moderator(user, value) do end defp set_admin(user, value) do - info = - user.info - |> Map.put("is_admin", value) - info_cng = User.Info.admin_api_update(user.info, %{is_admin: value}) user_cng = @@ -264,10 +256,6 @@ defp set_admin(user, value) do end defp set_locked(user, value) do - info = - user.info - |> Map.put("locked", value) - info_cng = User.Info.user_upgrade(user.info, %{locked: value}) user_cng = From f5726a2e12102fd0742a767e508fcd4ac9406a09 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Thu, 6 Dec 2018 18:45:07 +0100 Subject: [PATCH 25/25] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a73c3f1af..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 pleroma.gen.instance`. 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. + * 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)