Add ructe

This commit is contained in:
asonix 2020-03-20 13:40:18 -05:00
parent ef13e93140
commit fbed60248d
8 changed files with 271 additions and 23 deletions

1
.env
View file

@ -1 +1,2 @@
OUT_DIR="compiled_templates"
DATABASE_URL=postgres://ap_actix:ap_actix@localhost:5432/ap_actix DATABASE_URL=postgres://ap_actix:ap_actix@localhost:5432/ap_actix

57
Cargo.lock generated
View file

@ -581,6 +581,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "bytecount"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0017894339f586ccb943b01b9555de56770c11cda818e7e3d8bd93f4ed7f46e"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.3.4" version = "1.3.4"
@ -1160,6 +1166,15 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "itertools"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.5" version = "0.4.5"
@ -1443,6 +1458,17 @@ dependencies = [
"num-traits 0.2.11", "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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.1.43" version = "0.1.43"
@ -1764,12 +1790,14 @@ dependencies = [
"http-signature-normalization-actix", "http-signature-normalization-actix",
"log", "log",
"lru", "lru",
"mime",
"num_cpus", "num_cpus",
"pretty_env_logger", "pretty_env_logger",
"rand", "rand",
"rsa", "rsa",
"rsa-magic-public-key", "rsa-magic-public-key",
"rsa-pem", "rsa-pem",
"ructe",
"serde 1.0.105", "serde 1.0.105",
"serde_json", "serde_json",
"sha2", "sha2",
@ -1850,6 +1878,35 @@ dependencies = [
"yasna", "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]] [[package]]
name = "rust-ini" name = "rust-ini"
version = "0.13.0" version = "0.13.0"

View file

@ -8,6 +8,7 @@ readme = "README.md"
repository = "https://git.asonix.dog/asonix/ap-relay" repository = "https://git.asonix.dog/asonix/ap-relay"
keywords = ["activitypub", "relay"] keywords = ["activitypub", "relay"]
edition = "2018" edition = "2018"
build = "src/build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # 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"] } http-signature-normalization-actix = { version = "0.3.0-alpha.7", default-features = false, features = ["sha-2"] }
log = "0.4" log = "0.4"
lru = "0.4.3" lru = "0.4.3"
mime = "0.3.16"
num_cpus = "1.12" num_cpus = "1.12"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
rand = "0.7" rand = "0.7"
@ -42,5 +44,10 @@ tokio = { version = "0.2.13", features = ["sync"] }
ttl_cache = "0.5.1" ttl_cache = "0.5.1"
uuid = { version = "0.8", features = ["v4"] } 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] [profile.dev.package.rsa]
opt-level = 3 opt-level = 3

82
scss/index.scss Normal file
View file

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

12
src/build.rs Normal file
View file

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

View file

@ -64,6 +64,9 @@ pub enum MyError {
#[error("Too many CPUs, {0}")] #[error("Too many CPUs, {0}")]
CpuCount(#[from] std::num::TryFromIntError), CpuCount(#[from] std::num::TryFromIntError),
#[error("Couldn't flush buffer")]
FlushBuffer,
#[error("Timed out while waiting on db pool")] #[error("Timed out while waiting on db pool")]
DbTimeout, DbTimeout,

View file

@ -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 bb8_postgres::tokio_postgres;
use log::error;
use std::{
io::BufWriter,
time::{Duration, SystemTime},
};
mod actor; mod actor;
mod apub; mod apub;
@ -17,32 +26,42 @@ mod state;
mod verifier; mod verifier;
mod webfinger; mod webfinger;
use self::{args::Args, config::Config, db::Db, state::State, webfinger::RelayResolver}; use self::{
args::Args, config::Config, db::Db, error::MyError, state::State,
async fn index(state: web::Data<State>, config: web::Data<Config>) -> impl Responder { templates::statics::StaticFile, webfinger::RelayResolver,
let mut s = String::new(); };
s.push_str(&format!("Welcome to the relay on {}\n", config.hostname()));
async fn index(
state: web::Data<State>,
config: web::Data<Config>,
) -> Result<HttpResponse, MyError> {
let listeners = state.listeners().await; 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<String>) -> 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 { } else {
s.push_str("Here are the currently connected servers:\n"); HttpResponse::NotFound()
s.push_str("\n"); .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] #[actix_rt::main]
@ -107,9 +126,12 @@ async fn main() -> Result<(), anyhow::Error> {
.service(actix_webfinger::scoped::<_, RelayResolver>()) .service(actix_webfinger::scoped::<_, RelayResolver>())
.service(web::resource("/nodeinfo").route(web::get().to(nodeinfo::well_known))), .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)? .bind(bind_address)?
.run() .run()
.await?; .await?;
Ok(()) Ok(())
} }
include!(concat!(env!("OUT_DIR"), "/templates.rs"));

64
templates/index.rs.html Normal file
View file

@ -0,0 +1,64 @@
@use crate::{config::{Config, UrlKind}, templates::statics::index_css};
@use activitystreams::primitives::XsdAnyUri;
@(listeners: &[XsdAnyUri], config: &Config)
<!doctype html>
<html>
<head lang="en">
<meta charset="utf-8"/>
<title>@config.hostname() | ActivityPub Relay</title>
<link rel="stylesheet" href="/static/@index_css.name" type="text/css" />
</head>
<body>
<header>
<h1>Welcome to @config.software_name() on @config.hostname()</h1>
</header>
<main>
<section>
<h3>Connected Servers:</h3>
@if listeners.is_empty() {
<p>There are no connected servers at this time.</p>
} else {
<ul>
@for listener in listeners {
@if let Some(domain) = listener.as_url().domain() {
<li>@domain</li>
}
}
</ul>
}
</section>
<section>
<h3>Joining</h3>
<p>
If you are the admin of a server that supports activitypub relays, you can add
this relay to your server.
</p>
<h4>Mastodon</h4>
<p>
Mastodon admins can add this relay by adding
<pre>@config.generate_url(UrlKind::Inbox)</pre> in their relay settings.
</p>
<h4>Pleroma</h4>
<p>
Pleroma admins can add this relay by adding
<pre>@config.generate_url(UrlKind::Actor)</pre>
to their relay settings (I don't actually know how pleroma handles adding
relays, is it still a mix command?).
</p>
<h4>Others</h4>
<p>
Consult the documentation for your server. It's likely that it follows either
Mastodon or Pleroma's relay formatting.
</p>
</section>
</main>
<footer>
<p>
The source code for this project can be found at
<a href="@config.source_code()">@config.source_code()</a>
</p>
</footer>
</body>
</html>