aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRoman Chvanikov <chvanikoff@pm.me>2020-04-17 18:36:32 +0300
committerRoman Chvanikov <chvanikoff@pm.me>2020-04-17 18:36:32 +0300
commit863e61ad5f5bb29369dfc602509a0492d0b7ef7a (patch)
tree2168212cc999b51f5b3750e912a943218a8c63ad /lib
parente0d7847bc56c61156c8df0e7a94d728b82bf2d86 (diff)
downloadpleroma-863e61ad5f5bb29369dfc602509a0492d0b7ef7a.tar.gz
FE bundles
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/pleroma/frontend.ex209
-rw-r--r--lib/mix/tasks/pleroma/instance.ex92
-rw-r--r--lib/mix/tasks/pleroma/user.ex2
-rw-r--r--lib/pleroma/application.ex2
-rw-r--r--lib/pleroma/emails/user_email.ex2
-rw-r--r--lib/pleroma/frontend.ex28
-rw-r--r--lib/pleroma/plugs/frontend_plug.ex47
-rw-r--r--lib/pleroma/plugs/instance_static.ex79
-rw-r--r--lib/pleroma/plugs/static_fe_plug.ex12
-rw-r--r--lib/pleroma/web/controllers/frontend/admin_controller.ex4
-rw-r--r--lib/pleroma/web/controllers/frontend/default_controller.ex36
-rw-r--r--lib/pleroma/web/controllers/frontend/headless_controller.ex9
-rw-r--r--lib/pleroma/web/controllers/frontend/kenoma_controller.ex4
-rw-r--r--lib/pleroma/web/controllers/frontend/mastodon_controller.ex52
-rw-r--r--lib/pleroma/web/controllers/frontend/pleroma_controller.ex44
-rw-r--r--lib/pleroma/web/controllers/frontend/static_controller.ex (renamed from lib/pleroma/web/static_fe/static_fe_controller.ex)151
-rw-r--r--lib/pleroma/web/endpoint.ex10
-rw-r--r--lib/pleroma/web/fallback_redirect_controller.ex77
-rw-r--r--lib/pleroma/web/feed/user_controller.ex7
-rw-r--r--lib/pleroma/web/frontend_controller.ex74
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/auth_controller.ex6
-rw-r--r--lib/pleroma/web/ostatus/ostatus_controller.ex40
-rw-r--r--lib/pleroma/web/router.ex35
-rw-r--r--lib/pleroma/web/templates/frontend/mastodon/index.html.eex (renamed from lib/pleroma/web/templates/masto_fe/index.html.eex)4
-rw-r--r--lib/pleroma/web/templates/frontend/static/_attachment.html.eex (renamed from lib/pleroma/web/templates/static_fe/static_fe/_attachment.html.eex)0
-rw-r--r--lib/pleroma/web/templates/frontend/static/_notice.html.eex (renamed from lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex)8
-rw-r--r--lib/pleroma/web/templates/frontend/static/_user_card.html.eex (renamed from lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex)4
-rw-r--r--lib/pleroma/web/templates/frontend/static/conversation.html.eex (renamed from lib/pleroma/web/templates/static_fe/static_fe/conversation.html.eex)0
-rw-r--r--lib/pleroma/web/templates/frontend/static/error.html.eex (renamed from lib/pleroma/web/templates/static_fe/static_fe/error.html.eex)0
-rw-r--r--lib/pleroma/web/templates/frontend/static/profile.html.eex (renamed from lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex)4
-rw-r--r--lib/pleroma/web/templates/layout/static_fe.html.eex2
-rw-r--r--lib/pleroma/web/views/frontend/mastodon_view.ex121
-rw-r--r--lib/pleroma/web/views/frontend/static_view.ex (renamed from lib/pleroma/web/static_fe/static_fe_view.ex)7
-rw-r--r--lib/pleroma/web/views/masto_fe_view.ex2
-rw-r--r--lib/pleroma/web/web.ex6
35 files changed, 906 insertions, 274 deletions
diff --git a/lib/mix/tasks/pleroma/frontend.ex b/lib/mix/tasks/pleroma/frontend.ex
new file mode 100644
index 000000000..d701c1404
--- /dev/null
+++ b/lib/mix/tasks/pleroma/frontend.ex
@@ -0,0 +1,209 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Mix.Tasks.Pleroma.Frontend do
+ use Mix.Task
+
+ import Mix.Pleroma
+
+ # alias Pleroma.Config
+
+ @shortdoc "Manages bundled Pleroma frontends"
+ @moduledoc File.read!("docs/administration/CLI_tasks/frontend.md")
+
+ @known_frontends ~w(pleroma kenoma mastodon admin)
+ @pleroma_gitlab_host "git.pleroma.social"
+ @projects %{
+ "pleroma" => "pleroma/pleroma-fe",
+ "kenoma" => "lambadalambda/kenoma",
+ "admin" => "pleroma/admin-fe",
+ "mastodon" => "pleroma/mastofe"
+ }
+
+ def run(["install", "none" | _args]) do
+ shell_info("Skipping frontend installation because none was requested")
+ end
+
+ def run(["install", unknown_fe | _args]) when unknown_fe not in @known_frontends do
+ shell_error(
+ "Frontend #{unknown_fe} is not known. Known frontends are: #{
+ Enum.join(@known_frontends, ", ")
+ }"
+ )
+ end
+
+ def run(["install", frontend | args]) do
+ {:ok, _} = Application.ensure_all_started(:pleroma)
+
+ {options, [], []} =
+ OptionParser.parse(
+ args,
+ strict: [
+ ref: :string
+ ]
+ )
+
+ ref = suggest_ref(options, frontend)
+
+ %{"name" => bundle_name, "url" => bundle_url} =
+ get_bundle_meta(ref, @pleroma_gitlab_host, @projects[frontend])
+
+ shell_info("Installing #{frontend} frontend (version: #{bundle_name}, url: #{bundle_url})")
+
+ dest = Path.join([Pleroma.Config.get!([:instance, :static_dir]), "frontends", frontend, ref])
+
+ with :ok <- install_bundle(bundle_url, dest),
+ :ok <- post_install_bundle(frontend, dest) do
+ shell_info("Installed!")
+ else
+ {:error, error} ->
+ shell_error("Error: #{inspect(error)}")
+ end
+ end
+
+ defp post_install_bundle("mastodon", path) do
+ with :ok <- File.rename("#{path}/public/assets/sw.js", "#{path}/sw.js"),
+ :ok <- File.rename("#{path}/public/packs", "#{path}/packs"),
+ {:ok, _deleted_files} <- File.rm_rf("#{path}/public") do
+ :ok
+ else
+ error ->
+ error
+ end
+ end
+
+ defp post_install_bundle(_fe_name, _path), do: :ok
+
+ defp suggest_ref(options, frontend) do
+ case Pleroma.Config.get([:frontends, String.to_atom(frontend)]) do
+ nil ->
+ primary_fe_config = Pleroma.Config.get([:frontends, :primary])
+
+ case primary_fe_config["name"] == frontend do
+ true ->
+ primary_fe_config["ref"]
+
+ false ->
+ nil
+ end
+
+ val ->
+ val
+ end
+ |> case do
+ nil ->
+ stable_pleroma? = Pleroma.Application.stable?()
+
+ current_stable_out =
+ case stable_pleroma? do
+ true -> "stable"
+ false -> "develop"
+ end
+
+ get_option(
+ options,
+ :ref,
+ "You are currently running #{current_stable_out} version of Pleroma backend. What version of \"#{
+ frontend
+ }\" frontend you want to install? (\"stable\", \"develop\" or specific ref)",
+ current_stable_out
+ )
+
+ config_value ->
+ current_ref =
+ case config_value do
+ %{"ref" => ref} -> ref
+ ref -> ref
+ end
+
+ get_option(
+ options,
+ :ref,
+ "You are currently running #{current_ref} version of \"#{frontend}\" frontend. What version do you want to install? (\"stable\", \"develop\" or specific ref)",
+ current_ref
+ )
+ end
+ end
+
+ defp get_bundle_meta("develop", gitlab_base_url, project) do
+ url = "#{gitlab_api_url(gitlab_base_url, project)}/repository/branches"
+
+ http_client = http_client()
+ %{status: 200, body: json} = Tesla.get!(http_client, url)
+
+ %{"name" => name, "commit" => %{"short_id" => last_commit_ref}} =
+ Enum.find(json, &(&1["default"] == true))
+
+ %{
+ "name" => name,
+ "url" => build_url(gitlab_base_url, project, last_commit_ref)
+ }
+ end
+
+ defp get_bundle_meta("stable", gitlab_base_url, project) do
+ url = "#{gitlab_api_url(gitlab_base_url, project)}/releases"
+ http_client = http_client()
+ %{status: 200, body: json} = Tesla.get!(http_client, url)
+
+ [%{"commit" => %{"short_id" => commit_id}, "name" => name} | _] =
+ Enum.sort(json, fn r1, r2 -> r1 > r2 end)
+
+ %{
+ "name" => name,
+ "url" => build_url(gitlab_base_url, project, commit_id)
+ }
+ end
+
+ defp get_bundle_meta(ref, gitlab_base_url, project) do
+ %{
+ "name" => ref,
+ "url" => build_url(gitlab_base_url, project, ref)
+ }
+ end
+
+ defp install_bundle(bundle_url, dir) do
+ http_client = http_client()
+
+ with {:ok, %{status: 200, body: zip_body}} <- Tesla.get(http_client, bundle_url),
+ {:ok, unzipped} <- :zip.unzip(zip_body, [:memory]) do
+ File.rm_rf!(dir)
+
+ Enum.each(unzipped, fn {path, data} ->
+ path =
+ path
+ |> to_string()
+ |> String.replace(~r/^dist\//, "")
+
+ file_path = Path.join(dir, path)
+
+ file_path
+ |> Path.dirname()
+ |> File.mkdir_p!()
+
+ File.write!(file_path, data)
+ end)
+ else
+ {:ok, %{status: 404}} ->
+ {:error, "Bundle not found"}
+
+ error ->
+ {:error, error}
+ end
+ end
+
+ defp gitlab_api_url(gitlab_base_url, project),
+ do: "https://#{gitlab_base_url}/api/v4/projects/#{URI.encode_www_form(project)}"
+
+ defp build_url(gitlab_base_url, project, ref),
+ do: "https://#{gitlab_base_url}/#{project}/-/jobs/artifacts/#{ref}/download?job=build"
+
+ defp http_client do
+ middleware = [
+ Tesla.Middleware.FollowRedirects,
+ Tesla.Middleware.JSON
+ ]
+
+ Tesla.client(middleware)
+ end
+end
diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex
index bc842a59f..8bb3f71e8 100644
--- a/lib/mix/tasks/pleroma/instance.ex
+++ b/lib/mix/tasks/pleroma/instance.ex
@@ -33,7 +33,14 @@ defmodule Mix.Tasks.Pleroma.Instance do
uploads_dir: :string,
static_dir: :string,
listen_ip: :string,
- listen_port: :string
+ listen_port: :string,
+ fe_primary: :string,
+ fe_primary_ref: :string,
+ fe_mastodon: :string,
+ fe_mastodon_ref: :string,
+ fe_admin: :string,
+ fe_admin_ref: :string,
+ fe_static: :string
],
aliases: [
o: :output,
@@ -158,6 +165,62 @@ defmodule Mix.Tasks.Pleroma.Instance do
Config.put([:instance, :static_dir], static_dir)
+ install_fe =
+ case Mix.env() do
+ :test -> fn _, _ -> :ok end
+ _ -> &Mix.Tasks.Pleroma.Frontend.run(["install", &1, "--ref", &2])
+ end
+
+ fe_primary =
+ get_option(
+ options,
+ :fe_primary,
+ "Choose primary frontend for your instance (available: pleroma/kenoma/none)",
+ "pleroma"
+ )
+
+ fe_primary_ref =
+ get_frontend_ref(fe_primary !== "none", fe_primary, :fe_primary_ref, options)
+
+ install_fe.(fe_primary, fe_primary_ref)
+
+ enable_static_fe? =
+ get_option(
+ options,
+ :fe_static,
+ "Would you like to enable Static frontend (render profiles and posts using server-generated HTML that is viewable without using JavaScript)?",
+ "y"
+ ) === "y"
+
+ install_mastodon_fe? =
+ get_option(
+ options,
+ :fe_mastodon,
+ "Would you like to install Mastodon frontend?",
+ "y"
+ ) === "y"
+
+ fe_mastodon_ref =
+ get_frontend_ref(install_mastodon_fe?, "mastodon", :fe_mastodon_ref, options)
+
+ with true <- install_mastodon_fe? do
+ install_fe.("mastodon", fe_mastodon_ref)
+ end
+
+ install_admin_fe? =
+ get_option(
+ options,
+ :fe_admin,
+ "Would you like to install Admin frontend?",
+ "y"
+ ) === "y"
+
+ fe_admin_ref = get_frontend_ref(install_admin_fe?, "admin", :fe_admin_ref, options)
+
+ with true <- install_admin_fe? do
+ install_fe.("admin", fe_admin_ref)
+ end
+
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
@@ -186,7 +249,11 @@ defmodule Mix.Tasks.Pleroma.Instance do
uploads_dir: uploads_dir,
rum_enabled: rum_enabled,
listen_ip: listen_ip,
- listen_port: listen_port
+ listen_port: listen_port,
+ fe_primary: %{"name" => fe_primary, "ref" => fe_primary_ref},
+ fe_mastodon: %{"name" => "mastodon", "ref" => fe_mastodon_ref},
+ fe_admin: %{"name" => "admin", "ref" => fe_admin_ref},
+ enable_static_fe?: enable_static_fe?
)
result_psql =
@@ -247,4 +314,25 @@ defmodule Mix.Tasks.Pleroma.Instance do
File.write(robots_txt_path, robots_txt)
shell_info("Writing #{robots_txt_path}.")
end
+
+ defp get_frontend_ref(false, _frontend, _option_key, _options), do: ""
+
+ defp get_frontend_ref(true, frontend, option_key, options) do
+ stable_pleroma? = Pleroma.Application.stable?()
+
+ current_stable_out =
+ case stable_pleroma? do
+ true -> "stable"
+ false -> "develop"
+ end
+
+ get_option(
+ options,
+ option_key,
+ "You are currently running #{current_stable_out} version of Pleroma. What version of #{
+ frontend
+ } you want to install? (\"stable\", \"develop\" or specific ref)",
+ current_stable_out
+ )
+ end
end
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index 40dd9bdc0..f15112e9e 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -272,7 +272,7 @@ defmodule Mix.Tasks.Pleroma.User do
shell_info("Generated user invite token " <> String.replace(invite.invite_type, "_", " "))
url =
- Pleroma.Web.Router.Helpers.redirect_url(
+ Pleroma.Web.Router.Helpers.frontend_url(
Pleroma.Web.Endpoint,
:registration_page,
invite.token
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index a00938c04..e7a031c34 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -13,11 +13,13 @@ defmodule Pleroma.Application do
@name Mix.Project.config()[:name]
@version Mix.Project.config()[:version]
+ @stable? Mix.Project.config()[:stable?]
@repository Mix.Project.config()[:source_url]
@env Mix.env()
def name, do: @name
def version, do: @version
+ def stable?, do: @stable?
def named_version, do: @name <> " " <> @version
def repository, do: @repository
diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex
index dfadc10b3..d9cf2a6d7 100644
--- a/lib/pleroma/emails/user_email.ex
+++ b/lib/pleroma/emails/user_email.ex
@@ -47,7 +47,7 @@ defmodule Pleroma.Emails.UserEmail do
to_name \\ nil
) do
registration_url =
- Router.Helpers.redirect_url(
+ Router.Helpers.frontend_url(
Endpoint,
:registration_page,
user_invite_token.token
diff --git a/lib/pleroma/frontend.ex b/lib/pleroma/frontend.ex
new file mode 100644
index 000000000..92ed17bd4
--- /dev/null
+++ b/lib/pleroma/frontend.ex
@@ -0,0 +1,28 @@
+defmodule Pleroma.Frontend do
+ @type primary_fe_opts :: %{config: Map.t(), controller: Module.t(), static: boolean()}
+
+ @spec get_primary_fe_opts() :: primary_fe_opts()
+ def get_primary_fe_opts,
+ do: [:frontends] |> Pleroma.Config.get(%{}) |> Enum.into(%{}) |> get_primary_fe_opts()
+
+ @spec get_primary_fe_opts(Map.t()) :: primary_fe_opts()
+ def get_primary_fe_opts(%{primary: %{"name" => "none"}} = fe_config) do
+ %{
+ config: %{},
+ controller: Pleroma.Web.Frontend.HeadlessController,
+ static: fe_config[:static] || false
+ }
+ end
+
+ def get_primary_fe_opts(fe_config) do
+ %{
+ config: fe_config[:primary],
+ controller:
+ Module.concat([
+ Pleroma.Web.Frontend,
+ String.capitalize(fe_config[:primary]["name"]) <> "Controller"
+ ]),
+ static: fe_config[:static] || false
+ }
+ end
+end
diff --git a/lib/pleroma/plugs/frontend_plug.ex b/lib/pleroma/plugs/frontend_plug.ex
new file mode 100644
index 000000000..d3fd90a3c
--- /dev/null
+++ b/lib/pleroma/plugs/frontend_plug.ex
@@ -0,0 +1,47 @@
+defmodule Pleroma.Plugs.FrontendPlug do
+ @moduledoc """
+ Sets private key `:frontend` for the given connection.
+ It is set to one of admin|mastodon|primary frontends config values based
+ on `conn.request_path`
+ """
+
+ import Plug.Conn
+
+ @behaviour Plug
+
+ @mastodon_paths ~w(web packs sw.js api/web)
+ @admin_paths ~w(pleroma)
+
+ def init(opts) do
+ opts
+ end
+
+ for path <- @mastodon_paths do
+ def call(%{request_path: "/" <> unquote(path) <> _rest} = conn, _opts) do
+ fe_config =
+ Pleroma.Config.get([:frontends], %{mastodon: %{"name" => "mastodon", "ref" => ""}})
+
+ put_private(conn, :frontend, %{
+ config: fe_config[:mastodon],
+ controller: Pleroma.Web.Frontend.MastodonController,
+ static: false
+ })
+ end
+ end
+
+ for path <- @admin_paths do
+ def call(%{request_path: "/" <> unquote(path) <> _rest} = conn, _opts) do
+ fe_config = Pleroma.Config.get([:frontends], %{admin: %{"name" => "admin", "ref" => ""}})
+
+ put_private(conn, :frontend, %{
+ config: fe_config[:admin],
+ controller: Pleroma.Web.Frontend.AdminController,
+ static: false
+ })
+ end
+ end
+
+ def call(conn, _opts) do
+ put_private(conn, :frontend, Pleroma.Frontend.get_primary_fe_opts())
+ end
+end
diff --git a/lib/pleroma/plugs/instance_static.ex b/lib/pleroma/plugs/instance_static.ex
index 927fa2663..1436d16ae 100644
--- a/lib/pleroma/plugs/instance_static.ex
+++ b/lib/pleroma/plugs/instance_static.ex
@@ -1,28 +1,30 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
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.
+
+ Files in FE bundles can override files in priv/static, and files in
+ instance/static can override files in FE bundles:
+ instance/static > FE bundles > priv/static
"""
@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
+ # list of paths to be looked up in intance/static
+ @instance_overridable_paths ~w(robots.txt emoji sounds images instance favicon.png packs sw.js static index.html sw-pleroma.js static-fe.css)
- @only ~w(index.html robots.txt static emoji packs sounds images instance favicon.png sw.js
- sw-pleroma.js)
+ # both pleroma/{STATIC_PATH} and pleroma/admin/{STATIC_PATH} can be requested
+ @fe_prefixed_paths ~w(pleroma/admin pleroma)
+ @fe_paths [
+ # mastodon
+ "packs",
+ "sw.js",
+ # primary frontend
+ "static",
+ "index.html",
+ "sw-pleroma.js",
+ "static-fe.css"
+ ]
def init(opts) do
opts
@@ -31,29 +33,42 @@ defmodule Pleroma.Plugs.InstanceStatic do
|> Plug.Static.init()
end
- for only <- @only do
- at = Plug.Router.Utils.split("/")
+ for path <- @fe_prefixed_paths do
+ def call(%{request_path: "/" <> unquote(path) <> _} = conn, opts) do
+ fe_path = get_fe_path(conn)
+ opts = %{opts | at: Plug.Router.Utils.split(unquote(path)), from: fe_path}
+
+ Plug.Static.call(conn, opts)
+ end
+ end
+
+ for path <- @fe_paths do
+ def call(%{request_path: "/" <> unquote(path) <> _} = conn, opts) do
+ fe_path = get_fe_path(conn)
+ opts = %{opts | at: [], from: fe_path}
- def call(%{request_path: "/" <> unquote(only) <> _} = conn, opts) do
- call_static(
- conn,
- opts,
- unquote(at),
- Pleroma.Config.get([:instance, :static_dir], "instance/static")
- )
+ with ^conn <- call_instance_static(conn, opts) do
+ Plug.Static.call(conn, opts)
+ end
end
end
- def call(conn, _) do
- conn
+ for path <- @instance_overridable_paths do
+ def call(%{request_path: "/" <> unquote(path) <> _} = conn, opts) do
+ call_instance_static(conn, opts)
+ end
end
- defp call_static(conn, opts, at, from) do
- opts =
- opts
- |> Map.put(:from, from)
- |> Map.put(:at, at)
+ def call(conn, _), do: conn
+ defp call_instance_static(conn, opts) do
+ instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static")
+ opts = %{opts | at: [], from: instance_static_path}
Plug.Static.call(conn, opts)
end
+
+ defp get_fe_path(%{private: %{frontend: %{config: conf}}}) do
+ instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static")
+ Path.join([instance_static_path, "frontends", conf["name"], conf["ref"]])
+ end
end
diff --git a/lib/pleroma/plugs/static_fe_plug.ex b/lib/pleroma/plugs/static_fe_plug.ex
index 156e6788e..c12776f4d 100644
--- a/lib/pleroma/plugs/static_fe_plug.ex
+++ b/lib/pleroma/plugs/static_fe_plug.ex
@@ -4,21 +4,23 @@
defmodule Pleroma.Plugs.StaticFEPlug do
import Plug.Conn
- alias Pleroma.Web.StaticFE.StaticFEController
def init(options), do: options
- def call(conn, _) do
- if enabled?() and accepts_html?(conn) do
+ def call(%{private: %{frontend: %{static: true}}} = conn, _) do
+ action = Phoenix.Controller.action_name(conn)
+
+ if accepts_html?(conn) and
+ function_exported?(Pleroma.Web.Frontend.StaticController, action, 2) do
conn
- |> StaticFEController.call(:show)
+ |> Pleroma.Web.FrontendController.call(action)
|> halt()
else
conn
end
end
- defp enabled?, do: Pleroma.Config.get([:static_fe, :enabled], false)
+ def call(conn, _), do: conn
defp accepts_html?(conn) do
case get_req_header(conn, "accept") do
diff --git a/lib/pleroma/web/controllers/frontend/admin_controller.ex b/lib/pleroma/web/controllers/frontend/admin_controller.ex
new file mode 100644
index 000000000..f2e885e70
--- /dev/null
+++ b/lib/pleroma/web/controllers/frontend/admin_controller.ex
@@ -0,0 +1,4 @@
+defmodule Pleroma.Web.Frontend.AdminController do
+ use Pleroma.Web, :controller
+ use Pleroma.Web.Frontend.DefaultController
+end
diff --git a/lib/pleroma/web/controllers/frontend/default_controller.ex b/lib/pleroma/web/controllers/frontend/default_controller.ex
new file mode 100644
index 000000000..9f1711389
--- /dev/null
+++ b/lib/pleroma/web/controllers/frontend/default_controller.ex
@@ -0,0 +1,36 @@
+defmodule Pleroma.Web.Frontend.DefaultController do
+ defmacro __using__(_opts) do
+ quote do
+ import Pleroma.Web.FrontendController, only: [index_file_path: 0, index_file_path: 1]
+
+ def index(conn, _params) do
+ status = conn.status || 200
+ {:ok, index_file_path} = index_file_path(conn.private[:frontend][:config])
+
+ conn
+ |> put_resp_content_type("text/html")
+ |> send_file(status, index_file_path)
+ end
+
+ def api_not_implemented(conn, _params) do
+ conn
+ |> put_status(404)
+ |> json(%{error: "Not implemented"})
+ end
+
+ def empty(conn, _params) do
+ conn
+ |> put_status(204)
+ |> text("")
+ end
+
+ def fallback(conn, _params) do
+ conn
+ |> put_status(404)
+ |> text("Not found")
+ end
+
+ defoverridable index: 2, api_not_implemented: 2, empty: 2, fallback: 2
+ end
+ end
+end
diff --git a/lib/pleroma/web/controllers/frontend/headless_controller.ex b/lib/pleroma/web/controllers/frontend/headless_controller.ex
new file mode 100644
index 000000000..c61581588
--- /dev/null
+++ b/lib/pleroma/web/controllers/frontend/headless_controller.ex
@@ -0,0 +1,9 @@
+defmodule Pleroma.Web.Frontend.HeadlessController do
+ use Pleroma.Web, :controller
+
+ def index(conn, _params) do
+ conn
+ |> put_status(404)
+ |> text("")
+ end
+end
diff --git a/lib/pleroma/web/controllers/frontend/kenoma_controller.ex b/lib/pleroma/web/controllers/frontend/kenoma_controller.ex
new file mode 100644
index 000000000..8ea2635fb
--- /dev/null
+++ b/lib/pleroma/web/controllers/frontend/kenoma_controller.ex
@@ -0,0 +1,4 @@
+defmodule Pleroma.Web.Frontend.KenomaController do
+ use Pleroma.Web, :controller
+ use Pleroma.Web.Frontend.DefaultController
+end
diff --git a/lib/pleroma/web/controllers/frontend/mastodon_controller.ex b/lib/pleroma/web/controllers/frontend/mastodon_controller.ex
new file mode 100644
index 000000000..bb2e94220
--- /dev/null
+++ b/lib/pleroma/web/controllers/frontend/mastodon_controller.ex
@@ -0,0 +1,52 @@
+defmodule Pleroma.Web.Frontend.MastodonController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.User
+
+ plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings)
+
+ # Note: :index action handles attempt of unauthenticated access to private instance with redirect
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["read"], fallback: :proceed_unauthenticated, skip_instance_privacy_check: true}
+ when action == :index
+ )
+
+ plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index)
+
+ def index(%{assigns: %{user: user, token: token}} = conn, _params)
+ when not is_nil(user) and not is_nil(token) do
+ conn
+ |> put_layout(false)
+ |> render("index.html",
+ token: token.token,
+ user: user,
+ custom_emojis: Pleroma.Emoji.get_all()
+ )
+ end
+
+ def index(conn, _params) do
+ conn
+ |> put_session(:return_to, conn.request_path)
+ |> redirect(to: auth_path(conn, :login))
+ end
+
+ @doc "GET /web/manifest.json"
+ def manifest(conn, _params) do
+ render(conn, "manifest.json")
+ end
+
+ @doc "PUT /api/web/settings"
+ def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
+ case User.mastodon_settings_update(user, settings) do
+ {:ok, _} ->
+ json(conn, %{})
+
+ e ->
+ conn
+ |> put_status(:internal_server_error)
+ |> json(%{error: inspect(e)})
+ end
+ end
+end
diff --git a/lib/pleroma/web/controllers/frontend/pleroma_controller.ex b/lib/pleroma/web/controllers/frontend/pleroma_controller.ex
new file mode 100644
index 000000000..1085d05bf
--- /dev/null
+++ b/lib/pleroma/web/controllers/frontend/pleroma_controller.ex
@@ -0,0 +1,44 @@
+defmodule Pleroma.Web.Frontend.PleromaController do
+ use Pleroma.Web, :controller
+ use Pleroma.Web.Frontend.DefaultController
+
+ require Logger
+
+ alias Pleroma.User
+ alias Pleroma.Web.Metadata
+
+ def index_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do
+ case User.get_cached_by_nickname_or_id(maybe_nickname_or_id) do
+ %User{} = user ->
+ index_with_meta(conn, %{user: user})
+
+ _ ->
+ index(conn, params)
+ end
+ end
+
+ # not intended to be matched from router, but can be called from the app internally
+ def index_with_meta(conn, params) do
+ {:ok, path} = index_file_path()
+ {:ok, index_content} = File.read(path)
+
+ tags =
+ try do
+ Metadata.build_tags(params)
+ rescue
+ e ->
+ Logger.error(
+ "Metadata rendering for #{conn.request_path} failed.\n" <>
+ Exception.format(:error, e, __STACKTRACE__)
+ )
+
+ ""
+ end
+
+ response = String.replace(index_content, "<!--server-generated-meta-->", tags)
+
+ html(conn, response)
+ end
+
+ defdelegate registration_page(conn, params), to: __MODULE__, as: :index
+end
diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/controllers/frontend/static_controller.ex
index 7a35238d7..0746a9a6d 100644
--- a/lib/pleroma/web/static_fe/static_fe_controller.ex
+++ b/lib/pleroma/web/controllers/frontend/static_controller.ex
@@ -1,8 +1,4 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.StaticFE.StaticFEController do
+defmodule Pleroma.Web.Frontend.StaticController do
use Pleroma.Web, :controller
alias Pleroma.Activity
@@ -11,11 +7,8 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Metadata
- alias Pleroma.Web.Router.Helpers
plug(:put_layout, :static_fe)
- plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
- plug(:assign_id)
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
@@ -23,65 +16,20 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
- defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
- do: name
-
- defp get_title(%Object{data: %{"summary" => summary}}) when is_binary(summary),
- do: summary
-
- defp get_title(_), do: nil
-
- defp not_found(conn, message) do
- conn
- |> put_status(404)
- |> render("error.html", %{message: message, meta: ""})
- end
-
- defp get_counts(%Activity{} = activity) do
- %Object{data: data} = Object.normalize(activity)
-
- %{
- likes: data["like_count"] || 0,
- replies: data["repliesCount"] || 0,
- announces: data["announcement_count"] || 0
- }
- end
-
- defp represent(%Activity{} = activity), do: represent(activity, false)
-
- defp represent(%Activity{object: %Object{data: data}} = activity, selected) do
- {:ok, user} = User.get_or_fetch(activity.object.data["actor"])
-
- link =
- case user.local do
- true -> Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, activity)
- _ -> data["url"] || data["external_url"] || data["id"]
- end
+ def object(conn, %{"uuid" => _uuid}) do
+ url = url(conn) <> conn.request_path
- content =
- if data["content"] do
- data["content"]
- |> Pleroma.HTML.filter_tags()
- |> Pleroma.Emoji.Formatter.emojify(Map.get(data, "emoji", %{}))
- else
- nil
- end
+ case Activity.get_create_by_object_ap_id_with_object(url) do
+ %Activity{} = activity ->
+ to = o_status_path(Pleroma.Web.Endpoint, :notice, activity)
+ redirect(conn, to: to)
- %{
- user: User.sanitize_html(user),
- title: get_title(activity.object),
- content: content,
- attachment: data["attachment"],
- link: link,
- published: data["published"],
- sensitive: data["sensitive"],
- selected: selected,
- counts: get_counts(activity),
- id: activity.id
- }
+ _ ->
+ not_found(conn, "Post not found.")
+ end
end
- def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
+ def notice(conn, %{"id" => notice_id}) do
with %Activity{local: true} = activity <-
Activity.get_by_id_with_object(notice_id),
true <- Visibility.is_public?(activity.object),
@@ -106,7 +54,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
end
end
- def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
+ def feed_redirect(conn, %{"nickname" => username_or_id} = params) do
case User.get_cached_by_nickname_or_id(username_or_id) do
%User{} = user ->
meta = Metadata.build_tags(%{user: user})
@@ -134,12 +82,12 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
end
end
- def show(%{assigns: %{object_id: _}} = conn, _params) do
- url = Helpers.url(conn) <> conn.request_path
+ def activity(conn, %{"uuid" => _uuid}) do
+ url = url(conn) <> conn.request_path
- case Activity.get_create_by_object_ap_id_with_object(url) do
+ case Activity.get_by_ap_id(url) do
%Activity{} = activity ->
- to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
+ to = o_status_path(Pleroma.Web.Endpoint, :notice, activity)
redirect(conn, to: to)
_ ->
@@ -147,30 +95,61 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
end
end
- def show(%{assigns: %{activity_id: _}} = conn, _params) do
- url = Helpers.url(conn) <> conn.request_path
+ defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
+ do: name
- case Activity.get_by_ap_id(url) do
- %Activity{} = activity ->
- to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
- redirect(conn, to: to)
+ defp get_title(%Object{data: %{"summary" => summary}}) when is_binary(summary),
+ do: summary
- _ ->
- not_found(conn, "Post not found.")
- end
+ defp get_title(_), do: nil
+
+ defp not_found(conn, message) do
+ conn
+ |> put_status(404)
+ |> render("error.html", %{message: message, meta: ""})
+ end
+
+ defp get_counts(%Activity{} = activity) do
+ %Object{data: data} = Object.normalize(activity)
+
+ %{
+ likes: data["like_count"] || 0,
+ replies: data["repliesCount"] || 0,
+ announces: data["announcement_count"] || 0
+ }
end
- defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
- do: assign(conn, :notice_id, notice_id)
+ defp represent(%Activity{} = activity), do: represent(activity, false)
- defp assign_id(%{path_info: ["users", user_id]} = conn, _opts),
- do: assign(conn, :username_or_id, user_id)
+ defp represent(%Activity{object: %Object{data: data}} = activity, selected) do
+ {:ok, user} = User.get_or_fetch(activity.object.data["actor"])
- defp assign_id(%{path_info: ["objects", object_id]} = conn, _opts),
- do: assign(conn, :object_id, object_id)
+ link =
+ case user.local do
+ true -> o_status_url(Pleroma.Web.Endpoint, :notice, activity)
+ _ -> data["url"] || data["external_url"] || data["id"]
+ end
- defp assign_id(%{path_info: ["activities", activity_id]} = conn, _opts),
- do: assign(conn, :activity_id, activity_id)
+ content =
+ if data["content"] do
+ data["content"]
+ |> Pleroma.HTML.filter_tags()
+ |> Pleroma.Emoji.Formatter.emojify(Map.get(data, "emoji", %{}))
+ else
+ nil
+ end
- defp assign_id(conn, _opts), do: conn
+ %{
+ user: User.sanitize_html(user),
+ title: get_title(activity.object),
+ content: content,
+ attachment: data["attachment"],
+ link: link,
+ published: data["published"],
+ sensitive: data["sensitive"],
+ selected: selected,
+ counts: get_counts(activity),
+ id: activity.id
+ }
+ end
end
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index 72cb3ee27..2616273de 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -11,6 +11,7 @@ defmodule Pleroma.Web.Endpoint do
plug(CORSPlug)
plug(Pleroma.Plugs.HTTPSecurityPlug)
plug(Pleroma.Plugs.UploadedMedia)
+ plug(Pleroma.Plugs.FrontendPlug)
@static_cache_control "public, no-cache"
@@ -35,7 +36,7 @@ defmodule Pleroma.Web.Endpoint do
at: "/",
from: :pleroma,
only:
- ~w(index.html robots.txt static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc),
+ ~w(robots.txt static-fe.css finmoji emoji sounds images instance favicon.png schemas doc),
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
gzip: true,
cache_control_for_etags: @static_cache_control,
@@ -44,13 +45,6 @@ defmodule Pleroma.Web.Endpoint do
}
)
- plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
-
- plug(Plug.Static,
- at: "/pleroma/admin/",
- from: {:pleroma, "priv/static/adminfe/"}
- )
-
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
diff --git a/lib/pleroma/web/fallback_redirect_controller.ex b/lib/pleroma/web/fallback_redirect_controller.ex
deleted file mode 100644
index c13518030..000000000
--- a/lib/pleroma/web/fallback_redirect_controller.ex
+++ /dev/null
@@ -1,77 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Fallback.RedirectController do
- use Pleroma.Web, :controller
- require Logger
- alias Pleroma.User
- alias Pleroma.Web.Metadata
-
- def api_not_implemented(conn, _params) do
- conn
- |> put_status(404)
- |> json(%{error: "Not implemented"})
- end
-
- def redirector(conn, _params, code \\ 200)
-
- # redirect to admin section
- # /pleroma/admin -> /pleroma/admin/
- #
- def redirector(conn, %{"path" => ["pleroma", "admin"]} = _, _code) do
- redirect(conn, to: "/pleroma/admin/")
- end
-
- def redirector(conn, _params, code) do
- conn
- |> put_resp_content_type("text/html")
- |> send_file(code, index_file_path())
- end
-
- def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do
- with %User{} = user <- User.get_cached_by_nickname_or_id(maybe_nickname_or_id) do
- redirector_with_meta(conn, %{user: user})
- else
- nil ->
- redirector(conn, params)
- end
- end
-
- def redirector_with_meta(conn, params) do
- {:ok, index_content} = File.read(index_file_path())
-
- tags =
- try do
- Metadata.build_tags(params)
- rescue
- e ->
- Logger.error(
- "Metadata rendering for #{conn.request_path} failed.\n" <>
- Exception.format(:error, e, __STACKTRACE__)
- )
-
- ""
- end
-
- response = String.replace(index_content, "<!--server-generated-meta-->", tags)
-
- conn
- |> put_resp_content_type("text/html")
- |> send_resp(200, response)
- end
-
- def index_file_path do
- Pleroma.Plugs.InstanceStatic.file_path("index.html")
- end
-
- def registration_page(conn, params) do
- redirector(conn, params)
- end
-
- def empty(conn, _params) do
- conn
- |> put_status(204)
- |> text("")
- end
-end
diff --git a/lib/pleroma/web/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex
index e27f85929..3bad4663d 100644
--- a/lib/pleroma/web/feed/user_controller.ex
+++ b/lib/pleroma/web/feed/user_controller.ex
@@ -5,7 +5,6 @@
defmodule Pleroma.Web.Feed.UserController do
use Pleroma.Web, :controller
- alias Fallback.RedirectController
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ActivityPubController
@@ -15,11 +14,15 @@ defmodule Pleroma.Web.Feed.UserController do
plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect])
+ plug(Pleroma.Plugs.StaticFEPlug)
+
action_fallback(:errors)
def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
- RedirectController.redirector_with_meta(conn, %{user: user})
+ conn
+ |> Map.put(:params, %{user: user})
+ |> Pleroma.Web.FrontendController.call(:index_with_meta)
end
end
diff --git a/lib/pleroma/web/frontend_controller.ex b/lib/pleroma/web/frontend_controller.ex
new file mode 100644
index 000000000..e236119a0
--- /dev/null
+++ b/lib/pleroma/web/frontend_controller.ex
@@ -0,0 +1,74 @@
+defmodule Pleroma.Web.FrontendController do
+ use Pleroma.Web, :controller
+ import Pleroma.Frontend, only: [get_primary_fe_opts: 0]
+
+ def action(conn, _opts) do
+ # `conn.private[:frontend]` can be unset if the function is called outside
+ # of the standard controller pipeline
+ fe_config =
+ with nil <- conn.private[:frontend] do
+ get_primary_fe_opts()
+ end
+
+ # can only be true for :primary frontend
+ static_enabled? = Map.get(fe_config, :static, false)
+
+ action_name = action_name(conn)
+
+ {controller, action} =
+ cond do
+ static_enabled? and
+ function_exported?(Pleroma.Web.Frontend.StaticController, action_name, 2) ->
+ {Pleroma.Web.Frontend.StaticController, action_name}
+
+ function_exported?(fe_config[:controller], action_name, 2) ->
+ {fe_config[:controller], action_name}
+
+ true ->
+ {fe_config[:controller], :fallback}
+ end
+
+ conn
+ # in case we are serving internal call
+ |> put_private(:frontend, fe_config)
+ |> put_view(Phoenix.Controller.__view__(controller))
+ |> controller.call(controller.init(action))
+ end
+
+ @doc """
+ Returns path to index.html file for the frontend from the given config.
+ If config is not provided, config for the `:primary` frontend is fetched and used.
+ If index.html file is not found for the requested frontend, the function fallback
+ to looking the file at instance static directory and then, in case of failure,
+ in priv/static directory.
+ Path returned in case of success is guaranteed to be existing file.
+ """
+ @spec index_file_path(Map.t()) :: {:ok, String.t()} | {:error, String.t()}
+ def index_file_path(fe_config \\ nil) do
+ filename = "index.html"
+ instance_base_path = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
+
+ %{"name" => name, "ref" => ref} =
+ with nil <- fe_config do
+ Pleroma.Frontend.get_primary_fe_opts()[:config]
+ end
+
+ frontend_path = Path.join([instance_base_path, "frontends", name, ref, filename])
+ instance_path = Path.join([instance_base_path, filename])
+ priv_path = Application.app_dir(:pleroma, ["priv", "static", filename])
+
+ cond do
+ File.exists?(instance_path) ->
+ {:ok, instance_path}
+
+ File.exists?(frontend_path) ->
+ {:ok, frontend_path}
+
+ File.exists?(priv_path) ->
+ {:ok, priv_path}
+
+ true ->
+ {:error, "index.html file was not found"}
+ end
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
index 37b389382..70edeae7c 100644
--- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
@@ -51,8 +51,8 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
@doc "DELETE /auth/sign_out"
def logout(conn, _) do
conn
- |> clear_session
- |> redirect(to: "/")
+ |> clear_session()
+ |> redirect(to: frontend_path(conn, :index, []))
end
@doc "POST /auth/password"
@@ -75,7 +75,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
defp local_mastodon_root_path(conn) do
case get_session(conn, :return_to) do
nil ->
- masto_fe_path(conn, :index, ["getting-started"])
+ frontend_mastodon_path(conn, :index, ["getting-started"])
return_to ->
delete_session(conn, :return_to)
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
index 6fd3cfce5..57682c1ea 100644
--- a/lib/pleroma/web/ostatus/ostatus_controller.ex
+++ b/lib/pleroma/web/ostatus/ostatus_controller.ex
@@ -5,7 +5,6 @@
defmodule Pleroma.Web.OStatus.OStatusController do
use Pleroma.Web, :controller
- alias Fallback.RedirectController
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Plugs.RateLimiter
@@ -30,6 +29,8 @@ defmodule Pleroma.Web.OStatus.OStatusController do
when action in [:object, :activity, :notice]
)
+ plug(Pleroma.Plugs.StaticFEPlug)
+
action_fallback(:errors)
def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
@@ -37,14 +38,12 @@ defmodule Pleroma.Web.OStatus.OStatusController do
ActivityPubController.call(conn, :object)
end
- def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
+ def object(conn, %{"uuid" => uuid}) do
with id <- o_status_url(conn, :object, uuid),
{_, %Activity{} = activity} <-
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do
- case format do
- _ -> redirect(conn, to: "/notice/#{activity.id}")
- end
+ redirect(conn, to: o_status_path(conn, :notice, activity.id))
else
reason when reason in [{:public?, false}, {:activity, nil}] ->
{:error, :not_found}
@@ -59,13 +58,11 @@ defmodule Pleroma.Web.OStatus.OStatusController do
ActivityPubController.call(conn, :activity)
end
- def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
+ def activity(conn, %{"uuid" => uuid}) do
with id <- o_status_url(conn, :activity, uuid),
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do
- case format do
- _ -> redirect(conn, to: "/notice/#{activity.id}")
- end
+ redirect(conn, to: o_status_path(conn, :notice, activity.id))
else
reason when reason in [{:public?, false}, {:activity, nil}] ->
{:error, :not_found}
@@ -91,24 +88,25 @@ defmodule Pleroma.Web.OStatus.OStatusController do
activity.data["type"] == "Create" ->
%Object{} = object = Object.normalize(activity)
- RedirectController.redirector_with_meta(
- conn,
- %{
- activity_id: activity.id,
- object: object,
- url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id),
- user: user
- }
- )
+ params = %{
+ activity_id: activity.id,
+ object: object,
+ url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id),
+ user: user
+ }
+
+ conn
+ |> Map.put(:params, params)
+ |> Pleroma.Web.FrontendController.call(:index_with_meta)
true ->
- RedirectController.redirector(conn, nil)
+ Pleroma.Web.FrontendController.call(conn, :index)
end
else
reason when reason in [{:public?, false}, {:activity, nil}] ->
conn
|> put_status(404)
- |> RedirectController.redirector(nil, 404)
+ |> Pleroma.Web.FrontendController.call(:index)
e ->
e
@@ -135,7 +133,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
_error ->
conn
|> put_status(404)
- |> RedirectController.redirector(nil, 404)
+ |> Pleroma.Web.FrontendController.call(:index)
end
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 7e5960949..34eff708c 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -447,7 +447,7 @@ defmodule Pleroma.Web.Router do
scope "/api/web", Pleroma.Web do
pipe_through(:authenticated_api)
- put("/settings", MastoFEController, :put_settings)
+ put("/settings", FrontendController, :put_settings, as: :frontend_mastodon)
end
scope "/api/v1", Pleroma.Web.MastodonAPI do
@@ -535,16 +535,18 @@ defmodule Pleroma.Web.Router do
pipeline :ostatus do
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
- plug(Pleroma.Plugs.StaticFEPlug)
end
+ # pipeline :static_fe do
+ # plug(Pleroma.Plugs.StaticFEPlug)
+ # end
+
pipeline :oembed do
plug(:accepts, ["json", "xml"])
end
scope "/", Pleroma.Web do
- pipe_through(:ostatus)
- pipe_through(:http_signature)
+ pipe_through([:http_signature, :ostatus])
get("/objects/:uuid", OStatus.OStatusController, :object)
get("/activities/:uuid", OStatus.OStatusController, :activity)
@@ -646,7 +648,7 @@ defmodule Pleroma.Web.Router do
scope "/", Pleroma.Web do
pipe_through(:api)
- get("/web/manifest.json", MastoFEController, :manifest)
+ get("/web/manifest.json", FrontendController, :manifest, as: :frontend_mastodon)
end
scope "/", Pleroma.Web do
@@ -657,15 +659,10 @@ defmodule Pleroma.Web.Router do
post("/auth/password", MastodonAPI.AuthController, :password_reset)
- get("/web/*path", MastoFEController, :index)
- end
-
- pipeline :remote_media do
+ get("/web/*path", FrontendController, :index, as: :frontend_mastodon)
end
scope "/proxy/", Pleroma.Web.MediaProxy do
- pipe_through(:remote_media)
-
get("/:sig/:url", MediaProxyController, :remote)
get("/:sig/:url/:filename", MediaProxyController, :remote)
end
@@ -694,12 +691,16 @@ defmodule Pleroma.Web.Router do
get("/check_password", MongooseIMController, :check_password)
end
- scope "/", Fallback do
- get("/registration/:token", RedirectController, :registration_page)
- get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
- get("/api*path", RedirectController, :api_not_implemented)
- get("/*path", RedirectController, :redirector)
+ scope "/pleroma/admin", Pleroma.Web do
+ get("/*path", FrontendController, :index, as: :frontend_admin)
+ end
+
+ scope "/", Pleroma.Web do
+ get("/registration/:token", FrontendController, :registration_page)
+ get("/:maybe_nickname_or_id", FrontendController, :index_with_meta)
+ get("/api*path", FrontendController, :api_not_implemented)
+ get("/*path", FrontendController, :index)
- options("/*path", RedirectController, :empty)
+ options("/*path", FrontendController, :empty)
end
end
diff --git a/lib/pleroma/web/templates/masto_fe/index.html.eex b/lib/pleroma/web/templates/frontend/mastodon/index.html.eex
index c330960fa..22f3f3b5a 100644
--- a/lib/pleroma/web/templates/masto_fe/index.html.eex
+++ b/lib/pleroma/web/templates/frontend/mastodon/index.html.eex
@@ -6,8 +6,8 @@
<title>
<%= Config.get([:instance, :name]) %>
</title>
-<link rel="icon" type="image/png" href="/favicon.png"/>
-<link rel="manifest" type="applicaton/manifest+json" href="<%= masto_fe_path(Pleroma.Web.Endpoint, :manifest) %>" />
+<link rel="icon" type="image/png" href="favicon.png"/>
+<link rel="manifest" type="applicaton/manifest+json" href="<%= frontend_mastodon_path(Pleroma.Web.Endpoint, :manifest) %>" />
<meta name="theme-color" content="<%= Config.get([:manifest, :theme_color]) %>" />
diff --git a/lib/pleroma/web/templates/static_fe/static_fe/_attachment.html.eex b/lib/pleroma/web/templates/frontend/static/_attachment.html.eex
index 7e04e9550..7e04e9550 100644
--- a/lib/pleroma/web/templates/static_fe/static_fe/_attachment.html.eex
+++ b/lib/pleroma/web/templates/frontend/static/_attachment.html.eex
diff --git a/lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex b/lib/pleroma/web/templates/frontend/static/_notice.html.eex
index df5e5eedd..44171c86c 100644
--- a/lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex
+++ b/lib/pleroma/web/templates/frontend/static/_notice.html.eex
@@ -15,7 +15,7 @@
<%= for %{"name" => name, "url" => [url | _]} <- @attachment do %>
<%= if @sensitive do %>
<details class="nsfw">
- <summary><%= Gettext.gettext("sensitive media") %></summary>
+ <summary><%= gettext("sensitive media") %></summary>
<div>
<%= render("_attachment.html", %{name: name, url: url["href"],
mediaType: fetch_media_type(url)}) %>
@@ -29,9 +29,9 @@
</div>
<%= if @selected do %>
<dl class="counts">
- <dt><%= Gettext.gettext("replies") %></dt><dd><%= @counts.replies %></dd>
- <dt><%= Gettext.gettext("announces") %></dt><dd><%= @counts.announces %></dd>
- <dt><%= Gettext.gettext("likes") %></dt><dd><%= @counts.likes %></dd>
+ <dt><%= gettext("replies") %></dt><dd><%= @counts.replies %></dd>
+ <dt><%= gettext("announces") %></dt><dd><%= @counts.announces %></dd>
+ <dt><%= gettext("likes") %></dt><dd><%= @counts.likes %></dd>
</dl>
<% end %>
</div>
diff --git a/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex b/lib/pleroma/web/templates/frontend/static/_user_card.html.eex
index 56f3a1524..ba96cca41 100644
--- a/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex
+++ b/lib/pleroma/web/templates/frontend/static/_user_card.html.eex
@@ -1,10 +1,10 @@
<div class="p-author h-card">
<a class="u-url" rel="author noopener" href="<%= (@user.uri || @user.ap_id) %>">
<div class="avatar">
- <img src="<%= User.avatar_url(@user) |> MediaProxy.url %>" width="48" height="48" alt="">
+ <img src="<%= User.avatar_url(@user) |> Pleroma.Web.MediaProxy.url() %>" width="48" height="48" alt="">
</div>
<span class="display-name">
- <bdi><%= raw Formatter.emojify(@user.name, @user.emoji) %></bdi>
+ <bdi><%= raw Pleroma.Emoji.Formatter.emojify(@user.name, @user.emoji) %></bdi>
<span class="nickname"><%= @user.nickname %></span>
</span>
</a>
diff --git a/lib/pleroma/web/templates/static_fe/static_fe/conversation.html.eex b/lib/pleroma/web/templates/frontend/static/conversation.html.eex
index 2acd84828..2acd84828 100644
--- a/lib/pleroma/web/templates/static_fe/static_fe/conversation.html.eex
+++ b/lib/pleroma/web/templates/frontend/static/conversation.html.eex
diff --git a/lib/pleroma/web/templates/static_fe/static_fe/error.html.eex b/lib/pleroma/web/templates/frontend/static/error.html.eex
index d98a1eba7..d98a1eba7 100644
--- a/lib/pleroma/web/templates/static_fe/static_fe/error.html.eex
+++ b/lib/pleroma/web/templates/frontend/static/error.html.eex
diff --git a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex b/lib/pleroma/web/templates/frontend/static/profile.html.eex
index 3191bf450..1690c5edf 100644
--- a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex
+++ b/lib/pleroma/web/templates/frontend/static/profile.html.eex
@@ -2,12 +2,12 @@
<h1><%= link instance_name(), to: "/" %></h1>
<h3>
- <form class="pull-right collapse" method="POST" action="<%= Helpers.util_path(@conn, :remote_subscribe) %>">
+ <form class="pull-right collapse" method="POST" action="<%= util_path(@conn, :remote_subscribe) %>">
<input type="hidden" name="nickname" value="<%= @user.nickname %>">
<input type="hidden" name="profile" value="">
<button type="submit" class="collapse">Remote follow</button>
</form>
- <%= raw Formatter.emojify(@user.name, @user.emoji) %> |
+ <%= raw Pleroma.Emoji.Formatter.emojify(@user.name, @user.emoji) %> |
<%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %>
</h3>
<p><%= raw @user.bio %></p>
diff --git a/lib/pleroma/web/templates/layout/static_fe.html.eex b/lib/pleroma/web/templates/layout/static_fe.html.eex
index 819632cec..b7ec4d0c9 100644
--- a/lib/pleroma/web/templates/layout/static_fe.html.eex
+++ b/lib/pleroma/web/templates/layout/static_fe.html.eex
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui" />
<title><%= Pleroma.Config.get([:instance, :name]) %></title>
<%= Phoenix.HTML.raw(assigns[:meta] || "") %>
- <link rel="stylesheet" href="/static/static-fe.css">
+ <link rel="stylesheet" href="<%= static_path(@conn, "/static-fe.css") %>"/>
</head>
<body>
<div class="container">
diff --git a/lib/pleroma/web/views/frontend/mastodon_view.ex b/lib/pleroma/web/views/frontend/mastodon_view.ex
new file mode 100644
index 000000000..6428df601
--- /dev/null
+++ b/lib/pleroma/web/views/frontend/mastodon_view.ex
@@ -0,0 +1,121 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Frontend.MastodonView do
+ use Pleroma.Web, :view
+ alias Pleroma.Config
+ alias Pleroma.User
+ alias Pleroma.Web.MastodonAPI.AccountView
+ alias Pleroma.Web.MastodonAPI.CustomEmojiView
+
+ @default_settings %{
+ onboarded: true,
+ home: %{
+ shows: %{
+ reblog: true,
+ reply: true
+ }
+ },
+ notifications: %{
+ alerts: %{
+ follow: true,
+ favourite: true,
+ reblog: true,
+ mention: true
+ },
+ shows: %{
+ follow: true,
+ favourite: true,
+ reblog: true,
+ mention: true
+ },
+ sounds: %{
+ follow: true,
+ favourite: true,
+ reblog: true,
+ mention: true
+ }
+ }
+ }
+
+ def initial_state(token, user, custom_emojis) do
+ limit = Config.get([:instance, :limit])
+
+ %{
+ meta: %{
+ streaming_api_base_url: Pleroma.Web.Endpoint.websocket_url(),
+ access_token: token,
+ locale: "en",
+ domain: Pleroma.Web.Endpoint.host(),
+ admin: "1",
+ me: "#{user.id}",
+ unfollow_modal: false,
+ boost_modal: false,
+ delete_modal: true,
+ auto_play_gif: false,
+ display_sensitive_media: false,
+ reduce_motion: false,
+ max_toot_chars: limit,
+ mascot: User.get_mascot(user)["url"]
+ },
+ poll_limits: Config.get([:instance, :poll_limits]),
+ rights: %{
+ delete_others_notice: present?(user.is_moderator),
+ admin: present?(user.is_admin)
+ },
+ compose: %{
+ me: "#{user.id}",
+ default_privacy: user.default_scope,
+ default_sensitive: false,
+ allow_content_types: Config.get([:instance, :allowed_post_formats])
+ },
+ media_attachments: %{
+ accept_content_types: [
+ ".jpg",
+ ".jpeg",
+ ".png",
+ ".gif",
+ ".webm",
+ ".mp4",
+ ".m4v",
+ "image\/jpeg",
+ "image\/png",
+ "image\/gif",
+ "video\/webm",
+ "video\/mp4"
+ ]
+ },
+ settings: user.settings || @default_settings,
+ push_subscription: nil,
+ accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)},
+ custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis),
+ char_limit: limit
+ }
+ |> Jason.encode!()
+ |> Phoenix.HTML.raw()
+ end
+
+ defp present?(nil), do: false
+ defp present?(false), do: false
+ defp present?(_), do: true
+
+ def render("manifest.json", _params) do
+ %{
+ name: Config.get([:instance, :name]),
+ description: Config.get([:instance, :description]),
+ icons: Config.get([:manifest, :icons]),
+ theme_color: Config.get([:manifest, :theme_color]),
+ background_color: Config.get([:manifest, :background_color]),
+ display: "standalone",
+ scope: Pleroma.Web.base_url(),
+ start_url: frontend_mastodon_path(Pleroma.Web.Endpoint, :index, ["getting-started"]),
+ categories: [
+ "social"
+ ],
+ serviceworker: %{
+ src: "/sw.js"
+ }
+ }
+ end
+end
diff --git a/lib/pleroma/web/static_fe/static_fe_view.ex b/lib/pleroma/web/views/frontend/static_view.ex
index b3d1d1ec8..4cdb1128a 100644
--- a/lib/pleroma/web/static_fe/static_fe_view.ex
+++ b/lib/pleroma/web/views/frontend/static_view.ex
@@ -2,17 +2,12 @@
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
-defmodule Pleroma.Web.StaticFE.StaticFEView do
+defmodule Pleroma.Web.Frontend.StaticView do
use Pleroma.Web, :view
alias Calendar.Strftime
- alias Pleroma.Emoji.Formatter
alias Pleroma.User
- alias Pleroma.Web.Endpoint
- alias Pleroma.Web.Gettext
- alias Pleroma.Web.MediaProxy
alias Pleroma.Web.Metadata.Utils
- alias Pleroma.Web.Router.Helpers
use Phoenix.HTML
diff --git a/lib/pleroma/web/views/masto_fe_view.ex b/lib/pleroma/web/views/masto_fe_view.ex
index c3096006e..eb977b1bd 100644
--- a/lib/pleroma/web/views/masto_fe_view.ex
+++ b/lib/pleroma/web/views/masto_fe_view.ex
@@ -109,7 +109,7 @@ defmodule Pleroma.Web.MastoFEView do
background_color: Config.get([:manifest, :background_color]),
display: "standalone",
scope: Pleroma.Web.base_url(),
- start_url: masto_fe_path(Pleroma.Web.Endpoint, :index, ["getting-started"]),
+ start_url: frontend_mastodon_path(Pleroma.Web.Endpoint, :index, ["getting-started"]),
categories: [
"social"
],
diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex
index ae7c94640..ec1ab4664 100644
--- a/lib/pleroma/web/web.ex
+++ b/lib/pleroma/web/web.ex
@@ -50,7 +50,7 @@ defmodule Pleroma.Web do
end
# Here we can apply before-action hooks (e.g. verify whether auth checks were preformed)
- defp action(conn, params) do
+ def action(conn, params) do
if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) &&
not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do
conn
@@ -63,6 +63,8 @@ defmodule Pleroma.Web do
super(conn, params)
end
end
+
+ defoverridable(action: 2)
end
end
@@ -81,6 +83,8 @@ defmodule Pleroma.Web do
require Logger
+ alias Pleroma.Web.Endpoint
+
@doc "Same as `render/3` but wrapped in a rescue block"
def safe_render(view, template, assigns \\ %{}) do
Phoenix.View.render(view, template, assigns)