Remove precompiled javascript #55
11 changed files with 445 additions and 170 deletions
|
@ -722,6 +722,7 @@
|
||||||
# config :pleroma, :frontends,
|
# config :pleroma, :frontends,
|
||||||
# primary: %{"name" => "pleroma-fe", "ref" => "develop"},
|
# primary: %{"name" => "pleroma-fe", "ref" => "develop"},
|
||||||
# admin: %{"name" => "admin-fe", "ref" => "stable"},
|
# admin: %{"name" => "admin-fe", "ref" => "stable"},
|
||||||
|
# mastodon: %{"enabled" => true, "name" => "mastodon-fe", "ref" => "develop"}
|
||||||
# available: %{...}
|
# available: %{...}
|
||||||
|
|
||||||
config :pleroma, :frontends,
|
config :pleroma, :frontends,
|
||||||
|
@ -733,18 +734,27 @@
|
||||||
"ref" => "develop",
|
"ref" => "develop",
|
||||||
"build_dir" => "dist"
|
"build_dir" => "dist"
|
||||||
},
|
},
|
||||||
"admin-fe" => %{
|
# mastodon-Fe cannot be set as a primary - it can only be enabled via the mastodon configuration above
|
||||||
"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" => %{
|
"mastodon-fe" => %{
|
||||||
"name" => "mastodon-fe",
|
"name" => "mastodon-fe",
|
||||||
"git" => "https://akkoma.dev/AkkomaGang/masto-fe",
|
"git" => "https://akkoma.dev/AkkomaGang/masto-fe",
|
||||||
"build_url" => "https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/masto-fe.zip",
|
"build_url" => "https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/masto-fe.zip",
|
||||||
"build_dir" => "distribution",
|
"build_dir" => "distribution",
|
||||||
"ref" => "develop"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
@mastoFEEnabled Config.get([:frontends, :mastodon, "enabled"])
|
||||||
|
|
||||||
socket("/socket", Pleroma.Web.UserSocket)
|
socket("/socket", Pleroma.Web.UserSocket)
|
||||||
socket("/live", Phoenix.LiveView.Socket)
|
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.
|
# Serve at "/" the static files from "priv/static" directory.
|
||||||
#
|
#
|
||||||
# You should set gzip to true if you are running phoenix.digest
|
# 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/"}
|
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 reloading can be explicitly enabled under the
|
||||||
# :code_reloader configuration of your endpoint.
|
# :code_reloader configuration of your endpoint.
|
||||||
if code_reloading? do
|
if code_reloading? do
|
||||||
|
|
|
@ -8,61 +8,16 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
||||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.Helpers.AuthHelper
|
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
|
||||||
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
alias Pleroma.Web.MastodonFE.Controller, as: MastodonFEController
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
|
plug(Pleroma.Web.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
|
||||||
|
|
||||||
@local_mastodon_name "Mastodon-Local"
|
use MastodonFEController, :auth_controller_login
|
||||||
|
|
||||||
@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
|
|
||||||
|
|
||||||
@doc "DELETE /auth/sign_out"
|
@doc "DELETE /auth/sign_out"
|
||||||
def logout(conn, _) do
|
def logout(conn, _) do
|
||||||
|
@ -87,22 +42,5 @@ def password_reset(conn, params) do
|
||||||
json_response(conn, :no_content, "")
|
json_response(conn, :no_content, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_mastodon_post_login_path(conn) do
|
use MastodonFEController, :auth_controller_local_functions
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
90
lib/pleroma/web/mastodon_fe/masto_fe_controller.ex
Normal file
90
lib/pleroma/web/mastodon_fe/masto_fe_controller.ex
Normal 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
|
|
@ -597,7 +597,6 @@ def login(%User{} = user, %App{} = app, requested_scopes) when is_list(requested
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Special case: Local MastodonFE
|
|
||||||
defp redirect_uri(%Plug.Conn{} = conn, "."), do: Routes.auth_url(conn, :login)
|
defp redirect_uri(%Plug.Conn{} = conn, "."), do: Routes.auth_url(conn, :login)
|
||||||
|
|
||||||
defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri
|
defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri
|
||||||
|
|
|
@ -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
|
|
|
@ -4,8 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Router do
|
defmodule Pleroma.Web.Router do
|
||||||
use Pleroma.Web, :router
|
use Pleroma.Web, :router
|
||||||
|
alias Pleroma.Config
|
||||||
import Phoenix.LiveDashboard.Router
|
import Phoenix.LiveDashboard.Router
|
||||||
|
|
||||||
|
@mastoFEEnabled Config.get([:frontends, :mastodon, "enabled"])
|
||||||
|
|
||||||
pipeline :accepts_html do
|
pipeline :accepts_html do
|
||||||
plug(:accepts, ["html"])
|
plug(:accepts, ["html"])
|
||||||
end
|
end
|
||||||
|
@ -578,11 +581,13 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/timelines/list/:list_id", TimelineController, :list)
|
get("/timelines/list/:list_id", TimelineController, :list)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/web", Pleroma.Web do
|
if @mastoFEEnabled do
|
||||||
pipe_through(:authenticated_api)
|
scope "/api/web", Pleroma.Web do
|
||||||
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
# Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
|
# Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
|
||||||
put("/settings", MastoFEController, :put_settings)
|
put("/settings", MastoFEController, :put_settings)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
|
@ -791,24 +796,26 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
|
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web do
|
if @mastoFEEnabled do
|
||||||
pipe_through(:api)
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:api)
|
||||||
|
|
||||||
get("/manifest.json", ManifestController, :show)
|
get("/manifest.json", ManifestController, :show)
|
||||||
get("/web/manifest.json", MastoFEController, :manifest)
|
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
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web do
|
scope "/", Pleroma.Web do
|
||||||
pipe_through(:mastodon_html)
|
pipe_through(:pleroma_html)
|
||||||
|
post("/auth/password", TwitterAPI.PasswordController, :request)
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/proxy/", Pleroma.Web do
|
scope "/proxy/", Pleroma.Web do
|
||||||
|
|
BIN
priv/static/images/pleroma-fox-tan-smol.png
Normal file
BIN
priv/static/images/pleroma-fox-tan-smol.png
Normal file
Binary file not shown.
Before Width: | Height: | Size: 854 KiB After Width: | Height: | Size: 854 KiB |
296
priv/static/instance/static.css
Normal file
296
priv/static/instance/static.css
Normal 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;
|
||||||
|
}
|
BIN
priv/static/instance/thumbnail.jpeg
Normal file
BIN
priv/static/instance/thumbnail.jpeg
Normal file
Binary file not shown.
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
2
priv/static/robots.txt
Normal file
2
priv/static/robots.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
User-Agent: *
|
||||||
|
Disallow:
|
Loading…
Reference in a new issue