Merge remote-tracking branch 'origin/develop' into paradedb-search-backend
This commit is contained in:
commit
9fdc1a8d3b
15 changed files with 196 additions and 6 deletions
1
changelog.d/cache-control-immutable.add
Normal file
1
changelog.d/cache-control-immutable.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add immutable tag on cache-control header for several endpoints that's serving the same exact things.
|
||||||
1
changelog.d/poll-voters-count.fix
Normal file
1
changelog.d/poll-voters-count.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Federate `votersCount` correctly
|
||||||
|
|
@ -29,7 +29,7 @@ defmodule Pleroma.Constants do
|
||||||
|
|
||||||
const(static_only_files,
|
const(static_only_files,
|
||||||
do:
|
do:
|
||||||
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
|
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js schemas doc embed.js embed.css)
|
||||||
)
|
)
|
||||||
|
|
||||||
const(status_updatable_fields,
|
const(status_updatable_fields,
|
||||||
|
|
|
||||||
|
|
@ -374,10 +374,18 @@ defmodule Pleroma.Object do
|
||||||
|
|
||||||
voters = [actor | object.data["voters"] || []] |> Enum.uniq()
|
voters = [actor | object.data["voters"] || []] |> Enum.uniq()
|
||||||
|
|
||||||
|
voters_count =
|
||||||
|
if Map.has_key?(object.data, "votersCount") do
|
||||||
|
object.data["votersCount"] + 1
|
||||||
|
else
|
||||||
|
length(voters)
|
||||||
|
end
|
||||||
|
|
||||||
data =
|
data =
|
||||||
object.data
|
object.data
|
||||||
|> Map.put(key, options)
|
|> Map.put(key, options)
|
||||||
|> Map.put("voters", voters)
|
|> Map.put("voters", voters)
|
||||||
|
|> Map.put("votersCount", voters_count)
|
||||||
|
|
||||||
object
|
object
|
||||||
|> Object.change(%{data: data})
|
|> Object.change(%{data: data})
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ defmodule Pleroma.ReverseProxy do
|
||||||
@keep_resp_headers @resp_cache_headers ++
|
@keep_resp_headers @resp_cache_headers ++
|
||||||
~w(content-length content-type content-disposition content-encoding) ++
|
~w(content-length content-type content-disposition content-encoding) ++
|
||||||
~w(content-range accept-ranges vary)
|
~w(content-range accept-ranges vary)
|
||||||
@default_cache_control_header "public, max-age=1209600"
|
@default_cache_control_header "public, max-age=1209600, immutable"
|
||||||
@valid_resp_codes [200, 206, 304]
|
@valid_resp_codes [200, 206, 304]
|
||||||
@max_read_duration :timer.seconds(30)
|
@max_read_duration :timer.seconds(30)
|
||||||
@max_body_length :infinity
|
@max_body_length :infinity
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:closed, ObjectValidators.DateTime)
|
field(:closed, ObjectValidators.DateTime)
|
||||||
|
field(:votersCount, :integer)
|
||||||
field(:voters, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:voters, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
field(:nonAnonymous, :boolean)
|
field(:nonAnonymous, :boolean)
|
||||||
embeds_many(:anyOf, QuestionOptionsValidator)
|
embeds_many(:anyOf, QuestionOptionsValidator)
|
||||||
|
|
|
||||||
|
|
@ -783,6 +783,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
|
|
||||||
def set_replies(obj_data), do: obj_data
|
def set_replies(obj_data), do: obj_data
|
||||||
|
|
||||||
|
defp set_voters_count(%{"voters" => [_ | _] = voters} = obj) do
|
||||||
|
Map.merge(obj, %{"votersCount" => length(voters)})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_voters_count(obj), do: obj
|
||||||
|
|
||||||
# Prepares and sanitizes the object for federation.
|
# Prepares and sanitizes the object for federation.
|
||||||
def prepare_object(object) do
|
def prepare_object(object) do
|
||||||
object
|
object
|
||||||
|
|
@ -795,6 +801,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
|> set_reply_to_uri
|
|> set_reply_to_uri
|
||||||
|> set_quote_url
|
|> set_quote_url
|
||||||
|> set_replies
|
|> set_replies
|
||||||
|
|> set_voters_count
|
||||||
|> CommonFixes.maybe_add_content_map()
|
|> CommonFixes.maybe_add_content_map()
|
||||||
|> strip_internal_fields
|
|> strip_internal_fields
|
||||||
|> strip_internal_tags
|
|> strip_internal_tags
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,9 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
plug(Pleroma.Web.Plugs.HTTPSecurityPlug)
|
plug(Pleroma.Web.Plugs.HTTPSecurityPlug)
|
||||||
plug(Pleroma.Web.Plugs.UploadedMedia)
|
plug(Pleroma.Web.Plugs.UploadedMedia)
|
||||||
|
|
||||||
@static_cache_control "public, max-age=1209600"
|
@static_cache_control "public, max-age=1209600, immutable"
|
||||||
@static_cache_disabled "public, no-cache"
|
@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
|
# 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
|
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
|
||||||
|
|
@ -64,6 +65,15 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
plug(Pleroma.Web.Plugs.FaviconPlug,
|
||||||
|
at: "/",
|
||||||
|
only: ["favicon.png"],
|
||||||
|
cache_control_for_etags: @favicon_cache_control,
|
||||||
|
headers: %{
|
||||||
|
"cache-control" => @favicon_cache_control
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.InstanceStatic,
|
plug(Pleroma.Web.Plugs.InstanceStatic,
|
||||||
at: "/",
|
at: "/",
|
||||||
gzip: true,
|
gzip: true,
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,8 @@ defmodule Pleroma.Web.MastodonAPI.PollView do
|
||||||
length(voters)
|
length(voters)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp voters_count(%{data: %{"votersCount" => voters}}), do: voters
|
||||||
|
|
||||||
defp voters_count(_), do: 0
|
defp voters_count(_), do: 0
|
||||||
|
|
||||||
defp voted_and_own_votes(%{object: object} = params, options) do
|
defp voted_and_own_votes(%{object: object} = params, options) do
|
||||||
|
|
|
||||||
71
lib/pleroma/web/plugs/favicon_plug.ex
Normal file
71
lib/pleroma/web/plugs/favicon_plug.ex
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.FaviconPlug do
|
||||||
|
@behaviour Plug
|
||||||
|
|
||||||
|
@moduledoc """
|
||||||
|
This is a shim to call `Plug.Static` but with runtime `from` configuration for instance favicon.
|
||||||
|
|
||||||
|
Serves default or custom favicon.png with cacheable cache-control.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Plug.Conn, only: [put_resp_header: 3, send_resp: 3, halt: 1]
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def init(opts) do
|
||||||
|
opts
|
||||||
|
|> Keyword.put(:from, "__unconfigured_favicon_static_plug")
|
||||||
|
|> Plug.Static.init()
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(%{request_path: "/favicon.png"} = conn, opts) do
|
||||||
|
case find_favicon_dir() do
|
||||||
|
{:ok, dir} ->
|
||||||
|
call_static(conn, opts, dir)
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
# Favicon should always be available and this should never occur.
|
||||||
|
# If it does, halt the pipeline before having unintended side-effects.
|
||||||
|
Logger.error("No favicon.png found! Is the default favicon deleted?")
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> send_resp(404, "Not found")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, _) do
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
|
||||||
|
defp find_favicon_dir do
|
||||||
|
instance_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static")
|
||||||
|
instance_path = Path.join(instance_dir, "favicon.png")
|
||||||
|
|
||||||
|
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}
|
||||||
|
File.exists?(priv_path) -> {:ok, priv_dir}
|
||||||
|
true -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
|
||||||
# no slashes
|
# no slashes
|
||||||
@path "media"
|
@path "media"
|
||||||
|
|
||||||
@default_cache_control_header "public, max-age=1209600"
|
@default_cache_control_header "public, max-age=1209600, immutable"
|
||||||
|
|
||||||
def init(_opts) do
|
def init(_opts) do
|
||||||
static_plug_opts =
|
static_plug_opts =
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,7 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
|> expect(:stream_body, fn _ -> :done end)
|
|> expect(:stream_body, fn _ -> :done end)
|
||||||
|
|
||||||
conn = ReverseProxy.call(conn, "/cache")
|
conn = ReverseProxy.call(conn, "/cache")
|
||||||
assert {"cache-control", "public, max-age=1209600"} in conn.resp_headers
|
assert {"cache-control", "public, max-age=1209600, immutable"} in conn.resp_headers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,4 +170,23 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do
|
||||||
|
|
||||||
assert {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
|
assert {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it displays voters count for a poll" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "???",
|
||||||
|
poll: %{expires_in: 10, options: ["yes", "no"]}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
{:ok, _, _} = CommonAPI.vote(object, other_user, [1])
|
||||||
|
|
||||||
|
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||||
|
|
||||||
|
refute Map.has_key?(modified["object"], "voters")
|
||||||
|
assert modified["object"]["votersCount"] == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,14 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do
|
||||||
} = PollView.render("show.json", %{object: object})
|
} = PollView.render("show.json", %{object: object})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "that poll is non anonymous" do
|
test "displays correct voters count" do
|
||||||
|
object = Object.normalize("https://friends.grishka.me/posts/54642", fetch: true)
|
||||||
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
assert result[:voters_count] == 14
|
||||||
|
end
|
||||||
|
|
||||||
|
test "detects that poll is non anonymous" do
|
||||||
object = Object.normalize("https://friends.grishka.me/posts/54642", fetch: true)
|
object = Object.normalize("https://friends.grishka.me/posts/54642", fetch: true)
|
||||||
result = PollView.render("show.json", %{object: object})
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
|
|
||||||
63
test/pleroma/web/plugs/favicon_plug_test.exs
Normal file
63
test/pleroma/web/plugs/favicon_plug_test.exs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.FaviconPlugTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
@dir "test/tmp/favicon_static"
|
||||||
|
|
||||||
|
setup do
|
||||||
|
Pleroma.Backports.mkdir_p!(@dir)
|
||||||
|
|
||||||
|
on_exit(fn -> File.rm_rf!(@dir) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "default favicon" do
|
||||||
|
test "returns favicon", %{conn: conn} do
|
||||||
|
conn = get(conn, "/favicon.png")
|
||||||
|
body_size = byte_size(conn.resp_body)
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert body_size == 1583
|
||||||
|
assert response_content_type(conn, :png)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns correct cache-control", %{conn: conn} do
|
||||||
|
conn = get(conn, "/favicon.png")
|
||||||
|
cache = get_resp_header(conn, "cache-control")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert cache == ["public, max=age=86400, immutable"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "custom favicon" do
|
||||||
|
setup do
|
||||||
|
favicon_path = Path.join(@dir, "favicon.png")
|
||||||
|
donor_image = "test/fixtures/image.png"
|
||||||
|
|
||||||
|
File.cp!(donor_image, favicon_path)
|
||||||
|
clear_config([:instance, :static_dir], @dir)
|
||||||
|
|
||||||
|
on_exit(fn -> File.rm!(favicon_path) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns favicon", %{conn: conn} do
|
||||||
|
conn = get(conn, "/favicon.png")
|
||||||
|
body_size = byte_size(conn.resp_body)
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert body_size == 104_426
|
||||||
|
assert response_content_type(conn, :png)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns correct cache-control", %{conn: conn} do
|
||||||
|
conn = get(conn, "/favicon.png")
|
||||||
|
cache = get_resp_header(conn, "cache-control")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert cache == ["public, max=age=86400, immutable"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue