logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

cache.ex (4136B)


  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. defmodule Pleroma.Web.Plugs.Cache do
  5. @moduledoc """
  6. Caches successful GET responses.
  7. To enable the cache add the plug to a router pipeline or controller:
  8. plug(Pleroma.Web.Plugs.Cache)
  9. ## Configuration
  10. To configure the plug you need to pass settings as the second argument to the `plug/2` macro:
  11. plug(Pleroma.Web.Plugs.Cache, [ttl: nil, query_params: true])
  12. Available options:
  13. - `ttl`: An expiration time (time-to-live). This value should be in milliseconds or `nil` to disable expiration. Defaults to `nil`.
  14. - `query_params`: Take URL query string into account (`true`), ignore it (`false`) or limit to specific params only (list). Defaults to `true`.
  15. - `tracking_fun`: A function that is called on successful responses, no matter if the request is cached or not. It should accept a conn as the first argument and the value assigned to `tracking_fun_data` as the second.
  16. Additionally, you can overwrite the TTL inside a controller action by assigning `cache_ttl` to the connection struct:
  17. def index(conn, _params) do
  18. ttl = 60_000 # one minute
  19. conn
  20. |> assign(:cache_ttl, ttl)
  21. |> render("index.html")
  22. end
  23. """
  24. import Phoenix.Controller, only: [current_path: 1, json: 2]
  25. import Plug.Conn
  26. @behaviour Plug
  27. @defaults %{ttl: nil, query_params: true}
  28. @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
  29. @impl true
  30. def init([]), do: @defaults
  31. def init(opts) do
  32. opts = Map.new(opts)
  33. Map.merge(@defaults, opts)
  34. end
  35. @impl true
  36. def call(%{method: "GET"} = conn, opts) do
  37. key = cache_key(conn, opts)
  38. case @cachex.get(:web_resp_cache, key) do
  39. {:ok, nil} ->
  40. cache_resp(conn, opts)
  41. {:ok, {content_type, body, tracking_fun_data}} ->
  42. conn = opts.tracking_fun.(conn, tracking_fun_data)
  43. send_cached(conn, {content_type, body})
  44. {:ok, record} ->
  45. send_cached(conn, record)
  46. {atom, message} when atom in [:ignore, :error] ->
  47. render_error(conn, message)
  48. end
  49. end
  50. def call(conn, _), do: conn
  51. # full path including query params
  52. defp cache_key(conn, %{query_params: true}), do: current_path(conn)
  53. # request path without query params
  54. defp cache_key(conn, %{query_params: false}), do: conn.request_path
  55. # request path with specific query params
  56. defp cache_key(conn, %{query_params: query_params}) when is_list(query_params) do
  57. query_string =
  58. conn.params
  59. |> Map.take(query_params)
  60. |> URI.encode_query()
  61. conn.request_path <> "?" <> query_string
  62. end
  63. defp cache_resp(conn, opts) do
  64. register_before_send(conn, fn
  65. %{status: 200, resp_body: body} = conn ->
  66. ttl = Map.get(conn.assigns, :cache_ttl, opts.ttl)
  67. key = cache_key(conn, opts)
  68. content_type = content_type(conn)
  69. should_cache = not Map.get(conn.assigns, :skip_cache, false)
  70. conn =
  71. unless opts[:tracking_fun] do
  72. if should_cache do
  73. @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl)
  74. end
  75. conn
  76. else
  77. tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil)
  78. if should_cache do
  79. @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl)
  80. end
  81. opts.tracking_fun.(conn, tracking_fun_data)
  82. end
  83. put_resp_header(conn, "x-cache", "MISS from Pleroma")
  84. conn ->
  85. conn
  86. end)
  87. end
  88. defp content_type(conn) do
  89. conn
  90. |> Plug.Conn.get_resp_header("content-type")
  91. |> hd()
  92. end
  93. defp send_cached(conn, {content_type, body}) do
  94. conn
  95. |> put_resp_content_type(content_type, nil)
  96. |> put_resp_header("x-cache", "HIT from Pleroma")
  97. |> send_resp(:ok, body)
  98. |> halt()
  99. end
  100. defp render_error(conn, message) do
  101. conn
  102. |> put_status(:internal_server_error)
  103. |> json(%{error: message})
  104. |> halt()
  105. end
  106. end