# docker stuff
docker-db
+*.iml
alias Pleroma.Web.Plugs.OAuthScopesPlug
- @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
- plug(:skip_auth)
-
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["admin:metrics"]}
+ when action in [
+ :show
+ ]
+ )
def show(conn, _params) do
stats = TelemetryMetricsPrometheus.Core.scrape()
})
end
- defp ip(%{remote_ip: remote_ip}) do
+ defp ip(%{remote_ip: remote_ip}) when is_binary(remote_ip) do
+ remote_ip
+ end
+
+ defp ip(%{remote_ip: remote_ip}) when is_tuple(remote_ip) do
remote_ip
|> Tuple.to_list()
|> Enum.join(".")
scope "/" do
pipe_through([:pleroma_html, :authenticate, :require_admin])
- live_dashboard("/phoenix/live_dashboard", metrics: {Pleroma.Web.Telemetry, :live_dashboard_metrics}, csp_nonce_assign_key: :csp_nonce)
+
+ live_dashboard("/phoenix/live_dashboard",
+ metrics: {Pleroma.Web.Telemetry, :live_dashboard_metrics},
+ csp_nonce_assign_key: :csp_nonce
+ )
end
# Test-only routes needed to test action dispatching and plug chain execution
scope "/", Pleroma.Web.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_with_preload)
options("/*path", RedirectController, :empty)
def init(_arg) do
children = [
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000},
- {TelemetryMetricsPrometheus, metrics: prometheus_metrics(), plug_cowboy_opts: [ip: {127, 0, 0, 1}]}
+ {TelemetryMetricsPrometheus.Core, metrics: prometheus_metrics()}
]
Supervisor.init(children, strategy: :one_for_one)
end
- @doc """
- A seperate set of metrics for distributions because phoenix dashboard does NOT handle
- them well
- """
+ # A seperate set of metrics for distributions because phoenix dashboard does NOT handle them well
defp distribution_metrics do
[
distribution(
summary("vm.total_run_queue_lengths.total"),
summary("vm.total_run_queue_lengths.cpu"),
summary("vm.total_run_queue_lengths.io"),
-
-
last_value("pleroma.local_users.total"),
last_value("pleroma.domains.total"),
last_value("pleroma.local_statuses.total")
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui">
<title><%= Pleroma.Config.get([:instance, :name]) %></title>
- <link rel="stylesheet" href="/instance/static.css">
+ <link rel="stylesheet" href="/static-fe/static-fe.css">
+ <link rel="stylesheet" href="/static-fe/forms.css">
</head>
<body>
- <div class="instance-header">
- <a class="instance-header__content" href="/">
- <img class="instance-header__thumbnail" src="<%= Pleroma.Config.get([:instance, :instance_thumbnail]) %>">
- <h1 class="instance-header__title"><%= Pleroma.Config.get([:instance, :name]) %></h1>
- </a>
- </div>
+
+ <div class="background-image"></div>
+ <nav>
+ <div class="inner-nav">
+ <a class="site-brand" href="/">
+ <img class="favicon" src="/favicon.png" />
+ <span><%= Pleroma.Config.get([:instance, :name]) %></span>
+ </a>
+ </div>
+ </nav>
<div class="container">
- <%= @inner_content %>
+ <div class="underlay"></div>
+ <div class="column main flex">
+ <div class="panel oauth">
+ <%= @inner_content %>
+ </div>
+ </div>
</div>
</body>
+
+ <style>
+ :root {
+ --background-image: url("<%= Pleroma.Config.get([:instance, :background_image]) %>");
+ }
+ </style>
</html>
</nav>
<div class="container">
<div class="underlay"></div>
- <div class="column main">
- <%= @inner_content %>
+ <div class="column main">
+ <%= @inner_content %>
</div>
<div class="column sidebar">
<div class="about panel">
-<h1><%= Gettext.dpgettext("static_pages", "oauth authorized page title", "Successfully authorized") %></h1>
-<h2><%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is <br>%{token}", token: safe_to_string(html_escape(@auth.token))) %></h2>
+<div>
+ <div class="panel-heading">
+ <%= Gettext.dpgettext("static_pages", "oauth authorized page title", "Successfully authorized") %>
+ </div>
+ <div class="panel-content">
+ <%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is <br>%{token}", token: safe_to_string(html_escape(@auth.token))) %>
+ </div>
+</div>
<div class="container__content">
<%= if @app do %>
- <p><%= raw Gettext.dpgettext("static_pages", "oauth authorize message", "Application <strong>%{client_name}</strong> is requesting access to your account.", client_name: safe_to_string(html_escape(@app.client_name))) %></p>
+ <div class="panel-heading">
+ <p><%= raw Gettext.dpgettext("static_pages", "oauth authorize message", "Application <strong>%{client_name}</strong> is requesting access to your account.", client_name: safe_to_string(html_escape(@app.client_name))) %></p>
+ </div>
<%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %>
<% end %>
{:telemetry, "~> 0.3"},
{:telemetry_poller, "~> 0.4"},
{:telemetry_metrics, "~> 0.4"},
- {:telemetry_metrics_prometheus, "~> 1.1.0"},
+ {:telemetry_metrics_prometheus_core, "~> 1.1.0"},
{:poolboy, "~> 1.5"},
{:recon, "~> 2.5"},
{:joken, "~> 2.0"},
--- /dev/null
+form {
+ width: 100%;
+}
+
+.input {
+ color: var(--muted-text-color);
+ display: flex;
+ margin-left: 1em;
+ margin-right: 1em;
+ flex-direction: column;
+}
+
+input {
+ 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;
+ width: inherit;
+ box-sizing: border-box;
+}
+
+.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);
+}
+
+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;
+}
--profileTint: rgba(15, 22, 30, 0.5);
--btnText: rgba(185, 185, 186, 1);
--btn: rgba(21, 30, 43, 1);
- --btnShadow: 0px 0px 2px 0px rgba(0, 0, 0, 1) , 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
+ --btnShadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
--btnHoverShadow: 0px 0px 1px 2px rgba(185, 185, 186, 0.4) inset, 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
--lightText: rgba(236, 236, 236, 1);
- --panelShadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.5) , 0px 4px 6px 3px rgba(0, 0, 0, 0.3);
- --panelHeaderShadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4) , 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset;
+ --panelShadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.5), 0px 4px 6px 3px rgba(0, 0, 0, 0.3);
+ --panelHeaderShadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset;
--topBar: rgba(21, 30, 43, 1);
--topBarText: rgba(159, 159, 161, 1);
- --topBarShadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.4) , 0px 2px 7px 0px rgba(0, 0, 0, 0.3);
+ --topBarShadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.4), 0px 2px 7px 0px rgba(0, 0, 0, 0.3);
--underlay: rgba(9, 14, 20, 0.6);
--background: rgba(15, 22, 30, 1);
--faint: rgba(185, 185, 186, 0.5);
--border: rgba(26, 37, 53, 1);
--poll: rgba(99, 84, 72, 1);
}
+
@media (prefers-color-scheme: light) {
:root {
- --icon-filter: invert(67%) sepia(7%) saturate(525%) hue-rotate(173deg) brightness(90%) contrast(92%);;
+ --icon-filter: invert(67%) sepia(7%) saturate(525%) hue-rotate(173deg) brightness(90%) contrast(92%);
+ ;
--wallpaper: rgba(248, 250, 252, 1);
--alertNeutral: rgba(48, 64, 85, 0.5);
--alertNeutralText: rgba(0, 0, 0, 1);
--profileTint: rgba(242, 246, 249, 0.5);
--btnText: rgba(48, 64, 85, 1);
--btn: rgba(214, 223, 237, 1);
- --btnShadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.2) , 0px 1px 0px 0px rgba(255, 255, 255, 0.5) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
- --btnHoverShadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.2) , 0px 0px 1px 2px rgba(255, 195, 159, 1) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
+ --btnShadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.2), 0px 1px 0px 0px rgba(255, 255, 255, 0.5) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
+ --btnHoverShadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.2), 0px 0px 1px 2px rgba(255, 195, 159, 1) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
--lightText: rgba(11, 14, 19, 1);
- --panelShadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.5) , 0px 3px 6px 1px rgba(0, 0, 0, 0.2);
+ --panelShadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.5), 0px 3px 6px 1px rgba(0, 0, 0, 0.2);
--panelHeaderShadow: 0px 1px 0px 0px rgba(255, 255, 255, 0.5) inset, 0px 1px 3px 0px rgba(0, 0, 0, 0.3);
--topBar: rgba(214, 223, 237, 1);
--topBarText: rgba(48, 64, 85, 1);
padding-right: 5px
}
-body > .container {
+body>.container {
display: grid;
grid-template-columns: minmax(25em, 45em) 25em;
grid-template-areas: "content sidebar";
box-shadow: var(--panelHeaderShadow);
}
+.panel-content {
+ padding: 1em;
+}
+
.about-content {
padding: 0.6em;
}
padding-left: 0.5em;
}
+.column.flex {
+ grid-column-end: sidebar-end;
+}
+
+.scopes-input {
+ display: flex;
+ flex-direction: column;
+ margin: 1em 0;
+ color: var(--muted-text-color);
+}
+
+
.status-container,
.repeat-header,
.user-card {
.repeat-header .right-side {
color: var(--faint);
}
+
.repeat-header .u-photo {
height: 20px;
width: 20px;
.reply-to-link {
color: var(--faint);
}
+
.reply-to-link:hover {
text-decoration: underline;
}
margin-bottom: 8px;
}
-header a, .h-card a {
+header a,
+.h-card a {
text-decoration: none;
}
-header a:hover, .h-card a:hover {
+header a:hover,
+.h-card a:hover {
text-decoration: underline;
}
min-width: 0;
}
-.attachment > * {
+.attachment>* {
width: 100%;
object-fit: contain;
}
display: flex;
align-items: center;
}
+
.nsfw-banner div {
width: 100%;
text-align: center;
.nsfw-banner:not(:hover) {
background-color: var(--background);
}
+
.nsfw-banner:hover div {
display: none;
}
word-break: break-word;
z-index: 1;
}
+
.poll-option .percentage {
width: 3.5em;
flex-shrink: 0;
}
+
.poll-option .fill {
height: 100%;
position: absolute;
display: flex;
margin-top: 0.75em;
}
-.status-actions > * {
+
+.status-actions>* {
max-width: 4em;
flex: 1;
display: flex;
right: 0;
bottom: 0;
background-image: linear-gradient(to bottom, var(--profileTint), var(--profileTint)),
- var(--user-banner);
+ var(--user-banner);
background-size: cover;
background-color: var(--profileBg);
-webkit-mask: linear-gradient(to top, white, transparent) bottom no-repeat,
- linear-gradient(to top, white, white);
+ linear-gradient(to top, white, white);
-webkit-mask-composite: xor;
-webkit-mask-size: 100% 60%;
z-index: -2;
}
@media (max-width: 800px) {
- body > .container {
+ body>.container {
display: block;
}
.username img:not(.u-photo) {
width: 16px;
height: 16px;
-}
+}
\ No newline at end of file
--- /dev/null
+#!/bin/sh
+
+read -p "Instance URL (e.g https://example.com): " INSTANCE_URL
+
+echo "Creating oauth app..."
+
+RESP=$(curl \
+ -XPOST \
+ $INSTANCE_URL/api/v1/apps \
+ --silent \
+ --data-urlencode 'client_name=fedibash' \
+ --data-urlencode 'redirect_uris=urn:ietf:wg:oauth:2.0:oob' \
+ --data-urlencode 'scopes=admin:metrics' \
+ --header "Content-Type: application/x-www-form-urlencoded"
+)
+
+client_id=$(echo $RESP | jq -r .client_id)
+client_secret=$(echo $RESP | jq -r .client_secret)
+
+if [ -z "$client_id"]; then
+ echo "Could not create an app"
+ echo "$RESP"
+ exit 1
+fi
+
+echo "Please visit the following URL and input the code provided"
+AUTH_URL="$INSTANCE_URL/oauth/authorize?client_id=$client_id&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=admin:metrics&response_type=code"
+if [ ! -z "$BROWSER" ]; then
+ $BROWSER $AUTH_URL
+fi;
+
+echo $AUTH_URL
+
+read -p "Code: " CODE
+
+echo "Requesting code..."
+
+RESP=$(curl \
+ -XPOST \
+ $INSTANCE_URL/oauth/token \
+ --silent \
+ --header "Content-Type: application/x-www-form-urlencoded" \
+ --data-urlencode "client_id=$client_id" \
+ --data-urlencode "client_secret=$client_secret" \
+ --data-urlencode "code=$CODE" \
+ --data-urlencode "grant_type=authorization_code" \
+ --data-urlencode 'redirect_uri=urn:ietf:wg:oauth:2.0:oob' \
+ --data-urlencode "scope=admin:metrics"
+)
+
+ACCESS_TOKEN="$(echo $RESP | jq -r .access_token)"
+
+echo "Token is $ACCESS_TOKEN"
+DOMAIN=$(echo $INSTANCE_URL | sed -e 's/^https:\/\///')
+
+echo "Use the following config in your prometheus.yml:
+- job_name: akkoma
+ scheme: https
+ authorization:
+ credentials: $ACCESS_TOKEN
+ metrics_path: /api/v1/akkoma/metrics
+ static_configs:
+ - targets:
+ - $DOMAIN
+"
%Pleroma.Web.OAuth.App{
client_name: sequence(:client_name, &"Some client #{&1}"),
redirect_uris: "https://example.com/callback",
- scopes: ["read", "write", "follow", "push", "admin"],
+ scopes: ["read", "write", "follow", "push"],
website: "https://example.com",
client_id: Ecto.UUID.generate(),
client_secret: "aaa;/&bbb"