Browse Source

Add an 'About' section to the relay

asonix 1 year ago
parent
commit
a77a4cde22
9 changed files with 172 additions and 68 deletions
  1. 6 0
      .env
  2. 7 1
      README.md
  3. 16 6
      scss/index.scss
  4. 31 0
      src/config.rs
  5. 24 2
      src/routes/index.rs
  6. 11 11
      templates/admin.rs.html
  7. 41 12
      templates/index.rs.html
  8. 10 10
      templates/info.rs.html
  9. 26 26
      templates/instance.rs.html

+ 6 - 0
.env

@@ -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

+ 7 - 1
README.md

@@ -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.

+ 16 - 6
scss/index.scss

@@ -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;

+ 31 - 0
src/config.rs

@@ -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
     }

+ 24 - 2
src/routes/index.rs

@@ -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

+ 11 - 11
templates/admin.rs.html

@@ -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>

+ 41 - 12
templates/index.rs.html

@@ -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,45 @@ templates::{info, instance, statics::index_css},
     </div>
   </header>
   <main>
-    <section>
+    @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 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)
+        </li>
+        } else {
+        @if let Some(inf) = node.info.as_ref() {
+        <li>
+          @:info(inf, &node.base)
+        </li>
+        }
+        }
+        }
+      </ul>
+      }
+    </article>
+    }
+    @if !nodes.is_empty() {
+    <article>
       <h3>@nodes.len() Connected Servers</h3>
-      @if nodes.is_empty() {
-      <p>There are no connected servers at this time.</p>
-      } else {
       <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)
+          @: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() {
@@ -44,11 +73,11 @@ templates::{info, instance, statics::index_css},
         }
         }
       </ul>
-      }
-    </section>
-    <section>
+    </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() {

+ 10 - 10
templates/info.rs.html

@@ -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>

+ 26 - 26
templates/instance.rs.html

@@ -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.
-            }
+        <br>Registration is open.
+        @if instance.requires_approval {
+        Accounts must be approved by an admin.
+        }
         } else{
-            Registration is closed
+        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>
-            }
-            @if let Some(contact) = contact {
-                <h5 class="instance-admin">@instance.title's admin:</h5>
-                @:admin(contact, base)
-            }
+      </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>