Allow to use Mix tasks inside Releases
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
a269d77044
commit
01f746a5d2
|
@ -6,12 +6,18 @@ defmodule Mix.Tasks.Mobilizon.Actors do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
alias Mix.Tasks
|
alias Mix.Tasks
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
@shortdoc "Manages Mobilizon actors"
|
@shortdoc "Manages Mobilizon actors"
|
||||||
|
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.shell().info("\nAvailable tasks:")
|
shell_info("\nAvailable tasks:")
|
||||||
Tasks.Help.run(["--search", "mobilizon.actors."])
|
|
||||||
|
if mix_shell?() do
|
||||||
|
Tasks.Help.run(["--search", "mobilizon.actors."])
|
||||||
|
else
|
||||||
|
show_subtasks_for_module(__MODULE__)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Mix.Tasks.Mobilizon.Actors.Refresh do
|
||||||
alias Mobilizon.Federation.ActivityPub
|
alias Mobilizon.Federation.ActivityPub
|
||||||
alias Mobilizon.Storage.Repo
|
alias Mobilizon.Storage.Repo
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@shortdoc "Refresh an actor or all actors"
|
@shortdoc "Refresh an actor or all actors"
|
||||||
|
@ -26,11 +27,11 @@ defmodule Mix.Tasks.Mobilizon.Actors.Refresh do
|
||||||
|
|
||||||
verbose = Keyword.get(options, :verbose, false)
|
verbose = Keyword.get(options, :verbose, false)
|
||||||
|
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
total = count_actors()
|
total = count_actors()
|
||||||
|
|
||||||
Mix.shell().info("""
|
shell_info("""
|
||||||
#{total} actors to process
|
#{total} actors to process
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
@ -62,22 +63,22 @@ defmodule Mix.Tasks.Mobilizon.Actors.Refresh do
|
||||||
|
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run([preferred_username]) do
|
def run([preferred_username]) do
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
case ActivityPub.make_actor_from_nickname(preferred_username) do
|
case ActivityPub.make_actor_from_nickname(preferred_username) do
|
||||||
{:ok, %Actor{}} ->
|
{:ok, %Actor{}} ->
|
||||||
Mix.shell().info("""
|
shell_info("""
|
||||||
Actor #{preferred_username} refreshed
|
Actor #{preferred_username} refreshed
|
||||||
""")
|
""")
|
||||||
|
|
||||||
{:actor, nil} ->
|
{:actor, nil} ->
|
||||||
Mix.raise("Error: No such actor")
|
shell_error("Error: No such actor")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.raise("mobilizon.actors.refresh requires an username as argument or --all as an option")
|
shell_error("mobilizon.actors.refresh requires an username as argument or --all as an option")
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec make_actor(String.t(), boolean()) :: any()
|
@spec make_actor(String.t(), boolean()) :: any()
|
||||||
|
|
|
@ -5,16 +5,17 @@ defmodule Mix.Tasks.Mobilizon.Actors.Show do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
alias Mobilizon.Actors
|
alias Mobilizon.Actors
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
@shortdoc "Show a Mobilizon user details"
|
@shortdoc "Show a Mobilizon user details"
|
||||||
|
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run([preferred_username]) do
|
def run([preferred_username]) do
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
case {:actor, Actors.get_actor_by_name_with_preload(preferred_username)} do
|
case {:actor, Actors.get_actor_by_name_with_preload(preferred_username)} do
|
||||||
{:actor, %Actor{} = actor} ->
|
{:actor, %Actor{} = actor} ->
|
||||||
Mix.shell().info("""
|
shell_info("""
|
||||||
Informations for the actor #{actor.preferred_username}:
|
Informations for the actor #{actor.preferred_username}:
|
||||||
- Type: #{actor.type}
|
- Type: #{actor.type}
|
||||||
- Domain: #{if is_nil(actor.domain), do: "Local", else: actor.domain}
|
- Domain: #{if is_nil(actor.domain), do: "Local", else: actor.domain}
|
||||||
|
@ -24,11 +25,11 @@ defmodule Mix.Tasks.Mobilizon.Actors.Show do
|
||||||
""")
|
""")
|
||||||
|
|
||||||
{:actor, nil} ->
|
{:actor, nil} ->
|
||||||
Mix.raise("Error: No such actor")
|
shell_error("Error: No such actor")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.raise("mobilizon.actors.show requires an username as argument")
|
shell_error("mobilizon.actors.show requires an username as argument")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,32 +8,107 @@ defmodule Mix.Tasks.Mobilizon.Common do
|
||||||
Common functions to be reused in mix tasks
|
Common functions to be reused in mix tasks
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_option(options, opt, prompt, defval \\ nil, defname \\ nil) do
|
|
||||||
display = if defname || defval, do: "#{prompt} [#{defname || defval}]", else: "#{prompt}"
|
|
||||||
|
|
||||||
Keyword.get(options, opt) ||
|
|
||||||
case Mix.shell().prompt(display) do
|
|
||||||
"\n" ->
|
|
||||||
case defval do
|
|
||||||
nil ->
|
|
||||||
get_option(options, opt, prompt, defval)
|
|
||||||
|
|
||||||
defval ->
|
|
||||||
defval
|
|
||||||
end
|
|
||||||
|
|
||||||
opt ->
|
|
||||||
String.trim(opt)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def start_mobilizon do
|
def start_mobilizon do
|
||||||
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
|
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
|
||||||
|
|
||||||
{:ok, _} = Application.ensure_all_started(:mobilizon)
|
{:ok, _} = Application.ensure_all_started(:mobilizon)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_option(options, opt, prompt, defval \\ nil, defname \\ nil) do
|
||||||
|
Keyword.get(options, opt) || shell_prompt(prompt, defval, defname)
|
||||||
|
end
|
||||||
|
|
||||||
|
def shell_prompt(prompt, defval \\ nil, defname \\ nil) do
|
||||||
|
prompt_message = "#{prompt} [#{defname || defval}] "
|
||||||
|
|
||||||
|
input =
|
||||||
|
if mix_shell?(),
|
||||||
|
do: Mix.shell().prompt(prompt_message),
|
||||||
|
else: :io.get_line(prompt_message)
|
||||||
|
|
||||||
|
case input do
|
||||||
|
"\n" ->
|
||||||
|
case defval do
|
||||||
|
nil ->
|
||||||
|
shell_prompt(prompt, defval, defname)
|
||||||
|
|
||||||
|
defval ->
|
||||||
|
defval
|
||||||
|
end
|
||||||
|
|
||||||
|
input ->
|
||||||
|
String.trim(input)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def shell_yes?(message) do
|
||||||
|
if mix_shell?(),
|
||||||
|
do: Mix.shell().yes?("Continue?"),
|
||||||
|
else: shell_prompt(message, "Continue?") in ~w(Yn Y y)
|
||||||
|
end
|
||||||
|
|
||||||
|
def shell_info(message) do
|
||||||
|
if mix_shell?(),
|
||||||
|
do: Mix.shell().info(message),
|
||||||
|
else: IO.puts(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def shell_error(message) do
|
||||||
|
if mix_shell?(),
|
||||||
|
do: Mix.shell().error(message),
|
||||||
|
else: IO.puts(:stderr, message)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Performs a safe check whether `Mix.shell/0` is available (does not raise if Mix is not loaded)"
|
||||||
|
def mix_shell?, do: :erlang.function_exported(Mix, :shell, 0)
|
||||||
|
|
||||||
def escape_sh_path(path) do
|
def escape_sh_path(path) do
|
||||||
~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(')
|
~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@type task_module :: atom
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets the shortdoc for the given task `module`.
|
||||||
|
Returns the shortdoc or `nil`.
|
||||||
|
"""
|
||||||
|
@spec shortdoc(task_module) :: String.t() | nil
|
||||||
|
def shortdoc(module) when is_atom(module) do
|
||||||
|
case List.keyfind(module.__info__(:attributes), :shortdoc, 0) do
|
||||||
|
{:shortdoc, [shortdoc]} -> shortdoc
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_subtasks_for_module(module_name) do
|
||||||
|
tasks = list_subtasks_for_module(module_name)
|
||||||
|
|
||||||
|
max = Enum.reduce(tasks, 0, fn {name, _doc}, acc -> max(byte_size(name), acc) end)
|
||||||
|
|
||||||
|
Enum.each(tasks, fn {name, doc} ->
|
||||||
|
shell_info("#{String.pad_trailing(name, max + 2)} # #{doc}")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec list_subtasks_for_module(atom()) :: list({String.t(), String.t()})
|
||||||
|
def list_subtasks_for_module(module_name) do
|
||||||
|
Application.load(:mobilizon)
|
||||||
|
{:ok, modules} = :application.get_key(:mobilizon, :modules)
|
||||||
|
module_name = to_string(module_name)
|
||||||
|
|
||||||
|
modules
|
||||||
|
|> Enum.filter(fn module ->
|
||||||
|
String.starts_with?(to_string(module), to_string(module_name)) &&
|
||||||
|
to_string(module) != to_string(module_name)
|
||||||
|
end)
|
||||||
|
|> Enum.map(&format_module/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_module(module) do
|
||||||
|
{format_name(to_string(module)), shortdoc(module)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_name("Elixir.Mix.Tasks.Mobilizon." <> task_name) do
|
||||||
|
String.downcase(task_name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,12 +8,13 @@ defmodule Mix.Tasks.Mobilizon.CreateBot do
|
||||||
alias Mobilizon.{Actors, Users}
|
alias Mobilizon.{Actors, Users}
|
||||||
alias Mobilizon.Actors.Bot
|
alias Mobilizon.Actors.Bot
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@shortdoc "Create bot"
|
@shortdoc "Create bot"
|
||||||
def run([email, name, summary, type, url]) do
|
def run([email, name, summary, type, url]) do
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
with {:ok, %User{} = user} <- Users.get_user_by_email(email, true),
|
with {:ok, %User{} = user} <- Users.get_user_by_email(email, true),
|
||||||
actor <- Actors.register_bot(%{name: name, summary: summary}),
|
actor <- Actors.register_bot(%{name: name, summary: summary}),
|
||||||
|
|
54
lib/mix/tasks/mobilizon/ecto.ex
Normal file
54
lib/mix/tasks/mobilizon/ecto.ex
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-onl
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Mobilizon.Ecto do
|
||||||
|
@moduledoc """
|
||||||
|
Provides tools for Ecto-related tasks (such as migrations)
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Ensures the given repository's migrations path exists on the file system.
|
||||||
|
"""
|
||||||
|
@spec ensure_migrations_path(Ecto.Repo.t(), Keyword.t()) :: String.t()
|
||||||
|
def ensure_migrations_path(repo, opts) do
|
||||||
|
path = opts[:migrations_path] || Path.join(source_repo_priv(repo), "migrations")
|
||||||
|
|
||||||
|
path =
|
||||||
|
case Path.type(path) do
|
||||||
|
:relative ->
|
||||||
|
Path.join(Application.app_dir(:mobilizon), path)
|
||||||
|
|
||||||
|
:absolute ->
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
if not File.dir?(path) do
|
||||||
|
raise_missing_migrations(Path.relative_to_cwd(path), repo)
|
||||||
|
end
|
||||||
|
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the private repository path relative to the source.
|
||||||
|
"""
|
||||||
|
def source_repo_priv(repo) do
|
||||||
|
config = repo.config()
|
||||||
|
priv = config[:priv] || "priv/#{repo |> Module.split() |> List.last() |> Macro.underscore()}"
|
||||||
|
Path.join(Application.app_dir(:mobilizon), priv)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp raise_missing_migrations(path, repo) do
|
||||||
|
raise("""
|
||||||
|
Could not find migrations directory #{inspect(path)}
|
||||||
|
for repo #{inspect(repo)}.
|
||||||
|
This may be because you are in a new project and the
|
||||||
|
migration directory has not been created yet. Creating an
|
||||||
|
empty directory at the path above will fix this error.
|
||||||
|
If you expected existing migrations to be found, please
|
||||||
|
make sure your repository has been properly configured
|
||||||
|
and the configured path exists.
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
end
|
71
lib/mix/tasks/mobilizon/ecto/migrate.ex
Normal file
71
lib/mix/tasks/mobilizon/ecto/migrate.ex
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# Portions of this file are derived from Pleroma:
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Mobilizon.Ecto.Migrate do
|
||||||
|
use Mix.Task
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
alias Mix.Tasks.Mobilizon.Ecto, as: EctoTask
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@shortdoc "Wrapper on `ecto.migrate` task."
|
||||||
|
|
||||||
|
@aliases [
|
||||||
|
n: :step,
|
||||||
|
v: :to
|
||||||
|
]
|
||||||
|
|
||||||
|
@switches [
|
||||||
|
all: :boolean,
|
||||||
|
step: :integer,
|
||||||
|
to: :integer,
|
||||||
|
quiet: :boolean,
|
||||||
|
log_sql: :boolean,
|
||||||
|
strict_version_order: :boolean,
|
||||||
|
migrations_path: :string
|
||||||
|
]
|
||||||
|
|
||||||
|
@repo Mobilizon.Storage.Repo
|
||||||
|
|
||||||
|
@moduledoc """
|
||||||
|
Changes `Logger` level to `:info` before start migration.
|
||||||
|
Changes level back when migration ends.
|
||||||
|
|
||||||
|
## Start migration
|
||||||
|
|
||||||
|
mix mobilizon.ecto.migrate [OPTIONS]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- see https://hexdocs.pm/ecto/2.0.0/Mix.Tasks.Ecto.Migrate.html
|
||||||
|
"""
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def run(args \\ []) do
|
||||||
|
start_mobilizon()
|
||||||
|
{opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)
|
||||||
|
|
||||||
|
if Application.get_env(:mobilizon, @repo)[:ssl] do
|
||||||
|
Application.ensure_all_started(:ssl)
|
||||||
|
end
|
||||||
|
|
||||||
|
opts =
|
||||||
|
if opts[:to] || opts[:step] || opts[:all],
|
||||||
|
do: opts,
|
||||||
|
else: Keyword.put(opts, :all, true)
|
||||||
|
|
||||||
|
opts =
|
||||||
|
if opts[:quiet],
|
||||||
|
do: Keyword.merge(opts, log: false, log_sql: false),
|
||||||
|
else: opts
|
||||||
|
|
||||||
|
path = EctoTask.ensure_migrations_path(@repo, opts)
|
||||||
|
|
||||||
|
level = Logger.level()
|
||||||
|
Logger.configure(level: :info)
|
||||||
|
|
||||||
|
{:ok, _, _} = Ecto.Migrator.with_repo(@repo, &Ecto.Migrator.run(&1, path, :up, opts))
|
||||||
|
|
||||||
|
Logger.configure(level: level)
|
||||||
|
end
|
||||||
|
end
|
74
lib/mix/tasks/mobilizon/ecto/rollback.ex
Normal file
74
lib/mix/tasks/mobilizon/ecto/rollback.ex
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Portions of this file are derived from Pleroma:
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Mobilizon.Ecto.Rollback do
|
||||||
|
use Mix.Task
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
alias Mix.Tasks.Mobilizon.Ecto, as: EctoTask
|
||||||
|
require Logger
|
||||||
|
@shortdoc "Wrapper on `ecto.rollback` task"
|
||||||
|
|
||||||
|
@aliases [
|
||||||
|
n: :step,
|
||||||
|
v: :to
|
||||||
|
]
|
||||||
|
|
||||||
|
@switches [
|
||||||
|
all: :boolean,
|
||||||
|
step: :integer,
|
||||||
|
to: :integer,
|
||||||
|
start: :boolean,
|
||||||
|
quiet: :boolean,
|
||||||
|
log_sql: :boolean,
|
||||||
|
migrations_path: :string
|
||||||
|
]
|
||||||
|
|
||||||
|
@repo Mobilizon.Storage.Repo
|
||||||
|
|
||||||
|
@moduledoc """
|
||||||
|
Changes `Logger` level to `:info` before start rollback.
|
||||||
|
Changes level back when rollback ends.
|
||||||
|
|
||||||
|
## Start rollback
|
||||||
|
|
||||||
|
mix mobilizon.ecto.rollback
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- see https://hexdocs.pm/ecto/2.0.0/Mix.Tasks.Ecto.Rollback.html
|
||||||
|
"""
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def run(args \\ []) do
|
||||||
|
start_mobilizon()
|
||||||
|
{opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)
|
||||||
|
|
||||||
|
if Application.get_env(:mobilizon, @repo)[:ssl] do
|
||||||
|
Application.ensure_all_started(:ssl)
|
||||||
|
end
|
||||||
|
|
||||||
|
opts =
|
||||||
|
if opts[:to] || opts[:step] || opts[:all],
|
||||||
|
do: opts,
|
||||||
|
else: Keyword.put(opts, :step, 1)
|
||||||
|
|
||||||
|
opts =
|
||||||
|
if opts[:quiet],
|
||||||
|
do: Keyword.merge(opts, log: false, log_sql: false),
|
||||||
|
else: opts
|
||||||
|
|
||||||
|
path = EctoTask.ensure_migrations_path(@repo, opts)
|
||||||
|
|
||||||
|
level = Logger.level()
|
||||||
|
Logger.configure(level: :info)
|
||||||
|
|
||||||
|
if Mobilizon.Config.get(:env) == :test do
|
||||||
|
Logger.info("Rollback succesfully")
|
||||||
|
else
|
||||||
|
{:ok, _, _} = Ecto.Migrator.with_repo(@repo, &Ecto.Migrator.run(&1, path, :down, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
Logger.configure(level: level)
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,12 +6,13 @@ defmodule Mix.Tasks.Mobilizon.Groups do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
alias Mix.Tasks
|
alias Mix.Tasks
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
@shortdoc "Manages Mobilizon groups"
|
@shortdoc "Manages Mobilizon groups"
|
||||||
|
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.shell().info("\nAvailable tasks:")
|
shell_info("\nAvailable tasks:")
|
||||||
Tasks.Help.run(["--search", "mobilizon.groups."])
|
Tasks.Help.run(["--search", "mobilizon.groups."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,12 +6,13 @@ defmodule Mix.Tasks.Mobilizon.Groups.Refresh do
|
||||||
alias Mobilizon.Actors
|
alias Mobilizon.Actors
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
alias Mobilizon.Federation.ActivityPub.Refresher
|
alias Mobilizon.Federation.ActivityPub.Refresher
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
@shortdoc "Refresh a group private informations from an account member"
|
@shortdoc "Refresh a group private informations from an account member"
|
||||||
|
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run([group_url, on_behalf_of]) do
|
def run([group_url, on_behalf_of]) do
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
with %Actor{} = actor <- Actors.get_local_actor_by_name(on_behalf_of) do
|
with %Actor{} = actor <- Actors.get_local_actor_by_name(on_behalf_of) do
|
||||||
res = Refresher.fetch_group(group_url, actor)
|
res = Refresher.fetch_group(group_url, actor)
|
||||||
|
@ -20,7 +21,7 @@ defmodule Mix.Tasks.Mobilizon.Groups.Refresh do
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.raise(
|
shell_error(
|
||||||
"mobilizon.groups.refresh requires a group URL and an actor username which is member of the group as arguments"
|
"mobilizon.groups.refresh requires a group URL and an actor username which is member of the group as arguments"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,7 +29,7 @@ defmodule Mix.Tasks.Mobilizon.Instance do
|
||||||
|
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
alias Mix.Tasks.Mobilizon.Common
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
@preferred_cli_env "prod"
|
@preferred_cli_env "prod"
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ defmodule Mix.Tasks.Mobilizon.Instance do
|
||||||
if proceed? do
|
if proceed? do
|
||||||
[domain, port | _] =
|
[domain, port | _] =
|
||||||
String.split(
|
String.split(
|
||||||
Common.get_option(
|
get_option(
|
||||||
options,
|
options,
|
||||||
:domain,
|
:domain,
|
||||||
"What domain will your instance use? (e.g mobilizon.org)"
|
"What domain will your instance use? (e.g mobilizon.org)"
|
||||||
|
@ -79,25 +79,24 @@ defmodule Mix.Tasks.Mobilizon.Instance do
|
||||||
) ++ [443]
|
) ++ [443]
|
||||||
|
|
||||||
name =
|
name =
|
||||||
Common.get_option(
|
get_option(
|
||||||
options,
|
options,
|
||||||
:instance_name,
|
:instance_name,
|
||||||
"What is the name of your instance? (e.g. Mobilizon)"
|
"What is the name of your instance? (e.g. Mobilizon)"
|
||||||
)
|
)
|
||||||
|
|
||||||
email =
|
email =
|
||||||
Common.get_option(
|
get_option(
|
||||||
options,
|
options,
|
||||||
:admin_email,
|
:admin_email,
|
||||||
"What's the address email will be send with?",
|
"What's the address email will be send with?",
|
||||||
"noreply@#{domain}"
|
"noreply@#{domain}"
|
||||||
)
|
)
|
||||||
|
|
||||||
dbhost =
|
dbhost = get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
|
||||||
Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
|
|
||||||
|
|
||||||
dbname =
|
dbname =
|
||||||
Common.get_option(
|
get_option(
|
||||||
options,
|
options,
|
||||||
:dbname,
|
:dbname,
|
||||||
"What is the name of your database?",
|
"What is the name of your database?",
|
||||||
|
@ -105,7 +104,7 @@ defmodule Mix.Tasks.Mobilizon.Instance do
|
||||||
)
|
)
|
||||||
|
|
||||||
dbuser =
|
dbuser =
|
||||||
Common.get_option(
|
get_option(
|
||||||
options,
|
options,
|
||||||
:dbuser,
|
:dbuser,
|
||||||
"What is the user used to connect to your database?",
|
"What is the user used to connect to your database?",
|
||||||
|
@ -113,7 +112,7 @@ defmodule Mix.Tasks.Mobilizon.Instance do
|
||||||
)
|
)
|
||||||
|
|
||||||
dbpass =
|
dbpass =
|
||||||
Common.get_option(
|
get_option(
|
||||||
options,
|
options,
|
||||||
:dbpass,
|
:dbpass,
|
||||||
"What is the password used to connect to your database?",
|
"What is the password used to connect to your database?",
|
||||||
|
@ -122,7 +121,7 @@ defmodule Mix.Tasks.Mobilizon.Instance do
|
||||||
)
|
)
|
||||||
|
|
||||||
listen_port =
|
listen_port =
|
||||||
Common.get_option(
|
get_option(
|
||||||
options,
|
options,
|
||||||
:listen_port,
|
:listen_port,
|
||||||
"What port will the app listen to (leave it if you are using the default setup with nginx)?",
|
"What port will the app listen to (leave it if you are using the default setup with nginx)?",
|
||||||
|
@ -160,24 +159,24 @@ defmodule Mix.Tasks.Mobilizon.Instance do
|
||||||
database_password: dbpass
|
database_password: dbpass
|
||||||
)
|
)
|
||||||
|
|
||||||
Mix.shell().info("Writing config to #{config_path}.")
|
shell_info("Writing config to #{config_path}.")
|
||||||
|
|
||||||
File.write(config_path, result_config)
|
File.write(config_path, result_config)
|
||||||
Mix.shell().info("Writing #{psql_path}.")
|
shell_info("Writing #{psql_path}.")
|
||||||
File.write(psql_path, result_psql)
|
File.write(psql_path, result_psql)
|
||||||
|
|
||||||
Mix.shell().info(
|
shell_info(
|
||||||
"\n" <>
|
"\n" <>
|
||||||
"""
|
"""
|
||||||
To get started:
|
To get started:
|
||||||
1. Check the contents of the generated files.
|
1. Check the contents of the generated files.
|
||||||
2. Run `sudo -u postgres psql -f #{Common.escape_sh_path(psql_path)} && rm #{
|
2. Run `sudo -u postgres psql -f #{escape_sh_path(psql_path)} && rm #{
|
||||||
Common.escape_sh_path(psql_path)
|
escape_sh_path(psql_path)
|
||||||
}`.
|
}`.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
Mix.shell().error(
|
shell_error(
|
||||||
"The task would have overwritten the following files:\n" <>
|
"The task would have overwritten the following files:\n" <>
|
||||||
(will_overwrite |> Enum.map(&"- #{&1}\n") |> Enum.join("")) <>
|
(will_overwrite |> Enum.map(&"- #{&1}\n") |> Enum.join("")) <>
|
||||||
"Rerun with `-f/--force` to overwrite them."
|
"Rerun with `-f/--force` to overwrite them."
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
defmodule Mix.Tasks.Mobilizon.MoveParticipantStats do
|
|
||||||
@moduledoc """
|
|
||||||
Temporary task to move participant stats in the events table
|
|
||||||
|
|
||||||
This task will be removed in version 1.0.0-beta.3
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Mix.Task
|
|
||||||
|
|
||||||
import Ecto.Query
|
|
||||||
|
|
||||||
alias Mobilizon.Events
|
|
||||||
alias Mobilizon.Events.Event
|
|
||||||
alias Mobilizon.Events.ParticipantRole
|
|
||||||
alias Mobilizon.Storage.Repo
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
@shortdoc "Move participant stats to events table"
|
|
||||||
def run([]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
events =
|
|
||||||
Event
|
|
||||||
|> preload([e], :tags)
|
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
nb_events = length(events)
|
|
||||||
|
|
||||||
IO.puts(
|
|
||||||
"\nStarting inserting participants stats into #{nb_events} events, this can take a while…\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
insert_participants_stats_into_events(events, nb_events)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp insert_participants_stats_into_events([%Event{url: url} = event | events], nb_events) do
|
|
||||||
with roles <- ParticipantRole.__enum_map__(),
|
|
||||||
counts <-
|
|
||||||
Enum.reduce(roles, %{}, fn role, acc ->
|
|
||||||
Map.put(acc, role, count_participants(event, role))
|
|
||||||
end),
|
|
||||||
{:ok, _} <-
|
|
||||||
Events.update_event(event, %{
|
|
||||||
participant_stats: counts
|
|
||||||
}) do
|
|
||||||
Logger.debug("Added participants stats to event #{url}")
|
|
||||||
else
|
|
||||||
{:error, res} ->
|
|
||||||
Logger.error("Error while adding participants stats to event #{url} : #{inspect(res)}")
|
|
||||||
end
|
|
||||||
|
|
||||||
ProgressBar.render(nb_events - length(events), nb_events)
|
|
||||||
|
|
||||||
insert_participants_stats_into_events(events, nb_events)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp insert_participants_stats_into_events([], nb_events) do
|
|
||||||
IO.puts("\nFinished inserting participant stats for #{nb_events} events!\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp count_participants(%Event{id: event_id}, role) when is_atom(role) do
|
|
||||||
event_id
|
|
||||||
|> Events.count_participants_query()
|
|
||||||
|> Events.filter_role(role)
|
|
||||||
|> Repo.aggregate(:count, :id)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -22,60 +22,19 @@ defmodule Mix.Tasks.Mobilizon.Relay do
|
||||||
|
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
alias Mix.Tasks.Mobilizon.Common
|
alias Mix.Tasks
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
alias Mobilizon.Federation.ActivityPub.Relay
|
|
||||||
|
|
||||||
@shortdoc "Manages remote relays"
|
@shortdoc "Manages remote relays"
|
||||||
def run(["follow", target]) do
|
|
||||||
Common.start_mobilizon()
|
|
||||||
|
|
||||||
case Relay.follow(target) do
|
@impl Mix.Task
|
||||||
{:ok, _activity, _follow} ->
|
def run(_) do
|
||||||
# put this task to sleep to allow the genserver to push out the messages
|
shell_info("\nAvailable tasks:")
|
||||||
:timer.sleep(500)
|
|
||||||
|
|
||||||
{:error, e} ->
|
if mix_shell?() do
|
||||||
IO.puts(:stderr, "Error while following #{target}: #{inspect(e)}")
|
Tasks.Help.run(["--search", "mobilizon.relay."])
|
||||||
end
|
else
|
||||||
end
|
show_subtasks_for_module(__MODULE__)
|
||||||
|
|
||||||
def run(["unfollow", target]) do
|
|
||||||
Common.start_mobilizon()
|
|
||||||
|
|
||||||
case Relay.unfollow(target) do
|
|
||||||
{:ok, _activity, _follow} ->
|
|
||||||
# put this task to sleep to allow the genserver to push out the messages
|
|
||||||
:timer.sleep(500)
|
|
||||||
|
|
||||||
{:error, e} ->
|
|
||||||
IO.puts(:stderr, "Error while unfollowing #{target}: #{inspect(e)}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(["accept", target]) do
|
|
||||||
Common.start_mobilizon()
|
|
||||||
|
|
||||||
case Relay.accept(target) do
|
|
||||||
{:ok, _activity} ->
|
|
||||||
# put this task to sleep to allow the genserver to push out the messages
|
|
||||||
:timer.sleep(500)
|
|
||||||
|
|
||||||
{:error, e} ->
|
|
||||||
IO.puts(:stderr, "Error while accept #{target} follow: #{inspect(e)}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(["refresh", target]) do
|
|
||||||
Common.start_mobilizon()
|
|
||||||
IO.puts("Refreshing #{target}, this can take a while.")
|
|
||||||
|
|
||||||
case Relay.refresh(target) do
|
|
||||||
:ok ->
|
|
||||||
IO.puts("Refreshed #{target}")
|
|
||||||
|
|
||||||
err ->
|
|
||||||
IO.puts(:stderr, "Error while refreshing #{target}: #{inspect(err)}")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
28
lib/mix/tasks/mobilizon/relay/accept.ex
Normal file
28
lib/mix/tasks/mobilizon/relay/accept.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule Mix.Tasks.Mobilizon.Relay.Accept do
|
||||||
|
@moduledoc """
|
||||||
|
Task to accept an instance follow request
|
||||||
|
"""
|
||||||
|
use Mix.Task
|
||||||
|
alias Mobilizon.Federation.ActivityPub.Relay
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
|
@shortdoc "Accept an instance follow request"
|
||||||
|
|
||||||
|
@impl Mix.Task
|
||||||
|
def run([target]) do
|
||||||
|
start_mobilizon()
|
||||||
|
|
||||||
|
case Relay.accept(target) do
|
||||||
|
{:ok, _activity} ->
|
||||||
|
# put this task to sleep to allow the genserver to push out the messages
|
||||||
|
:timer.sleep(500)
|
||||||
|
|
||||||
|
{:error, e} ->
|
||||||
|
IO.puts(:stderr, "Error while accept #{target} follow: #{inspect(e)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(_) do
|
||||||
|
shell_error("mobilizon.relay.accept requires an instance hostname as arguments")
|
||||||
|
end
|
||||||
|
end
|
28
lib/mix/tasks/mobilizon/relay/follow.ex
Normal file
28
lib/mix/tasks/mobilizon/relay/follow.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule Mix.Tasks.Mobilizon.Relay.Follow do
|
||||||
|
@moduledoc """
|
||||||
|
Task to follow an instance
|
||||||
|
"""
|
||||||
|
use Mix.Task
|
||||||
|
alias Mobilizon.Federation.ActivityPub.Relay
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
|
@shortdoc "Follow an instance"
|
||||||
|
|
||||||
|
@impl Mix.Task
|
||||||
|
def run([target]) do
|
||||||
|
start_mobilizon()
|
||||||
|
|
||||||
|
case Relay.follow(target) do
|
||||||
|
{:ok, _activity, _follow} ->
|
||||||
|
# put this task to sleep to allow the genserver to push out the messages
|
||||||
|
:timer.sleep(500)
|
||||||
|
|
||||||
|
{:error, e} ->
|
||||||
|
IO.puts(:stderr, "Error while following #{target}: #{inspect(e)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(_) do
|
||||||
|
shell_error("mobilizon.relay.follow requires an instance hostname as arguments")
|
||||||
|
end
|
||||||
|
end
|
28
lib/mix/tasks/mobilizon/relay/refresh.ex
Normal file
28
lib/mix/tasks/mobilizon/relay/refresh.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule Mix.Tasks.Mobilizon.Relay.Refresh do
|
||||||
|
@moduledoc """
|
||||||
|
Task to refresh an instance details
|
||||||
|
"""
|
||||||
|
use Mix.Task
|
||||||
|
alias Mobilizon.Federation.ActivityPub.Relay
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
|
@shortdoc "Refresh an instance informations and crawl their outbox"
|
||||||
|
|
||||||
|
@impl Mix.Task
|
||||||
|
def run([target]) do
|
||||||
|
start_mobilizon()
|
||||||
|
IO.puts("Refreshing #{target}, this can take a while.")
|
||||||
|
|
||||||
|
case Relay.refresh(target) do
|
||||||
|
:ok ->
|
||||||
|
IO.puts("Refreshed #{target}")
|
||||||
|
|
||||||
|
err ->
|
||||||
|
IO.puts(:stderr, "Error while refreshing #{target}: #{inspect(err)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(_) do
|
||||||
|
shell_error("mobilizon.relay.refresh requires an instance hostname as arguments")
|
||||||
|
end
|
||||||
|
end
|
28
lib/mix/tasks/mobilizon/relay/unfollow.ex
Normal file
28
lib/mix/tasks/mobilizon/relay/unfollow.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule Mix.Tasks.Mobilizon.Relay.Unfollow do
|
||||||
|
@moduledoc """
|
||||||
|
Task to unfollow an instance
|
||||||
|
"""
|
||||||
|
use Mix.Task
|
||||||
|
alias Mobilizon.Federation.ActivityPub.Relay
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
|
@shortdoc "Unfollow an instance"
|
||||||
|
|
||||||
|
@impl Mix.Task
|
||||||
|
def run([target]) do
|
||||||
|
start_mobilizon()
|
||||||
|
|
||||||
|
case Relay.unfollow(target) do
|
||||||
|
{:ok, _activity, _follow} ->
|
||||||
|
# put this task to sleep to allow the genserver to push out the messages
|
||||||
|
:timer.sleep(500)
|
||||||
|
|
||||||
|
{:error, e} ->
|
||||||
|
IO.puts(:stderr, "Error while unfollowing #{target}: #{inspect(e)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(_) do
|
||||||
|
shell_error("mobilizon.relay.unfollow requires an instance hostname as arguments")
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,50 +0,0 @@
|
||||||
defmodule Mix.Tasks.Mobilizon.SetupSearch do
|
|
||||||
@moduledoc """
|
|
||||||
Temporary task to insert search data from existing events
|
|
||||||
|
|
||||||
This task will be removed in version 1.0.0-beta.3
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Mix.Task
|
|
||||||
|
|
||||||
import Ecto.Query
|
|
||||||
|
|
||||||
alias Mobilizon.Events.Event
|
|
||||||
alias Mobilizon.Service.Workers
|
|
||||||
alias Mobilizon.Storage.Repo
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
@shortdoc "Insert search data"
|
|
||||||
def run([]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
events =
|
|
||||||
Event
|
|
||||||
|> preload([e], :tags)
|
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
nb_events = length(events)
|
|
||||||
|
|
||||||
IO.puts("\nStarting setting up search for #{nb_events} events, this can take a while…\n")
|
|
||||||
insert_search_event(events, nb_events)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp insert_search_event([%Event{url: url} = event | events], nb_events) do
|
|
||||||
case Workers.BuildSearch.insert_search_event(event) do
|
|
||||||
{:ok, _} ->
|
|
||||||
Logger.debug("Added event #{url} to the search")
|
|
||||||
|
|
||||||
{:error, res} ->
|
|
||||||
Logger.error("Error while adding event #{url} to the search: #{inspect(res)}")
|
|
||||||
end
|
|
||||||
|
|
||||||
ProgressBar.render(nb_events - length(events), nb_events)
|
|
||||||
|
|
||||||
insert_search_event(events, nb_events)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp insert_search_event([], nb_events) do
|
|
||||||
IO.puts("\nFinished setting up search for #{nb_events} events!\n")
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -4,7 +4,7 @@ defmodule Mix.Tasks.Mobilizon.SiteMap do
|
||||||
"""
|
"""
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
alias Mix.Tasks.Mobilizon.Common
|
import Mix.Tasks.Mobilizon.Common
|
||||||
alias Mobilizon.Service.SiteMap
|
alias Mobilizon.Service.SiteMap
|
||||||
alias Mobilizon.Web.Endpoint
|
alias Mobilizon.Web.Endpoint
|
||||||
|
|
||||||
|
@ -12,10 +12,10 @@ defmodule Mix.Tasks.Mobilizon.SiteMap do
|
||||||
|
|
||||||
@shortdoc "Generates a new Sitemap"
|
@shortdoc "Generates a new Sitemap"
|
||||||
def run(["generate"]) do
|
def run(["generate"]) do
|
||||||
Common.start_mobilizon()
|
start_mobilizon()
|
||||||
|
|
||||||
with {:ok, :ok} <- SiteMap.generate_sitemap() do
|
with {:ok, :ok} <- SiteMap.generate_sitemap() do
|
||||||
Mix.shell().info("Sitemap saved to #{Endpoint.url()}/sitemap.xml")
|
shell_info("Sitemap saved to #{Endpoint.url()}/sitemap.xml")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
defmodule Mix.Tasks.Mobilizon.Toot do
|
|
||||||
@moduledoc """
|
|
||||||
Creates a bot from a source.
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Mix.Task
|
|
||||||
|
|
||||||
alias Mobilizon.Actors
|
|
||||||
alias Mobilizon.Actors.Actor
|
|
||||||
|
|
||||||
alias Mobilizon.GraphQL.API.Comments
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
@shortdoc "Toot to an user"
|
|
||||||
def run([from, text]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with {:local_actor, %Actor{} = actor} <- {:local_actor, Actors.get_local_actor_by_name(from)},
|
|
||||||
{:ok, _, _} <- Comments.create_comment(%{actor: actor, text: text}) do
|
|
||||||
Mix.shell().info("Tooted")
|
|
||||||
else
|
|
||||||
{:local_actor, _, _} ->
|
|
||||||
Mix.shell().error("Failed to toot.\nActor #{from} doesn't exist")
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Mix.shell().error("Failed to toot.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -6,12 +6,18 @@ defmodule Mix.Tasks.Mobilizon.Users do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
alias Mix.Tasks
|
alias Mix.Tasks
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
@shortdoc "Manages Mobilizon users"
|
@shortdoc "Manages Mobilizon users"
|
||||||
|
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.shell().info("\nAvailable tasks:")
|
shell_info("\nAvailable tasks:")
|
||||||
Tasks.Help.run(["--search", "mobilizon.users."])
|
|
||||||
|
if mix_shell?() do
|
||||||
|
Tasks.Help.run(["--search", "mobilizon.users."])
|
||||||
|
else
|
||||||
|
show_subtasks_for_module(__MODULE__)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ defmodule Mix.Tasks.Mobilizon.Users.Delete do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
alias Mobilizon.Users
|
alias Mobilizon.Users
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
|
|
||||||
@shortdoc "Deletes a Mobilizon user"
|
@shortdoc "Deletes a Mobilizon user"
|
||||||
|
|
||||||
|
@ -26,25 +27,25 @@ defmodule Mix.Tasks.Mobilizon.Users.Delete do
|
||||||
assume_yes? = Keyword.get(options, :assume_yes, false)
|
assume_yes? = Keyword.get(options, :assume_yes, false)
|
||||||
keep_email? = Keyword.get(options, :keep_email, false)
|
keep_email? = Keyword.get(options, :keep_email, false)
|
||||||
|
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
with {:ok, %User{} = user} <- Users.get_user_by_email(email),
|
with {:ok, %User{} = user} <- Users.get_user_by_email(email),
|
||||||
true <- assume_yes? or Mix.shell().yes?("Continue with deleting user #{user.email}?"),
|
true <- assume_yes? or shell_yes?("Continue with deleting user #{user.email}?"),
|
||||||
{:ok, %User{} = user} <-
|
{:ok, %User{} = user} <-
|
||||||
Users.delete_user(user, reserve_email: keep_email?) do
|
Users.delete_user(user, reserve_email: keep_email?) do
|
||||||
Mix.shell().info("""
|
shell_info("""
|
||||||
The user #{user.email} has been deleted
|
The user #{user.email} has been deleted
|
||||||
""")
|
""")
|
||||||
else
|
else
|
||||||
{:error, :user_not_found} ->
|
{:error, :user_not_found} ->
|
||||||
Mix.raise("Error: No such user")
|
shell_error("Error: No such user")
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Mix.raise("User has not been deleted.")
|
shell_error("User has not been deleted.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.raise("mobilizon.users.delete requires an email as argument")
|
shell_error("mobilizon.users.delete requires an email as argument")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ defmodule Mix.Tasks.Mobilizon.Users.Modify do
|
||||||
Task to modify an existing Mobilizon user
|
Task to modify an existing Mobilizon user
|
||||||
"""
|
"""
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
alias Mobilizon.Users
|
alias Mobilizon.Users
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
|
||||||
|
@ -31,10 +32,10 @@ defmodule Mix.Tasks.Mobilizon.Users.Modify do
|
||||||
new_email = Keyword.get(options, :email)
|
new_email = Keyword.get(options, :email)
|
||||||
|
|
||||||
if disable? && enable? do
|
if disable? && enable? do
|
||||||
Mix.raise("Can't use both --enabled and --disable options at the same time.")
|
shell_error("Can't use both --enabled and --disable options at the same time.")
|
||||||
end
|
end
|
||||||
|
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
with {:ok, %User{} = user} <- Users.get_user_by_email(email),
|
with {:ok, %User{} = user} <- Users.get_user_by_email(email),
|
||||||
attrs <- %{},
|
attrs <- %{},
|
||||||
|
@ -53,7 +54,7 @@ defmodule Mix.Tasks.Mobilizon.Users.Modify do
|
||||||
),
|
),
|
||||||
{:makes_changes, true} <- {:makes_changes, attrs != %{}},
|
{:makes_changes, true} <- {:makes_changes, attrs != %{}},
|
||||||
{:ok, %User{} = user} <- Users.update_user(user, attrs) do
|
{:ok, %User{} = user} <- Users.update_user(user, attrs) do
|
||||||
Mix.shell().info("""
|
shell_info("""
|
||||||
An user has been modified with the following information:
|
An user has been modified with the following information:
|
||||||
- email: #{user.email}
|
- email: #{user.email}
|
||||||
- Role: #{user.role}
|
- Role: #{user.role}
|
||||||
|
@ -61,23 +62,23 @@ defmodule Mix.Tasks.Mobilizon.Users.Modify do
|
||||||
""")
|
""")
|
||||||
else
|
else
|
||||||
{:makes_changes, false} ->
|
{:makes_changes, false} ->
|
||||||
Mix.shell().info("No change has been made")
|
shell_info("No change has been made")
|
||||||
|
|
||||||
{:error, :user_not_found} ->
|
{:error, :user_not_found} ->
|
||||||
Mix.raise("Error: No such user")
|
shell_error("Error: No such user")
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{errors: errors}} ->
|
{:error, %Ecto.Changeset{errors: errors}} ->
|
||||||
Mix.shell().error(inspect(errors))
|
shell_error(inspect(errors))
|
||||||
Mix.raise("User has not been modified because of the above reason.")
|
shell_error("User has not been modified because of the above reason.")
|
||||||
|
|
||||||
err ->
|
err ->
|
||||||
Mix.shell().error(inspect(err))
|
shell_error(inspect(err))
|
||||||
Mix.raise("User has not been modified because of an unknown reason.")
|
shell_error("User has not been modified because of an unknown reason.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.raise("mobilizon.users.new requires an email as argument")
|
shell_error("mobilizon.users.new requires an email as argument")
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec process_new_value(map(), atom(), any(), any()) :: map()
|
@spec process_new_value(map(), atom(), any(), any()) :: map()
|
||||||
|
|
|
@ -3,6 +3,7 @@ defmodule Mix.Tasks.Mobilizon.Users.New do
|
||||||
Task to create a new user
|
Task to create a new user
|
||||||
"""
|
"""
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
alias Mobilizon.Users
|
alias Mobilizon.Users
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ defmodule Mix.Tasks.Mobilizon.Users.New do
|
||||||
:crypto.strong_rand_bytes(16) |> Base.encode64() |> binary_part(0, 16)
|
:crypto.strong_rand_bytes(16) |> Base.encode64() |> binary_part(0, 16)
|
||||||
)
|
)
|
||||||
|
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
case Users.register(%{
|
case Users.register(%{
|
||||||
email: email,
|
email: email,
|
||||||
|
@ -51,7 +52,7 @@ defmodule Mix.Tasks.Mobilizon.Users.New do
|
||||||
confirmation_token: nil
|
confirmation_token: nil
|
||||||
}) do
|
}) do
|
||||||
{:ok, %User{} = user} ->
|
{:ok, %User{} = user} ->
|
||||||
Mix.shell().info("""
|
shell_info("""
|
||||||
An user has been created with the following information:
|
An user has been created with the following information:
|
||||||
- email: #{user.email}
|
- email: #{user.email}
|
||||||
- password: #{password}
|
- password: #{password}
|
||||||
|
@ -60,16 +61,16 @@ defmodule Mix.Tasks.Mobilizon.Users.New do
|
||||||
""")
|
""")
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{errors: errors}} ->
|
{:error, %Ecto.Changeset{errors: errors}} ->
|
||||||
Mix.shell().error(inspect(errors))
|
shell_error(inspect(errors))
|
||||||
Mix.raise("User has not been created because of the above reason.")
|
shell_error("User has not been created because of the above reason.")
|
||||||
|
|
||||||
err ->
|
err ->
|
||||||
Mix.shell().error(inspect(err))
|
shell_error(inspect(err))
|
||||||
Mix.raise("User has not been created because of an unknown reason.")
|
shell_error("User has not been created because of an unknown reason.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.raise("mobilizon.users.new requires an email as argument")
|
shell_error("mobilizon.users.new requires an email as argument")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@ defmodule Mix.Tasks.Mobilizon.Users.Show do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
import Mix.Tasks.Mobilizon.Common
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
alias Mobilizon.Users
|
alias Mobilizon.Users
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
@ -13,11 +13,11 @@ defmodule Mix.Tasks.Mobilizon.Users.Show do
|
||||||
|
|
||||||
@impl Mix.Task
|
@impl Mix.Task
|
||||||
def run([email]) do
|
def run([email]) do
|
||||||
Mix.Task.run("app.start")
|
start_mobilizon()
|
||||||
|
|
||||||
with {:ok, %User{} = user} <- Users.get_user_by_email(email),
|
with {:ok, %User{} = user} <- Users.get_user_by_email(email),
|
||||||
actors <- Users.get_actors_for_user(user) do
|
actors <- Users.get_actors_for_user(user) do
|
||||||
Mix.shell().info("""
|
shell_info("""
|
||||||
Informations for the user #{user.email}:
|
Informations for the user #{user.email}:
|
||||||
- Activated: #{user.confirmed_at}
|
- Activated: #{user.confirmed_at}
|
||||||
- Disabled: #{user.disabled}
|
- Disabled: #{user.disabled}
|
||||||
|
@ -26,12 +26,12 @@ defmodule Mix.Tasks.Mobilizon.Users.Show do
|
||||||
""")
|
""")
|
||||||
else
|
else
|
||||||
{:error, :user_not_found} ->
|
{:error, :user_not_found} ->
|
||||||
Mix.raise("Error: No such user")
|
shell_error("Error: No such user")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
def run(_) do
|
||||||
Mix.raise("mobilizon.users.show requires an email as argument")
|
shell_error("mobilizon.users.show requires an email as argument")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp display_actors([]), do: ""
|
defp display_actors([]), do: ""
|
||||||
|
|
|
@ -1,11 +1,53 @@
|
||||||
|
# Portions of this file are derived from Pleroma:
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Mobilizon.CLI do
|
defmodule Mobilizon.CLI do
|
||||||
@app :mobilizon
|
@moduledoc """
|
||||||
|
CLI wrapper for releases
|
||||||
|
"""
|
||||||
|
alias Mix.Tasks.Mobilizon.Ecto.{Migrate, Rollback}
|
||||||
|
|
||||||
def migrate do
|
def run(args) do
|
||||||
Application.load(@app)
|
[task | args] = String.split(args)
|
||||||
|
|
||||||
for repo <- Application.fetch_env!(@app, :ecto_repos) do
|
case task do
|
||||||
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
|
"migrate" -> migrate(args)
|
||||||
|
"rollback" -> rollback(args)
|
||||||
|
task -> mix_task(task, args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp mix_task(task, args) do
|
||||||
|
Application.load(:mobilizon)
|
||||||
|
{:ok, modules} = :application.get_key(:mobilizon, :modules)
|
||||||
|
|
||||||
|
module =
|
||||||
|
Enum.find(modules, fn module ->
|
||||||
|
module = Module.split(module)
|
||||||
|
|
||||||
|
case module do
|
||||||
|
["Mix", "Tasks", "Mobilizon" | rest] ->
|
||||||
|
String.downcase(Enum.join(rest, ".")) == task
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if module do
|
||||||
|
module.run(args)
|
||||||
|
else
|
||||||
|
IO.puts("The task #{task} does not exist")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def migrate(args) do
|
||||||
|
Migrate.run(args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def rollback(args) do
|
||||||
|
Rollback.run(args)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,7 +48,9 @@ defmodule Mix.Tasks.Mobilizon.ActorsTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "show non-existing actor" do
|
test "show non-existing actor" do
|
||||||
assert_raise Mix.Error, "Error: No such actor", fn -> Show.run([@username]) end
|
Show.run([@username])
|
||||||
|
assert_received {:mix_shell, :error, [message]}
|
||||||
|
assert message =~ "Error: No such actor"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Mix.Tasks.Mobilizon.RelayTest do
|
||||||
|
|
||||||
alias Mobilizon.Actors
|
alias Mobilizon.Actors
|
||||||
alias Mobilizon.Actors.{Actor, Follower}
|
alias Mobilizon.Actors.{Actor, Follower}
|
||||||
|
alias Mix.Tasks.Mobilizon.Relay.{Follow, Unfollow}
|
||||||
|
|
||||||
alias Mobilizon.Federation.ActivityPub.Relay
|
alias Mobilizon.Federation.ActivityPub.Relay
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ defmodule Mix.Tasks.Mobilizon.RelayTest do
|
||||||
use_cassette "relay/fetch_relay_follow" do
|
use_cassette "relay/fetch_relay_follow" do
|
||||||
target_instance = "mobilizon1.com"
|
target_instance = "mobilizon1.com"
|
||||||
|
|
||||||
Mix.Tasks.Mobilizon.Relay.run(["follow", target_instance])
|
Follow.run([target_instance])
|
||||||
|
|
||||||
local_actor = Relay.get_actor()
|
local_actor = Relay.get_actor()
|
||||||
assert local_actor.url =~ "/relay"
|
assert local_actor.url =~ "/relay"
|
||||||
|
@ -35,7 +36,7 @@ defmodule Mix.Tasks.Mobilizon.RelayTest do
|
||||||
use_cassette "relay/fetch_relay_unfollow" do
|
use_cassette "relay/fetch_relay_unfollow" do
|
||||||
target_instance = "mobilizon1.com"
|
target_instance = "mobilizon1.com"
|
||||||
|
|
||||||
Mix.Tasks.Mobilizon.Relay.run(["follow", target_instance])
|
Follow.run([target_instance])
|
||||||
|
|
||||||
%Actor{} = local_actor = Relay.get_actor()
|
%Actor{} = local_actor = Relay.get_actor()
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ defmodule Mix.Tasks.Mobilizon.RelayTest do
|
||||||
|
|
||||||
assert %Follower{} = Actors.is_following(local_actor, target_actor)
|
assert %Follower{} = Actors.is_following(local_actor, target_actor)
|
||||||
|
|
||||||
Mix.Tasks.Mobilizon.Relay.run(["unfollow", target_instance])
|
Unfollow.run([target_instance])
|
||||||
|
|
||||||
refute Actors.is_following(local_actor, target_actor)
|
refute Actors.is_following(local_actor, target_actor)
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,9 +42,15 @@ defmodule Mix.Tasks.Mobilizon.UsersTest do
|
||||||
test "create with already used email" do
|
test "create with already used email" do
|
||||||
insert(:user, email: @email)
|
insert(:user, email: @email)
|
||||||
|
|
||||||
assert_raise Mix.Error, "User has not been created because of the above reason.", fn ->
|
New.run([@email])
|
||||||
New.run([@email])
|
# Debug message
|
||||||
end
|
assert_received {:mix_shell, :error, [message]}
|
||||||
|
|
||||||
|
assert message =~
|
||||||
|
"[email: {\"This email is already used.\", [constraint: :unique, constraint_name: \"users_email_index\"]}]"
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :error, [message]}
|
||||||
|
assert message =~ "User has not been created because of the above reason."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -62,7 +68,9 @@ defmodule Mix.Tasks.Mobilizon.UsersTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete non-existing user" do
|
test "delete non-existing user" do
|
||||||
assert_raise Mix.Error, "Error: No such user", fn -> Delete.run([@email, "-y"]) end
|
Delete.run([@email, "-y"])
|
||||||
|
assert_received {:mix_shell, :error, [message]}
|
||||||
|
assert message =~ "Error: No such user"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -87,7 +95,9 @@ defmodule Mix.Tasks.Mobilizon.UsersTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "show non-existing user" do
|
test "show non-existing user" do
|
||||||
assert_raise Mix.Error, "Error: No such user", fn -> Show.run([@email]) end
|
Show.run([@email])
|
||||||
|
assert_received {:mix_shell, :error, [message]}
|
||||||
|
assert message =~ "Error: No such user"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -160,11 +170,9 @@ defmodule Mix.Tasks.Mobilizon.UsersTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "enable and disable at the same time" do
|
test "enable and disable at the same time" do
|
||||||
assert_raise Mix.Error,
|
Modify.run([@email, "--disable", "--enable"])
|
||||||
"Can't use both --enabled and --disable options at the same time.",
|
assert_received {:mix_shell, :error, [message]}
|
||||||
fn ->
|
assert message =~ "Can't use both --enabled and --disable options at the same time."
|
||||||
Modify.run([@email, "--disable", "--enable"])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue