First
[anni] / lib / pleroma / web / endpoint.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.Endpoint do
6   use Phoenix.Endpoint, otp_app: :pleroma
7
8   require Pleroma.Constants
9
10   alias Pleroma.Config
11
12   socket("/socket", Pleroma.Web.UserSocket)
13   socket("/live", Phoenix.LiveView.Socket)
14
15   plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
16
17   plug(Pleroma.Web.Plugs.SetLocalePlug)
18   plug(CORSPlug)
19   plug(Pleroma.Web.Plugs.HTTPSecurityPlug)
20   plug(Pleroma.Web.Plugs.UploadedMedia)
21
22   @static_cache_control "public, no-cache"
23
24   # InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
25   # If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
26   # Cache-control headers are duplicated in case we turn off etags in the future
27   plug(
28     Pleroma.Web.Plugs.InstanceStatic,
29     at: "/",
30     from: :pleroma,
31     only: ["emoji", "images"],
32     gzip: true,
33     cache_control_for_etags: "public, max-age=1209600",
34     headers: %{
35       "cache-control" => "public, max-age=1209600"
36     }
37   )
38
39   plug(Pleroma.Web.Plugs.InstanceStatic,
40     at: "/",
41     gzip: true,
42     cache_control_for_etags: @static_cache_control,
43     headers: %{
44       "cache-control" => @static_cache_control
45     }
46   )
47
48   # Careful! No `only` restriction here, as we don't know what frontends contain.
49   plug(Pleroma.Web.Plugs.FrontendStatic,
50     at: "/",
51     frontend_type: :primary,
52     gzip: true,
53     cache_control_for_etags: @static_cache_control,
54     headers: %{
55       "cache-control" => @static_cache_control
56     }
57   )
58
59   plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
60
61   plug(Pleroma.Web.Plugs.FrontendStatic,
62     at: "/pleroma/admin",
63     frontend_type: :admin,
64     gzip: true,
65     cache_control_for_etags: @static_cache_control,
66     headers: %{
67       "cache-control" => @static_cache_control
68     }
69   )
70
71   # Serve at "/" the static files from "priv/static" directory.
72   #
73   # You should set gzip to true if you are running phoenix.digest
74   # when deploying your static files in production.
75   plug(
76     Plug.Static,
77     at: "/",
78     from: :pleroma,
79     only: Pleroma.Constants.static_only_files(),
80     # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
81     gzip: true,
82     cache_control_for_etags: @static_cache_control,
83     headers: %{
84       "cache-control" => @static_cache_control
85     }
86   )
87
88   plug(Plug.Static,
89     at: "/pleroma/admin/",
90     from: {:pleroma, "priv/static/adminfe/"}
91   )
92
93   # Code reloading can be explicitly enabled under the
94   # :code_reloader configuration of your endpoint.
95   if code_reloading? do
96     plug(Phoenix.CodeReloader)
97   end
98
99   plug(Pleroma.Web.Plugs.TrailingFormatPlug)
100   plug(Plug.RequestId)
101   plug(Plug.Logger, log: :debug)
102
103   plug(Plug.Parsers,
104     parsers: [
105       :urlencoded,
106       {:multipart, length: {Config, :get, [[:instance, :upload_limit]]}},
107       :json
108     ],
109     pass: ["*/*"],
110     json_decoder: Jason,
111     length: Config.get([:instance, :upload_limit]),
112     body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
113   )
114
115   plug(Plug.MethodOverride)
116   plug(Plug.Head)
117
118   secure_cookies = Config.get([__MODULE__, :secure_cookie_flag])
119
120   cookie_name =
121     if secure_cookies,
122       do: "__Host-pleroma_key",
123       else: "pleroma_key"
124
125   extra =
126     Config.get([__MODULE__, :extra_cookie_attrs])
127     |> Enum.join(";")
128
129   # The session will be stored in the cookie and signed,
130   # this means its contents can be read but not tampered with.
131   # Set :encryption_salt if you would also like to encrypt it.
132   plug(
133     Plug.Session,
134     store: :cookie,
135     key: cookie_name,
136     signing_salt: Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
137     http_only: true,
138     secure: secure_cookies,
139     extra: extra
140   )
141
142   plug(Pleroma.Web.Plugs.RemoteIp)
143
144   defmodule Instrumenter do
145     use Prometheus.PhoenixInstrumenter
146   end
147
148   defmodule PipelineInstrumenter do
149     use Prometheus.PlugPipelineInstrumenter
150   end
151
152   defmodule MetricsExporter do
153     use Prometheus.PlugExporter
154   end
155
156   defmodule MetricsExporterCaller do
157     @behaviour Plug
158
159     def init(opts), do: opts
160
161     def call(conn, opts) do
162       prometheus_config = Application.get_env(:prometheus, MetricsExporter, [])
163       ip_whitelist = List.wrap(prometheus_config[:ip_whitelist])
164
165       cond do
166         !prometheus_config[:enabled] ->
167           conn
168
169         ip_whitelist != [] and
170             !Enum.find(ip_whitelist, fn ip ->
171               Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip}
172             end) ->
173           conn
174
175         true ->
176           MetricsExporter.call(conn, opts)
177       end
178     end
179   end
180
181   plug(PipelineInstrumenter)
182
183   plug(MetricsExporterCaller)
184
185   plug(Pleroma.Web.Router)
186
187   @doc """
188   Dynamically loads configuration from the system environment
189   on startup.
190
191   It receives the endpoint configuration from the config files
192   and must return the updated configuration.
193   """
194   def load_from_system_env(config) do
195     port = System.get_env("PORT") || raise "expected the PORT environment variable to be set"
196     {:ok, Keyword.put(config, :http, [:inet6, port: port])}
197   end
198
199   def websocket_url do
200     String.replace_leading(url(), "http", "ws")
201   end
202 end