From 8abd25950a483efcfa42ba113086dd6cc5730ebf Mon Sep 17 00:00:00 2001 From: Yonle Date: Wed, 4 Mar 2026 02:54:47 +0700 Subject: [PATCH] endpoint: use favicon plug --- lib/pleroma/web/endpoint.ex | 12 +++++- lib/pleroma/web/plugs/favicon.ex | 73 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 lib/pleroma/web/plugs/favicon.ex diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index f7c58f459..e769b11b1 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -48,6 +48,7 @@ defmodule Pleroma.Web.Endpoint do @static_cache_control "public, max-age=1209600, immutable" @static_cache_disabled "public, no-cache" + @favicon_cache_control "public, max=age=86400, immutable" # cache for a day # InstanceStatic needs to be before Plug.Static to be able to override shipped-static files # If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well @@ -56,7 +57,7 @@ defmodule Pleroma.Web.Endpoint do Pleroma.Web.Plugs.InstanceStatic, at: "/", from: :pleroma, - only: ["emoji", "images", "favicon.png"], + only: ["emoji", "images"], gzip: true, cache_control_for_etags: @static_cache_control, headers: %{ @@ -73,6 +74,15 @@ defmodule Pleroma.Web.Endpoint do } ) + plug(Pleroma.Web.Plugs.Favicon, + at: "/", + only: ["favicon.png"], + cache_control_for_etags: @favicon_cache_control, + headers: %{ + "cache-control" => @favicon_cache_control + } + ) + plug(Pleroma.Web.Plugs.FrontendStatic, at: "/", frontend_type: :primary, diff --git a/lib/pleroma/web/plugs/favicon.ex b/lib/pleroma/web/plugs/favicon.ex new file mode 100644 index 000000000..ad105c59a --- /dev/null +++ b/lib/pleroma/web/plugs/favicon.ex @@ -0,0 +1,73 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2026 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.Favicon do + @behaviour Plug + + @moduledoc """ + Serves favicon.png directly from the instance static directory, + bypassing the frontend-specific logic. + """ + + alias Pleroma.Web.Plugs.FrontendStatic + import Plug.Conn, only: [put_resp_header: 3] + + def init(opts) do + opts + |> Keyword.put(:from, "__unconfigured_favicon_static_plug") + |> Plug.Static.init() + end + + def call(conn, opts) do + if favicon_route?(conn.path_info) do + case find_favicon_dir(conn) do + {:ok, dir} -> + call_static(conn, opts, dir) + |> Plug.Conn.halt() + + :error -> + conn # Let the request keep going to a 404 + end + else + conn + end + end + + defp find_favicon_dir(conn) do + instance_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static") + instance_path = Path.join(instance_dir, "favicon.png") + + frontend_type = FrontendStatic.preferred_or_fallback(conn, :primary) + frontend_dir = FrontendStatic.file_path("", frontend_type) + frontend_path = if frontend_dir, do: Path.join(frontend_dir, "favicon.png"), else: nil + + priv_dir = Application.app_dir(:pleroma, "priv/static") + priv_path = Path.join(priv_dir, "favicon.png") + + cond do + File.exists?(instance_path) -> {:ok, instance_dir} + frontend_path && File.exists?(frontend_path) -> {:ok, frontend_dir} + File.exists?(priv_path) -> {:ok, priv_dir} + true -> :error + end + end + + defp favicon_route?(["favicon.png"]), do: true + defp favicon_route?(_), do: false + + defp call_static(conn, opts, from) do + opts = + opts + |> Map.put(:from, from) + |> Map.put(:content_types, false) + + conn = set_content_type(conn) + + Plug.Static.call(conn, opts) + end + + defp set_content_type(conn) do + put_resp_header(conn, "content-type", "image/png") + end +end