Merge branch 'preferred-frontend' into 'develop'
Port Akkoma frontend preference code See merge request pleroma/pleroma!4398
This commit is contained in:
commit
d41e2fbaaf
14 changed files with 264 additions and 13 deletions
1
changelog.d/preferred-frontend.add
Normal file
1
changelog.d/preferred-frontend.add
Normal file
|
|
@ -0,0 +1 @@
|
|||
Allow users to select preferred frontend
|
||||
|
|
@ -3333,6 +3333,12 @@ config :pleroma, :config_description, [
|
|||
description:
|
||||
"A map containing available frontends and parameters for their installation.",
|
||||
children: frontend_options
|
||||
},
|
||||
%{
|
||||
key: :pickable,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list containing all frontends users can pick as their preference, format is :name/:ref, e.g pleroma-fe/stable."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -151,7 +151,8 @@ defmodule Pleroma.Web.ApiSpec do
|
|||
"Suggestions",
|
||||
"Announcements",
|
||||
"Remote interaction",
|
||||
"Others"
|
||||
"Others",
|
||||
"Preferred frontends"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
defmodule Pleroma.Web.ApiSpec.PleromaFrontendSettingsOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
@spec open_api_operation(atom) :: Operation.t()
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def available_frontends_operation do
|
||||
%Operation{
|
||||
tags: ["Preferred frontends"],
|
||||
summary: "Frontend settings profiles",
|
||||
description: "List frontend setting profiles",
|
||||
operationId: "PleromaAPI.FrontendSettingsController.available_frontends",
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Frontends", "application/json", %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :string
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def update_preferred_frontend_operation do
|
||||
%Operation{
|
||||
tags: ["Preferred frontends"],
|
||||
summary: "Update preferred frontend setting",
|
||||
description: "Store preferred frontend in cookies",
|
||||
operationId: "PleromaAPI.FrontendSettingsController.update_preferred_frontend",
|
||||
requestBody:
|
||||
request_body(
|
||||
"Frontend",
|
||||
%Schema{
|
||||
type: :object,
|
||||
required: [:frontend_name],
|
||||
properties: %{
|
||||
frontend_name: %Schema{
|
||||
type: :string,
|
||||
description: "Frontend name"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: true
|
||||
),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Preferred frontend", "application/json", %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
frontend_name: %Schema{
|
||||
type: :string,
|
||||
description: "Frontend name"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -30,7 +30,7 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
end
|
||||
|
||||
def redirector(conn, _params, code \\ 200) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
{:ok, index_content} = File.read(index_file_path(conn))
|
||||
|
||||
response =
|
||||
index_content
|
||||
|
|
@ -51,7 +51,7 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
end
|
||||
|
||||
def redirector_with_meta(conn, params) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
{:ok, index_content} = File.read(index_file_path(conn))
|
||||
tags = build_tags(conn, params)
|
||||
preloads = preload_data(conn, params)
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
end
|
||||
|
||||
def redirector_with_preload(conn, params) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
{:ok, index_content} = File.read(index_file_path(conn))
|
||||
preloads = preload_data(conn, params)
|
||||
|
||||
response =
|
||||
|
|
@ -91,8 +91,10 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
|> text("")
|
||||
end
|
||||
|
||||
defp index_file_path do
|
||||
Pleroma.Web.Plugs.InstanceStatic.file_path("index.html")
|
||||
defp index_file_path(conn) do
|
||||
frontend_type = Pleroma.Web.Plugs.FrontendStatic.preferred_or_fallback(conn, :primary)
|
||||
|
||||
Pleroma.Web.Plugs.InstanceStatic.file_path("index.html", frontend_type)
|
||||
end
|
||||
|
||||
defp build_tags(conn, params) do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
defmodule Pleroma.Web.FrontendSwitcher.FrontendSwitcherController do
|
||||
use Pleroma.Web, :controller
|
||||
alias Pleroma.Config
|
||||
|
||||
@doc "GET /frontend_switcher"
|
||||
def switch(conn, _params) do
|
||||
pickable = Config.get([:frontends, :pickable], [])
|
||||
|
||||
conn
|
||||
|> put_view(Pleroma.Web.FrontendSwitcher.FrontendSwitcherView)
|
||||
|> render("switch.html", choices: pickable)
|
||||
end
|
||||
|
||||
@doc "POST /frontend_switcher"
|
||||
def do_switch(conn, params) do
|
||||
conn
|
||||
|> put_resp_cookie("preferred_frontend", params["frontend"])
|
||||
|> html(~s(<meta http-equiv="refresh" content="0; url=/">))
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
defmodule Pleroma.Web.FrontendSwitcher.FrontendSwitcherView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
import Phoenix.HTML.Form
|
||||
end
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
defmodule Pleroma.Web.PleromaAPI.FrontendSettingsController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{fallback: :proceed_unauthenticated, scopes: []}
|
||||
when action in [
|
||||
:available_frontends,
|
||||
:update_preferred_frontend
|
||||
]
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaFrontendSettingsOperation
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
@doc "GET /api/v1/pleroma/preferred_frontend/available"
|
||||
def available_frontends(conn, _params) do
|
||||
available = Pleroma.Config.get([:frontends, :pickable])
|
||||
|
||||
conn
|
||||
|> json(available)
|
||||
end
|
||||
|
||||
@doc "PUT /api/v1/pleroma/preferred_frontend"
|
||||
def update_preferred_frontend(
|
||||
%{body_params: %{frontend_name: preferred_frontend}} = conn,
|
||||
_params
|
||||
) do
|
||||
conn
|
||||
|> put_resp_cookie("preferred_frontend", preferred_frontend)
|
||||
|> json(%{frontend_name: preferred_frontend})
|
||||
end
|
||||
end
|
||||
|
|
@ -5,17 +5,23 @@
|
|||
defmodule Pleroma.Web.Plugs.FrontendStatic do
|
||||
require Pleroma.Constants
|
||||
|
||||
@frontend_cookie_name "preferred_frontend"
|
||||
|
||||
@moduledoc """
|
||||
This is a shim to call `Plug.Static` but with runtime `from` configuration`. It dispatches to the different frontends.
|
||||
"""
|
||||
@behaviour Plug
|
||||
|
||||
def file_path(path, frontend_type \\ :primary) do
|
||||
if configuration = Pleroma.Config.get([:frontends, frontend_type]) do
|
||||
instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static")
|
||||
defp instance_static_path do
|
||||
Pleroma.Config.get([:instance, :static_dir], "instance/static")
|
||||
end
|
||||
|
||||
def file_path(path, frontend_type \\ :primary)
|
||||
|
||||
def file_path(path, frontend_type) when is_atom(frontend_type) do
|
||||
if configuration = Pleroma.Config.get([:frontends, frontend_type]) do
|
||||
Path.join([
|
||||
instance_static_path,
|
||||
instance_static_path(),
|
||||
"frontends",
|
||||
configuration["name"],
|
||||
configuration["ref"],
|
||||
|
|
@ -26,6 +32,15 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
|
|||
end
|
||||
end
|
||||
|
||||
def file_path(path, frontend_type) when is_binary(frontend_type) do
|
||||
Path.join([
|
||||
instance_static_path(),
|
||||
"frontends",
|
||||
frontend_type,
|
||||
path
|
||||
])
|
||||
end
|
||||
|
||||
def init(opts) do
|
||||
opts
|
||||
|> Keyword.put(:from, "__unconfigured_frontend_static_plug")
|
||||
|
|
@ -36,7 +51,8 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
|
|||
def call(conn, opts) do
|
||||
with false <- api_route?(conn.path_info),
|
||||
false <- invalid_path?(conn.path_info),
|
||||
frontend_type <- Map.get(opts, :frontend_type, :primary),
|
||||
fallback_frontend_type <- Map.get(opts, :frontend_type, :primary),
|
||||
frontend_type <- preferred_or_fallback(conn, fallback_frontend_type),
|
||||
path when not is_nil(path) <- file_path("", frontend_type) do
|
||||
call_static(conn, opts, path)
|
||||
else
|
||||
|
|
@ -45,6 +61,31 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
|
|||
end
|
||||
end
|
||||
|
||||
def preferred_frontend(conn) do
|
||||
%{req_cookies: cookies} =
|
||||
conn
|
||||
|> Plug.Conn.fetch_cookies()
|
||||
|
||||
Map.get(cookies, @frontend_cookie_name)
|
||||
end
|
||||
|
||||
# Only override primary frontend
|
||||
def preferred_or_fallback(conn, :primary) do
|
||||
case preferred_frontend(conn) do
|
||||
nil ->
|
||||
:primary
|
||||
|
||||
frontend ->
|
||||
if Enum.member?(Pleroma.Config.get([:frontends, :pickable], []), frontend) do
|
||||
frontend
|
||||
else
|
||||
:primary
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def preferred_or_fallback(_conn, fallback), do: fallback
|
||||
|
||||
defp invalid_path?(list) do
|
||||
invalid_path?(list, :binary.compile_pattern(["/", "\\", ":", "\0"]))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ defmodule Pleroma.Web.Plugs.InstanceStatic do
|
|||
"""
|
||||
@behaviour Plug
|
||||
|
||||
def file_path(path) do
|
||||
def file_path(path, frontend_type \\ :primary) do
|
||||
instance_path =
|
||||
Path.join(Pleroma.Config.get([:instance, :static_dir], "instance/static/"), path)
|
||||
|
||||
frontend_path = Pleroma.Web.Plugs.FrontendStatic.file_path(path, :primary)
|
||||
frontend_path = Pleroma.Web.Plugs.FrontendStatic.file_path(path, frontend_type)
|
||||
|
||||
(File.exists?(instance_path) && instance_path) ||
|
||||
(frontend_path && File.exists?(frontend_path) && frontend_path) ||
|
||||
|
|
|
|||
|
|
@ -561,6 +561,18 @@ defmodule Pleroma.Web.Router do
|
|||
get("/apps", AppController, :index)
|
||||
get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
|
||||
get("/statuses/:id/reactions", EmojiReactionController, :index)
|
||||
|
||||
get(
|
||||
"/preferred_frontend/available",
|
||||
FrontendSettingsController,
|
||||
:available_frontends
|
||||
)
|
||||
|
||||
put(
|
||||
"/preferred_frontend",
|
||||
FrontendSettingsController,
|
||||
:update_preferred_frontend
|
||||
)
|
||||
end
|
||||
|
||||
scope "/api/v0/pleroma", Pleroma.Web.PleromaAPI do
|
||||
|
|
@ -906,7 +918,11 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:browser)
|
||||
|
||||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||
|
||||
get("/frontend_switcher", FrontendSwitcher.FrontendSwitcherController, :switch)
|
||||
post("/frontend_switcher", FrontendSwitcher.FrontendSwitcherController, :do_switch)
|
||||
end
|
||||
|
||||
pipeline :ap_service_actor do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<h2>Switch frontend</h2>
|
||||
|
||||
<%= form_for @conn, Routes.frontend_switcher_path(@conn, :do_switch), fn f -> %>
|
||||
<%= select(f, :frontend, @choices) %>
|
||||
|
||||
<%= submit do: "submit" %>
|
||||
<% end %>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Pleroma.Web.PleromaAPI.FrontendSettingsControllerTest do
|
||||
use Pleroma.Web.ConnCase, async: false
|
||||
|
||||
describe "PUT /api/v1/pleroma/preferred_frontend" do
|
||||
test "sets a cookie with selected frontend" do
|
||||
%{conn: conn} = oauth_access(["read"])
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/pleroma/preferred_frontend", %{"frontend_name" => "pleroma-fe/stable"})
|
||||
|
||||
json_response_and_validate_schema(response, 200)
|
||||
assert %{"preferred_frontend" => %{value: "pleroma-fe/stable"}} = response.resp_cookies
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -97,6 +97,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
|||
"users",
|
||||
"tags",
|
||||
"mailer",
|
||||
"frontend_switcher",
|
||||
"inbox",
|
||||
"relay",
|
||||
"internal",
|
||||
|
|
@ -113,4 +114,36 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
|||
|
||||
assert expected_routes == Pleroma.Web.Router.get_api_routes()
|
||||
end
|
||||
|
||||
describe "preferred frontend cookie handling" do
|
||||
test "returns preferred frontend file", %{conn: conn} do
|
||||
name = "test-fe"
|
||||
ref = "develop"
|
||||
|
||||
clear_config([:frontends, :pickable], ["#{name}/#{ref}"])
|
||||
path = "#{@dir}/frontends/#{name}/#{ref}"
|
||||
|
||||
Pleroma.Backports.mkdir_p!(path)
|
||||
File.write!("#{path}/index.html", "from frontend plug")
|
||||
|
||||
index =
|
||||
conn
|
||||
|> put_req_cookie("preferred_frontend", "#{name}/#{ref}")
|
||||
|> get("/")
|
||||
|
||||
assert html_response(index, 200) == "from frontend plug"
|
||||
end
|
||||
|
||||
test "only returns content from pickable frontends", %{conn: conn} do
|
||||
clear_config([:instance, :static_dir], "instance/static")
|
||||
clear_config([:frontends, :pickable], ["pleroma-fe/develop", "pl-fe/develop"])
|
||||
|
||||
config_file =
|
||||
conn
|
||||
|> put_req_cookie("preferred_frontend", "../../../config")
|
||||
|> get("/config.exs")
|
||||
|
||||
refute response(config_file, 200) =~ "import Config"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue