Instance/Static runtime plug
authorhref <href@random.sh>
Mon, 17 Dec 2018 21:50:59 +0000 (22:50 +0100)
committerhref <href@random.sh>
Mon, 17 Dec 2018 21:50:59 +0000 (22:50 +0100)
This allows to set-up an arbitrary directory which overrides most of the
static files: index.html static/ emoji/ packs/ sounds/ images/ instance/
favicon.png.

If the files are not present in the directory, the bundled ones in
priv/static will be used.

.gitignore
config/config.exs
lib/pleroma/plugs/instance_static.ex [new file with mode: 0644]
lib/pleroma/web/endpoint.ex
lib/pleroma/web/ostatus/ostatus_controller.ex
lib/pleroma/web/router.ex
test/plugs/instance_static_test.exs [new file with mode: 0644]

index b71dfa9fa549368bfe489e140085248a967ebc89..72fe2ce430dab93a386bd72e391b3ea79375caf3 100644 (file)
@@ -8,7 +8,9 @@
 /.elixir_ls
 /test/fixtures/test_tmp.txt
 /test/fixtures/image_tmp.jpg
+/test/tmp/
 /doc
+/instance
 
 # Prevent committing custom emojis
 /priv/static/emoji/custom/*
@@ -31,4 +33,4 @@ erl_crash.dump
 .env
 
 # Editor config
-/.vscode
+/.vscode/
index 036f1ac0b34fdb50bd713499cd2982b56d6794a5..e4b31bf8132765fff620542a1ab27440487ee518 100644 (file)
@@ -110,6 +110,7 @@ config :pleroma, :instance,
   public: true,
   quarantined_instances: [],
   managed_config: true,
+  static_dir: "instance/static/",
   allowed_post_formats: [
     "text/plain",
     "text/html",
diff --git a/lib/pleroma/plugs/instance_static.ex b/lib/pleroma/plugs/instance_static.ex
new file mode 100644 (file)
index 0000000..46ee77e
--- /dev/null
@@ -0,0 +1,54 @@
+defmodule Pleroma.Plugs.InstanceStatic do
+  @moduledoc """
+  This is a shim to call `Plug.Static` but with runtime `from` configuration.
+
+  Mountpoints are defined directly in the module to avoid calling the configuration for every request including non-static ones.
+  """
+  @behaviour Plug
+
+  def file_path(path) do
+    instance_path =
+      Path.join(Pleroma.Config.get([:instance, :static_dir], "instance/static/"), path)
+
+    if File.exists?(instance_path) do
+      instance_path
+    else
+      Path.join(Application.app_dir(:pleroma, "priv/static/"), path)
+    end
+  end
+
+  @only ~w(index.html static emoji packs sounds images instance favicon.png)
+
+  def init(opts) do
+    opts
+    |> Keyword.put(:from, "__unconfigured_instance_static_plug")
+    |> Keyword.put(:at, "/__unconfigured_instance_static_plug")
+    |> Plug.Static.init()
+  end
+
+  for only <- @only do
+    at = Plug.Router.Utils.split("/")
+
+    def call(conn = %{request_path: "/" <> unquote(only) <> _}, opts) do
+      call_static(
+        conn,
+        opts,
+        unquote(at),
+        Pleroma.Config.get([:instance, :static_dir], "instance/static")
+      )
+    end
+  end
+
+  def call(conn, _) do
+    conn
+  end
+
+  defp call_static(conn, opts, at, from) do
+    opts =
+      opts
+      |> Map.put(:from, from)
+      |> Map.put(:at, at)
+
+    Plug.Static.call(conn, opts)
+  end
+end
index e52667c72691245aabb10e1b06ae9788526478dc..d79f61b2e613000375b252355d672ddf788b790e 100644 (file)
@@ -12,6 +12,10 @@ defmodule Pleroma.Web.Endpoint do
 
   plug(Pleroma.Plugs.UploadedMedia)
 
+  # 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
+  plug(Pleroma.Plugs.InstanceStatic, at: "/")
+
   plug(
     Plug.Static,
     at: "/",
index 9dfcf0f95a9f94a5d1bf9ff1d8503acd821b6fbf..6005eadb2e2f447b4f1d996ad4ae373a3771cb08 100644 (file)
@@ -136,7 +136,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
         "html" ->
           conn
           |> put_resp_content_type("text/html")
-          |> send_file(200, Application.app_dir(:pleroma, "priv/static/index.html"))
+          |> send_file(200, Pleroma.Plugs.InstanceStatic.file_path("index.html"))
 
         _ ->
           represent_activity(conn, format, activity, user)
index 60342cfb447644fc2609056b51b52b6ecaea633f..dd1985d6ee1b91a16e0c88517f0edc9fa1e49f27 100644 (file)
@@ -459,7 +459,7 @@ defmodule Fallback.RedirectController do
   def redirector(conn, _params) do
     conn
     |> put_resp_content_type("text/html")
-    |> send_file(200, Application.app_dir(:pleroma, "priv/static/index.html"))
+    |> send_file(200, Pleroma.Plugs.InstanceStatic.file_path("index.html"))
   end
 
   def registration_page(conn, params) do
diff --git a/test/plugs/instance_static_test.exs b/test/plugs/instance_static_test.exs
new file mode 100644 (file)
index 0000000..526679a
--- /dev/null
@@ -0,0 +1,43 @@
+defmodule Pleroma.Web.RuntimeStaticPlugTest do
+  use Pleroma.Web.ConnCase
+
+  @dir "test/tmp/instance_static"
+
+  setup do
+    static_dir = Pleroma.Config.get([:instance, :static_dir])
+    Pleroma.Config.put([:instance, :static_dir], @dir)
+    File.mkdir_p!(@dir)
+
+    on_exit(fn ->
+      Pleroma.Config.put([:instance, :static_dir], static_dir)
+      File.rm_rf(@dir)
+    end)
+  end
+
+  test "overrides index" do
+    bundled_index = get(build_conn(), "/")
+    assert html_response(bundled_index, 200) == File.read!("priv/static/index.html")
+
+    File.write!(@dir <> "/index.html", "hello world")
+
+    index = get(build_conn(), "/")
+    assert html_response(index, 200) == "hello world"
+  end
+
+  test "overrides any file in static/static" do
+    bundled_index = get(build_conn(), "/static/terms-of-service.html")
+
+    assert html_response(bundled_index, 200) ==
+             File.read!("priv/static/static/terms-of-service.html")
+
+    File.mkdir!(@dir <> "/static")
+    File.write!(@dir <> "/static/terms-of-service.html", "plz be kind")
+
+    index = get(build_conn(), "/static/terms-of-service.html")
+    assert html_response(index, 200) == "plz be kind"
+
+    File.write!(@dir <> "/static/kaniini.html", "<h1>rabbit hugs as a service</h1>")
+    index = get(build_conn(), "/static/kaniini.html")
+    assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>"
+  end
+end