From 9ada30626bf1ae24e5ffe1e5d4e43e169998a701 Mon Sep 17 00:00:00 2001
From: asonix
Date: Wed, 25 Mar 2020 22:26:45 -0500
Subject: [PATCH] Add media cache, improve default styles
---
Cargo.lock | 178 +++++++++++++++++++++++++++++++++++++
Cargo.toml | 2 +
scss/index.scss | 134 +++++++++++++++++++++++++---
src/config.rs | 2 +
src/data/media.rs | 60 +++++++++++++
src/data/mod.rs | 4 +-
src/error.rs | 3 +
src/jobs/instance.rs | 13 ++-
src/jobs/mod.rs | 47 +++++++---
src/main.rs | 17 +++-
src/requests.rs | 50 +++++++++++
src/routes/index.rs | 1 -
src/routes/media.rs | 27 ++++++
src/routes/mod.rs | 2 +
templates/admin.rs.html | 15 ++++
templates/index.rs.html | 75 +++++++++-------
templates/info.rs.html | 16 ++++
templates/instance.rs.html | 32 +++++++
18 files changed, 613 insertions(+), 65 deletions(-)
create mode 100644 src/data/media.rs
create mode 100644 src/routes/media.rs
create mode 100644 templates/admin.rs.html
create mode 100644 templates/info.rs.html
create mode 100644 templates/instance.rs.html
diff --git a/Cargo.lock b/Cargo.lock
index cce9243..fd9babd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -364,6 +364,21 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "ammonia"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89eac85170f4b3fb3dc5e442c1cfb036cb8eecf9dbbd431a161ffad15d90ea3b"
+dependencies = [
+ "html5ever",
+ "lazy_static",
+ "maplit",
+ "markup5ever_rcdom",
+ "matches",
+ "tendril",
+ "url",
+]
+
[[package]]
name = "ansi_term"
version = "0.11.0"
@@ -930,6 +945,16 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+[[package]]
+name = "futf"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b"
+dependencies = [
+ "mac",
+ "new_debug_unreachable",
+]
+
[[package]]
name = "futures"
version = "0.3.4"
@@ -1118,6 +1143,20 @@ dependencies = [
"winapi 0.3.8",
]
+[[package]]
+name = "html5ever"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b"
+dependencies = [
+ "log",
+ "mac",
+ "markup5ever",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "http"
version = "0.2.0"
@@ -1339,6 +1378,47 @@ dependencies = [
"linked-hash-map 0.5.2",
]
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
+[[package]]
+name = "markup5ever"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab"
+dependencies = [
+ "log",
+ "phf",
+ "phf_codegen",
+ "serde 1.0.105",
+ "serde_derive",
+ "serde_json",
+ "string_cache",
+ "string_cache_codegen",
+ "tendril",
+]
+
+[[package]]
+name = "markup5ever_rcdom"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
+dependencies = [
+ "html5ever",
+ "markup5ever",
+ "tendril",
+ "xml5ever",
+]
+
[[package]]
name = "match_cfg"
version = "0.1.0"
@@ -1437,6 +1517,12 @@ dependencies = [
"winapi 0.3.8",
]
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
+
[[package]]
name = "nodrop"
version = "0.1.14"
@@ -1600,6 +1686,26 @@ dependencies = [
"phf_shared",
]
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
[[package]]
name = "phf_shared"
version = "0.8.0"
@@ -1680,6 +1786,12 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
@@ -1763,6 +1875,7 @@ dependencies = [
"rand_chacha",
"rand_core",
"rand_hc",
+ "rand_pcg",
]
[[package]]
@@ -1793,6 +1906,15 @@ dependencies = [
"rand_core",
]
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core",
+]
+
[[package]]
name = "redox_syscall"
version = "0.1.56"
@@ -1826,12 +1948,14 @@ dependencies = [
"actix-rt",
"actix-web",
"actix-webfinger",
+ "ammonia",
"anyhow",
"async-trait",
"background-jobs",
"background-jobs-core",
"base64 0.12.0",
"bb8-postgres",
+ "bytes",
"config",
"dotenv",
"env_logger",
@@ -2246,6 +2370,31 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
+[[package]]
+name = "string_cache"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a"
+dependencies = [
+ "lazy_static",
+ "new_debug_unreachable",
+ "phf_shared",
+ "precomputed-hash",
+ "serde 1.0.105",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+]
+
[[package]]
name = "stringprep"
version = "0.1.2"
@@ -2332,6 +2481,17 @@ dependencies = [
"unicode-xid",
]
+[[package]]
+name = "tendril"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
[[package]]
name = "termcolor"
version = "1.1.0"
@@ -2636,6 +2796,12 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "utf-8"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
+
[[package]]
name = "uuid"
version = "0.8.1"
@@ -2815,6 +2981,18 @@ dependencies = [
"winapi-build",
]
+[[package]]
+name = "xml5ever"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59"
+dependencies = [
+ "log",
+ "mac",
+ "markup5ever",
+ "time 0.1.42",
+]
+
[[package]]
name = "yaml-rust"
version = "0.4.3"
diff --git a/Cargo.toml b/Cargo.toml
index 7854da9..ee5e057 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,9 +19,11 @@ actix-rt = "1.0.0"
actix-web = { version = "3.0.0-alpha.1", features = ["rustls"] }
actix-webfinger = "0.3.0-alpha.3"
activitystreams = "0.5.0-alpha.11"
+ammonia = "3.1.0"
async-trait = "0.1.24"
background-jobs = { version = "0.8.0-alpha.0", git = "https://git.asonix.dog/Aardwolf/background-jobs", default-features = false, features = ["background-jobs-actix"] }
background-jobs-core = { version = "0.7.0", git = "https://git.asonix.dog/Aardwolf/background-jobs" }
+bytes = "0.5.4"
base64 = "0.12"
bb8-postgres = { version = "0.4.0", features = ["with-serde_json-1", "with-uuid-0_8", "with-chrono-0_4"] }
config = "0.10.1"
diff --git a/scss/index.scss b/scss/index.scss
index f89982a..624a322 100644
--- a/scss/index.scss
+++ b/scss/index.scss
@@ -7,32 +7,69 @@ body {
padding-bottom: 96px;
}
+ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
body,
body * {
box-sizing: border-box;
}
header {
- padding: 32px 0;
background-color: #333;
color: #f5f5f5;
- text-align: center;
+
+ .header-text {
+ max-width: 700px;
+ margin: auto;
+ padding: 24px 0;
+ }
h1 {
margin: 0px;
}
+
+ p {
+ margin: 0;
+ margin-top: 8px;
+ font-style: italic;
+ }
}
section {
- padding: 24px;
background-color: #fff;
border: 1px solid #e5e5e5;
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
border-radius: 3px;
margin: 32px auto 0;
max-width: 700px;
+ padding-bottom: 32px;
+
+ > p:first-child {
+ margin-top: 0;
+ }
h3 {
- margin-top: 0px;
+ padding: 24px;
+ margin: 0px;
+ }
+
+ li {
+ padding-top: 24px;
+ padding-bottom: 24px;
+ border-top: 1px solid #e5e5e5;
+ }
+
+ .padded {
+ padding: 0 24px;
+ }
+
+ .joining {
+ padding: 24px;
+ border-top: 1px solid #e5e5e5;
}
}
@@ -57,6 +94,15 @@ pre {
}
}
+a {
+ &,
+ &:focus,
+ &:hover,
+ &:active {
+ color: #ea7fbc;
+ }
+}
+
footer {
background-color: #333;
color: #f5f5f5;
@@ -67,16 +113,78 @@ footer {
right: 0;
text-align: center;
- a {
- &,
- &:focus,
- &:hover,
- &:active {
- color: #ea7fbc;
- }
- }
-
p {
margin: 0;
}
}
+
+.instance,
+.info {
+ h4 {
+ font-size: 20px;
+ margin: 0;
+ }
+
+ .instance-info {
+ background-color: #f5f5f5;
+ border-top: 1px solid #e5e5e5;
+ border-bottom: 1px solid #e5e5e5;
+ padding: 32px;
+
+ .instance-description {
+ margin: 0;
+ margin-bottom: 24px;
+ }
+ }
+
+ a {
+ text-decoration: none;
+ }
+}
+
+.admin {
+ margin-top: 32px;
+ display: flex;
+ align-items: center;
+ background-color: #fff;
+ border: 1px solid #e5e5e5;
+ border-radius: 3px;
+ box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
+
+ .display-name {
+ font-weight: 600;
+ font-size: 16px;
+ margin: 0;
+ }
+
+ .username {
+ font-size: 14px;
+ color: #777;
+ margin: 0;
+ margin-top: 8px;
+ }
+}
+
+.avatar {
+ width: 80px;
+ height: 80px;
+
+ img {
+ width: 100%;
+ border-radius: 40px;
+ border: 1px solid #333;
+ background-color: #f5f5f5;
+ box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.1);
+ }
+}
+
+@media(max-width: 700px) {
+ header .header-text {
+ padding: 24px;
+ }
+
+ section {
+ border-left: none;
+ border-right: none;
+ }
+}
diff --git a/src/config.rs b/src/config.rs
index edeacb4..820f70d 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -28,6 +28,7 @@ pub enum UrlKind {
Inbox,
Index,
MainKey,
+ Media(Uuid),
NodeInfo,
Outbox,
}
@@ -136,6 +137,7 @@ impl Config {
UrlKind::Inbox => format!("{}://{}/inbox", scheme, self.hostname),
UrlKind::Index => format!("{}://{}/", scheme, self.hostname),
UrlKind::MainKey => format!("{}://{}/actor#main-key", scheme, self.hostname),
+ UrlKind::Media(uuid) => format!("{}://{}/media/{}", scheme, self.hostname, uuid),
UrlKind::NodeInfo => format!("{}://{}/nodeinfo/2.0.json", scheme, self.hostname),
UrlKind::Outbox => format!("{}://{}/outbox", scheme, self.hostname),
}
diff --git a/src/data/media.rs b/src/data/media.rs
new file mode 100644
index 0000000..e854ec4
--- /dev/null
+++ b/src/data/media.rs
@@ -0,0 +1,60 @@
+use activitystreams::primitives::XsdAnyUri;
+use bytes::Bytes;
+use lru::LruCache;
+use std::{collections::HashMap, sync::Arc, time::Duration};
+use tokio::sync::{Mutex, RwLock};
+use ttl_cache::TtlCache;
+use uuid::Uuid;
+
+static MEDIA_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 2);
+
+#[derive(Clone)]
+pub struct Media {
+ inverse: Arc>>,
+ url_cache: Arc>>,
+ byte_cache: Arc>>,
+}
+
+impl Media {
+ pub fn new() -> Self {
+ Media {
+ inverse: Arc::new(Mutex::new(HashMap::new())),
+ url_cache: Arc::new(Mutex::new(LruCache::new(128))),
+ byte_cache: Arc::new(RwLock::new(TtlCache::new(128))),
+ }
+ }
+
+ pub async fn get_uuid(&self, url: &XsdAnyUri) -> Option {
+ let uuid = self.inverse.lock().await.get(url).cloned()?;
+
+ if self.url_cache.lock().await.contains(&uuid) {
+ return Some(uuid);
+ }
+
+ self.inverse.lock().await.remove(url);
+
+ None
+ }
+
+ pub async fn get_url(&self, uuid: Uuid) -> Option {
+ self.url_cache.lock().await.get(&uuid).cloned()
+ }
+
+ pub async fn get_bytes(&self, uuid: Uuid) -> Option<(String, Bytes)> {
+ self.byte_cache.read().await.get(&uuid).cloned()
+ }
+
+ pub async fn store_url(&self, url: &XsdAnyUri) -> Uuid {
+ let uuid = Uuid::new_v4();
+ self.inverse.lock().await.insert(url.clone(), uuid);
+ self.url_cache.lock().await.put(uuid, url.clone());
+ uuid
+ }
+
+ pub async fn store_bytes(&self, uuid: Uuid, content_type: String, bytes: Bytes) {
+ self.byte_cache
+ .write()
+ .await
+ .insert(uuid, (content_type, bytes), MEDIA_DURATION);
+ }
+}
diff --git a/src/data/mod.rs b/src/data/mod.rs
index 9af1f01..dd373bf 100644
--- a/src/data/mod.rs
+++ b/src/data/mod.rs
@@ -1,9 +1,11 @@
mod actor;
+mod media;
mod node;
mod state;
pub use self::{
actor::{Actor, ActorCache},
- node::{Node, NodeCache},
+ media::Media,
+ node::{Contact, Info, Instance, Node, NodeCache},
state::State,
};
diff --git a/src/error.rs b/src/error.rs
index b9aff00..4515681 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -70,6 +70,9 @@ pub enum MyError {
#[error("Hosts don't match, {0}, {1}")]
HostMismatch(String, String),
+ #[error("Invalid or missing content type")]
+ ContentType,
+
#[error("Couldn't flush buffer")]
FlushBuffer,
diff --git a/src/jobs/instance.rs b/src/jobs/instance.rs
index a74e327..8de0185 100644
--- a/src/jobs/instance.rs
+++ b/src/jobs/instance.rs
@@ -1,4 +1,4 @@
-use crate::jobs::JobState;
+use crate::{config::UrlKind, jobs::JobState};
use activitystreams::primitives::XsdAnyUri;
use anyhow::Error;
use background_jobs::{Job, Processor};
@@ -44,7 +44,14 @@ impl QueryInstance {
instance.description
};
- if let Some(contact) = instance.contact {
+ if let Some(mut contact) = instance.contact {
+ if let Some(uuid) = state.media.get_uuid(&contact.avatar).await {
+ contact.avatar = state.config.generate_url(UrlKind::Media(uuid)).parse()?;
+ } else {
+ let uuid = state.media.store_url(&contact.avatar).await;
+ contact.avatar = state.config.generate_url(UrlKind::Media(uuid)).parse()?;
+ }
+
state
.node_cache
.set_contact(
@@ -57,6 +64,8 @@ impl QueryInstance {
.await?;
}
+ let description = ammonia::clean(&description);
+
state
.node_cache
.set_instance(
diff --git a/src/jobs/mod.rs b/src/jobs/mod.rs
index 19cc788..802a0f4 100644
--- a/src/jobs/mod.rs
+++ b/src/jobs/mod.rs
@@ -9,7 +9,8 @@ pub use self::{
};
use crate::{
- data::{ActorCache, NodeCache, State},
+ config::Config,
+ data::{ActorCache, Media, NodeCache, State},
db::Db,
error::MyError,
jobs::{
@@ -33,17 +34,31 @@ pub fn create_server(db: Db) -> JobServer {
JobServer::new(shared)
}
-pub fn create_workers(state: State, actors: ActorCache, job_server: JobServer) {
+pub fn create_workers(
+ state: State,
+ actors: ActorCache,
+ job_server: JobServer,
+ media: Media,
+ config: Config,
+) {
let remote_handle = job_server.remote.clone();
- WorkerConfig::new(move || JobState::new(state.clone(), actors.clone(), job_server.clone()))
- .register(DeliverProcessor)
- .register(DeliverManyProcessor)
- .register(NodeinfoProcessor)
- .register(InstanceProcessor)
- .register(ListenersProcessor)
- .set_processor_count("default", 4)
- .start(remote_handle);
+ WorkerConfig::new(move || {
+ JobState::new(
+ state.clone(),
+ actors.clone(),
+ job_server.clone(),
+ media.clone(),
+ config.clone(),
+ )
+ })
+ .register(DeliverProcessor)
+ .register(DeliverManyProcessor)
+ .register(NodeinfoProcessor)
+ .register(InstanceProcessor)
+ .register(ListenersProcessor)
+ .set_processor_count("default", 4)
+ .start(remote_handle);
}
#[derive(Clone)]
@@ -51,6 +66,8 @@ pub struct JobState {
requests: Requests,
state: State,
actors: ActorCache,
+ config: Config,
+ media: Media,
node_cache: NodeCache,
job_server: JobServer,
}
@@ -61,11 +78,19 @@ pub struct JobServer {
}
impl JobState {
- fn new(state: State, actors: ActorCache, job_server: JobServer) -> Self {
+ fn new(
+ state: State,
+ actors: ActorCache,
+ job_server: JobServer,
+ media: Media,
+ config: Config,
+ ) -> Self {
JobState {
requests: state.requests(),
node_cache: state.node_cache(),
actors,
+ config,
+ media,
state,
job_server,
}
diff --git a/src/main.rs b/src/main.rs
index ca39a7f..799d046 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,7 +16,7 @@ mod routes;
use self::{
args::Args,
config::Config,
- data::{ActorCache, State},
+ data::{ActorCache, Media, State},
db::Db,
jobs::{create_server, create_workers},
middleware::RelayResolver,
@@ -62,6 +62,7 @@ async fn main() -> Result<(), anyhow::Error> {
return Ok(());
}
+ let media = Media::new();
let state = State::hydrate(config.clone(), &db).await?;
let actors = ActorCache::new(db.clone());
let job_server = create_server(db.clone());
@@ -84,9 +85,11 @@ async fn main() -> Result<(), anyhow::Error> {
let state = state.clone();
let actors = actors.clone();
let job_server = job_server.clone();
+ let media = media.clone();
+ let config = config.clone();
Arbiter::new().exec_fn(move || {
- create_workers(state, actors, job_server);
+ create_workers(state, actors, job_server, media, config);
});
}
actix_rt::signal::ctrl_c().await?;
@@ -98,7 +101,13 @@ async fn main() -> Result<(), anyhow::Error> {
let bind_address = config.bind_address();
HttpServer::new(move || {
if !no_jobs {
- create_workers(state.clone(), actors.clone(), job_server.clone());
+ create_workers(
+ state.clone(),
+ actors.clone(),
+ job_server.clone(),
+ media.clone(),
+ config.clone(),
+ );
}
App::new()
@@ -109,7 +118,9 @@ async fn main() -> Result<(), anyhow::Error> {
.data(actors.clone())
.data(config.clone())
.data(job_server.clone())
+ .data(media.clone())
.service(web::resource("/").route(web::get().to(index)))
+ .service(web::resource("/media/{path}").route(web::get().to(routes::media)))
.service(
web::resource("/inbox")
.wrap(config.digest_middleware())
diff --git a/src/requests.rs b/src/requests.rs
index 569c366..c78b252 100644
--- a/src/requests.rs
+++ b/src/requests.rs
@@ -1,6 +1,7 @@
use crate::error::MyError;
use activitystreams::primitives::XsdAnyUri;
use actix_web::client::Client;
+use bytes::Bytes;
use http_signature_normalization_actix::prelude::*;
use log::error;
use rsa::{hash::Hashes, padding::PaddingScheme, RSAPrivateKey};
@@ -63,6 +64,55 @@ impl Requests {
})
}
+ pub async fn fetch_bytes(&self, url: &str) -> Result<(String, Bytes), MyError> {
+ let mut res = self
+ .client
+ .get(url)
+ .header("Accept", "application/activity+json")
+ .header("User-Agent", self.user_agent.as_str())
+ .signature(&self.config, &self.key_id, |signing_string| {
+ self.sign(signing_string)
+ })?
+ .send()
+ .await
+ .map_err(|e| {
+ error!("Couldn't send request to {}, {}", url, e);
+ MyError::SendRequest
+ })?;
+
+ let content_type = if let Some(content_type) = res.headers().get("content-type") {
+ if let Ok(s) = content_type.to_str() {
+ s.to_owned()
+ } else {
+ return Err(MyError::ContentType);
+ }
+ } else {
+ return Err(MyError::ContentType);
+ };
+
+ if !res.status().is_success() {
+ if let Ok(bytes) = res.body().await {
+ if let Ok(s) = String::from_utf8(bytes.as_ref().to_vec()) {
+ if !s.is_empty() {
+ error!("Response, {}", s);
+ }
+ }
+ }
+
+ return Err(MyError::Status(res.status()));
+ }
+
+ let bytes = match res.body().limit(1024 * 1024 * 4).await {
+ Err(e) => {
+ error!("Coudn't fetch json from {}, {}", url, e);
+ return Err(MyError::ReceiveResponse);
+ }
+ Ok(bytes) => bytes,
+ };
+
+ Ok((content_type, bytes))
+ }
+
pub async fn deliver(&self, inbox: XsdAnyUri, item: &T) -> Result<(), MyError>
where
T: serde::ser::Serialize,
diff --git a/src/routes/index.rs b/src/routes/index.rs
index 470d63b..6d9e564 100644
--- a/src/routes/index.rs
+++ b/src/routes/index.rs
@@ -8,7 +8,6 @@ pub async fn route(
config: web::Data,
) -> Result {
let nodes = state.node_cache().nodes().await;
-
let mut buf = BufWriter::new(Vec::new());
crate::templates::index(&mut buf, &nodes, &config)?;
diff --git a/src/routes/media.rs b/src/routes/media.rs
new file mode 100644
index 0000000..64b4b43
--- /dev/null
+++ b/src/routes/media.rs
@@ -0,0 +1,27 @@
+use crate::{data::Media, error::MyError, requests::Requests};
+use actix_web::{web, HttpResponse};
+use uuid::Uuid;
+
+pub async fn route(
+ media: web::Data,
+ requests: web::Data,
+ uuid: web::Path,
+) -> Result {
+ let uuid = uuid.into_inner();
+
+ if let Some((content_type, bytes)) = media.get_bytes(uuid).await {
+ return Ok(HttpResponse::Ok().content_type(content_type).body(bytes));
+ }
+
+ if let Some(url) = media.get_url(uuid).await {
+ let (content_type, bytes) = requests.fetch_bytes(url.as_str()).await?;
+
+ media
+ .store_bytes(uuid, content_type.clone(), bytes.clone())
+ .await;
+
+ return Ok(HttpResponse::Ok().content_type(content_type).body(bytes));
+ }
+
+ Ok(HttpResponse::NotFound().finish())
+}
diff --git a/src/routes/mod.rs b/src/routes/mod.rs
index 75b95bf..67e5aa1 100644
--- a/src/routes/mod.rs
+++ b/src/routes/mod.rs
@@ -1,6 +1,7 @@
mod actor;
mod inbox;
mod index;
+mod media;
mod nodeinfo;
mod statics;
@@ -8,6 +9,7 @@ pub use self::{
actor::route as actor,
inbox::route as inbox,
index::route as index,
+ media::route as media,
nodeinfo::{route as nodeinfo, well_known as nodeinfo_meta},
statics::route as statics,
};
diff --git a/templates/admin.rs.html b/templates/admin.rs.html
new file mode 100644
index 0000000..d88e047
--- /dev/null
+++ b/templates/admin.rs.html
@@ -0,0 +1,15 @@
+@use crate::data::Contact;
+
+@(contact: &Contact)
+
+
+
+
+
+
+
diff --git a/templates/index.rs.html b/templates/index.rs.html
index becfef1..96f0f99 100644
--- a/templates/index.rs.html
+++ b/templates/index.rs.html
@@ -1,4 +1,8 @@
-@use crate::{config::{Config, UrlKind}, templates::statics::index_css, data::Node};
+@use crate::{
+ config::{Config, UrlKind},
+ data::Node,
+ templates::{info, instance, statics::index_css},
+};
@(nodes: &[Node], config: &Config)
@@ -11,28 +15,29 @@
- Welcome to @config.software_name() on @config.hostname()
+
- Connected Servers:
+ Connected Servers
@if nodes.is_empty() {
There are no connected servers at this time.
} else {
@@ -40,27 +45,29 @@
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.
-
+
+
+ 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.
+
+