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
PORT=8079
HTTPS=false
DEBUG=true
RESTRICTED_MODE=true
VALIDATE_SIGNATURES=false
API_TOKEN=somesecretpassword
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

View file

@ -100,7 +100,9 @@ TELEGRAM_TOKEN=secret
TELEGRAM_ADMIN_HANDLE=your_handle
TLS_KEY=/path/to/key
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
@ -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
##### `FOOTER_BLURB`
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
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;
color: #333;
border: 1px solid #e5e5e5;
@ -51,8 +51,16 @@ section {
max-width: 700px;
padding-bottom: 32px;
> p:first-child {
margin-top: 0;
section {
border-bottom: 1px solid #e5e5e5;
> h4:first-child,
> p:first-child {
margin-top: 0;
}
> p:last-child {
margin-bottom: 0;
}
}
h3 {
@ -67,13 +75,13 @@ section {
li {
padding-top: 36px;
border-bottom: 1px solid #e5e5e5;
}
.padded {
padding: 0 24px;
}
.local-explainer,
.joining {
padding: 24px;
}
@ -174,9 +182,11 @@ footer {
li {
padding: 0;
border-bottom: none;
}
}
article section {
border-bottom: none;
}
}
}
@ -226,7 +236,7 @@ footer {
padding: 24px;
}
section {
article {
border-left: none;
border-right: none;
border-radius: 0;

View file

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

View file

@ -20,7 +20,29 @@ pub(crate) async fn route(
state: web::Data<State>,
config: web::Data<Config>,
) -> 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)) {
(true, true) | (false, false) => std::cmp::Ordering::Equal,
@ -37,7 +59,7 @@ pub(crate) async fn route(
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| {
tracing::error!("Error rendering template, {}", e.error());
ErrorKind::FlushBuffer

View file

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

View file

@ -4,7 +4,7 @@ data::Node,
templates::{info, instance, statics::index_css},
};
@(nodes: &[Node], config: &Config)
@(local: &[Node], nodes: &[Node], config: &Config)
<!doctype html>
<html>
@ -24,16 +24,23 @@ templates::{info, instance, statics::index_css},
</div>
</header>
<main>
<section>
<h3>@nodes.len() Connected Servers</h3>
@if nodes.is_empty() {
<p>There are no connected servers at this time.</p>
} else {
@if !local.is_empty() || config.local_blurb().is_some() {
<article>
<h3>About</h3>
<section class="local-explainer">
@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>
@for node in nodes {
@for node in local {
@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)
@: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() {
@ -45,10 +52,32 @@ templates::{info, instance, statics::index_css},
}
</ul>
}
</section>
<section>
</article>
}
@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>
<article class="joining">
<section class="joining">
@if config.restricted_mode() {
<h4>
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
Mastodon or Pleroma's relay formatting.
</p>
</article>
</section>
</section>
</article>
</main>
<footer>
@if let Some(blurb) = config.footer_blurb() {

View file

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

View file

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