diff options
Diffstat (limited to 'lib/pleroma/application.ex')
-rw-r--r-- | lib/pleroma/application.ex | 288 |
1 files changed, 103 insertions, 185 deletions
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 06d399b2e..b5cf96964 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -5,8 +5,6 @@ defmodule Pleroma.Application do use Application - import Cachex.Spec - alias Pleroma.Config require Logger @@ -15,12 +13,17 @@ defmodule Pleroma.Application do @version Mix.Project.config()[:version] @repository Mix.Project.config()[:source_url] @mix_env Mix.env() + @dynamic_supervisor Pleroma.Application.Supervisor + + @type env() :: :test | :benchmark | :dev | :prod def name, do: @name def version, do: @version def named_version, do: @name <> " " <> @version def repository, do: @repository + def dynamic_supervisor, do: @dynamic_supervisor + @spec user_agent() :: String.t() def user_agent do if Process.whereis(Pleroma.Web.Endpoint) do case Config.get([:http, :user_agent], :default) do @@ -37,9 +40,92 @@ defmodule Pleroma.Application do end end - # See http://elixir-lang.org/docs/stable/elixir/Application.html - # for more information on OTP Applications + @spec config_path() :: Path.t() + def config_path do + if Config.get(:release) do + Config.get(:config_path) + else + Config.get(:config_path_in_test) || "config/#{@mix_env}.secret.exs" + end + end + + @doc """ + Checks that config file exists and starts application, otherwise starts web UI for configuration. + Under main supervisor is started DynamicSupervisor, which later starts pleroma startup dependencies. + Pleroma start is splitted into three `phases`: + - running prestart requirements (runtime compilation, warnings, deprecations, monitoring, etc.) + - loading and updating environment (if database config is used and enabled) + - starting dependencies + """ + @impl true def start(_type, _args) do + children = [ + {DynamicSupervisor, strategy: :one_for_one, name: @dynamic_supervisor}, + {Pleroma.Application.ConfigDependentDeps, [dynamic_supervisor: @dynamic_supervisor]} + ] + + {:ok, main_supervisor} = + Supervisor.start_link(children, strategy: :one_for_one, name: Pleroma.Supervisor) + + if @mix_env == :test or File.exists?(Pleroma.Application.config_path()) do + :ok = start_pleroma() + else + DynamicSupervisor.start_child( + @dynamic_supervisor, + Pleroma.InstallerWeb.Endpoint + ) + + token = Ecto.UUID.generate() + + Pleroma.Config.put(:installer_token, token) + + installer_port = + Pleroma.InstallerWeb.Endpoint + |> Pleroma.Config.get() + |> get_in([:http, :port]) + + ip = + with {:ok, ip} <- Pleroma.Helpers.ServerIPHelper.real_ip() do + ip + else + _ -> "IP not found" + end + + Logger.warn("Access installer at http://#{ip}:#{installer_port}/?token=#{token}") + end + + {:ok, main_supervisor} + end + + defp start_pleroma do + {:ok, _} = DynamicSupervisor.start_child(@dynamic_supervisor, Pleroma.Repo) + run_prestart_requirements() + + Pleroma.Application.Environment.load_from_db_and_update() + + Pleroma.Application.StartUpDependencies.start_all(@mix_env) + end + + @spec stop_installer_and_start_pleroma() :: {:ok, pid()} + def stop_installer_and_start_pleroma do + Pleroma.Application.config_path() + |> Pleroma.Application.Environment.update() + + start_pleroma() + + Task.start(fn -> + Process.sleep(100) + + installer_endpoint = Process.whereis(Pleroma.InstallerWeb.Endpoint) + + DynamicSupervisor.terminate_child( + @dynamic_supervisor, + installer_endpoint + ) + end) + end + + defp run_prestart_requirements do # Scrubbers are compiled at runtime and therefore will cause a conflict # every time the application is restarted, so we disable module # conflicts at runtime @@ -47,72 +133,26 @@ defmodule Pleroma.Application do # Disable warnings_as_errors at runtime, it breaks Phoenix live reload # due to protocol consolidation warnings Code.compiler_options(warnings_as_errors: false) + + # compilation in runtime + Pleroma.HTML.compile_scrubbers() + compile_custom_modules() + Pleroma.Docs.JSON.compile() + + # telemetry and prometheus Pleroma.Telemetry.Logger.attach() + setup_instrumenters() + Config.Holder.save_default() - Pleroma.HTML.compile_scrubbers() - Pleroma.Config.Oban.warn() + Config.DeprecationWarnings.warn() Pleroma.Web.Plugs.HTTPSecurityPlug.warn_if_disabled() - Pleroma.ApplicationRequirements.verify!() - setup_instrumenters() - load_custom_modules() - Pleroma.Docs.JSON.compile() - limiters_setup() - adapter = Application.get_env(:tesla, :adapter) - - if adapter == Tesla.Adapter.Gun do - if version = Pleroma.OTPVersion.version() do - [major, minor] = - version - |> String.split(".") - |> Enum.map(&String.to_integer/1) - |> Enum.take(2) - - if (major == 22 and minor < 2) or major < 22 do - raise " - !!!OTP VERSION WARNING!!! - You are using gun adapter with OTP version #{version}, which doesn't support correct handling of unordered certificates chains. Please update your Erlang/OTP to at least 22.2. - " - end - else - raise " - !!!OTP VERSION WARNING!!! - To support correct handling of unordered certificates chains - OTP version must be > 22.2. - " - end - end - - # Define workers and child supervisors to be supervised - children = - [ - Pleroma.Repo, - Config.TransferTask, - Pleroma.Emoji, - Pleroma.Web.Plugs.RateLimiter.Supervisor - ] ++ - cachex_children() ++ - http_children(adapter, @mix_env) ++ - [ - Pleroma.Stats, - Pleroma.JobQueueMonitor, - {Majic.Pool, [name: Pleroma.MajicPool, pool_size: Config.get([:majic_pool, :size], 2)]}, - {Oban, Config.get(Oban)}, - Pleroma.Web.Endpoint - ] ++ - task_children(@mix_env) ++ - dont_run_in_test(@mix_env) ++ - chat_child(chat_enabled?()) ++ - [Pleroma.Gopher.Server] - - # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html - # for other strategies and supported options - opts = [strategy: :one_for_one, name: Pleroma.Supervisor] - result = Supervisor.start_link(children, opts) + limiters_setup() set_postgres_server_version() - result + Pleroma.Application.Requirements.verify!() end defp set_postgres_server_version do @@ -132,7 +172,7 @@ defmodule Pleroma.Application do :persistent_term.put({Pleroma.Repo, :postgres_version}, version) end - def load_custom_modules do + defp compile_custom_modules do dir = Config.get([:modules, :runtime_dir]) if dir && File.exists?(dir) do @@ -177,128 +217,6 @@ defmodule Pleroma.Application do PrometheusPhx.setup() end - defp cachex_children do - [ - build_cachex("used_captcha", ttl_interval: seconds_valid_interval()), - build_cachex("user", default_ttl: 25_000, ttl_interval: 1000, limit: 2500), - build_cachex("object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500), - build_cachex("rich_media", default_ttl: :timer.minutes(120), limit: 5000), - build_cachex("scrubber", limit: 2500), - build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500), - build_cachex("web_resp", limit: 2500), - build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10), - build_cachex("failed_proxy_url", limit: 2500), - build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000), - build_cachex("chat_message_id_idempotency_key", - expiration: chat_message_id_idempotency_key_expiration(), - limit: 500_000 - ) - ] - end - - defp emoji_packs_expiration, - do: expiration(default: :timer.seconds(5 * 60), interval: :timer.seconds(60)) - - defp idempotency_expiration, - do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60)) - - defp chat_message_id_idempotency_key_expiration, - do: expiration(default: :timer.minutes(2), interval: :timer.seconds(60)) - - defp seconds_valid_interval, - do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid])) - - @spec build_cachex(String.t(), keyword()) :: map() - def build_cachex(type, opts), - do: %{ - id: String.to_atom("cachex_" <> type), - start: {Cachex, :start_link, [String.to_atom(type <> "_cache"), opts]}, - type: :worker - } - - defp chat_enabled?, do: Config.get([:chat, :enabled]) - - defp dont_run_in_test(env) when env in [:test, :benchmark], do: [] - - defp dont_run_in_test(_) do - [ - {Registry, - [ - name: Pleroma.Web.Streamer.registry(), - keys: :duplicate, - partitions: System.schedulers_online() - ]} - ] ++ background_migrators() - end - - defp background_migrators do - [ - Pleroma.Migrators.HashtagsTableMigrator - ] - end - - defp chat_child(true) do - [ - Pleroma.Web.ChatChannel.ChatChannelState, - {Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]} - ] - end - - defp chat_child(_), do: [] - - defp task_children(:test) do - [ - %{ - id: :web_push_init, - start: {Task, :start_link, [&Pleroma.Web.Push.init/0]}, - restart: :temporary - } - ] - end - - defp task_children(_) do - [ - %{ - id: :web_push_init, - start: {Task, :start_link, [&Pleroma.Web.Push.init/0]}, - restart: :temporary - }, - %{ - id: :internal_fetch_init, - start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]}, - restart: :temporary - } - ] - end - - # start hackney and gun pools in tests - defp http_children(_, :test) do - http_children(Tesla.Adapter.Hackney, nil) ++ http_children(Tesla.Adapter.Gun, nil) - end - - defp http_children(Tesla.Adapter.Hackney, _) do - pools = [:federation, :media] - - pools = - if Config.get([Pleroma.Upload, :proxy_remote]) do - [:upload | pools] - else - pools - end - - for pool <- pools do - options = Config.get([:hackney_pools, pool]) - :hackney_pool.child_spec(pool, options) - end - end - - defp http_children(Tesla.Adapter.Gun, _) do - Pleroma.Gun.ConnectionPool.children() ++ - [{Task, &Pleroma.HTTP.AdapterHelper.Gun.limiter_setup/0}] - end - - defp http_children(_, _), do: [] - @spec limiters_setup() :: :ok def limiters_setup do config = Config.get(ConcurrentLimiter, []) |