3.8 KiB · text History 4b6fe3b
defmodule OhMyExOhIrcdWeb.Endpoint do
  # `:tls_mode` is a compile-time decision. See `config/config.exs`.
  #   :standalone — site_encrypt wraps this endpoint, serves the HTTP-01
  #                 challenge, and provisions a cert via ACME.
  #   :kubernetes — endpoint runs unwrapped; an external provisioner
  #                 (cert-manager etc.) drops cert files where
  #                 `OhMyExOhIrcd.IRC.TLSListener` can read them.
  @tls_mode Application.compile_env(:oh_my_ex_oh_ircd, :tls_mode, :standalone)

  use Phoenix.Endpoint, otp_app: :oh_my_ex_oh_ircd

  @doc "Compile-time TLS mode flag — read by app supervisor and TLSListener."
  def __tls_mode__, do: @tls_mode

  # `SiteEncrypt.Phoenix.Endpoint.start_endpoint/3` calls this when wrapping
  # us — normally it would be injected by `use SiteEncrypt.Phoenix.Endpoint`,
  # but since we run plain `use Phoenix.Endpoint` (so :kubernetes mode works),
  # we have to define it explicitly.
  def app_env_config, do: Application.get_env(:oh_my_ex_oh_ircd, __MODULE__, [])

  # In :standalone mode the supervisor wraps us in
  # `SiteEncrypt.Phoenix.Endpoint`, so we need the ACME challenge plug
  # and the SiteEncrypt behaviour. In :kubernetes mode we're a plain
  # Phoenix.Endpoint — no extra plug, no callbacks.
  if @tls_mode == :standalone do
    @behaviour SiteEncrypt
    require SiteEncrypt
    plug SiteEncrypt.AcmeChallenge, __MODULE__
  end

  # The session will be stored in the cookie and signed,
  # this means its contents can be read but not tampered with.
  # Set :encryption_salt if you would also like to encrypt it.
  @session_options [
    store: :cookie,
    key: "_oh_my_ex_oh_ircd_key",
    signing_salt: "3TmYniL6",
    same_site: "Lax"
  ]

  socket "/live", Phoenix.LiveView.Socket,
    websocket: [connect_info: [session: @session_options]],
    longpoll: [connect_info: [session: @session_options]]

  plug Plug.Static,
    at: "/",
    from: :oh_my_ex_oh_ircd,
    gzip: not code_reloading?,
    only: OhMyExOhIrcdWeb.static_paths(),
    raise_on_missing_only: code_reloading?

  if code_reloading? do
    socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
    plug Phoenix.LiveReloader
    plug Phoenix.CodeReloader
    plug Phoenix.Ecto.CheckRepoStatus, otp_app: :oh_my_ex_oh_ircd
  end

  plug Phoenix.LiveDashboard.RequestLogger,
    param_key: "request_logger",
    cookie_key: "request_logger"

  plug Plug.RequestId
  plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]

  plug Plug.Parsers,
    parsers: [:urlencoded, :multipart, :json],
    pass: ["*/*"],
    json_decoder: Phoenix.json_library()

  plug Plug.MethodOverride
  plug Plug.Head
  plug Plug.Session, @session_options
  plug OhMyExOhIrcdWeb.Router

  ## SiteEncrypt callbacks — only compiled in :standalone mode.

  if @tls_mode == :standalone do
    require SiteEncrypt

    @impl SiteEncrypt
    def certification do
      cfg = Application.get_env(:oh_my_ex_oh_ircd, :acme, [])

      SiteEncrypt.configure(
        client: Keyword.get(cfg, :client, :native),
        domains: Keyword.get(cfg, :domains, ["localhost"]),
        emails: Keyword.get(cfg, :emails, ["admin@localhost"]),
        db_folder:
          Keyword.get(
            cfg,
            :db_folder,
            Path.join(System.tmp_dir!(), "oh_my_ex_oh_ircd_site_encrypt")
          ),
        directory_url: Keyword.get(cfg, :directory_url, {:internal, [port: 4002]})
      )
    end

    @impl SiteEncrypt
    def handle_new_cert do
      # New TLS handshakes pick up the fresh cert from disk automatically
      # (`:ssl` re-reads keyfile/certfile each handshake); this hook is a
      # place to nudge long-lived TLS sessions to reconnect if we ever
      # need to.
      Phoenix.PubSub.broadcast(OhMyExOhIrcd.PubSub, "tls", :cert_renewed)
      :ok
    end
  end
end