include mastoFE config

This commit is contained in:
FloatingGhost 2022-06-18 15:29:00 +01:00
parent f25bc0210d
commit 04d3661fcf
11 changed files with 445 additions and 170 deletions

View File

@ -722,6 +722,7 @@ config :pleroma, :static_fe, enabled: false
# config :pleroma, :frontends,
# primary: %{"name" => "pleroma-fe", "ref" => "develop"},
# admin: %{"name" => "admin-fe", "ref" => "stable"},
# mastodon: %{"enabled" => true, "name" => "mastodon-fe", "ref" => "develop"}
# available: %{...}
config :pleroma, :frontends,
@ -733,18 +734,27 @@ config :pleroma, :frontends,
"ref" => "develop",
"build_dir" => "dist"
},
"admin-fe" => %{
"name" => "admin-fe",
"git" => "https://akkoma.dev/AkkomaGang/admin-fe",
"build_url" => "https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/akkoma-fe.zip",
"ref" => "develop"
},
# mastodon-Fe cannot be set as a primary - it can only be enabled via the mastodon configuration above
"mastodon-fe" => %{
"name" => "mastodon-fe",
"git" => "https://akkoma.dev/AkkomaGang/masto-fe",
"build_url" => "https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/masto-fe.zip",
"build_dir" => "distribution",
"ref" => "develop"
},
"admin-fe" => %{
"name" => "admin-fe",
"git" => "https://akkoma.dev/AkkomaGang/admin-fe",
"build_url" => "https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/admin-fe.zip",
"ref" => "develop"
},
"soapbox-fe" => %{
"name" => "soapbox-fe",
"git" => "https://gitlab.com/soapbox-pub/soapbox-fe",
"build_url" =>
"https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/${ref}/download?job=build-production",
"ref" => "v1.0.0",
"build_dir" => "static"
}
}

View File

@ -8,6 +8,7 @@ defmodule Pleroma.Web.Endpoint do
require Pleroma.Constants
alias Pleroma.Config
@mastoFEEnabled Config.get([:frontends, :mastodon, "enabled"])
socket("/socket", Pleroma.Web.UserSocket)
socket("/live", Phoenix.LiveView.Socket)
@ -68,18 +69,6 @@ defmodule Pleroma.Web.Endpoint do
}
)
plug(Plug.Static.IndexHtml, at: "/pleroma/fedife/")
plug(Pleroma.Web.Plugs.FrontendStatic,
at: "/pleroma/fedife",
frontend_type: :fedife,
gzip: true,
cache_control_for_etags: @static_cache_control,
headers: %{
"cache-control" => @static_cache_control
}
)
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phoenix.digest
@ -102,6 +91,18 @@ defmodule Pleroma.Web.Endpoint do
from: {:pleroma, "priv/static/adminfe/"}
)
if @mastoFEEnabled do
plug(Pleroma.Web.Plugs.FrontendStatic,
at: "/",
frontend_type: :mastodon,
gzip: true,
cache_control_for_etags: @static_cache_control,
headers: %{
"cache-control" => @static_cache_control
}
)
end
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do

View File

@ -8,61 +8,16 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
alias Pleroma.Helpers.AuthHelper
alias Pleroma.Helpers.UriHelper
alias Pleroma.User
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.Token
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
alias Pleroma.Web.TwitterAPI.TwitterAPI
alias Pleroma.Web.MastodonFE.Controller, as: MastodonFEController
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
plug(Pleroma.Web.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
@local_mastodon_name "Mastodon-Local"
@doc "GET /web/login"
# Local Mastodon FE login callback action
def login(conn, %{"code" => auth_token} = params) do
with {:ok, app} <- local_mastofe_app(),
{:ok, auth} <- Authorization.get_by_token(app, auth_token),
{:ok, oauth_token} <- Token.exchange_token(app, auth) do
redirect_to =
conn
|> local_mastodon_post_login_path()
|> UriHelper.modify_uri_params(%{"access_token" => oauth_token.token})
conn
|> AuthHelper.put_session_token(oauth_token.token)
|> redirect(to: redirect_to)
else
_ -> redirect_to_oauth_form(conn, params)
end
end
def login(conn, params) do
with %{assigns: %{user: %User{}, token: %Token{app_id: app_id}}} <- conn,
{:ok, %{id: ^app_id}} <- local_mastofe_app() do
redirect(conn, to: local_mastodon_post_login_path(conn))
else
_ -> redirect_to_oauth_form(conn, params)
end
end
defp redirect_to_oauth_form(conn, _params) do
with {:ok, app} <- local_mastofe_app() do
path =
Routes.o_auth_path(conn, :authorize,
response_type: "code",
client_id: app.client_id,
redirect_uri: ".",
scope: Enum.join(app.scopes, " ")
)
redirect(conn, to: path)
end
end
use MastodonFEController, :auth_controller_login
@doc "DELETE /auth/sign_out"
def logout(conn, _) do
@ -87,22 +42,5 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
json_response(conn, :no_content, "")
end
defp local_mastodon_post_login_path(conn) do
case get_session(conn, :return_to) do
nil ->
Routes.masto_fe_path(conn, :index, ["getting-started"])
return_to ->
delete_session(conn, :return_to)
return_to
end
end
@spec local_mastofe_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def local_mastofe_app do
App.get_or_make(
%{client_name: @local_mastodon_name, redirect_uris: "."},
["read", "write", "follow", "push", "admin"]
)
end
use MastodonFEController, :auth_controller_local_functions
end

View File

@ -0,0 +1,90 @@
defmodule Pleroma.Web.MastodonFE.Controller do
alias Pleroma.Config
@local_mastodon_name "Mastodon-Local"
@mastoFEEnabled Config.get([:frontends, :mastodon, "enabled"])
def auth_controller_login do
if @mastoFEEnabled do
quote bind_quoted: [local_mastodon_name: @local_mastodon_name] do
@doc "GET /web/login"
# Local Mastodon FE login callback action
def login(conn, %{"code" => auth_token} = params) do
with {:ok, app} <- local_mastofe_app(),
{:ok, auth} <- Pleroma.Web.OAuth.Authorization.get_by_token(app, auth_token),
{:ok, oauth_token} <- Pleroma.Web.OAuth.Token.exchange_token(app, auth) do
redirect_to =
conn
|> local_mastodon_post_login_path()
|> Pleroma.Helpers.UriHelper.modify_uri_params(%{
"access_token" => oauth_token.token
})
conn
|> Pleroma.Helpers.AuthHelper.put_session_token(oauth_token.token)
|> redirect(to: redirect_to)
else
_ -> redirect_to_oauth_form(conn, params)
end
end
def login(conn, params) do
with %{
assigns: %{
user: %Pleroma.User{},
token: %Pleroma.Web.OAuth.Token{app_id: app_id}
}
} <- conn,
{:ok, %{id: ^app_id}} <- local_mastofe_app() do
redirect(conn, to: local_mastodon_post_login_path(conn))
else
_ -> redirect_to_oauth_form(conn, params)
end
end
defp redirect_to_oauth_form(conn, _params) do
with {:ok, app} <- local_mastofe_app() do
path =
Pleroma.Web.Router.Helpers.o_auth_path(conn, :authorize,
response_type: "code",
client_id: app.client_id,
redirect_uri: ".",
scope: Enum.join(app.scopes, " ")
)
redirect(conn, to: path)
end
end
@spec local_mastofe_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def local_mastofe_app do
Pleroma.Web.OAuth.App.get_or_make(
%{client_name: unquote(local_mastodon_name), redirect_uris: "."},
["read", "write", "follow", "push", "admin"]
)
end
end
end
end
def auth_controller_local_functions do
if @mastoFEEnabled do
quote do
defp local_mastodon_post_login_path(conn) do
case get_session(conn, :return_to) do
nil ->
Pleroma.Web.Router.Helpers.masto_fe_path(conn, :index, ["getting-started"])
return_to ->
delete_session(conn, :return_to)
return_to
end
end
end
end
end
defmacro __using__(which) when is_atom(which) do
apply(__MODULE__, which, [])
end
end

View File

@ -597,7 +597,6 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
end
# Special case: Local MastodonFE
defp redirect_uri(%Plug.Conn{} = conn, "."), do: Routes.auth_url(conn, :login)
defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri

View File

@ -1,68 +0,0 @@
defmodule Pleroma.Web.PluggableFrontend do
alias Pleroma.Config
def routers do
conf =
[:frontends, :enabled]
|> Config.get()
unless is_nil(conf) do
conf
|> Keyword.to_list()
|> Enum.map(&router/1)
end
end
def plugs do
conf =
[:frontends, :extra]
|> Config.get()
unless is_nil(conf) do
conf
|> Keyword.to_list()
|> Enum.map(&plug/1)
end
end
defp router({name, config}) do
unless is_nil(Map.get(config, "router")) do
forwarder({name, config})
end
end
defp plug({name, config}) do
if is_nil(Map.get(config, "router")) do
forwarder({name, config})
end
end
defp forwarder({frontend, %{"url_prefix" => prefixes} = config}) when is_list(prefixes) do
prefixes
|> Enum.map(fn prefix -> forwarder({frontend, Map.put(config, "url_prefix", prefix)}) end)
end
defp forwarder({_frontend, %{"url_prefix" => prefix, "router" => router}}) do
quote bind_quoted: [prefix: prefix, router: router] do
match(:*, prefix, router, :any)
end
end
defp forwarder({frontend, %{"url_prefix" => prefix}}) do
quote bind_quoted: [prefix: prefix, frontend: frontend] do
plug(Pleroma.Web.Plugs.FrontendStatic,
at: prefix,
frontend_type: frontend,
gzip: true,
cache_control_for_etags: @static_cache_control,
headers: %{
"cache-control" => @static_cache_control
}
)
end
end
defmacro __using__(which) when is_atom(which) do
apply(__MODULE__, which, [])
end
end

View File

@ -4,8 +4,11 @@
defmodule Pleroma.Web.Router do
use Pleroma.Web, :router
alias Pleroma.Config
import Phoenix.LiveDashboard.Router
@mastoFEEnabled Config.get([:frontends, :mastodon, "enabled"])
pipeline :accepts_html do
plug(:accepts, ["html"])
end
@ -578,11 +581,13 @@ defmodule Pleroma.Web.Router do
get("/timelines/list/:list_id", TimelineController, :list)
end
scope "/api/web", Pleroma.Web do
pipe_through(:authenticated_api)
if @mastoFEEnabled do
scope "/api/web", Pleroma.Web do
pipe_through(:authenticated_api)
# Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
put("/settings", MastoFEController, :put_settings)
# Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
put("/settings", MastoFEController, :put_settings)
end
end
scope "/api/v1", Pleroma.Web.MastodonAPI do
@ -791,24 +796,26 @@ defmodule Pleroma.Web.Router do
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
end
scope "/", Pleroma.Web do
pipe_through(:api)
if @mastoFEEnabled do
scope "/", Pleroma.Web do
pipe_through(:api)
get("/manifest.json", ManifestController, :show)
get("/web/manifest.json", MastoFEController, :manifest)
get("/manifest.json", ManifestController, :show)
get("/web/manifest.json", MastoFEController, :manifest)
end
scope "/", Pleroma.Web do
pipe_through(:mastodon_html)
get("/web/login", MastodonAPI.AuthController, :login)
delete("/auth/sign_out", MastodonAPI.AuthController, :logout)
get("/web/*path", MastoFEController, :index)
get("/embed/:id", EmbedController, :show)
end
end
scope "/", Pleroma.Web do
pipe_through(:mastodon_html)
get("/web/login", MastodonAPI.AuthController, :login)
delete("/auth/sign_out", MastodonAPI.AuthController, :logout)
post("/auth/password", MastodonAPI.AuthController, :password_reset)
get("/web/*path", MastoFEController, :index)
get("/embed/:id", EmbedController, :show)
pipe_through(:pleroma_html)
post("/auth/password", TwitterAPI.PasswordController, :request)
end
scope "/proxy/", Pleroma.Web do

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 KiB

View File

@ -0,0 +1,296 @@
* {
box-sizing: border-box;
}
:root {
--brand-color: #d8a070;
--background-color: #121a24;
--foreground-color: #182230;
--primary-text-color: #b9b9ba;
--muted-text-color: #89898a;
}
body {
background-color: var(--background-color);
font-family: sans-serif;
color: var(--primary-text-color);
padding: 0;
margin: 0;
}
.instance-header {
height: 60px;
padding: 10px;
background: var(--foreground-color);
box-shadow: 0 1px 4px 0px rgba(0, 0, 0, 0.5);
}
.instance-header__content {
display: flex;
align-items: center;
max-width: 400px;
margin: 0 auto;
}
.instance-header__thumbnail {
max-width: 40px;
border-radius: 4px;
margin-right: 12px;
}
.instance-header__title {
font-size: 16px;
font-weight: bold;
color: var(--primary-text-color);
}
.container {
max-width: 400px;
background-color: var(--foreground-color);
border-radius: 4px;
overflow: hidden;
margin: 35px auto;
box-shadow: 0 1px 4px 0px rgba(0, 0, 0, 0.5);
}
.container__content {
padding: 0 20px;
}
h1 {
margin: 0;
font-size: 24px;
text-align: center;
}
h2 {
color: var(--primary-text-color);
font-weight: normal;
font-size: 18px;
margin-bottom: 20px;
}
a {
color: var(--brand-color);
text-decoration: none;
}
form {
width: 100%;
}
.input {
color: var(--muted-text-color);
display: flex;
flex-direction: column;
}
input {
box-sizing: content-box;
padding: 10px;
margin-top: 5px;
margin-bottom: 10px;
background-color: var(--background-color);
color: var(--primary-text-color);
border: 0;
transition-property: border-bottom;
transition-duration: 0.35s;
border-bottom: 2px solid #2a384a;
font-size: 14px;
}
.scopes-input {
display: flex;
flex-direction: column;
margin: 1em 0;
color: var(--muted-text-color);
}
.scopes-input label:first-child {
height: 2em;
}
.scopes {
display: flex;
flex-wrap: wrap;
color: var(--primary-text-color);
}
.scope {
display: flex;
flex-basis: 100%;
height: 2em;
align-items: center;
}
.scope:before {
color: var(--primary-text-color);
content: "✔\fe0e";
margin-left: 1em;
margin-right: 1em;
}
[type="checkbox"] + label {
display: none;
cursor: pointer;
margin: 0.5em;
}
[type="checkbox"] {
display: none;
}
[type="checkbox"] + label:before {
cursor: pointer;
display: inline-block;
color: white;
background-color: var(--background-color);
border: 4px solid var(--background-color);
box-shadow: 0px 0px 1px 0 var(--brand-color);
width: 1.2em;
height: 1.2em;
margin-right: 1.0em;
content: "";
transition-property: background-color;
transition-duration: 0.35s;
color: var(--background-color);
margin-bottom: -0.2em;
border-radius: 2px;
}
[type="checkbox"]:checked + label:before {
background-color: var(--brand-color);
}
input:focus {
outline: none;
border-bottom: 2px solid var(--brand-color);
}
.actions {
display: flex;
justify-content: flex-end;
}
.actions button,
.actions a.button {
width: auto;
margin-left: 10px;
}
a.button,
button {
width: 100%;
background-color: #1c2a3a;
color: var(--primary-text-color);
border-radius: 4px;
border: none;
padding: 10px 16px;
margin-top: 20px;
margin-bottom: 20px;
text-transform: uppercase;
font-size: 16px;
box-shadow: 0px 0px 2px 0px black,
0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset,
0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
}
a.button:hover,
button:hover {
cursor: pointer;
box-shadow: 0px 0px 0px 1px var(--brand-color),
0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset,
0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
}
.alert-danger {
width: 100%;
background-color: #931014;
border: 1px solid #a06060;
border-radius: 4px;
padding: 10px;
margin-top: 20px;
font-weight: 500;
font-size: 16px;
}
.alert-info {
width: 100%;
border-radius: 4px;
border: 1px solid #7d796a;
padding: 10px;
margin-top: 20px;
font-weight: 500;
font-size: 16px;
}
.account-header__banner {
width: 100%;
height: 112px;
background-size: cover;
background-position: center;
}
.account-header__avatar {
width: 94px;
height: 94px;
background-size: cover;
background-position: center;
margin: -47px 10px 0;
border: 6px solid var(--foreground-color);
border-radius: 999px;
}
.account-header__meta {
padding: 6px 20px 17px;
}
.account-header__display-name {
font-size: 20px;
font-weight: bold;
}
.account-header__nickname {
font-size: 14px;
color: var(--muted-text-color);
}
@media all and (max-width: 420px) {
.container {
margin: 0 auto;
border-radius: 0;
}
.scope {
flex-basis: 0%;
}
.scope:before {
content: "";
margin-left: 0em;
margin-right: 1em;
}
.scope:first-child:before {
margin-left: 1em;
content: "✔\fe0e";
}
.scope:after {
content: ",";
}
.scope:last-child:after {
content: "";
}
}
.form-row {
display: flex;
}
.form-row > label {
line-height: 47px;
flex: 1;
}
.form-row > input {
flex: 2;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

2
priv/static/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-Agent: *
Disallow: