Remove precompiled javascript #55
11 changed files with 445 additions and 170 deletions
|
@ -722,6 +722,7 @@
|
|||
# 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 @@
|
|||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 @@ def password_reset(conn, params) 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
|
||||
|
|
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
|
||||
|
||||
# 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
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
|
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