Merge branch 'Pascoual/mobilizon-docker-multi-stage-prod' into 'master'
Docker support See merge request framasoft/mobilizon!674
This commit is contained in:
commit
c0591567f4
1
.gitignore
vendored
1
.gitignore
vendored
@ -38,3 +38,4 @@ release/
|
|||||||
*.mo
|
*.mo
|
||||||
*.po~
|
*.po~
|
||||||
.weblate
|
.weblate
|
||||||
|
docker/production/.env
|
||||||
|
42
docker/production/Dockerfile
Normal file
42
docker/production/Dockerfile
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# First build the application assets
|
||||||
|
FROM node:alpine as assets
|
||||||
|
|
||||||
|
RUN apk add --no-cache python build-base
|
||||||
|
|
||||||
|
COPY js .
|
||||||
|
RUN yarn install \
|
||||||
|
&& yarn run build
|
||||||
|
|
||||||
|
# Then, build the application binary
|
||||||
|
FROM elixir:alpine AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache build-base git cmake
|
||||||
|
|
||||||
|
COPY mix.exs mix.lock ./
|
||||||
|
ENV MIX_ENV=prod
|
||||||
|
RUN mix local.hex --force \
|
||||||
|
&& mix local.rebar --force \
|
||||||
|
&& mix deps.get
|
||||||
|
|
||||||
|
COPY lib ./lib
|
||||||
|
COPY priv ./priv
|
||||||
|
COPY config ./config
|
||||||
|
COPY rel ./rel
|
||||||
|
COPY docker/production/releases.exs ./config/
|
||||||
|
COPY --from=assets ./priv/static ./priv/static
|
||||||
|
|
||||||
|
RUN mix phx.digest \
|
||||||
|
&& mix release
|
||||||
|
|
||||||
|
# Finally setup the app
|
||||||
|
FROM alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache openssl ncurses-libs file
|
||||||
|
|
||||||
|
USER nobody
|
||||||
|
EXPOSE 4000
|
||||||
|
|
||||||
|
COPY --from=builder --chown=nobody:nobody _build/prod/rel/mobilizon ./
|
||||||
|
COPY docker/production/docker-entrypoint.sh ./
|
||||||
|
|
||||||
|
ENTRYPOINT ["./docker-entrypoint.sh"]
|
42
docker/production/docker-compose.yml
Normal file
42
docker/production/docker-compose.yml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
mobilizon:
|
||||||
|
image: framasoft/mobilizon
|
||||||
|
environment:
|
||||||
|
- MOBILIZON_INSTANCE_NAME
|
||||||
|
- MOBILIZON_INSTANCE_HOST
|
||||||
|
- MOBILIZON_INSTANCE_EMAIL
|
||||||
|
- MOBILIZON_REPLY_EMAIL
|
||||||
|
- MOBILIZON_ADMIN_EMAIL
|
||||||
|
- MOBILIZON_INSTANCE_REGISTRATIONS_OPEN
|
||||||
|
- MOBILIZON_DATABASE_USERNAME=${POSTGRES_USER}
|
||||||
|
- MOBILIZON_DATABASE_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
- MOBILIZON_DATABASE_DBNAME=${POSTGRES_DB}
|
||||||
|
- MOBILIZON_DATABASE_HOST=db
|
||||||
|
- MOBILIZON_INSTANCE_SECRET_KEY_BASE
|
||||||
|
- MOBILIZON_INSTANCE_SECRET_KEY
|
||||||
|
- MOBILIZON_SMTP_SERVER
|
||||||
|
- MOBILIZON_SMTP_HOSTNAME
|
||||||
|
- MOBILIZON_SMTP_PORT
|
||||||
|
- MOBILIZON_SMTP_SSL
|
||||||
|
- MOBILIZON_SMTP_USERNAME
|
||||||
|
- MOBILIZON_SMTP_PASSWORD
|
||||||
|
volumes:
|
||||||
|
- ./public/uploads:/app/uploads
|
||||||
|
ports:
|
||||||
|
- "4000:4000"
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgis/postgis
|
||||||
|
volumes:
|
||||||
|
- ./db:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER
|
||||||
|
- POSTGRES_PASSWORD
|
||||||
|
- POSTGRES_DB
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
ipam:
|
||||||
|
driver: default
|
9
docker/production/docker-entrypoint.sh
Executable file
9
docker/production/docker-entrypoint.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "-- Running migrations..."
|
||||||
|
/bin/mobilizon_ctl migrate
|
||||||
|
|
||||||
|
echo "-- Starting!"
|
||||||
|
exec /bin/mobilizon start
|
20
docker/production/env.template
Normal file
20
docker/production/env.template
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Copy this file to .env, then update it with your own settings
|
||||||
|
|
||||||
|
# Database settings
|
||||||
|
POSTGRES_USER=mobilizon
|
||||||
|
POSTGRES_PASSWORD=changethis
|
||||||
|
POSTGRES_DB=mobilizon
|
||||||
|
|
||||||
|
# Instance configuration
|
||||||
|
MOBILIZON_INSTANCE_NAME=My Mobilizon Instance
|
||||||
|
MOBILIZON_INSTANCE_HOST=mobilizon.lan
|
||||||
|
MOBILIZON_INSTANCE_SECRET_KEY_BASE=changethis
|
||||||
|
MOBILIZON_INSTANCE_SECRET_KEY=changethis
|
||||||
|
MOBILIZON_INSTANCE_EMAIL=noreply@mobilizon.lan
|
||||||
|
MOBILIZON_REPLY_EMAIL=contact@mobilizon.lan
|
||||||
|
|
||||||
|
# Email settings
|
||||||
|
MOBILIZON_SMTP_SERVER=localhost
|
||||||
|
MOBILIZON_SMTP_HOSTNAME=localhost
|
||||||
|
MOBILIZON_SMTP_USERNAME=noreply@mobilizon.lan
|
||||||
|
MOBILIZON_SMTP_PASSWORD=password
|
51
docker/production/releases.exs
Normal file
51
docker/production/releases.exs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Mobilizon instance configuration
|
||||||
|
|
||||||
|
import Config
|
||||||
|
|
||||||
|
config :mobilizon, Mobilizon.Web.Endpoint,
|
||||||
|
server: true,
|
||||||
|
url: [host: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.lan")],
|
||||||
|
http: [port: 4000],
|
||||||
|
secret_key_base: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY_BASE", "changethis")
|
||||||
|
|
||||||
|
config :mobilizon, Mobilizon.Web.Auth.Guardian,
|
||||||
|
secret_key: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY", "changethis")
|
||||||
|
|
||||||
|
config :mobilizon, :instance,
|
||||||
|
name: System.get_env("MOBILIZON_INSTANCE_NAME", "Mobilizon"),
|
||||||
|
description: "Change this to a proper description of your instance",
|
||||||
|
hostname: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.lan"),
|
||||||
|
registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN", "false") == "true",
|
||||||
|
demo: false,
|
||||||
|
allow_relay: true,
|
||||||
|
federating: true,
|
||||||
|
email_from: System.get_env("MOBILIZON_INSTANCE_EMAIL", "noreply@mobilizon.lan"),
|
||||||
|
email_reply_to: System.get_env("MOBILIZON_REPLY_EMAIL", "noreply@mobilizon.lan")
|
||||||
|
|
||||||
|
|
||||||
|
config :mobilizon, Mobilizon.Web.Upload.Uploader.Local,
|
||||||
|
uploads: System.get_env("MOBILIZON_UPLOADS", "/app/uploads")
|
||||||
|
|
||||||
|
|
||||||
|
config :mobilizon, Mobilizon.Storage.Repo,
|
||||||
|
adapter: Ecto.Adapters.Postgres,
|
||||||
|
username: System.get_env("MOBILIZON_DATABASE_USERNAME", "username"),
|
||||||
|
password: System.get_env("MOBILIZON_DATABASE_PASSWORD", "password"),
|
||||||
|
database: System.get_env("MOBILIZON_DATABASE_DBNAME", "mobilizon"),
|
||||||
|
hostname: System.get_env("MOBILIZON_DATABASE_HOST", "postgres"),
|
||||||
|
port: 5432,
|
||||||
|
pool_size: 10
|
||||||
|
|
||||||
|
config :mobilizon, Mobilizon.Web.Email.Mailer,
|
||||||
|
adapter: Bamboo.SMTPAdapter,
|
||||||
|
server: System.get_env("MOBILIZON_SMTP_SERVER", "localhost"),
|
||||||
|
hostname: System.get_env("MOBILIZON_SMTP_HOSTNAME", "localhost"),
|
||||||
|
port: System.get_env("MOBILIZON_SMTP_PORT", "25"),
|
||||||
|
username: System.get_env("MOBILIZON_SMTP_USERNAME", nil),
|
||||||
|
password: System.get_env("MOBILIZON_SMTP_PASSWORD", nil),
|
||||||
|
tls: :if_available,
|
||||||
|
allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
|
||||||
|
ssl: System.get_env("MOBILIZON_SMTP_SSL", "false"),
|
||||||
|
retries: 1,
|
||||||
|
no_mx_lookups: false,
|
||||||
|
auth: :if_available
|
@ -79,7 +79,7 @@ defmodule Mobilizon.Federation.ActivityPub.Federator do
|
|||||||
def enqueue(type, payload, priority \\ 1) do
|
def enqueue(type, payload, priority \\ 1) do
|
||||||
Logger.debug("enqueue something with type #{inspect(type)}")
|
Logger.debug("enqueue something with type #{inspect(type)}")
|
||||||
|
|
||||||
if Mix.env() == :test do
|
if Application.fetch_env!(:mobilizon, :env) == :test do
|
||||||
handle(type, payload)
|
handle(type, payload)
|
||||||
else
|
else
|
||||||
GenServer.cast(__MODULE__, {:enqueue, type, payload, priority})
|
GenServer.cast(__MODULE__, {:enqueue, type, payload, priority})
|
||||||
|
@ -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: ""
|
||||||
|
@ -21,7 +21,7 @@ defmodule Mobilizon do
|
|||||||
|
|
||||||
@name Mix.Project.config()[:name]
|
@name Mix.Project.config()[:name]
|
||||||
@version Mix.Project.config()[:version]
|
@version Mix.Project.config()[:version]
|
||||||
@env Mix.env()
|
@env Application.fetch_env!(:mobilizon, :env)
|
||||||
|
|
||||||
@spec named_version :: String.t()
|
@spec named_version :: String.t()
|
||||||
def named_version, do: "#{@name} #{@version}"
|
def named_version, do: "#{@name} #{@version}"
|
||||||
|
53
lib/mobilizon/cli.ex
Normal file
53
lib/mobilizon/cli.ex
Normal file
@ -0,0 +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
|
||||||
|
@moduledoc """
|
||||||
|
CLI wrapper for releases
|
||||||
|
"""
|
||||||
|
alias Mix.Tasks.Mobilizon.Ecto.{Migrate, Rollback}
|
||||||
|
|
||||||
|
def run(args) do
|
||||||
|
[task | args] = String.split(args)
|
||||||
|
|
||||||
|
case task do
|
||||||
|
"migrate" -> migrate(args)
|
||||||
|
"rollback" -> rollback(args)
|
||||||
|
task -> mix_task(task, args)
|
||||||
|
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
|
@ -38,8 +38,7 @@ defmodule Mobilizon.Web.Endpoint do
|
|||||||
at: "/",
|
at: "/",
|
||||||
from: {:mobilizon, "priv/static"},
|
from: {:mobilizon, "priv/static"},
|
||||||
gzip: false,
|
gzip: false,
|
||||||
only:
|
only: ~w(index.html manifest.json service-worker.js css fonts img js favicon.ico robots.txt),
|
||||||
~w(index.html manifest.json service-worker.js css fonts images js favicon.ico robots.txt),
|
|
||||||
only_matching: ["precache-manifest"]
|
only_matching: ["precache-manifest"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ defmodule Mobilizon.Web.Router do
|
|||||||
get("/:sig/:url/:filename", MediaProxyController, :remote)
|
get("/:sig/:url/:filename", MediaProxyController, :remote)
|
||||||
end
|
end
|
||||||
|
|
||||||
if Mix.env() in [:dev, :e2e] do
|
if Application.fetch_env!(:mobilizon, :env) in [:dev, :e2e] do
|
||||||
# If using Phoenix
|
# If using Phoenix
|
||||||
forward("/sent_emails", Bamboo.SentEmailViewerPlug)
|
forward("/sent_emails", Bamboo.SentEmailViewerPlug)
|
||||||
end
|
end
|
||||||
|
41
rel/overlays/bin/mobilizon_ctl
Executable file
41
rel/overlays/bin/mobilizon_ctl
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# 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
|
||||||
|
|
||||||
|
if [ -z "$1" ] || [ "$1" = "help" ]; then
|
||||||
|
echo "Usage: $(basename "$0") COMMAND [ARGS]
|
||||||
|
|
||||||
|
The known commands are:
|
||||||
|
|
||||||
|
migrate
|
||||||
|
Execute database migrations (needs to be done after updates)
|
||||||
|
|
||||||
|
rollback [VERSION]
|
||||||
|
Rollback database migrations (needs to be done before downgrading)
|
||||||
|
|
||||||
|
and any mix tasks under Mobilizon namespace, for example \`mix mobilizon.user.show COMMAND\` is
|
||||||
|
equivalent to \`$(basename "$0") user.show COMMAND\`
|
||||||
|
|
||||||
|
By default mobilizon_ctl will try calling into a running instance to execute non migration-related commands,
|
||||||
|
if for some reason this is undesired, set MOBILIZON_CTL_RPC_DISABLED environment variable.
|
||||||
|
|
||||||
|
"
|
||||||
|
else
|
||||||
|
SCRIPT=$(readlink -f "$0")
|
||||||
|
SCRIPTPATH=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
FULL_ARGS="$*"
|
||||||
|
|
||||||
|
ACTION="$1"
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$ACTION" = "migrate" ] || [ "$ACTION" = "rollback" ] || [ "$ACTION" = "create" ] || [ "$MOBILIZON_CTL_RPC_DISABLED" = true ]; then
|
||||||
|
"$SCRIPTPATH"/mobilizon eval 'Mobilizon.CLI.run("'"$FULL_ARGS"'")'
|
||||||
|
else
|
||||||
|
"$SCRIPTPATH"/mobilizon rpc 'Mobilizon.CLI.run("'"$FULL_ARGS"'")'
|
||||||
|
fi
|
||||||
|
fi
|
@ -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 New Issue
Block a user