From fbed60248dd8abd0d7c4cd100cdc33234bf89173 Mon Sep 17 00:00:00 2001 From: asonix Date: Fri, 20 Mar 2020 13:40:18 -0500 Subject: [PATCH] Add ructe --- .env | 1 + Cargo.lock | 57 ++++++++++++++++++++++++++++ Cargo.toml | 7 ++++ scss/index.scss | 82 +++++++++++++++++++++++++++++++++++++++++ src/build.rs | 12 ++++++ src/error.rs | 3 ++ src/main.rs | 68 ++++++++++++++++++++++------------ templates/index.rs.html | 64 ++++++++++++++++++++++++++++++++ 8 files changed, 271 insertions(+), 23 deletions(-) create mode 100644 scss/index.scss create mode 100644 src/build.rs create mode 100644 templates/index.rs.html diff --git a/.env b/.env index c60e236..8332a0a 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ +OUT_DIR="compiled_templates" DATABASE_URL=postgres://ap_actix:ap_actix@localhost:5432/ap_actix diff --git a/Cargo.lock b/Cargo.lock index 1e63911..9947ae0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,6 +581,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecount" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0017894339f586ccb943b01b9555de56770c11cda818e7e3d8bd93f4ed7f46e" + [[package]] name = "byteorder" version = "1.3.4" @@ -1160,6 +1166,15 @@ dependencies = [ "winreg", ] +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.5" @@ -1443,6 +1458,17 @@ dependencies = [ "num-traits 0.2.11", ] +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits 0.2.11", +] + [[package]] name = "num-traits" version = "0.1.43" @@ -1764,12 +1790,14 @@ dependencies = [ "http-signature-normalization-actix", "log", "lru", + "mime", "num_cpus", "pretty_env_logger", "rand", "rsa", "rsa-magic-public-key", "rsa-pem", + "ructe", "serde 1.0.105", "serde_json", "sha2", @@ -1850,6 +1878,35 @@ dependencies = [ "yasna", ] +[[package]] +name = "rsass" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53314103d427bab245db7b3d1602faf509d2dd03db85b32421d1748e93bb4475" +dependencies = [ + "bytecount", + "lazy_static", + "nom", + "num-rational", + "num-traits 0.2.11", + "rand", +] + +[[package]] +name = "ructe" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85620b8046f88a870d93d90fa56904dec76cc79139bfcc22e71e87f0cd2169f" +dependencies = [ + "base64 0.11.0", + "bytecount", + "itertools", + "md5", + "mime", + "nom", + "rsass", +] + [[package]] name = "rust-ini" version = "0.13.0" diff --git a/Cargo.toml b/Cargo.toml index 6bd4503..8613906 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ readme = "README.md" repository = "https://git.asonix.dog/asonix/ap-relay" keywords = ["activitypub", "relay"] edition = "2018" +build = "src/build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -27,6 +28,7 @@ futures = "0.3.4" http-signature-normalization-actix = { version = "0.3.0-alpha.7", default-features = false, features = ["sha-2"] } log = "0.4" lru = "0.4.3" +mime = "0.3.16" num_cpus = "1.12" pretty_env_logger = "0.4.0" rand = "0.7" @@ -42,5 +44,10 @@ tokio = { version = "0.2.13", features = ["sync"] } ttl_cache = "0.5.1" uuid = { version = "0.8", features = ["v4"] } +[build-dependencies] +anyhow = "1.0" +dotenv = "0.15.0" +ructe = { version = "0.9.2", features = ["sass", "mime03"] } + [profile.dev.package.rsa] opt-level = 3 diff --git a/scss/index.scss b/scss/index.scss new file mode 100644 index 0000000..f89982a --- /dev/null +++ b/scss/index.scss @@ -0,0 +1,82 @@ +body { + background-color: #f5f5f5; + font-family: sans-serif; + margin: 0; + position: relative; + min-height: 100vh; + padding-bottom: 96px; +} + +body, +body * { + box-sizing: border-box; +} + +header { + padding: 32px 0; + background-color: #333; + color: #f5f5f5; + text-align: center; + + h1 { + margin: 0px; + } +} + +section { + padding: 24px; + background-color: #fff; + border: 1px solid #e5e5e5; + border-radius: 3px; + margin: 32px auto 0; + max-width: 700px; + + h3 { + margin-top: 0px; + } +} + +pre { + border: 1px solid #e5e5e5; + border-radius: 3px; + background-color: #f5f5f5; + padding: 8px; + padding-left: 32px; + padding-top: 10px; + position: relative; + + &:before { + content: ' '; + display: block; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 24px; + background-color: #e5e5e5; + } +} + +footer { + background-color: #333; + color: #f5f5f5; + position: absolute; + padding: 16px 8px; + bottom: 0; + left: 0; + right: 0; + text-align: center; + + a { + &, + &:focus, + &:hover, + &:active { + color: #ea7fbc; + } + } + + p { + margin: 0; + } +} diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..4430193 --- /dev/null +++ b/src/build.rs @@ -0,0 +1,12 @@ +use ructe::Ructe; + +fn main() -> Result<(), anyhow::Error> { + dotenv::dotenv().ok(); + + let mut ructe = Ructe::from_env()?; + let mut statics = ructe.statics()?; + statics.add_sass_file("scss/index.scss")?; + ructe.compile_templates("templates")?; + + Ok(()) +} diff --git a/src/error.rs b/src/error.rs index eacc97f..23253a1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -64,6 +64,9 @@ pub enum MyError { #[error("Too many CPUs, {0}")] CpuCount(#[from] std::num::TryFromIntError), + #[error("Couldn't flush buffer")] + FlushBuffer, + #[error("Timed out while waiting on db pool")] DbTimeout, diff --git a/src/main.rs b/src/main.rs index 74e0b49..ca3a204 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,14 @@ -use actix_web::{middleware::Logger, web, App, HttpServer, Responder}; +use actix_web::{ + http::header::{ContentType, Expires}, + middleware::Logger, + web, App, HttpResponse, HttpServer, +}; use bb8_postgres::tokio_postgres; +use log::error; +use std::{ + io::BufWriter, + time::{Duration, SystemTime}, +}; mod actor; mod apub; @@ -17,32 +26,42 @@ mod state; mod verifier; mod webfinger; -use self::{args::Args, config::Config, db::Db, state::State, webfinger::RelayResolver}; - -async fn index(state: web::Data, config: web::Data) -> impl Responder { - let mut s = String::new(); - s.push_str(&format!("Welcome to the relay on {}\n", config.hostname())); +use self::{ + args::Args, config::Config, db::Db, error::MyError, state::State, + templates::statics::StaticFile, webfinger::RelayResolver, +}; +async fn index( + state: web::Data, + config: web::Data, +) -> Result { let listeners = state.listeners().await; - if listeners.is_empty() { - s.push_str("There are no currently connected servers\n"); + + let mut buf = BufWriter::new(Vec::new()); + + templates::index(&mut buf, &listeners, &config)?; + let buf = buf.into_inner().map_err(|e| { + error!("Error rendering template, {}", e.error()); + MyError::FlushBuffer + })?; + + Ok(HttpResponse::Ok().content_type("text/html").body(buf)) +} + +static FAR: Duration = Duration::from_secs(60 * 60 * 24); + +async fn static_file(filename: web::Path) -> HttpResponse { + if let Some(data) = StaticFile::get(&filename.into_inner()) { + let far_expires = SystemTime::now() + FAR; + HttpResponse::Ok() + .set(Expires(far_expires.into())) + .set(ContentType(data.mime.clone())) + .body(data.content) } else { - s.push_str("Here are the currently connected servers:\n"); - s.push_str("\n"); + HttpResponse::NotFound() + .reason("No such static file.") + .finish() } - - for listener in listeners { - if let Some(domain) = listener.as_url().domain() { - s.push_str(&format!("{}\n", domain)); - } - } - s.push_str("\n"); - s.push_str(&format!( - "The source code for this project can be found at {}\n", - config.source_code() - )); - - s } #[actix_rt::main] @@ -107,9 +126,12 @@ async fn main() -> Result<(), anyhow::Error> { .service(actix_webfinger::scoped::<_, RelayResolver>()) .service(web::resource("/nodeinfo").route(web::get().to(nodeinfo::well_known))), ) + .service(web::resource("/static/{filename}").route(web::get().to(static_file))) }) .bind(bind_address)? .run() .await?; Ok(()) } + +include!(concat!(env!("OUT_DIR"), "/templates.rs")); diff --git a/templates/index.rs.html b/templates/index.rs.html new file mode 100644 index 0000000..e97af0b --- /dev/null +++ b/templates/index.rs.html @@ -0,0 +1,64 @@ +@use crate::{config::{Config, UrlKind}, templates::statics::index_css}; +@use activitystreams::primitives::XsdAnyUri; + +@(listeners: &[XsdAnyUri], config: &Config) + + + + + + @config.hostname() | ActivityPub Relay + + + +
+

Welcome to @config.software_name() on @config.hostname()

+
+
+
+

Connected Servers:

+ @if listeners.is_empty() { +

There are no connected servers at this time.

+ } else { +
    + @for listener in listeners { + @if let Some(domain) = listener.as_url().domain() { +
  • @domain
  • + } + } +
+ } +
+
+

Joining

+

+ If you are the admin of a server that supports activitypub relays, you can add + this relay to your server. +

+

Mastodon

+

+ Mastodon admins can add this relay by adding +

@config.generate_url(UrlKind::Inbox)
in their relay settings. +

+

Pleroma

+

+ Pleroma admins can add this relay by adding +

@config.generate_url(UrlKind::Actor)
+ to their relay settings (I don't actually know how pleroma handles adding + relays, is it still a mix command?). +

+

Others

+

+ Consult the documentation for your server. It's likely that it follows either + Mastodon or Pleroma's relay formatting. +

+
+
+ + +