+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
--- /dev/null
+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
+# 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,
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
--- /dev/null
+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;
--- /dev/null
+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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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;
+++ /dev/null
-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