Add an 'About' section to the relay

This commit is contained in:
asonix 2022-11-21 14:23:37 -06:00
parent 5043892981
commit a77a4cde22
9 changed files with 174 additions and 70 deletions

6
.env
View file

@ -1,6 +1,12 @@
HOSTNAME=localhost:8079 HOSTNAME=localhost:8079
PORT=8079 PORT=8079
HTTPS=false
DEBUG=true
RESTRICTED_MODE=true RESTRICTED_MODE=true
VALIDATE_SIGNATURES=false
API_TOKEN=somesecretpassword API_TOKEN=somesecretpassword
FOOTER_BLURB="Contact <a href=\"https://masto.asonix.dog/@asonix\">@asonix</a> for inquiries" FOOTER_BLURB="Contact <a href=\"https://masto.asonix.dog/@asonix\">@asonix</a> for inquiries"
LOCAL_DOMAINS="masto.asonix.dog"
LOCAL_BLURB="<p>Welcome to my cool relay where I have cool relay things happening. I hope you enjoy your stay!</p>"
RUST_LOG=info
# OPENTELEMETRY_URL=http://localhost:4317 # OPENTELEMETRY_URL=http://localhost:4317

View file

@ -100,7 +100,9 @@ TELEGRAM_TOKEN=secret
TELEGRAM_ADMIN_HANDLE=your_handle TELEGRAM_ADMIN_HANDLE=your_handle
TLS_KEY=/path/to/key TLS_KEY=/path/to/key
TLS_CERT=/path/to/cert TLS_CERT=/path/to/cert
FOOTER_BLURB="Contact <a href=\"https://masto.asonix.dog/@asonix\">@asonix</a> FOOTER_BLURB="Contact <a href=\"https://masto.asonix.dog/@asonix\">@asonix</a> for inquiries"
LOCAL_DOMAINS=masto.asonix.dog
LOCAL_BLURB="<p>Welcome to my cool relay where I have cool relay things happening. I hope you enjoy your stay!</p>"
``` ```
#### Descriptions #### Descriptions
@ -140,6 +142,10 @@ Optional - This is specified if you are running the relay directly on the intern
Optional - This is specified if you are running the relay directly on the internet and have a TLS certificate chain to provide HTTPS for your relay Optional - This is specified if you are running the relay directly on the internet and have a TLS certificate chain to provide HTTPS for your relay
##### `FOOTER_BLURB` ##### `FOOTER_BLURB`
Optional - Add custom notes in the footer of the page Optional - Add custom notes in the footer of the page
##### `LOCAL_DOMAINS`
Optional - domains of mastodon servers run by the same admin as the relay
##### `LOCAL_BLURB`
Optional - description for the relay
### Subscribing ### Subscribing
Mastodon admins can subscribe to this relay by adding the `/inbox` route to their relay settings. Mastodon admins can subscribe to this relay by adding the `/inbox` route to their relay settings.

View file

@ -41,7 +41,7 @@ header {
} }
} }
section { article {
background-color: #fff; background-color: #fff;
color: #333; color: #333;
border: 1px solid #e5e5e5; border: 1px solid #e5e5e5;
@ -51,8 +51,16 @@ section {
max-width: 700px; max-width: 700px;
padding-bottom: 32px; padding-bottom: 32px;
> p:first-child { section {
margin-top: 0; border-bottom: 1px solid #e5e5e5;
> h4:first-child,
> p:first-child {
margin-top: 0;
}
> p:last-child {
margin-bottom: 0;
}
} }
h3 { h3 {
@ -67,13 +75,13 @@ section {
li { li {
padding-top: 36px; padding-top: 36px;
border-bottom: 1px solid #e5e5e5;
} }
.padded { .padded {
padding: 0 24px; padding: 0 24px;
} }
.local-explainer,
.joining { .joining {
padding: 24px; padding: 24px;
} }
@ -174,9 +182,11 @@ footer {
li { li {
padding: 0; padding: 0;
border-bottom: none;
} }
} }
article section {
border-bottom: none;
}
} }
} }
@ -226,7 +236,7 @@ footer {
padding: 24px; padding: 24px;
} }
section { article {
border-left: none; border-left: none;
border-right: none; border-right: none;
border-radius: 0; border-radius: 0;

View file

@ -38,6 +38,8 @@ pub(crate) struct ParsedConfig {
tls_key: Option<PathBuf>, tls_key: Option<PathBuf>,
tls_cert: Option<PathBuf>, tls_cert: Option<PathBuf>,
footer_blurb: Option<String>, footer_blurb: Option<String>,
local_domains: Option<String>,
local_blurb: Option<String>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -58,6 +60,8 @@ pub struct Config {
api_token: Option<String>, api_token: Option<String>,
tls: Option<TlsConfig>, tls: Option<TlsConfig>,
footer_blurb: Option<String>, footer_blurb: Option<String>,
local_domains: Vec<String>,
local_blurb: Option<String>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -115,6 +119,8 @@ impl std::fmt::Debug for Config {
.field("tls_key", &"[redacted]") .field("tls_key", &"[redacted]")
.field("tls_cert", &"[redacted]") .field("tls_cert", &"[redacted]")
.field("footer_blurb", &self.footer_blurb) .field("footer_blurb", &self.footer_blurb)
.field("local_domains", &self.local_domains)
.field("local_blurb", &self.local_blurb)
.finish() .finish()
} }
} }
@ -139,6 +145,8 @@ impl Config {
.set_default("tls_key", None as Option<&str>)? .set_default("tls_key", None as Option<&str>)?
.set_default("tls_cert", None as Option<&str>)? .set_default("tls_cert", None as Option<&str>)?
.set_default("footer_blurb", None as Option<&str>)? .set_default("footer_blurb", None as Option<&str>)?
.set_default("local_domains", None as Option<&str>)?
.set_default("local_blurb", None as Option<&str>)?
.add_source(Environment::default()) .add_source(Environment::default())
.build()?; .build()?;
@ -160,6 +168,13 @@ impl Config {
(None, None) => None, (None, None) => None,
}; };
let local_domains = config
.local_domains
.iter()
.flat_map(|s| s.split(","))
.map(|d| d.to_string())
.collect();
Ok(Config { Ok(Config {
hostname: config.hostname, hostname: config.hostname,
addr: config.addr, addr: config.addr,
@ -177,6 +192,8 @@ impl Config {
api_token: config.api_token, api_token: config.api_token,
tls, tls,
footer_blurb: config.footer_blurb, footer_blurb: config.footer_blurb,
local_domains,
local_blurb: config.local_blurb,
}) })
} }
@ -229,6 +246,20 @@ impl Config {
None None
} }
pub(crate) fn local_blurb(&self) -> Option<crate::templates::Html<&str>> {
if let Some(blurb) = &self.local_blurb {
if !blurb.is_empty() {
return Some(crate::templates::Html(blurb));
}
}
None
}
pub(crate) fn local_domains(&self) -> &[String] {
&self.local_domains
}
pub(crate) fn sled_path(&self) -> &PathBuf { pub(crate) fn sled_path(&self) -> &PathBuf {
&self.sled_path &self.sled_path
} }

View file

@ -20,7 +20,29 @@ pub(crate) async fn route(
state: web::Data<State>, state: web::Data<State>,
config: web::Data<Config>, config: web::Data<Config>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let mut nodes = state.node_cache().nodes().await?; let all_nodes = state.node_cache().nodes().await?;
let mut nodes = Vec::new();
let mut local = Vec::new();
for node in all_nodes {
if node
.base
.authority_str()
.map(|authority| {
config
.local_domains()
.iter()
.find(|domain| domain.as_str() == authority)
.is_some()
})
.unwrap_or(false)
{
local.push(node);
} else {
nodes.push(node);
}
}
nodes.sort_by(|lhs, rhs| match (open_reg(lhs), open_reg(rhs)) { nodes.sort_by(|lhs, rhs| match (open_reg(lhs), open_reg(rhs)) {
(true, true) | (false, false) => std::cmp::Ordering::Equal, (true, true) | (false, false) => std::cmp::Ordering::Equal,
@ -37,7 +59,7 @@ pub(crate) async fn route(
let mut buf = BufWriter::new(Vec::new()); let mut buf = BufWriter::new(Vec::new());
crate::templates::index(&mut buf, &nodes, &config)?; crate::templates::index(&mut buf, &local, &nodes, &config)?;
let buf = buf.into_inner().map_err(|e| { let buf = buf.into_inner().map_err(|e| {
tracing::error!("Error rendering template, {}", e.error()); tracing::error!("Error rendering template, {}", e.error());
ErrorKind::FlushBuffer ErrorKind::FlushBuffer

View file

@ -4,15 +4,15 @@
@(contact: &Contact, base: &IriString) @(contact: &Contact, base: &IriString)
<div class="admin"> <div class="admin">
<div class="left"> <div class="left">
<figure class="avatar"> <figure class="avatar">
<img src="@contact.avatar" alt="@contact.display_name's avatar"> <img src="@contact.avatar" alt="@contact.display_name's avatar">
</figure> </figure>
</div> </div>
<div class="right"> <div class="right">
<p class="display-name"><a href="@contact.url">@contact.display_name</a></p> <p class="display-name"><a href="@contact.url">@contact.display_name</a></p>
<p class="username"> <p class="username">
@@@contact.username@if let Some(authority) = base.authority_str() {@@@authority} @@@contact.username@if let Some(authority) = base.authority_str() {@@@authority}
</p> </p>
</div> </div>
</div> </div>

View file

@ -4,7 +4,7 @@ data::Node,
templates::{info, instance, statics::index_css}, templates::{info, instance, statics::index_css},
}; };
@(nodes: &[Node], config: &Config) @(local: &[Node], nodes: &[Node], config: &Config)
<!doctype html> <!doctype html>
<html> <html>
@ -24,16 +24,23 @@ templates::{info, instance, statics::index_css},
</div> </div>
</header> </header>
<main> <main>
<section> @if !local.is_empty() || config.local_blurb().is_some() {
<h3>@nodes.len() Connected Servers</h3> <article>
@if nodes.is_empty() { <h3>About</h3>
<p>There are no connected servers at this time.</p> <section class="local-explainer">
} else { @if let Some(blurb) = config.local_blurb() {
@blurb
} else {
<p>These domains are run by the same admins as this relay.</p>
}
</section>
@if !local.is_empty() {
<ul> <ul>
@for node in nodes { @for node in local {
@if let Some(inst) = node.instance.as_ref() { @if let Some(inst) = node.instance.as_ref() {
<li> <li>
@:instance(inst, node.info.as_ref().map(|info| { info.software.as_ref() }), node.contact.as_ref(), &node.base) @:instance(inst, node.info.as_ref().map(|info| { info.software.as_ref() }), node.contact.as_ref(),
&node.base)
</li> </li>
} else { } else {
@if let Some(inf) = node.info.as_ref() { @if let Some(inf) = node.info.as_ref() {
@ -45,10 +52,32 @@ templates::{info, instance, statics::index_css},
} }
</ul> </ul>
} }
</section> </article>
<section> }
@if !nodes.is_empty() {
<article>
<h3>@nodes.len() Connected Servers</h3>
<ul>
@for node in nodes {
@if let Some(inst) = node.instance.as_ref() {
<li>
@:instance(inst, node.info.as_ref().map(|info| { info.software.as_ref() }), node.contact.as_ref(),
&node.base)
</li>
} else {
@if let Some(inf) = node.info.as_ref() {
<li>
@:info(inf, &node.base)
</li>
}
}
}
</ul>
</article>
}
<article>
<h3>Joining</h3> <h3>Joining</h3>
<article class="joining"> <section class="joining">
@if config.restricted_mode() { @if config.restricted_mode() {
<h4> <h4>
This relay is Restricted This relay is Restricted
@ -80,8 +109,8 @@ templates::{info, instance, statics::index_css},
Consult the documentation for your server. It's likely that it follows either Consult the documentation for your server. It's likely that it follows either
Mastodon or Pleroma's relay formatting. Mastodon or Pleroma's relay formatting.
</p> </p>
</article> </section>
</section> </article>
</main> </main>
<footer> <footer>
@if let Some(blurb) = config.footer_blurb() { @if let Some(blurb) = config.footer_blurb() {

View file

@ -3,14 +3,14 @@
@(info: &Info, base: &IriString) @(info: &Info, base: &IriString)
<article class="info"> <section class="info">
@if let Some(authority) = base.authority_str() { @if let Some(authority) = base.authority_str() {
<h4 class="padded"><a href="@base">@authority</a></h4> <h4 class="padded"><a href="@base">@authority</a></h4>
}
<p class="padded">
Running @info.software, version @info.version.
@if info.reg {
Registration is open
} }
<p class="padded"> </p>
Running @info.software, version @info.version. </section>
@if info.reg {
Registration is open
}
</p>
</article>

View file

@ -3,35 +3,35 @@
@(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &IriString) @(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &IriString)
<article class="instance"> <section class="instance">
<h4 class="padded"><a href="@base">@instance.title</a></h4> <h4 class="padded"><a href="@base">@instance.title</a></h4>
<p class="padded"> <p class="padded">
@if let Some(software) = software { @if let Some(software) = software {
Running @software, version @instance.version. Running @software, version @instance.version.
} }
@if instance.reg { @if instance.reg {
<br>Registration is open. <br>Registration is open.
@if instance.requires_approval { @if instance.requires_approval {
Accounts must be approved by an admin. Accounts must be approved by an admin.
}
} else{
Registration is closed
} }
</p> } else{
@if !instance.description.trim().is_empty() || contact.is_some() { Registration is closed
<div class="instance-info"> }
@if !instance.description.trim().is_empty() { </p>
<h5 class="instance-description">@instance.title's description:</h5> @if !instance.description.trim().is_empty() || contact.is_some() {
<div class="description"> <div class="instance-info">
<div class="please-stay"> @if !instance.description.trim().is_empty() {
@Html(instance.description.trim()) <h5 class="instance-description">@instance.title's description:</h5>
</div> <div class="description">
</div> <div class="please-stay">
} @Html(instance.description.trim())
@if let Some(contact) = contact { </div>
<h5 class="instance-admin">@instance.title's admin:</h5>
@:admin(contact, base)
}
</div> </div>
} }
</article> @if let Some(contact) = contact {
<h5 class="instance-admin">@instance.title's admin:</h5>
@:admin(contact, base)
}
</div>
}
</section>