application.ex 9.29 KB
Newer Older
1
# Pleroma: A lightweight social networking server
Sean King's avatar
Sean King committed
2
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3
4
# SPDX-License-Identifier: AGPL-3.0-only

lain's avatar
lain committed
5
6
defmodule Pleroma.Application do
  use Application
Alexander Strizhakov's avatar
Alexander Strizhakov committed
7
8
9
10
11

  import Cachex.Spec

  alias Pleroma.Config

minibikini's avatar
minibikini committed
12
  require Logger
lain's avatar
lain committed
13

14
  @name Mix.Project.config()[:name]
href's avatar
href committed
15
  @version Mix.Project.config()[:version]
16
  @repository Mix.Project.config()[:source_url]
17
  @mix_env Mix.env()
Steven Fuchs's avatar
Steven Fuchs committed
18

href's avatar
href committed
19
20
  def name, do: @name
  def version, do: @version
21
  def named_version, do: @name <> " " <> @version
luna's avatar
luna committed
22
  def repository, do: @repository
href's avatar
href committed
23

24
  def user_agent do
25
26
27
    if Process.whereis(Pleroma.Web.Endpoint) do
      case Config.get([:http, :user_agent], :default) do
        :default ->
28
          info = "#{Pleroma.Web.Endpoint.url()} <#{Config.get([:instance, :email], "")}>"
29
30
31
32
33
34
35
36
          named_version() <> "; " <> info

        custom ->
          custom
      end
    else
      # fallback, if endpoint is not started yet
      "Pleroma Data Loader"
37
    end
href's avatar
href committed
38
39
  end

lain's avatar
lain committed
40
41
42
  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
43
44
45
46
    # Scrubbers are compiled at runtime and therefore will cause a conflict
    # every time the application is restarted, so we disable module
    # conflicts at runtime
    Code.compiler_options(ignore_module_conflict: true)
47
48
49
    # Disable warnings_as_errors at runtime, it breaks Phoenix live reload
    # due to protocol consolidation warnings
    Code.compiler_options(warnings_as_errors: false)
50
    Pleroma.Telemetry.Logger.attach()
feld's avatar
feld committed
51
    Config.Holder.save_default()
rinpatch's avatar
rinpatch committed
52
    Pleroma.HTML.compile_scrubbers()
53
    Pleroma.Config.Oban.warn()
Alexander Strizhakov's avatar
Alexander Strizhakov committed
54
    Config.DeprecationWarnings.warn()
55
    Pleroma.Web.Plugs.HTTPSecurityPlug.warn_if_disabled()
56
    Pleroma.ApplicationRequirements.verify!()
href's avatar
href committed
57
    setup_instrumenters()
minibikini's avatar
minibikini committed
58
    load_custom_modules()
59
    Pleroma.Docs.JSON.compile()
60
    limiters_setup()
lain's avatar
lain committed
61

Alexander Strizhakov's avatar
Alexander Strizhakov committed
62
63
    adapter = Application.get_env(:tesla, :adapter)

64
65
    if match?({Tesla.Adapter.Finch, _}, adapter) do
      Logger.info("Starting Finch")
66
67
68
      Finch.start_link(name: MyFinch)
    end

Alexander Strizhakov's avatar
Alexander Strizhakov committed
69
    if adapter == Tesla.Adapter.Gun do
Alexander Strizhakov's avatar
Alexander Strizhakov committed
70
71
72
73
74
75
76
77
78
79
      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!!!
80
            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.
Alexander Strizhakov's avatar
Alexander Strizhakov committed
81
82
83
84
85
86
87
88
            "
        end
      else
        raise "
          !!!OTP VERSION WARNING!!!
          To support correct handling of unordered certificates chains - OTP version must be > 22.2.
          "
      end
89
90
    end

lain's avatar
lain committed
91
    # Define workers and child supervisors to be supervised
lain's avatar
lain committed
92
93
    children =
      [
Steven Fuchs's avatar
Steven Fuchs committed
94
        Pleroma.Repo,
Alexander Strizhakov's avatar
Alexander Strizhakov committed
95
        Config.TransferTask,
Steven Fuchs's avatar
Steven Fuchs committed
96
        Pleroma.Emoji,
97
        Pleroma.Web.Plugs.RateLimiter.Supervisor
lain's avatar
lain committed
98
      ] ++
Steven Fuchs's avatar
Steven Fuchs committed
99
        cachex_children() ++
100
        http_children(adapter, @mix_env) ++
href's avatar
href committed
101
        [
Steven Fuchs's avatar
Steven Fuchs committed
102
          Pleroma.Stats,
minibikini's avatar
minibikini committed
103
          Pleroma.JobQueueMonitor,
104
          {Majic.Pool, [name: Pleroma.MajicPool, pool_size: Config.get([:majic_pool, :size], 2)]},
105
106
          {Oban, Config.get(Oban)},
          Pleroma.Web.Endpoint
href's avatar
href committed
107
        ] ++
108
109
        task_children(@mix_env) ++
        dont_run_in_test(@mix_env) ++
110
        shout_child(shout_enabled?()) ++
111
        [Pleroma.Gopher.Server]
lain's avatar
lain committed
112
113
114
115

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Pleroma.Supervisor]
116
117
118
119
120
121
122
    result = Supervisor.start_link(children, opts)

    set_postgres_server_version()

    result
  end

lain's avatar
lain committed
123
  defp set_postgres_server_version do
124
125
126
127
128
129
130
131
132
133
134
135
136
    version =
      with %{rows: [[version]]} <- Ecto.Adapters.SQL.query!(Pleroma.Repo, "show server_version"),
           {num, _} <- Float.parse(version) do
        num
      else
        e ->
          Logger.warn(
            "Could not get the postgres version: #{inspect(e)}.\nSetting the default value of 9.6"
          )

          9.6
      end

137
    :persistent_term.put({Pleroma.Repo, :postgres_version}, version)
lain's avatar
lain committed
138
  end
139

minibikini's avatar
minibikini committed
140
  def load_custom_modules do
Alexander Strizhakov's avatar
Alexander Strizhakov committed
141
    dir = Config.get([:modules, :runtime_dir])
minibikini's avatar
minibikini committed
142
143
144

    if dir && File.exists?(dir) do
      dir
minibikini's avatar
minibikini committed
145
      |> Pleroma.Utils.compile_dir()
minibikini's avatar
minibikini committed
146
147
148
149
150
      |> case do
        {:error, _errors, _warnings} ->
          raise "Invalid custom modules"

        {:ok, modules, _warnings} ->
151
          if @mix_env != :test do
152
            Enum.each(modules, fn mod ->
minibikini's avatar
minibikini committed
153
              Logger.info("Custom module loaded: #{inspect(mod)}")
154
155
            end)
          end
minibikini's avatar
minibikini committed
156
157
158
159
160
161

          :ok
      end
    end
  end

feld's avatar
feld committed
162
  defp setup_instrumenters do
minibikini's avatar
minibikini committed
163
164
    require Prometheus.Registry

165
166
167
168
169
170
171
172
173
174
175
    if Application.get_env(:prometheus, Pleroma.Repo.Instrumenter) do
      :ok =
        :telemetry.attach(
          "prometheus-ecto",
          [:pleroma, :repo, :query],
          &Pleroma.Repo.Instrumenter.handle_event/4,
          %{}
        )

      Pleroma.Repo.Instrumenter.setup()
    end
minibikini's avatar
minibikini committed
176
177
178

    Pleroma.Web.Endpoint.MetricsExporter.setup()
    Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
179
180
181
182

    # Note: disabled until prometheus-phx is integrated into prometheus-phoenix:
    # Pleroma.Web.Endpoint.Instrumenter.setup()
    PrometheusPhx.setup()
minibikini's avatar
minibikini committed
183
184
  end

Steven Fuchs's avatar
Steven Fuchs committed
185
186
187
188
189
190
191
  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),
minibikini's avatar
minibikini committed
192
      build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500),
vaartis's avatar
vaartis committed
193
      build_cachex("web_resp", limit: 2500),
194
      build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
Maksim's avatar
Maksim committed
195
      build_cachex("failed_proxy_url", limit: 2500),
196
197
198
199
200
      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
      )
Steven Fuchs's avatar
Steven Fuchs committed
201
202
    ]
  end
href's avatar
href committed
203

vaartis's avatar
vaartis committed
204
205
206
  defp emoji_packs_expiration,
    do: expiration(default: :timer.seconds(5 * 60), interval: :timer.seconds(60))

Steven Fuchs's avatar
Steven Fuchs committed
207
  defp idempotency_expiration,
Steven Fuchs's avatar
Steven Fuchs committed
208
    do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
Steven Fuchs's avatar
Steven Fuchs committed
209

210
211
212
  defp chat_message_id_idempotency_key_expiration,
    do: expiration(default: :timer.minutes(2), interval: :timer.seconds(60))

Steven Fuchs's avatar
Steven Fuchs committed
213
  defp seconds_valid_interval,
Alexander Strizhakov's avatar
Alexander Strizhakov committed
214
    do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
Steven Fuchs's avatar
Steven Fuchs committed
215

216
217
  @spec build_cachex(String.t(), keyword()) :: map()
  def build_cachex(type, opts),
Steven Fuchs's avatar
Steven Fuchs committed
218
219
220
221
222
    do: %{
      id: String.to_atom("cachex_" <> type),
      start: {Cachex, :start_link, [String.to_atom(type <> "_cache"), opts]},
      type: :worker
    }
Steven Fuchs's avatar
Steven Fuchs committed
223

224
  defp shout_enabled?, do: Config.get([:shout, :enabled])
Steven Fuchs's avatar
Steven Fuchs committed
225

226
  defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
Steven Fuchs's avatar
Steven Fuchs committed
227

228
  defp dont_run_in_test(_) do
href's avatar
href committed
229
230
231
232
233
234
    [
      {Registry,
       [
         name: Pleroma.Web.Streamer.registry(),
         keys: :duplicate,
         partitions: System.schedulers_online()
rinpatch's avatar
rinpatch committed
235
       ]}
236
237
238
239
240
241
    ] ++ background_migrators()
  end

  defp background_migrators do
    [
      Pleroma.Migrators.HashtagsTableMigrator
href's avatar
href committed
242
    ]
243
  end
href's avatar
href committed
244

245
  defp shout_child(true) do
feld's avatar
feld committed
246
    [
feld's avatar
feld committed
247
      Pleroma.Web.ShoutChannel.ShoutChannelState,
feld's avatar
feld committed
248
249
      {Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
    ]
Steven Fuchs's avatar
Steven Fuchs committed
250
251
  end

252
  defp shout_child(_), do: []
Steven Fuchs's avatar
Steven Fuchs committed
253

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
  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
Alexander Strizhakov's avatar
Alexander Strizhakov committed
278
279

  # start hackney and gun pools in tests
Alexander Strizhakov's avatar
Alexander Strizhakov committed
280
  defp http_children(_, :test) do
281
    http_children(Tesla.Adapter.Hackney, nil) ++ http_children(Tesla.Adapter.Gun, nil)
Alexander Strizhakov's avatar
Alexander Strizhakov committed
282
283
  end

Alexander Strizhakov's avatar
Alexander Strizhakov committed
284
  defp http_children(Tesla.Adapter.Hackney, _) do
Alexander Strizhakov's avatar
Alexander Strizhakov committed
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
    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

300
  defp http_children(Tesla.Adapter.Gun, _) do
rinpatch's avatar
rinpatch committed
301
302
    Pleroma.Gun.ConnectionPool.children() ++
      [{Task, &Pleroma.HTTP.AdapterHelper.Gun.limiter_setup/0}]
303
  end
Alexander Strizhakov's avatar
Alexander Strizhakov committed
304

Alexander Strizhakov's avatar
Alexander Strizhakov committed
305
  defp http_children(_, _), do: []
306

307
  @spec limiters_setup() :: :ok
308
  def limiters_setup do
309
310
    config = Config.get(ConcurrentLimiter, [])

311
312
313
314
315
    [
      Pleroma.Web.RichMedia.Helpers,
      Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy,
      Pleroma.Webhook.Notify
    ]
316
317
318
319
320
321
322
323
    |> Enum.each(fn module ->
      mod_config = Keyword.get(config, module, [])

      max_running = Keyword.get(mod_config, :max_running, 5)
      max_waiting = Keyword.get(mod_config, :max_waiting, 5)

      ConcurrentLimiter.new(module, max_running, max_waiting)
    end)
324
  end
lain's avatar
lain committed
325
end