Instrument with tracing
This commit is contained in:
parent
ebba8e3f60
commit
43e5b6d873
34 changed files with 748 additions and 707 deletions
725
Cargo.lock
generated
725
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
28
Cargo.toml
28
Cargo.toml
|
@ -15,26 +15,22 @@ build = "src/build.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
actix-rt = "2.0.2"
|
actix-rt = "2.0.2"
|
||||||
actix-web = { version = "4.0.0-beta.7", default-features = false, features = ["compress-brotli", "compress-gzip", "compress-zstd"] }
|
actix-web = { version = "4.0.0-beta.7", default-features = false }
|
||||||
actix-webfinger = "0.4.0-beta.3"
|
actix-webfinger = "0.4.0-beta.3"
|
||||||
activitystreams = "0.7.0-alpha.10"
|
activitystreams = "0.7.0-alpha.10"
|
||||||
activitystreams-ext = "0.1.0-alpha.2"
|
activitystreams-ext = "0.1.0-alpha.2"
|
||||||
ammonia = "3.1.0"
|
ammonia = "3.1.0"
|
||||||
async-mutex = "1.0.1"
|
async-mutex = "1.0.1"
|
||||||
async-rwlock = "1.3.0"
|
async-rwlock = "1.3.0"
|
||||||
awc = { version = "3.0.0-beta.6", default-features = false, features = ["compress-brotli", "compress-gzip", "compress-zstd", "rustls"] }
|
awc = { version = "3.0.0-beta.6", default-features = false, features = ["rustls"] }
|
||||||
background-jobs = "0.9.0"
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
config = "0.11.0"
|
config = "0.11.0"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
env_logger = "0.9.0"
|
futures-util = "0.3.17"
|
||||||
futures = "0.3.12"
|
http-signature-normalization-actix = { version = "0.5.0-beta.7", default-features = false, features = ["sha-2"], git = "https://git.asonix.dog/asonix/http-signature-normalization" }
|
||||||
http-signature-normalization-actix = { version = "0.5.0-beta.6", default-features = false, features = ["sha-2"] }
|
|
||||||
log = "0.4"
|
|
||||||
lru = "0.6.0"
|
lru = "0.6.0"
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
pretty_env_logger = "0.4.0"
|
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rsa = "0.5"
|
rsa = "0.5"
|
||||||
rsa-magic-public-key = "0.4.0"
|
rsa-magic-public-key = "0.4.0"
|
||||||
|
@ -44,8 +40,24 @@ sha2 = "0.9"
|
||||||
sled = "0.34.6"
|
sled = "0.34.6"
|
||||||
structopt = "0.3.12"
|
structopt = "0.3.12"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-actix-web = { version = "0.4.0-beta.12", git = "https://github.com/asonix/tracing-actix-web", branch = "asonix/tracing-error-work-around" }
|
||||||
|
tracing-error = "0.1"
|
||||||
|
tracing-futures = "0.2"
|
||||||
|
tracing-log = "0.1"
|
||||||
|
tracing-subscriber = { version = "0.2", features = ["ansi", "fmt"] }
|
||||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
uuid = { version = "0.8", features = ["v4", "serde"] }
|
||||||
|
|
||||||
|
[dependencies.background-jobs]
|
||||||
|
version = "0.10.0"
|
||||||
|
git = "https://git.asonix.dog/asonix/background-jobs"
|
||||||
|
default-features = false
|
||||||
|
features = [
|
||||||
|
"background-jobs-actix",
|
||||||
|
"error-logging"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{ActorCache, State},
|
data::{ActorCache, State},
|
||||||
error::MyError,
|
error::Error,
|
||||||
middleware::MyVerify,
|
middleware::MyVerify,
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,6 @@ pub(crate) struct ParsedConfig {
|
||||||
restricted_mode: bool,
|
restricted_mode: bool,
|
||||||
validate_signatures: bool,
|
validate_signatures: bool,
|
||||||
https: bool,
|
https: bool,
|
||||||
pretty_log: bool,
|
|
||||||
publish_blocks: bool,
|
publish_blocks: bool,
|
||||||
sled_path: PathBuf,
|
sled_path: PathBuf,
|
||||||
source_repo: Url,
|
source_repo: Url,
|
||||||
|
@ -34,7 +33,6 @@ pub struct Config {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
restricted_mode: bool,
|
restricted_mode: bool,
|
||||||
validate_signatures: bool,
|
validate_signatures: bool,
|
||||||
pretty_log: bool,
|
|
||||||
publish_blocks: bool,
|
publish_blocks: bool,
|
||||||
base_uri: Url,
|
base_uri: Url,
|
||||||
sled_path: PathBuf,
|
sled_path: PathBuf,
|
||||||
|
@ -55,7 +53,7 @@ pub enum UrlKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub(crate) fn build() -> Result<Self, MyError> {
|
pub(crate) fn build() -> Result<Self, Error> {
|
||||||
let mut config = config::Config::new();
|
let mut config = config::Config::new();
|
||||||
config
|
config
|
||||||
.set_default("hostname", "localhost:8080")?
|
.set_default("hostname", "localhost:8080")?
|
||||||
|
@ -65,7 +63,6 @@ impl Config {
|
||||||
.set_default("restricted_mode", false)?
|
.set_default("restricted_mode", false)?
|
||||||
.set_default("validate_signatures", false)?
|
.set_default("validate_signatures", false)?
|
||||||
.set_default("https", false)?
|
.set_default("https", false)?
|
||||||
.set_default("pretty_log", true)?
|
|
||||||
.set_default("publish_blocks", false)?
|
.set_default("publish_blocks", false)?
|
||||||
.set_default("sled_path", "./sled/db-0-34")?
|
.set_default("sled_path", "./sled/db-0-34")?
|
||||||
.set_default("source_repo", "https://git.asonix.dog/asonix/relay")?
|
.set_default("source_repo", "https://git.asonix.dog/asonix/relay")?
|
||||||
|
@ -83,7 +80,6 @@ impl Config {
|
||||||
debug: config.debug,
|
debug: config.debug,
|
||||||
restricted_mode: config.restricted_mode,
|
restricted_mode: config.restricted_mode,
|
||||||
validate_signatures: config.validate_signatures,
|
validate_signatures: config.validate_signatures,
|
||||||
pretty_log: config.pretty_log,
|
|
||||||
publish_blocks: config.publish_blocks,
|
publish_blocks: config.publish_blocks,
|
||||||
base_uri,
|
base_uri,
|
||||||
sled_path: config.sled_path,
|
sled_path: config.sled_path,
|
||||||
|
@ -95,10 +91,6 @@ impl Config {
|
||||||
&self.sled_path
|
&self.sled_path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pretty_log(&self) -> bool {
|
|
||||||
self.pretty_log
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn validate_signatures(&self) -> bool {
|
pub(crate) fn validate_signatures(&self) -> bool {
|
||||||
self.validate_signatures
|
self.validate_signatures
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
apub::AcceptedActors,
|
apub::AcceptedActors,
|
||||||
db::{Actor, Db},
|
db::{Actor, Db},
|
||||||
error::MyError,
|
error::{Error, ErrorKind},
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
};
|
};
|
||||||
use activitystreams::{prelude::*, url::Url};
|
use activitystreams::{prelude::*, url::Url};
|
||||||
|
@ -30,7 +30,7 @@ impl<T> MaybeCached<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ActorCache {
|
pub struct ActorCache {
|
||||||
db: Db,
|
db: Db,
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,12 @@ impl ActorCache {
|
||||||
ActorCache { db }
|
ActorCache { db }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Get Actor", skip(requests))]
|
||||||
pub(crate) async fn get(
|
pub(crate) async fn get(
|
||||||
&self,
|
&self,
|
||||||
id: &Url,
|
id: &Url,
|
||||||
requests: &Requests,
|
requests: &Requests,
|
||||||
) -> Result<MaybeCached<Actor>, MyError> {
|
) -> Result<MaybeCached<Actor>, Error> {
|
||||||
if let Some(actor) = self.db.actor(id.clone()).await? {
|
if let Some(actor) = self.db.actor(id.clone()).await? {
|
||||||
if actor.saved_at + REFETCH_DURATION > SystemTime::now() {
|
if actor.saved_at + REFETCH_DURATION > SystemTime::now() {
|
||||||
return Ok(MaybeCached::Cached(actor));
|
return Ok(MaybeCached::Cached(actor));
|
||||||
|
@ -56,26 +57,25 @@ impl ActorCache {
|
||||||
.map(MaybeCached::Fetched)
|
.map(MaybeCached::Fetched)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn add_connection(&self, actor: Actor) -> Result<(), MyError> {
|
#[tracing::instrument(name = "Add Connection")]
|
||||||
|
pub(crate) async fn add_connection(&self, actor: Actor) -> Result<(), Error> {
|
||||||
self.db.add_connection(actor.id.clone()).await?;
|
self.db.add_connection(actor.id.clone()).await?;
|
||||||
self.db.save_actor(actor).await
|
self.db.save_actor(actor).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn remove_connection(&self, actor: &Actor) -> Result<(), MyError> {
|
#[tracing::instrument(name = "Remove Connection")]
|
||||||
|
pub(crate) async fn remove_connection(&self, actor: &Actor) -> Result<(), Error> {
|
||||||
self.db.remove_connection(actor.id.clone()).await
|
self.db.remove_connection(actor.id.clone()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_no_cache(
|
#[tracing::instrument(name = "Fetch remote actor", skip(requests))]
|
||||||
&self,
|
pub(crate) async fn get_no_cache(&self, id: &Url, requests: &Requests) -> Result<Actor, Error> {
|
||||||
id: &Url,
|
|
||||||
requests: &Requests,
|
|
||||||
) -> Result<Actor, MyError> {
|
|
||||||
let accepted_actor = requests.fetch::<AcceptedActors>(id.as_str()).await?;
|
let accepted_actor = requests.fetch::<AcceptedActors>(id.as_str()).await?;
|
||||||
|
|
||||||
let input_domain = id.domain().ok_or(MyError::MissingDomain)?;
|
let input_domain = id.domain().ok_or(ErrorKind::MissingDomain)?;
|
||||||
let accepted_actor_id = accepted_actor
|
let accepted_actor_id = accepted_actor
|
||||||
.id(&input_domain)?
|
.id(&input_domain)?
|
||||||
.ok_or(MyError::MissingId)?;
|
.ok_or(ErrorKind::MissingId)?;
|
||||||
|
|
||||||
let inbox = get_inbox(&accepted_actor)?.clone();
|
let inbox = get_inbox(&accepted_actor)?.clone();
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ impl ActorCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inbox(actor: &AcceptedActors) -> Result<&Url, MyError> {
|
fn get_inbox(actor: &AcceptedActors) -> Result<&Url, Error> {
|
||||||
Ok(actor
|
Ok(actor
|
||||||
.endpoints()?
|
.endpoints()?
|
||||||
.and_then(|e| e.shared_inbox)
|
.and_then(|e| e.shared_inbox)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{Db, MediaMeta},
|
db::{Db, MediaMeta},
|
||||||
error::MyError,
|
error::Error,
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use actix_web::web::Bytes;
|
use actix_web::web::Bytes;
|
||||||
|
@ -9,7 +9,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
static MEDIA_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 2);
|
static MEDIA_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 2);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MediaCache {
|
pub struct MediaCache {
|
||||||
db: Db,
|
db: Db,
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,18 @@ impl MediaCache {
|
||||||
MediaCache { db }
|
MediaCache { db }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_uuid(&self, url: Url) -> Result<Option<Uuid>, MyError> {
|
#[tracing::instrument(name = "Get media uuid")]
|
||||||
|
pub(crate) async fn get_uuid(&self, url: Url) -> Result<Option<Uuid>, Error> {
|
||||||
self.db.media_id(url).await
|
self.db.media_id(url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_url(&self, uuid: Uuid) -> Result<Option<Url>, MyError> {
|
#[tracing::instrument(name = "Get media url")]
|
||||||
|
pub(crate) async fn get_url(&self, uuid: Uuid) -> Result<Option<Url>, Error> {
|
||||||
self.db.media_url(uuid).await
|
self.db.media_url(uuid).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn is_outdated(&self, uuid: Uuid) -> Result<bool, MyError> {
|
#[tracing::instrument(name = "Is media outdated")]
|
||||||
|
pub(crate) async fn is_outdated(&self, uuid: Uuid) -> Result<bool, Error> {
|
||||||
if let Some(meta) = self.db.media_meta(uuid).await? {
|
if let Some(meta) = self.db.media_meta(uuid).await? {
|
||||||
if meta.saved_at + MEDIA_DURATION > SystemTime::now() {
|
if meta.saved_at + MEDIA_DURATION > SystemTime::now() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
@ -37,7 +40,8 @@ impl MediaCache {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_bytes(&self, uuid: Uuid) -> Result<Option<(String, Bytes)>, MyError> {
|
#[tracing::instrument(name = "Get media bytes")]
|
||||||
|
pub(crate) async fn get_bytes(&self, uuid: Uuid) -> Result<Option<(String, Bytes)>, Error> {
|
||||||
if let Some(meta) = self.db.media_meta(uuid).await? {
|
if let Some(meta) = self.db.media_meta(uuid).await? {
|
||||||
if meta.saved_at + MEDIA_DURATION > SystemTime::now() {
|
if meta.saved_at + MEDIA_DURATION > SystemTime::now() {
|
||||||
return self
|
return self
|
||||||
|
@ -51,7 +55,8 @@ impl MediaCache {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn store_url(&self, url: Url) -> Result<Uuid, MyError> {
|
#[tracing::instrument(name = "Store media url")]
|
||||||
|
pub(crate) async fn store_url(&self, url: Url) -> Result<Uuid, Error> {
|
||||||
let uuid = Uuid::new_v4();
|
let uuid = Uuid::new_v4();
|
||||||
|
|
||||||
self.db.save_url(url, uuid).await?;
|
self.db.save_url(url, uuid).await?;
|
||||||
|
@ -59,12 +64,13 @@ impl MediaCache {
|
||||||
Ok(uuid)
|
Ok(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "store media bytes", skip(bytes))]
|
||||||
pub(crate) async fn store_bytes(
|
pub(crate) async fn store_bytes(
|
||||||
&self,
|
&self,
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
media_type: String,
|
media_type: String,
|
||||||
bytes: Bytes,
|
bytes: Bytes,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
self.db
|
self.db
|
||||||
.save_bytes(
|
.save_bytes(
|
||||||
uuid,
|
uuid,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{Contact, Db, Info, Instance},
|
db::{Contact, Db, Info, Instance},
|
||||||
error::MyError,
|
error::Error,
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct NodeCache {
|
pub struct NodeCache {
|
||||||
db: Db,
|
db: Db,
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ impl NodeCache {
|
||||||
NodeCache { db }
|
NodeCache { db }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn nodes(&self) -> Result<Vec<Node>, MyError> {
|
#[tracing::instrument(name = "Get nodes")]
|
||||||
|
pub(crate) async fn nodes(&self) -> Result<Vec<Node>, Error> {
|
||||||
let infos = self.db.connected_info().await?;
|
let infos = self.db.connected_info().await?;
|
||||||
let instances = self.db.connected_instance().await?;
|
let instances = self.db.connected_instance().await?;
|
||||||
let contacts = self.db.connected_contact().await?;
|
let contacts = self.db.connected_contact().await?;
|
||||||
|
@ -48,6 +49,7 @@ impl NodeCache {
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Is NodeInfo Outdated")]
|
||||||
pub(crate) async fn is_nodeinfo_outdated(&self, actor_id: Url) -> bool {
|
pub(crate) async fn is_nodeinfo_outdated(&self, actor_id: Url) -> bool {
|
||||||
self.db
|
self.db
|
||||||
.info(actor_id)
|
.info(actor_id)
|
||||||
|
@ -56,6 +58,7 @@ impl NodeCache {
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Is Contact Outdated")]
|
||||||
pub(crate) async fn is_contact_outdated(&self, actor_id: Url) -> bool {
|
pub(crate) async fn is_contact_outdated(&self, actor_id: Url) -> bool {
|
||||||
self.db
|
self.db
|
||||||
.contact(actor_id)
|
.contact(actor_id)
|
||||||
|
@ -64,6 +67,7 @@ impl NodeCache {
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Is Instance Outdated")]
|
||||||
pub(crate) async fn is_instance_outdated(&self, actor_id: Url) -> bool {
|
pub(crate) async fn is_instance_outdated(&self, actor_id: Url) -> bool {
|
||||||
self.db
|
self.db
|
||||||
.instance(actor_id)
|
.instance(actor_id)
|
||||||
|
@ -72,13 +76,14 @@ impl NodeCache {
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Save node info")]
|
||||||
pub(crate) async fn set_info(
|
pub(crate) async fn set_info(
|
||||||
&self,
|
&self,
|
||||||
actor_id: Url,
|
actor_id: Url,
|
||||||
software: String,
|
software: String,
|
||||||
version: String,
|
version: String,
|
||||||
reg: bool,
|
reg: bool,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
self.db
|
self.db
|
||||||
.save_info(
|
.save_info(
|
||||||
actor_id,
|
actor_id,
|
||||||
|
@ -92,6 +97,7 @@ impl NodeCache {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Save instance info")]
|
||||||
pub(crate) async fn set_instance(
|
pub(crate) async fn set_instance(
|
||||||
&self,
|
&self,
|
||||||
actor_id: Url,
|
actor_id: Url,
|
||||||
|
@ -100,7 +106,7 @@ impl NodeCache {
|
||||||
version: String,
|
version: String,
|
||||||
reg: bool,
|
reg: bool,
|
||||||
requires_approval: bool,
|
requires_approval: bool,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
self.db
|
self.db
|
||||||
.save_instance(
|
.save_instance(
|
||||||
actor_id,
|
actor_id,
|
||||||
|
@ -116,6 +122,7 @@ impl NodeCache {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Save contact info")]
|
||||||
pub(crate) async fn set_contact(
|
pub(crate) async fn set_contact(
|
||||||
&self,
|
&self,
|
||||||
actor_id: Url,
|
actor_id: Url,
|
||||||
|
@ -123,7 +130,7 @@ impl NodeCache {
|
||||||
display_name: String,
|
display_name: String,
|
||||||
url: Url,
|
url: Url,
|
||||||
avatar: Url,
|
avatar: Url,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
self.db
|
self.db
|
||||||
.save_contact(
|
.save_contact(
|
||||||
actor_id,
|
actor_id,
|
||||||
|
|
|
@ -2,17 +2,17 @@ use crate::{
|
||||||
config::{Config, UrlKind},
|
config::{Config, UrlKind},
|
||||||
data::NodeCache,
|
data::NodeCache,
|
||||||
db::Db,
|
db::Db,
|
||||||
error::MyError,
|
error::Error,
|
||||||
requests::{Breakers, Requests},
|
requests::{Breakers, Requests},
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use actix_web::web;
|
use actix_web::web;
|
||||||
use async_rwlock::RwLock;
|
use async_rwlock::RwLock;
|
||||||
use log::info;
|
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rsa::{RsaPrivateKey, RsaPublicKey};
|
use rsa::{RsaPrivateKey, RsaPublicKey};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
@ -25,6 +25,20 @@ pub struct State {
|
||||||
pub(crate) db: Db,
|
pub(crate) db: Db,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for State {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("State")
|
||||||
|
.field("public_key", &"PublicKey")
|
||||||
|
.field("private_key", &"[redacted]")
|
||||||
|
.field("config", &self.config)
|
||||||
|
.field("object_cache", &"Object Cache")
|
||||||
|
.field("node_cache", &self.node_cache)
|
||||||
|
.field("breakers", &self.breakers)
|
||||||
|
.field("db", &self.db)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub(crate) fn node_cache(&self) -> NodeCache {
|
pub(crate) fn node_cache(&self) -> NodeCache {
|
||||||
self.node_cache.clone()
|
self.node_cache.clone()
|
||||||
|
@ -44,11 +58,12 @@ impl State {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Get inboxes for other domains")]
|
||||||
pub(crate) async fn inboxes_without(
|
pub(crate) async fn inboxes_without(
|
||||||
&self,
|
&self,
|
||||||
existing_inbox: &Url,
|
existing_inbox: &Url,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
) -> Result<Vec<Url>, MyError> {
|
) -> Result<Vec<Url>, Error> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.db
|
.db
|
||||||
.inboxes()
|
.inboxes()
|
||||||
|
@ -74,8 +89,10 @@ impl State {
|
||||||
self.object_cache.write().await.put(object_id, actor_id);
|
self.object_cache.write().await.put(object_id, actor_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn build(config: Config, db: Db) -> Result<Self, MyError> {
|
#[tracing::instrument(name = "Building state")]
|
||||||
|
pub(crate) async fn build(config: Config, db: Db) -> Result<Self, Error> {
|
||||||
let private_key = if let Ok(Some(key)) = db.private_key().await {
|
let private_key = if let Ok(Some(key)) = db.private_key().await {
|
||||||
|
info!("Using existing key");
|
||||||
key
|
key
|
||||||
} else {
|
} else {
|
||||||
info!("Generating new keys");
|
info!("Generating new keys");
|
||||||
|
|
90
src/db.rs
90
src/db.rs
|
@ -1,4 +1,4 @@
|
||||||
use crate::{config::Config, error::MyError};
|
use crate::{config::Config, error::Error};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use actix_web::web::Bytes;
|
use actix_web::web::Bytes;
|
||||||
use rsa::{
|
use rsa::{
|
||||||
|
@ -9,7 +9,7 @@ use sled::Tree;
|
||||||
use std::{collections::HashMap, sync::Arc, time::SystemTime};
|
use std::{collections::HashMap, sync::Arc, time::SystemTime};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct Db {
|
pub(crate) struct Db {
|
||||||
inner: Arc<Inner>,
|
inner: Arc<Inner>,
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,14 @@ struct Inner {
|
||||||
restricted_mode: bool,
|
restricted_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Inner {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Inner")
|
||||||
|
.field("restricted_mode", &self.restricted_mode)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct Actor {
|
pub struct Actor {
|
||||||
pub(crate) id: Url,
|
pub(crate) id: Url,
|
||||||
|
@ -194,12 +202,12 @@ impl Inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Db {
|
impl Db {
|
||||||
pub(crate) fn build(config: &Config) -> Result<Self, MyError> {
|
pub(crate) fn build(config: &Config) -> Result<Self, Error> {
|
||||||
let db = sled::open(config.sled_path())?;
|
let db = sled::open(config.sled_path())?;
|
||||||
Self::build_inner(config.restricted_mode(), db)
|
Self::build_inner(config.restricted_mode(), db)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_inner(restricted_mode: bool, db: sled::Db) -> Result<Self, MyError> {
|
fn build_inner(restricted_mode: bool, db: sled::Db) -> Result<Self, Error> {
|
||||||
Ok(Db {
|
Ok(Db {
|
||||||
inner: Arc::new(Inner {
|
inner: Arc::new(Inner {
|
||||||
actor_id_actor: db.open_tree("actor-id-actor")?,
|
actor_id_actor: db.open_tree("actor-id-actor")?,
|
||||||
|
@ -222,8 +230,8 @@ impl Db {
|
||||||
|
|
||||||
async fn unblock<T>(
|
async fn unblock<T>(
|
||||||
&self,
|
&self,
|
||||||
f: impl Fn(&Inner) -> Result<T, MyError> + Send + 'static,
|
f: impl Fn(&Inner) -> Result<T, Error> + Send + 'static,
|
||||||
) -> Result<T, MyError>
|
) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
{
|
{
|
||||||
|
@ -234,11 +242,11 @@ impl Db {
|
||||||
Ok(t)
|
Ok(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn connected_ids(&self) -> Result<Vec<Url>, MyError> {
|
pub(crate) async fn connected_ids(&self) -> Result<Vec<Url>, Error> {
|
||||||
self.unblock(|inner| Ok(inner.connected().collect())).await
|
self.unblock(|inner| Ok(inner.connected().collect())).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn save_info(&self, actor_id: Url, info: Info) -> Result<(), MyError> {
|
pub(crate) async fn save_info(&self, actor_id: Url, info: Info) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
let vec = serde_json::to_vec(&info)?;
|
let vec = serde_json::to_vec(&info)?;
|
||||||
|
|
||||||
|
@ -251,7 +259,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn info(&self, actor_id: Url) -> Result<Option<Info>, MyError> {
|
pub(crate) async fn info(&self, actor_id: Url) -> Result<Option<Info>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.actor_id_info.get(actor_id.as_str().as_bytes())? {
|
if let Some(ivec) = inner.actor_id_info.get(actor_id.as_str().as_bytes())? {
|
||||||
let info = serde_json::from_slice(&ivec)?;
|
let info = serde_json::from_slice(&ivec)?;
|
||||||
|
@ -263,7 +271,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn connected_info(&self) -> Result<HashMap<Url, Info>, MyError> {
|
pub(crate) async fn connected_info(&self) -> Result<HashMap<Url, Info>, Error> {
|
||||||
self.unblock(|inner| Ok(inner.connected_info().collect()))
|
self.unblock(|inner| Ok(inner.connected_info().collect()))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -272,7 +280,7 @@ impl Db {
|
||||||
&self,
|
&self,
|
||||||
actor_id: Url,
|
actor_id: Url,
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
let vec = serde_json::to_vec(&instance)?;
|
let vec = serde_json::to_vec(&instance)?;
|
||||||
|
|
||||||
|
@ -285,7 +293,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn instance(&self, actor_id: Url) -> Result<Option<Instance>, MyError> {
|
pub(crate) async fn instance(&self, actor_id: Url) -> Result<Option<Instance>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.actor_id_instance.get(actor_id.as_str().as_bytes())? {
|
if let Some(ivec) = inner.actor_id_instance.get(actor_id.as_str().as_bytes())? {
|
||||||
let instance = serde_json::from_slice(&ivec)?;
|
let instance = serde_json::from_slice(&ivec)?;
|
||||||
|
@ -297,16 +305,12 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn connected_instance(&self) -> Result<HashMap<Url, Instance>, MyError> {
|
pub(crate) async fn connected_instance(&self) -> Result<HashMap<Url, Instance>, Error> {
|
||||||
self.unblock(|inner| Ok(inner.connected_instance().collect()))
|
self.unblock(|inner| Ok(inner.connected_instance().collect()))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn save_contact(
|
pub(crate) async fn save_contact(&self, actor_id: Url, contact: Contact) -> Result<(), Error> {
|
||||||
&self,
|
|
||||||
actor_id: Url,
|
|
||||||
contact: Contact,
|
|
||||||
) -> Result<(), MyError> {
|
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
let vec = serde_json::to_vec(&contact)?;
|
let vec = serde_json::to_vec(&contact)?;
|
||||||
|
|
||||||
|
@ -319,7 +323,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn contact(&self, actor_id: Url) -> Result<Option<Contact>, MyError> {
|
pub(crate) async fn contact(&self, actor_id: Url) -> Result<Option<Contact>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.actor_id_contact.get(actor_id.as_str().as_bytes())? {
|
if let Some(ivec) = inner.actor_id_contact.get(actor_id.as_str().as_bytes())? {
|
||||||
let contact = serde_json::from_slice(&ivec)?;
|
let contact = serde_json::from_slice(&ivec)?;
|
||||||
|
@ -331,12 +335,12 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn connected_contact(&self) -> Result<HashMap<Url, Contact>, MyError> {
|
pub(crate) async fn connected_contact(&self) -> Result<HashMap<Url, Contact>, Error> {
|
||||||
self.unblock(|inner| Ok(inner.connected_contact().collect()))
|
self.unblock(|inner| Ok(inner.connected_contact().collect()))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn save_url(&self, url: Url, id: Uuid) -> Result<(), MyError> {
|
pub(crate) async fn save_url(&self, url: Url, id: Uuid) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
inner
|
inner
|
||||||
.media_id_media_url
|
.media_id_media_url
|
||||||
|
@ -354,7 +358,7 @@ impl Db {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
meta: MediaMeta,
|
meta: MediaMeta,
|
||||||
bytes: Bytes,
|
bytes: Bytes,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
let vec = serde_json::to_vec(&meta)?;
|
let vec = serde_json::to_vec(&meta)?;
|
||||||
|
|
||||||
|
@ -368,7 +372,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn media_id(&self, url: Url) -> Result<Option<Uuid>, MyError> {
|
pub(crate) async fn media_id(&self, url: Url) -> Result<Option<Uuid>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.media_url_media_id.get(url.as_str().as_bytes())? {
|
if let Some(ivec) = inner.media_url_media_id.get(url.as_str().as_bytes())? {
|
||||||
Ok(uuid_from_ivec(ivec))
|
Ok(uuid_from_ivec(ivec))
|
||||||
|
@ -379,7 +383,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn media_url(&self, id: Uuid) -> Result<Option<Url>, MyError> {
|
pub(crate) async fn media_url(&self, id: Uuid) -> Result<Option<Url>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.media_id_media_url.get(id.as_bytes())? {
|
if let Some(ivec) = inner.media_id_media_url.get(id.as_bytes())? {
|
||||||
Ok(url_from_ivec(ivec))
|
Ok(url_from_ivec(ivec))
|
||||||
|
@ -390,7 +394,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn media_bytes(&self, id: Uuid) -> Result<Option<Bytes>, MyError> {
|
pub(crate) async fn media_bytes(&self, id: Uuid) -> Result<Option<Bytes>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.media_id_media_bytes.get(id.as_bytes())? {
|
if let Some(ivec) = inner.media_id_media_bytes.get(id.as_bytes())? {
|
||||||
Ok(Some(Bytes::copy_from_slice(&ivec)))
|
Ok(Some(Bytes::copy_from_slice(&ivec)))
|
||||||
|
@ -401,7 +405,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn media_meta(&self, id: Uuid) -> Result<Option<MediaMeta>, MyError> {
|
pub(crate) async fn media_meta(&self, id: Uuid) -> Result<Option<MediaMeta>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.media_id_media_meta.get(id.as_bytes())? {
|
if let Some(ivec) = inner.media_id_media_meta.get(id.as_bytes())? {
|
||||||
let meta = serde_json::from_slice(&ivec)?;
|
let meta = serde_json::from_slice(&ivec)?;
|
||||||
|
@ -413,16 +417,16 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn blocks(&self) -> Result<Vec<String>, MyError> {
|
pub(crate) async fn blocks(&self) -> Result<Vec<String>, Error> {
|
||||||
self.unblock(|inner| Ok(inner.blocks().collect())).await
|
self.unblock(|inner| Ok(inner.blocks().collect())).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn inboxes(&self) -> Result<Vec<Url>, MyError> {
|
pub(crate) async fn inboxes(&self) -> Result<Vec<Url>, Error> {
|
||||||
self.unblock(|inner| Ok(inner.connected_actors().map(|actor| actor.inbox).collect()))
|
self.unblock(|inner| Ok(inner.connected_actors().map(|actor| actor.inbox).collect()))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn is_connected(&self, mut id: Url) -> Result<bool, MyError> {
|
pub(crate) async fn is_connected(&self, mut id: Url) -> Result<bool, Error> {
|
||||||
id.set_path("");
|
id.set_path("");
|
||||||
id.set_query(None);
|
id.set_query(None);
|
||||||
id.set_fragment(None);
|
id.set_fragment(None);
|
||||||
|
@ -444,7 +448,7 @@ impl Db {
|
||||||
pub(crate) async fn actor_id_from_public_key_id(
|
pub(crate) async fn actor_id_from_public_key_id(
|
||||||
&self,
|
&self,
|
||||||
public_key_id: Url,
|
public_key_id: Url,
|
||||||
) -> Result<Option<Url>, MyError> {
|
) -> Result<Option<Url>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner
|
if let Some(ivec) = inner
|
||||||
.public_key_id_actor_id
|
.public_key_id_actor_id
|
||||||
|
@ -458,7 +462,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn actor(&self, actor_id: Url) -> Result<Option<Actor>, MyError> {
|
pub(crate) async fn actor(&self, actor_id: Url) -> Result<Option<Actor>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.actor_id_actor.get(actor_id.as_str().as_bytes())? {
|
if let Some(ivec) = inner.actor_id_actor.get(actor_id.as_str().as_bytes())? {
|
||||||
let actor = serde_json::from_slice(&ivec)?;
|
let actor = serde_json::from_slice(&ivec)?;
|
||||||
|
@ -470,7 +474,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn save_actor(&self, actor: Actor) -> Result<(), MyError> {
|
pub(crate) async fn save_actor(&self, actor: Actor) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
let vec = serde_json::to_vec(&actor)?;
|
let vec = serde_json::to_vec(&actor)?;
|
||||||
|
|
||||||
|
@ -486,8 +490,8 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn remove_connection(&self, actor_id: Url) -> Result<(), MyError> {
|
pub(crate) async fn remove_connection(&self, actor_id: Url) -> Result<(), Error> {
|
||||||
log::debug!("Removing Connection: {}", actor_id);
|
tracing::debug!("Removing Connection: {}", actor_id);
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
inner
|
inner
|
||||||
.connected_actor_ids
|
.connected_actor_ids
|
||||||
|
@ -498,8 +502,8 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn add_connection(&self, actor_id: Url) -> Result<(), MyError> {
|
pub(crate) async fn add_connection(&self, actor_id: Url) -> Result<(), Error> {
|
||||||
log::debug!("Adding Connection: {}", actor_id);
|
tracing::debug!("Adding Connection: {}", actor_id);
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
inner
|
inner
|
||||||
.connected_actor_ids
|
.connected_actor_ids
|
||||||
|
@ -510,7 +514,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn add_blocks(&self, domains: Vec<String>) -> Result<(), MyError> {
|
pub(crate) async fn add_blocks(&self, domains: Vec<String>) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
for connected in inner.connected_by_domain(&domains) {
|
for connected in inner.connected_by_domain(&domains) {
|
||||||
inner
|
inner
|
||||||
|
@ -530,7 +534,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn remove_blocks(&self, domains: Vec<String>) -> Result<(), MyError> {
|
pub(crate) async fn remove_blocks(&self, domains: Vec<String>) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
for domain in &domains {
|
for domain in &domains {
|
||||||
inner.blocked_domains.remove(domain_key(domain))?;
|
inner.blocked_domains.remove(domain_key(domain))?;
|
||||||
|
@ -541,7 +545,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn add_allows(&self, domains: Vec<String>) -> Result<(), MyError> {
|
pub(crate) async fn add_allows(&self, domains: Vec<String>) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
for domain in &domains {
|
for domain in &domains {
|
||||||
inner
|
inner
|
||||||
|
@ -554,7 +558,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn remove_allows(&self, domains: Vec<String>) -> Result<(), MyError> {
|
pub(crate) async fn remove_allows(&self, domains: Vec<String>) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if inner.restricted_mode {
|
if inner.restricted_mode {
|
||||||
for connected in inner.connected_by_domain(&domains) {
|
for connected in inner.connected_by_domain(&domains) {
|
||||||
|
@ -573,7 +577,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn is_allowed(&self, url: Url) -> Result<bool, MyError> {
|
pub(crate) async fn is_allowed(&self, url: Url) -> Result<bool, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(domain) = url.domain() {
|
if let Some(domain) = url.domain() {
|
||||||
Ok(inner.is_allowed(domain))
|
Ok(inner.is_allowed(domain))
|
||||||
|
@ -584,7 +588,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn private_key(&self) -> Result<Option<RsaPrivateKey>, MyError> {
|
pub(crate) async fn private_key(&self) -> Result<Option<RsaPrivateKey>, Error> {
|
||||||
self.unblock(|inner| {
|
self.unblock(|inner| {
|
||||||
if let Some(ivec) = inner.settings.get("private-key")? {
|
if let Some(ivec) = inner.settings.get("private-key")? {
|
||||||
let key_str = String::from_utf8_lossy(&ivec);
|
let key_str = String::from_utf8_lossy(&ivec);
|
||||||
|
@ -601,7 +605,7 @@ impl Db {
|
||||||
pub(crate) async fn update_private_key(
|
pub(crate) async fn update_private_key(
|
||||||
&self,
|
&self,
|
||||||
private_key: &RsaPrivateKey,
|
private_key: &RsaPrivateKey,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
let pem_pkcs8 = private_key.to_pkcs8_pem()?;
|
let pem_pkcs8 = private_key.to_pkcs8_pem()?;
|
||||||
|
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
|
|
72
src/error.rs
72
src/error.rs
|
@ -5,11 +5,43 @@ use actix_web::{
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
};
|
};
|
||||||
use http_signature_normalization_actix::PrepareSignError;
|
use http_signature_normalization_actix::PrepareSignError;
|
||||||
use log::error;
|
use std::{convert::Infallible, fmt::Debug, io};
|
||||||
use std::{convert::Infallible, fmt::Debug, io::Error};
|
use tracing::error;
|
||||||
|
use tracing_error::SpanTrace;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Error {
|
||||||
|
context: SpanTrace,
|
||||||
|
kind: ErrorKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.kind)?;
|
||||||
|
std::fmt::Display::fmt(&self.context, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
self.kind.source()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for Error
|
||||||
|
where
|
||||||
|
ErrorKind: From<T>,
|
||||||
|
{
|
||||||
|
fn from(error: T) -> Self {
|
||||||
|
Error {
|
||||||
|
context: SpanTrace::capture(),
|
||||||
|
kind: error.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub(crate) enum MyError {
|
pub(crate) enum ErrorKind {
|
||||||
#[error("Error queueing job, {0}")]
|
#[error("Error queueing job, {0}")]
|
||||||
Queue(anyhow::Error),
|
Queue(anyhow::Error),
|
||||||
|
|
||||||
|
@ -23,7 +55,7 @@ pub(crate) enum MyError {
|
||||||
Uri(#[from] ParseError),
|
Uri(#[from] ParseError),
|
||||||
|
|
||||||
#[error("Couldn't perform IO, {0}")]
|
#[error("Couldn't perform IO, {0}")]
|
||||||
Io(#[from] Error),
|
Io(#[from] io::Error),
|
||||||
|
|
||||||
#[error("Couldn't sign string, {0}")]
|
#[error("Couldn't sign string, {0}")]
|
||||||
Rsa(rsa::errors::Error),
|
Rsa(rsa::errors::Error),
|
||||||
|
@ -111,19 +143,23 @@ pub(crate) enum MyError {
|
||||||
|
|
||||||
#[error("Not trying request due to failed breaker")]
|
#[error("Not trying request due to failed breaker")]
|
||||||
Breaker,
|
Breaker,
|
||||||
|
|
||||||
|
#[error("Failed to extract fields from {0}")]
|
||||||
|
Extract(&'static str)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for MyError {
|
impl ResponseError for Error {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self.kind {
|
||||||
MyError::NotAllowed(_) | MyError::WrongActor(_) | MyError::BadActor(_, _) => {
|
ErrorKind::NotAllowed(_) | ErrorKind::WrongActor(_) | ErrorKind::BadActor(_, _) => {
|
||||||
StatusCode::FORBIDDEN
|
StatusCode::FORBIDDEN
|
||||||
}
|
}
|
||||||
MyError::NotSubscribed(_) => StatusCode::UNAUTHORIZED,
|
ErrorKind::NotSubscribed(_) => StatusCode::UNAUTHORIZED,
|
||||||
MyError::Duplicate => StatusCode::ACCEPTED,
|
ErrorKind::Duplicate => StatusCode::ACCEPTED,
|
||||||
MyError::Kind(_) | MyError::MissingKind | MyError::MissingId | MyError::ObjectCount => {
|
ErrorKind::Kind(_)
|
||||||
StatusCode::BAD_REQUEST
|
| ErrorKind::MissingKind
|
||||||
}
|
| ErrorKind::MissingId
|
||||||
|
| ErrorKind::ObjectCount => StatusCode::BAD_REQUEST,
|
||||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,27 +169,27 @@ impl ResponseError for MyError {
|
||||||
.insert_header(("Content-Type", "application/activity+json"))
|
.insert_header(("Content-Type", "application/activity+json"))
|
||||||
.body(
|
.body(
|
||||||
serde_json::to_string(&serde_json::json!({
|
serde_json::to_string(&serde_json::json!({
|
||||||
"error": self.to_string(),
|
"error": self.kind.to_string(),
|
||||||
}))
|
}))
|
||||||
.unwrap_or("{}".to_string()),
|
.unwrap_or("{}".to_string()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BlockingError> for MyError {
|
impl From<BlockingError> for ErrorKind {
|
||||||
fn from(_: BlockingError) -> Self {
|
fn from(_: BlockingError) -> Self {
|
||||||
MyError::Canceled
|
ErrorKind::Canceled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Infallible> for MyError {
|
impl From<Infallible> for ErrorKind {
|
||||||
fn from(i: Infallible) -> Self {
|
fn from(i: Infallible) -> Self {
|
||||||
match i {}
|
match i {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<rsa::errors::Error> for MyError {
|
impl From<rsa::errors::Error> for ErrorKind {
|
||||||
fn from(e: rsa::errors::Error) -> Self {
|
fn from(e: rsa::errors::Error) -> Self {
|
||||||
MyError::Rsa(e)
|
ErrorKind::Rsa(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, UrlKind},
|
config::{Config, UrlKind},
|
||||||
db::Actor,
|
db::Actor,
|
||||||
error::MyError,
|
error::Error,
|
||||||
jobs::{
|
jobs::{
|
||||||
apub::{get_inboxes, prepare_activity},
|
apub::{get_inboxes, prepare_activity},
|
||||||
DeliverMany, JobState,
|
DeliverMany, JobState,
|
||||||
|
@ -22,7 +22,8 @@ impl Announce {
|
||||||
Announce { object_id, actor }
|
Announce { object_id, actor }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn perform(self, state: JobState) -> Result<(), anyhow::Error> {
|
#[tracing::instrument(name = "Announce")]
|
||||||
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
let activity_id = state.config.generate_url(UrlKind::Activity);
|
let activity_id = state.config.generate_url(UrlKind::Activity);
|
||||||
|
|
||||||
let announce = generate_announce(&state.config, &activity_id, &self.object_id)?;
|
let announce = generate_announce(&state.config, &activity_id, &self.object_id)?;
|
||||||
|
@ -41,7 +42,7 @@ fn generate_announce(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
activity_id: &Url,
|
activity_id: &Url,
|
||||||
object_id: &Url,
|
object_id: &Url,
|
||||||
) -> Result<AsAnnounce, MyError> {
|
) -> Result<AsAnnounce, Error> {
|
||||||
let announce = AsAnnounce::new(config.generate_url(UrlKind::Actor), object_id.clone());
|
let announce = AsAnnounce::new(config.generate_url(UrlKind::Actor), object_id.clone());
|
||||||
|
|
||||||
prepare_activity(
|
prepare_activity(
|
||||||
|
@ -58,6 +59,6 @@ impl ActixJob for Announce {
|
||||||
const NAME: &'static str = "relay::jobs::apub::Announce";
|
const NAME: &'static str = "relay::jobs::apub::Announce";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
apub::AcceptedActivities,
|
apub::AcceptedActivities,
|
||||||
config::{Config, UrlKind},
|
config::{Config, UrlKind},
|
||||||
db::Actor,
|
db::Actor,
|
||||||
error::MyError,
|
error::{Error, ErrorKind},
|
||||||
jobs::{apub::prepare_activity, Deliver, JobState, QueryInstance, QueryNodeinfo},
|
jobs::{apub::prepare_activity, Deliver, JobState, QueryInstance, QueryNodeinfo},
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
|
@ -24,7 +24,8 @@ impl Follow {
|
||||||
Follow { input, actor }
|
Follow { input, actor }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn perform(self, state: JobState) -> Result<(), anyhow::Error> {
|
#[tracing::instrument(name = "Follow")]
|
||||||
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
let my_id = state.config.generate_url(UrlKind::Actor);
|
let my_id = state.config.generate_url(UrlKind::Actor);
|
||||||
|
|
||||||
// if following relay directly, not just following 'public', followback
|
// if following relay directly, not just following 'public', followback
|
||||||
|
@ -42,7 +43,7 @@ impl Follow {
|
||||||
let accept = generate_accept_follow(
|
let accept = generate_accept_follow(
|
||||||
&state.config,
|
&state.config,
|
||||||
&self.actor.id,
|
&self.actor.id,
|
||||||
self.input.id_unchecked().ok_or(MyError::MissingId)?,
|
self.input.id_unchecked().ok_or(ErrorKind::MissingId)?,
|
||||||
&my_id,
|
&my_id,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ impl Follow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a type that says "I want to follow you"
|
// Generate a type that says "I want to follow you"
|
||||||
fn generate_follow(config: &Config, actor_id: &Url, my_id: &Url) -> Result<AsFollow, MyError> {
|
fn generate_follow(config: &Config, actor_id: &Url, my_id: &Url) -> Result<AsFollow, Error> {
|
||||||
let follow = AsFollow::new(my_id.clone(), actor_id.clone());
|
let follow = AsFollow::new(my_id.clone(), actor_id.clone());
|
||||||
|
|
||||||
prepare_activity(
|
prepare_activity(
|
||||||
|
@ -77,7 +78,7 @@ fn generate_accept_follow(
|
||||||
actor_id: &Url,
|
actor_id: &Url,
|
||||||
input_id: &Url,
|
input_id: &Url,
|
||||||
my_id: &Url,
|
my_id: &Url,
|
||||||
) -> Result<AsAccept, MyError> {
|
) -> Result<AsAccept, Error> {
|
||||||
let mut follow = AsFollow::new(actor_id.clone(), my_id.clone());
|
let mut follow = AsFollow::new(actor_id.clone(), my_id.clone());
|
||||||
|
|
||||||
follow.set_id(input_id.clone());
|
follow.set_id(input_id.clone());
|
||||||
|
@ -98,6 +99,6 @@ impl ActixJob for Follow {
|
||||||
const NAME: &'static str = "relay::jobs::apub::Follow";
|
const NAME: &'static str = "relay::jobs::apub::Follow";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
apub::AcceptedActivities,
|
apub::AcceptedActivities,
|
||||||
db::Actor,
|
db::Actor,
|
||||||
error::MyError,
|
error::{Error, ErrorKind},
|
||||||
jobs::{apub::get_inboxes, DeliverMany, JobState},
|
jobs::{apub::get_inboxes, DeliverMany, JobState},
|
||||||
};
|
};
|
||||||
use activitystreams::prelude::*;
|
use activitystreams::prelude::*;
|
||||||
|
@ -19,12 +19,13 @@ impl Forward {
|
||||||
Forward { input, actor }
|
Forward { input, actor }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn perform(self, state: JobState) -> Result<(), anyhow::Error> {
|
#[tracing::instrument(name = "Forward")]
|
||||||
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
let object_id = self
|
let object_id = self
|
||||||
.input
|
.input
|
||||||
.object()
|
.object()
|
||||||
.as_single_id()
|
.as_single_id()
|
||||||
.ok_or(MyError::MissingId)?;
|
.ok_or(ErrorKind::MissingId)?;
|
||||||
|
|
||||||
let inboxes = get_inboxes(&state.state, &self.actor, object_id).await?;
|
let inboxes = get_inboxes(&state.state, &self.actor, object_id).await?;
|
||||||
|
|
||||||
|
@ -43,6 +44,6 @@ impl ActixJob for Forward {
|
||||||
const NAME: &'static str = "relay::jobs::apub::Forward";
|
const NAME: &'static str = "relay::jobs::apub::Forward";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
config::{Config, UrlKind},
|
config::{Config, UrlKind},
|
||||||
data::State,
|
data::State,
|
||||||
db::Actor,
|
db::Actor,
|
||||||
error::MyError,
|
error::{Error, ErrorKind},
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Follow as AsFollow, Undo as AsUndo},
|
activity::{Follow as AsFollow, Undo as AsUndo},
|
||||||
|
@ -23,8 +23,8 @@ pub(crate) use self::{
|
||||||
announce::Announce, follow::Follow, forward::Forward, reject::Reject, undo::Undo,
|
announce::Announce, follow::Follow, forward::Forward, reject::Reject, undo::Undo,
|
||||||
};
|
};
|
||||||
|
|
||||||
async fn get_inboxes(state: &State, actor: &Actor, object_id: &Url) -> Result<Vec<Url>, MyError> {
|
async fn get_inboxes(state: &State, actor: &Actor, object_id: &Url) -> Result<Vec<Url>, Error> {
|
||||||
let domain = object_id.host().ok_or(MyError::Domain)?.to_string();
|
let domain = object_id.host().ok_or(ErrorKind::Domain)?.to_string();
|
||||||
|
|
||||||
state.inboxes_without(&actor.inbox, &domain).await
|
state.inboxes_without(&actor.inbox, &domain).await
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,10 @@ fn prepare_activity<T, U, V, Kind>(
|
||||||
mut t: T,
|
mut t: T,
|
||||||
id: impl TryInto<Url, Error = U>,
|
id: impl TryInto<Url, Error = U>,
|
||||||
to: impl TryInto<Url, Error = V>,
|
to: impl TryInto<Url, Error = V>,
|
||||||
) -> Result<T, MyError>
|
) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: ObjectExt<Kind> + BaseExt<Kind>,
|
T: ObjectExt<Kind> + BaseExt<Kind>,
|
||||||
MyError: From<U> + From<V>,
|
Error: From<U> + From<V>,
|
||||||
{
|
{
|
||||||
t.set_id(id.try_into()?)
|
t.set_id(id.try_into()?)
|
||||||
.set_many_tos(vec![to.try_into()?])
|
.set_many_tos(vec![to.try_into()?])
|
||||||
|
@ -45,7 +45,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a type that says "I want to stop following you"
|
// Generate a type that says "I want to stop following you"
|
||||||
fn generate_undo_follow(config: &Config, actor_id: &Url, my_id: &Url) -> Result<AsUndo, MyError> {
|
fn generate_undo_follow(config: &Config, actor_id: &Url, my_id: &Url) -> Result<AsUndo, Error> {
|
||||||
let mut follow = AsFollow::new(my_id.clone(), actor_id.clone());
|
let mut follow = AsFollow::new(my_id.clone(), actor_id.clone());
|
||||||
|
|
||||||
follow.set_id(config.generate_url(UrlKind::Activity));
|
follow.set_id(config.generate_url(UrlKind::Activity));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::UrlKind,
|
config::UrlKind,
|
||||||
db::Actor,
|
db::Actor,
|
||||||
|
error::Error,
|
||||||
jobs::{apub::generate_undo_follow, Deliver, JobState},
|
jobs::{apub::generate_undo_follow, Deliver, JobState},
|
||||||
};
|
};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
|
@ -10,7 +11,8 @@ use std::{future::Future, pin::Pin};
|
||||||
pub(crate) struct Reject(pub(crate) Actor);
|
pub(crate) struct Reject(pub(crate) Actor);
|
||||||
|
|
||||||
impl Reject {
|
impl Reject {
|
||||||
async fn perform(self, state: JobState) -> Result<(), anyhow::Error> {
|
#[tracing::instrument(name = "Reject")]
|
||||||
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
state.actors.remove_connection(&self.0).await?;
|
state.actors.remove_connection(&self.0).await?;
|
||||||
|
|
||||||
let my_id = state.config.generate_url(UrlKind::Actor);
|
let my_id = state.config.generate_url(UrlKind::Actor);
|
||||||
|
@ -29,6 +31,6 @@ impl ActixJob for Reject {
|
||||||
const NAME: &'static str = "relay::jobs::apub::Reject";
|
const NAME: &'static str = "relay::jobs::apub::Reject";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::{
|
||||||
apub::AcceptedActivities,
|
apub::AcceptedActivities,
|
||||||
config::UrlKind,
|
config::UrlKind,
|
||||||
db::Actor,
|
db::Actor,
|
||||||
|
error::Error,
|
||||||
jobs::{apub::generate_undo_follow, Deliver, JobState},
|
jobs::{apub::generate_undo_follow, Deliver, JobState},
|
||||||
};
|
};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
|
@ -18,7 +19,8 @@ impl Undo {
|
||||||
Undo { input, actor }
|
Undo { input, actor }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn perform(self, state: JobState) -> Result<(), anyhow::Error> {
|
#[tracing::instrument(name = "Undo")]
|
||||||
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
let was_following = state.state.db.is_connected(self.actor.id.clone()).await?;
|
let was_following = state.state.db.is_connected(self.actor.id.clone()).await?;
|
||||||
|
|
||||||
state.actors.remove_connection(&self.actor).await?;
|
state.actors.remove_connection(&self.actor).await?;
|
||||||
|
@ -42,6 +44,6 @@ impl ActixJob for Undo {
|
||||||
const NAME: &'static str = "relay::jobs::apub::Undo";
|
const NAME: &'static str = "relay::jobs::apub::Undo";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::jobs::JobState;
|
use crate::{error::Error, jobs::JobState};
|
||||||
use anyhow::Error;
|
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -14,6 +13,7 @@ impl CacheMedia {
|
||||||
CacheMedia { uuid }
|
CacheMedia { uuid }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Cache media")]
|
||||||
async fn perform(self, state: JobState) -> Result<(), Error> {
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
if !state.media.is_outdated(self.uuid).await? {
|
if !state.media.is_outdated(self.uuid).await? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -34,11 +34,11 @@ impl CacheMedia {
|
||||||
|
|
||||||
impl ActixJob for CacheMedia {
|
impl ActixJob for CacheMedia {
|
||||||
type State = JobState;
|
type State = JobState;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::CacheMedia";
|
const NAME: &'static str = "relay::jobs::CacheMedia";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::{apub::AcceptedActors, jobs::JobState};
|
use crate::{
|
||||||
|
apub::AcceptedActors,
|
||||||
|
error::{Error, ErrorKind},
|
||||||
|
jobs::JobState,
|
||||||
|
};
|
||||||
use activitystreams::{object::Image, prelude::*, url::Url};
|
use activitystreams::{object::Image, prelude::*, url::Url};
|
||||||
use anyhow::Error;
|
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
@ -33,8 +36,8 @@ impl QueryContact {
|
||||||
.fetch::<AcceptedActors>(self.contact_id.as_str())
|
.fetch::<AcceptedActors>(self.contact_id.as_str())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (username, display_name, url, avatar) = to_contact(contact)
|
let (username, display_name, url, avatar) =
|
||||||
.ok_or_else(|| anyhow::anyhow!("Failed to extract fields from contact"))?;
|
to_contact(contact).ok_or_else(|| ErrorKind::Extract("contact"))?;
|
||||||
|
|
||||||
state
|
state
|
||||||
.node_cache
|
.node_cache
|
||||||
|
@ -63,12 +66,12 @@ fn to_contact(contact: AcceptedActors) -> Option<(String, String, Url, Url)> {
|
||||||
|
|
||||||
impl ActixJob for QueryContact {
|
impl ActixJob for QueryContact {
|
||||||
type State = JobState;
|
type State = JobState;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::QueryContact";
|
const NAME: &'static str = "relay::jobs::QueryContact";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{error::MyError, jobs::JobState};
|
use crate::{error::Error, jobs::JobState};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use anyhow::Error;
|
|
||||||
use background_jobs::{ActixJob, Backoff};
|
use background_jobs::{ActixJob, Backoff};
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
@ -11,7 +10,7 @@ pub(crate) struct Deliver {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deliver {
|
impl Deliver {
|
||||||
pub(crate) fn new<T>(to: Url, data: T) -> Result<Self, MyError>
|
pub(crate) fn new<T>(to: Url, data: T) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
T: serde::ser::Serialize,
|
T: serde::ser::Serialize,
|
||||||
{
|
{
|
||||||
|
@ -20,20 +19,22 @@ impl Deliver {
|
||||||
data: serde_json::to_value(data)?,
|
data: serde_json::to_value(data)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Deliver")]
|
||||||
|
async fn permform(self, state: JobState) -> Result<(), Error> {
|
||||||
|
state.requests.deliver(self.to, &self.data).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActixJob for Deliver {
|
impl ActixJob for Deliver {
|
||||||
type State = JobState;
|
type State = JobState;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::Deliver";
|
const NAME: &'static str = "relay::jobs::Deliver";
|
||||||
const BACKOFF: Backoff = Backoff::Exponential(8);
|
const BACKOFF: Backoff = Backoff::Exponential(8);
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move {
|
Box::pin(async move { self.permform(state).await.map_err(Into::into) })
|
||||||
state.requests.deliver(self.to, &self.data).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
error::MyError,
|
error::Error,
|
||||||
jobs::{Deliver, JobState},
|
jobs::{Deliver, JobState},
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use anyhow::Error;
|
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use futures::future::{ready, Ready};
|
use std::future::{ready, Ready};
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct DeliverMany {
|
pub(crate) struct DeliverMany {
|
||||||
|
@ -14,7 +13,7 @@ pub(crate) struct DeliverMany {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeliverMany {
|
impl DeliverMany {
|
||||||
pub(crate) fn new<T>(to: Vec<Url>, data: T) -> Result<Self, MyError>
|
pub(crate) fn new<T>(to: Vec<Url>, data: T) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
T: serde::ser::Serialize,
|
T: serde::ser::Serialize,
|
||||||
{
|
{
|
||||||
|
@ -24,6 +23,7 @@ impl DeliverMany {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Deliver many")]
|
||||||
fn perform(self, state: JobState) -> Result<(), Error> {
|
fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
for inbox in self.to {
|
for inbox in self.to {
|
||||||
state
|
state
|
||||||
|
@ -37,11 +37,11 @@ impl DeliverMany {
|
||||||
|
|
||||||
impl ActixJob for DeliverMany {
|
impl ActixJob for DeliverMany {
|
||||||
type State = JobState;
|
type State = JobState;
|
||||||
type Future = Ready<Result<(), Error>>;
|
type Future = Ready<Result<(), anyhow::Error>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::DeliverMany";
|
const NAME: &'static str = "relay::jobs::DeliverMany";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
ready(self.perform(state))
|
ready(self.perform(state).map_err(Into::into))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::UrlKind,
|
config::UrlKind,
|
||||||
|
error::Error,
|
||||||
jobs::{cache_media::CacheMedia, JobState},
|
jobs::{cache_media::CacheMedia, JobState},
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use anyhow::Error;
|
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ impl QueryInstance {
|
||||||
QueryInstance { actor_id }
|
QueryInstance { actor_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Query instance")]
|
||||||
async fn perform(self, state: JobState) -> Result<(), Error> {
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
let contact_outdated = state
|
let contact_outdated = state
|
||||||
.node_cache
|
.node_cache
|
||||||
|
@ -91,12 +92,12 @@ impl QueryInstance {
|
||||||
|
|
||||||
impl ActixJob for QueryInstance {
|
impl ActixJob for QueryInstance {
|
||||||
type State = JobState;
|
type State = JobState;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::QueryInstance";
|
const NAME: &'static str = "relay::jobs::QueryInstance";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
data::{ActorCache, MediaCache, NodeCache, State},
|
data::{ActorCache, MediaCache, NodeCache, State},
|
||||||
db::Db,
|
db::Db,
|
||||||
error::MyError,
|
error::{Error, ErrorKind},
|
||||||
jobs::process_listeners::Listeners,
|
jobs::process_listeners::Listeners,
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
};
|
};
|
||||||
|
@ -67,7 +67,7 @@ pub(crate) fn create_workers(
|
||||||
.start(remote_handle);
|
.start(remote_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct JobState {
|
pub(crate) struct JobState {
|
||||||
db: Db,
|
db: Db,
|
||||||
requests: Requests,
|
requests: Requests,
|
||||||
|
@ -84,6 +84,14 @@ pub(crate) struct JobServer {
|
||||||
remote: QueueHandle,
|
remote: QueueHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for JobServer {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("JobServer")
|
||||||
|
.field("queue_handle", &"QueueHandle")
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl JobState {
|
impl JobState {
|
||||||
fn new(
|
fn new(
|
||||||
db: Db,
|
db: Db,
|
||||||
|
@ -113,10 +121,13 @@ impl JobServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn queue<J>(&self, job: J) -> Result<(), MyError>
|
pub(crate) fn queue<J>(&self, job: J) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
J: Job,
|
J: Job,
|
||||||
{
|
{
|
||||||
self.remote.queue(job).map_err(MyError::Queue)
|
self.remote
|
||||||
|
.queue(job)
|
||||||
|
.map_err(ErrorKind::Queue)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::jobs::{JobState, QueryContact};
|
use crate::{
|
||||||
|
error::Error,
|
||||||
|
jobs::{JobState, QueryContact},
|
||||||
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use anyhow::Error;
|
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
@ -14,6 +16,7 @@ impl QueryNodeinfo {
|
||||||
QueryNodeinfo { actor_id }
|
QueryNodeinfo { actor_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Query node info")]
|
||||||
async fn perform(self, state: JobState) -> Result<(), Error> {
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
if !state
|
if !state
|
||||||
.node_cache
|
.node_cache
|
||||||
|
@ -65,12 +68,12 @@ impl QueryNodeinfo {
|
||||||
|
|
||||||
impl ActixJob for QueryNodeinfo {
|
impl ActixJob for QueryNodeinfo {
|
||||||
type State = JobState;
|
type State = JobState;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::QueryNodeinfo";
|
const NAME: &'static str = "relay::jobs::QueryNodeinfo";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::jobs::{instance::QueryInstance, nodeinfo::QueryNodeinfo, JobState};
|
use crate::{
|
||||||
use anyhow::Error;
|
error::Error,
|
||||||
|
jobs::{instance::QueryInstance, nodeinfo::QueryNodeinfo, JobState},
|
||||||
|
};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
@ -7,6 +9,7 @@ use std::{future::Future, pin::Pin};
|
||||||
pub(crate) struct Listeners;
|
pub(crate) struct Listeners;
|
||||||
|
|
||||||
impl Listeners {
|
impl Listeners {
|
||||||
|
#[tracing::instrument(name = "Spawn query instances")]
|
||||||
async fn perform(self, state: JobState) -> Result<(), Error> {
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
for actor_id in state.state.db.connected_ids().await? {
|
for actor_id in state.state.db.connected_ids().await? {
|
||||||
state
|
state
|
||||||
|
@ -21,11 +24,11 @@ impl Listeners {
|
||||||
|
|
||||||
impl ActixJob for Listeners {
|
impl ActixJob for Listeners {
|
||||||
type State = JobState;
|
type State = JobState;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::Listeners";
|
const NAME: &'static str = "relay::jobs::Listeners";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(self.perform(state))
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -1,7 +1,8 @@
|
||||||
use actix_web::{
|
use actix_web::{web, App, HttpServer};
|
||||||
middleware::{Compress, Logger},
|
use tracing_actix_web::TracingLogger;
|
||||||
web, App, HttpServer,
|
use tracing_error::ErrorLayer;
|
||||||
};
|
use tracing_log::LogTracer;
|
||||||
|
use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, EnvFilter};
|
||||||
|
|
||||||
mod apub;
|
mod apub;
|
||||||
mod args;
|
mod args;
|
||||||
|
@ -28,23 +29,23 @@ use self::{
|
||||||
async fn main() -> Result<(), anyhow::Error> {
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
|
|
||||||
|
LogTracer::init()?;
|
||||||
|
|
||||||
|
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||||
|
|
||||||
|
let format_layer = tracing_subscriber::fmt::layer()
|
||||||
|
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
||||||
|
.pretty();
|
||||||
|
|
||||||
|
let subscriber = tracing_subscriber::Registry::default()
|
||||||
|
.with(env_filter)
|
||||||
|
.with(ErrorLayer::default())
|
||||||
|
.with(format_layer);
|
||||||
|
|
||||||
|
tracing::subscriber::set_global_default(subscriber)?;
|
||||||
|
|
||||||
let config = Config::build()?;
|
let config = Config::build()?;
|
||||||
|
|
||||||
if config.debug() {
|
|
||||||
std::env::set_var(
|
|
||||||
"RUST_LOG",
|
|
||||||
"debug,h2=info,trust_dns_resolver=info,trust_dns_proto=info,rustls=info,html5ever=info",
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
std::env::set_var("RUST_LOG", "info")
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.pretty_log() {
|
|
||||||
pretty_env_logger::init();
|
|
||||||
} else {
|
|
||||||
env_logger::init();
|
|
||||||
}
|
|
||||||
|
|
||||||
let db = Db::build(&config)?;
|
let db = Db::build(&config)?;
|
||||||
|
|
||||||
let args = Args::new();
|
let args = Args::new();
|
||||||
|
@ -77,8 +78,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||||
let bind_address = config.bind_address();
|
let bind_address = config.bind_address();
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::default())
|
.wrap(TracingLogger::default())
|
||||||
.wrap(Logger::default())
|
|
||||||
.app_data(web::Data::new(db.clone()))
|
.app_data(web::Data::new(db.clone()))
|
||||||
.app_data(web::Data::new(state.clone()))
|
.app_data(web::Data::new(state.clone()))
|
||||||
.app_data(web::Data::new(state.requests()))
|
.app_data(web::Data::new(state.requests()))
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::{Payload, Service, ServiceRequest, Transform},
|
dev::{Payload, Service, ServiceRequest, Transform},
|
||||||
http::{Method, StatusCode},
|
http::Method,
|
||||||
web::BytesMut,
|
web::BytesMut,
|
||||||
HttpMessage, HttpResponse, ResponseError,
|
HttpMessage,
|
||||||
};
|
};
|
||||||
use futures::{
|
use futures_util::{
|
||||||
future::{ok, LocalBoxFuture, Ready, TryFutureExt},
|
future::{LocalBoxFuture, TryFutureExt},
|
||||||
stream::{once, TryStreamExt},
|
stream::{once, TryStreamExt},
|
||||||
};
|
};
|
||||||
use log::{error, info};
|
use std::{
|
||||||
use std::task::{Context, Poll};
|
future::{ready, Ready},
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct DebugPayload(pub bool);
|
pub(crate) struct DebugPayload(pub bool);
|
||||||
|
@ -18,20 +21,6 @@ pub(crate) struct DebugPayload(pub bool);
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct DebugPayloadMiddleware<S>(bool, S);
|
pub(crate) struct DebugPayloadMiddleware<S>(bool, S);
|
||||||
|
|
||||||
#[derive(Clone, Debug, thiserror::Error)]
|
|
||||||
#[error("Failed to read payload")]
|
|
||||||
pub(crate) struct DebugError;
|
|
||||||
|
|
||||||
impl ResponseError for DebugError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error_response(&self) -> HttpResponse {
|
|
||||||
HttpResponse::new(self.status_code())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Transform<S, ServiceRequest> for DebugPayload
|
impl<S> Transform<S, ServiceRequest> for DebugPayload
|
||||||
where
|
where
|
||||||
S: Service<ServiceRequest, Error = actix_web::Error>,
|
S: Service<ServiceRequest, Error = actix_web::Error>,
|
||||||
|
@ -45,7 +34,7 @@ where
|
||||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||||
|
|
||||||
fn new_transform(&self, service: S) -> Self::Future {
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
ok(DebugPayloadMiddleware(self.0, service))
|
ready(Ok(DebugPayloadMiddleware(self.0, service)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
apub::AcceptedActors,
|
apub::AcceptedActors,
|
||||||
data::{ActorCache, State},
|
data::{ActorCache, State},
|
||||||
error::MyError,
|
error::{Error, ErrorKind},
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
};
|
};
|
||||||
use activitystreams::{base::BaseExt, uri, url::Url};
|
use activitystreams::{base::BaseExt, uri, url::Url};
|
||||||
use actix_web::web;
|
use actix_web::web;
|
||||||
use http_signature_normalization_actix::{prelude::*, verify::DeprecatedAlgorithm};
|
use http_signature_normalization_actix::{prelude::*, verify::DeprecatedAlgorithm};
|
||||||
use log::error;
|
|
||||||
use rsa::{hash::Hash, padding::PaddingScheme, pkcs8::FromPublicKey, PublicKey, RsaPublicKey};
|
use rsa::{hash::Hash, padding::PaddingScheme, pkcs8::FromPublicKey, PublicKey, RsaPublicKey};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct MyVerify(pub Requests, pub ActorCache, pub State);
|
pub(crate) struct MyVerify(pub Requests, pub ActorCache, pub State);
|
||||||
|
|
||||||
impl MyVerify {
|
impl MyVerify {
|
||||||
|
#[tracing::instrument("Verify signature")]
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&self,
|
&self,
|
||||||
algorithm: Option<Algorithm>,
|
algorithm: Option<Algorithm>,
|
||||||
key_id: String,
|
key_id: String,
|
||||||
signature: String,
|
signature: String,
|
||||||
signing_string: String,
|
signing_string: String,
|
||||||
) -> Result<bool, MyError> {
|
) -> Result<bool, Error> {
|
||||||
let public_key_id = uri!(key_id);
|
let public_key_id = uri!(key_id);
|
||||||
|
|
||||||
let actor_id = if let Some(mut actor_id) = self
|
let actor_id = if let Some(mut actor_id) = self
|
||||||
|
@ -32,7 +32,7 @@ impl MyVerify {
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
if !self.2.db.is_allowed(actor_id.clone()).await? {
|
if !self.2.db.is_allowed(actor_id.clone()).await? {
|
||||||
return Err(MyError::NotAllowed(key_id));
|
return Err(ErrorKind::NotAllowed(key_id).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
actor_id.set_fragment(None);
|
actor_id.set_fragment(None);
|
||||||
|
@ -44,7 +44,7 @@ impl MyVerify {
|
||||||
Some(Algorithm::Hs2019) => (),
|
Some(Algorithm::Hs2019) => (),
|
||||||
Some(Algorithm::Deprecated(DeprecatedAlgorithm::RsaSha256)) => (),
|
Some(Algorithm::Deprecated(DeprecatedAlgorithm::RsaSha256)) => (),
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
return Err(MyError::Algorithm(other.to_string()));
|
return Err(ErrorKind::Algorithm(other.to_string()).into());
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
};
|
};
|
||||||
|
@ -65,7 +65,7 @@ impl MyVerify {
|
||||||
.fetch::<PublicKeyResponse>(public_key_id.as_str())
|
.fetch::<PublicKeyResponse>(public_key_id.as_str())
|
||||||
.await?
|
.await?
|
||||||
.actor_id()
|
.actor_id()
|
||||||
.ok_or_else(|| MyError::MissingId)?
|
.ok_or_else(|| ErrorKind::MissingId)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Previously we verified the sig from an actor's local cache
|
// Previously we verified the sig from an actor's local cache
|
||||||
|
@ -106,7 +106,7 @@ async fn do_verify(
|
||||||
public_key: &str,
|
public_key: &str,
|
||||||
signature: String,
|
signature: String,
|
||||||
signing_string: String,
|
signing_string: String,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
let public_key = RsaPublicKey::from_public_key_pem(public_key)?;
|
let public_key = RsaPublicKey::from_public_key_pem(public_key)?;
|
||||||
|
|
||||||
web::block(move || {
|
web::block(move || {
|
||||||
|
@ -121,7 +121,7 @@ async fn do_verify(
|
||||||
&decoded,
|
&decoded,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(()) as Result<(), MyError>
|
Ok(()) as Result<(), Error>
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ async fn do_verify(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignatureVerify for MyVerify {
|
impl SignatureVerify for MyVerify {
|
||||||
type Error = MyError;
|
type Error = Error;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<bool, Self::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<bool, Self::Error>>>>;
|
||||||
|
|
||||||
fn signature_verify(
|
fn signature_verify(
|
||||||
|
@ -144,10 +144,6 @@ impl SignatureVerify for MyVerify {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
this.verify(algorithm, key_id, signature, signing_string)
|
this.verify(algorithm, key_id, signature, signing_string)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
|
||||||
error!("Failed to verify, {}", e);
|
|
||||||
e
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ use crate::{
|
||||||
};
|
};
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_webfinger::{Resolver, Webfinger};
|
use actix_webfinger::{Resolver, Webfinger};
|
||||||
|
use futures_util::future::LocalBoxFuture;
|
||||||
use rsa_magic_public_key::AsMagicPublicKey;
|
use rsa_magic_public_key::AsMagicPublicKey;
|
||||||
use std::{future::Future, pin::Pin};
|
|
||||||
|
|
||||||
pub(crate) struct RelayResolver;
|
pub(crate) struct RelayResolver;
|
||||||
|
|
||||||
|
@ -13,8 +13,6 @@ pub(crate) struct RelayResolver;
|
||||||
#[error("Error resolving webfinger data")]
|
#[error("Error resolving webfinger data")]
|
||||||
pub(crate) struct RelayError;
|
pub(crate) struct RelayError;
|
||||||
|
|
||||||
type FutResult<T, E> = dyn Future<Output = Result<T, E>>;
|
|
||||||
|
|
||||||
impl Resolver for RelayResolver {
|
impl Resolver for RelayResolver {
|
||||||
type State = (Data<State>, Data<Config>);
|
type State = (Data<State>, Data<Config>);
|
||||||
type Error = RelayError;
|
type Error = RelayError;
|
||||||
|
@ -23,7 +21,7 @@ impl Resolver for RelayResolver {
|
||||||
account: &str,
|
account: &str,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
(state, config): Self::State,
|
(state, config): Self::State,
|
||||||
) -> Pin<Box<FutResult<Option<Webfinger>, Self::Error>>> {
|
) -> LocalBoxFuture<'static, Result<Option<Webfinger>, Self::Error>> {
|
||||||
let domain = domain.to_owned();
|
let domain = domain.to_owned();
|
||||||
let account = account.to_owned();
|
let account = account.to_owned();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::error::MyError;
|
use crate::error::{Error, ErrorKind};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::url::Url;
|
||||||
use actix_web::{http::header::Date, web::Bytes};
|
use actix_web::{http::header::Date, web::Bytes};
|
||||||
use async_mutex::Mutex;
|
use async_mutex::Mutex;
|
||||||
|
@ -6,7 +6,6 @@ use async_rwlock::RwLock;
|
||||||
use awc::Client;
|
use awc::Client;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use http_signature_normalization_actix::prelude::*;
|
use http_signature_normalization_actix::prelude::*;
|
||||||
use log::{debug, info, warn};
|
|
||||||
use rsa::{hash::Hash, padding::PaddingScheme, RsaPrivateKey};
|
use rsa::{hash::Hash, padding::PaddingScheme, RsaPrivateKey};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -19,12 +18,19 @@ use std::{
|
||||||
},
|
},
|
||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct Breakers {
|
pub(crate) struct Breakers {
|
||||||
inner: Arc<RwLock<HashMap<String, Arc<Mutex<Breaker>>>>>,
|
inner: Arc<RwLock<HashMap<String, Arc<Mutex<Breaker>>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Breakers {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Breakers").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Breakers {
|
impl Breakers {
|
||||||
async fn should_try(&self, url: &Url) -> bool {
|
async fn should_try(&self, url: &Url) -> bool {
|
||||||
if let Some(domain) = url.domain() {
|
if let Some(domain) = url.domain() {
|
||||||
|
@ -97,6 +103,7 @@ impl Default for Breakers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct Breaker {
|
struct Breaker {
|
||||||
failures: usize,
|
failures: usize,
|
||||||
last_attempt: DateTime<Utc>,
|
last_attempt: DateTime<Utc>,
|
||||||
|
@ -153,6 +160,21 @@ pub(crate) struct Requests {
|
||||||
breakers: Breakers,
|
breakers: Breakers,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Requests {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Requests")
|
||||||
|
.field("client", &"Client")
|
||||||
|
.field("consecutive_errors", &"AtomicUsize")
|
||||||
|
.field("error_limit", &self.error_limit)
|
||||||
|
.field("key_id", &self.key_id)
|
||||||
|
.field("user_agent", &self.user_agent)
|
||||||
|
.field("private_key", &"[redacted]")
|
||||||
|
.field("config", &self.config)
|
||||||
|
.field("breakers", &self.breakers)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Requests {
|
impl Requests {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
key_id: String,
|
key_id: String,
|
||||||
|
@ -191,28 +213,28 @@ impl Requests {
|
||||||
self.consecutive_errors.swap(0, Ordering::Relaxed);
|
self.consecutive_errors.swap(0, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn fetch_json<T>(&self, url: &str) -> Result<T, MyError>
|
pub(crate) async fn fetch_json<T>(&self, url: &str) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
self.do_fetch(url, "application/json").await
|
self.do_fetch(url, "application/json").await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn fetch<T>(&self, url: &str) -> Result<T, MyError>
|
pub(crate) async fn fetch<T>(&self, url: &str) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
self.do_fetch(url, "application/activity+json").await
|
self.do_fetch(url, "application/activity+json").await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_fetch<T>(&self, url: &str, accept: &str) -> Result<T, MyError>
|
async fn do_fetch<T>(&self, url: &str, accept: &str) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
let parsed_url = url.parse::<Url>()?;
|
let parsed_url = url.parse::<Url>()?;
|
||||||
|
|
||||||
if !self.breakers.should_try(&parsed_url).await {
|
if !self.breakers.should_try(&parsed_url).await {
|
||||||
return Err(MyError::Breaker);
|
return Err(ErrorKind::Breaker.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let signer = self.signer();
|
let signer = self.signer();
|
||||||
|
@ -236,7 +258,7 @@ impl Requests {
|
||||||
self.breakers.fail(&parsed_url).await;
|
self.breakers.fail(&parsed_url).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut res = res.map_err(|e| MyError::SendRequest(url.to_string(), e.to_string()))?;
|
let mut res = res.map_err(|e| ErrorKind::SendRequest(url.to_string(), e.to_string()))?;
|
||||||
|
|
||||||
self.reset_err();
|
self.reset_err();
|
||||||
|
|
||||||
|
@ -251,7 +273,7 @@ impl Requests {
|
||||||
|
|
||||||
self.breakers.fail(&parsed_url).await;
|
self.breakers.fail(&parsed_url).await;
|
||||||
|
|
||||||
return Err(MyError::Status(url.to_string(), res.status()));
|
return Err(ErrorKind::Status(url.to_string(), res.status()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.breakers.succeed(&parsed_url).await;
|
self.breakers.succeed(&parsed_url).await;
|
||||||
|
@ -259,16 +281,16 @@ impl Requests {
|
||||||
let body = res
|
let body = res
|
||||||
.body()
|
.body()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| MyError::ReceiveResponse(url.to_string(), e.to_string()))?;
|
.map_err(|e| ErrorKind::ReceiveResponse(url.to_string(), e.to_string()))?;
|
||||||
|
|
||||||
Ok(serde_json::from_slice(body.as_ref())?)
|
Ok(serde_json::from_slice(body.as_ref())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn fetch_bytes(&self, url: &str) -> Result<(String, Bytes), MyError> {
|
pub(crate) async fn fetch_bytes(&self, url: &str) -> Result<(String, Bytes), Error> {
|
||||||
let parsed_url = url.parse::<Url>()?;
|
let parsed_url = url.parse::<Url>()?;
|
||||||
|
|
||||||
if !self.breakers.should_try(&parsed_url).await {
|
if !self.breakers.should_try(&parsed_url).await {
|
||||||
return Err(MyError::Breaker);
|
return Err(ErrorKind::Breaker.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Fetching bytes for {}", url);
|
info!("Fetching bytes for {}", url);
|
||||||
|
@ -293,7 +315,7 @@ impl Requests {
|
||||||
self.count_err();
|
self.count_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut res = res.map_err(|e| MyError::SendRequest(url.to_string(), e.to_string()))?;
|
let mut res = res.map_err(|e| ErrorKind::SendRequest(url.to_string(), e.to_string()))?;
|
||||||
|
|
||||||
self.reset_err();
|
self.reset_err();
|
||||||
|
|
||||||
|
@ -301,10 +323,10 @@ impl Requests {
|
||||||
if let Ok(s) = content_type.to_str() {
|
if let Ok(s) = content_type.to_str() {
|
||||||
s.to_owned()
|
s.to_owned()
|
||||||
} else {
|
} else {
|
||||||
return Err(MyError::ContentType);
|
return Err(ErrorKind::ContentType.into());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(MyError::ContentType);
|
return Err(ErrorKind::ContentType.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
if !res.status().is_success() {
|
if !res.status().is_success() {
|
||||||
|
@ -318,14 +340,14 @@ impl Requests {
|
||||||
|
|
||||||
self.breakers.fail(&parsed_url).await;
|
self.breakers.fail(&parsed_url).await;
|
||||||
|
|
||||||
return Err(MyError::Status(url.to_string(), res.status()));
|
return Err(ErrorKind::Status(url.to_string(), res.status()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.breakers.succeed(&parsed_url).await;
|
self.breakers.succeed(&parsed_url).await;
|
||||||
|
|
||||||
let bytes = match res.body().limit(1024 * 1024 * 4).await {
|
let bytes = match res.body().limit(1024 * 1024 * 4).await {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(MyError::ReceiveResponse(url.to_string(), e.to_string()));
|
return Err(ErrorKind::ReceiveResponse(url.to_string(), e.to_string()).into());
|
||||||
}
|
}
|
||||||
Ok(bytes) => bytes,
|
Ok(bytes) => bytes,
|
||||||
};
|
};
|
||||||
|
@ -333,12 +355,12 @@ impl Requests {
|
||||||
Ok((content_type, bytes))
|
Ok((content_type, bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn deliver<T>(&self, inbox: Url, item: &T) -> Result<(), MyError>
|
pub(crate) async fn deliver<T>(&self, inbox: Url, item: &T) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: serde::ser::Serialize,
|
T: serde::ser::Serialize,
|
||||||
{
|
{
|
||||||
if !self.breakers.should_try(&inbox).await {
|
if !self.breakers.should_try(&inbox).await {
|
||||||
return Err(MyError::Breaker);
|
return Err(ErrorKind::Breaker.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let signer = self.signer();
|
let signer = self.signer();
|
||||||
|
@ -366,7 +388,7 @@ impl Requests {
|
||||||
self.breakers.fail(&inbox).await;
|
self.breakers.fail(&inbox).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut res = res.map_err(|e| MyError::SendRequest(inbox.to_string(), e.to_string()))?;
|
let mut res = res.map_err(|e| ErrorKind::SendRequest(inbox.to_string(), e.to_string()))?;
|
||||||
|
|
||||||
self.reset_err();
|
self.reset_err();
|
||||||
|
|
||||||
|
@ -380,7 +402,7 @@ impl Requests {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.breakers.fail(&inbox).await;
|
self.breakers.fail(&inbox).await;
|
||||||
return Err(MyError::Status(inbox.to_string(), res.status()));
|
return Err(ErrorKind::Status(inbox.to_string(), res.status()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.breakers.succeed(&inbox).await;
|
self.breakers.succeed(&inbox).await;
|
||||||
|
@ -400,7 +422,7 @@ struct Signer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Signer {
|
impl Signer {
|
||||||
fn sign(&self, signing_string: &str) -> Result<String, MyError> {
|
fn sign(&self, signing_string: &str) -> Result<String, Error> {
|
||||||
let hashed = Sha256::digest(signing_string.as_bytes());
|
let hashed = Sha256::digest(signing_string.as_bytes());
|
||||||
let bytes = self.private_key.sign(
|
let bytes = self.private_key.sign(
|
||||||
PaddingScheme::PKCS1v15Sign {
|
PaddingScheme::PKCS1v15Sign {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
apub::{PublicKey, PublicKeyInner},
|
apub::{PublicKey, PublicKeyInner},
|
||||||
config::{Config, UrlKind},
|
config::{Config, UrlKind},
|
||||||
data::State,
|
data::State,
|
||||||
error::MyError,
|
error::Error,
|
||||||
routes::ok,
|
routes::ok,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
|
@ -15,10 +15,11 @@ use activitystreams_ext::Ext1;
|
||||||
use actix_web::{web, Responder};
|
use actix_web::{web, Responder};
|
||||||
use rsa::pkcs8::ToPublicKey;
|
use rsa::pkcs8::ToPublicKey;
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Actor")]
|
||||||
pub(crate) async fn route(
|
pub(crate) async fn route(
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
) -> Result<impl Responder, MyError> {
|
) -> Result<impl Responder, Error> {
|
||||||
let mut application = Ext1::new(
|
let mut application = Ext1::new(
|
||||||
ApActor::new(config.generate_url(UrlKind::Inbox), Application::new()),
|
ApActor::new(config.generate_url(UrlKind::Inbox), Application::new()),
|
||||||
PublicKey {
|
PublicKey {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
config::{Config, UrlKind},
|
config::{Config, UrlKind},
|
||||||
data::{ActorCache, State},
|
data::{ActorCache, State},
|
||||||
db::Actor,
|
db::Actor,
|
||||||
error::MyError,
|
error::{Error, ErrorKind},
|
||||||
jobs::apub::{Announce, Follow, Forward, Reject, Undo},
|
jobs::apub::{Announce, Follow, Forward, Reject, Undo},
|
||||||
jobs::JobServer,
|
jobs::JobServer,
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
|
@ -14,8 +14,9 @@ use activitystreams::{
|
||||||
};
|
};
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use http_signature_normalization_actix::prelude::{DigestVerified, SignatureVerified};
|
use http_signature_normalization_actix::prelude::{DigestVerified, SignatureVerified};
|
||||||
use log::error;
|
use tracing::error;
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Inbox")]
|
||||||
pub(crate) async fn route(
|
pub(crate) async fn route(
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
actors: web::Data<ActorCache>,
|
actors: web::Data<ActorCache>,
|
||||||
|
@ -24,12 +25,12 @@ pub(crate) async fn route(
|
||||||
jobs: web::Data<JobServer>,
|
jobs: web::Data<JobServer>,
|
||||||
input: web::Json<AcceptedActivities>,
|
input: web::Json<AcceptedActivities>,
|
||||||
verified: Option<(SignatureVerified, DigestVerified)>,
|
verified: Option<(SignatureVerified, DigestVerified)>,
|
||||||
) -> Result<HttpResponse, MyError> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let input = input.into_inner();
|
let input = input.into_inner();
|
||||||
|
|
||||||
let actor = actors
|
let actor = actors
|
||||||
.get(
|
.get(
|
||||||
input.actor()?.as_single_id().ok_or(MyError::MissingId)?,
|
input.actor()?.as_single_id().ok_or(ErrorKind::MissingId)?,
|
||||||
&client,
|
&client,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
|
@ -39,28 +40,29 @@ pub(crate) async fn route(
|
||||||
let is_connected = state.db.is_connected(actor.id.clone()).await?;
|
let is_connected = state.db.is_connected(actor.id.clone()).await?;
|
||||||
|
|
||||||
if !is_allowed {
|
if !is_allowed {
|
||||||
return Err(MyError::NotAllowed(actor.id.to_string()));
|
return Err(ErrorKind::NotAllowed(actor.id.to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_connected && !valid_without_listener(&input)? {
|
if !is_connected && !valid_without_listener(&input)? {
|
||||||
return Err(MyError::NotSubscribed(actor.id.to_string()));
|
return Err(ErrorKind::NotSubscribed(actor.id.to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.validate_signatures() && verified.is_none() {
|
if config.validate_signatures() && verified.is_none() {
|
||||||
return Err(MyError::NoSignature(actor.public_key_id.to_string()));
|
return Err(ErrorKind::NoSignature(actor.public_key_id.to_string()).into());
|
||||||
} else if config.validate_signatures() {
|
} else if config.validate_signatures() {
|
||||||
if let Some((verified, _)) = verified {
|
if let Some((verified, _)) = verified {
|
||||||
if actor.public_key_id.as_str() != verified.key_id() {
|
if actor.public_key_id.as_str() != verified.key_id() {
|
||||||
error!("Bad actor, more info: {:?}", input);
|
error!("Bad actor, more info: {:?}", input);
|
||||||
return Err(MyError::BadActor(
|
return Err(ErrorKind::BadActor(
|
||||||
actor.public_key_id.to_string(),
|
actor.public_key_id.to_string(),
|
||||||
verified.key_id().to_owned(),
|
verified.key_id().to_owned(),
|
||||||
));
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match input.kind().ok_or(MyError::MissingKind)? {
|
match input.kind().ok_or(ErrorKind::MissingKind)? {
|
||||||
ValidTypes::Accept => handle_accept(&config, input).await?,
|
ValidTypes::Accept => handle_accept(&config, input).await?,
|
||||||
ValidTypes::Reject => handle_reject(&config, &jobs, input, actor).await?,
|
ValidTypes::Reject => handle_reject(&config, &jobs, input, actor).await?,
|
||||||
ValidTypes::Announce | ValidTypes::Create => {
|
ValidTypes::Announce | ValidTypes::Create => {
|
||||||
|
@ -74,7 +76,7 @@ pub(crate) async fn route(
|
||||||
Ok(accepted(serde_json::json!({})))
|
Ok(accepted(serde_json::json!({})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid_without_listener(input: &AcceptedActivities) -> Result<bool, MyError> {
|
fn valid_without_listener(input: &AcceptedActivities) -> Result<bool, Error> {
|
||||||
match input.kind() {
|
match input.kind() {
|
||||||
Some(ValidTypes::Follow) => Ok(true),
|
Some(ValidTypes::Follow) => Ok(true),
|
||||||
Some(ValidTypes::Undo) => Ok(single_object(input.object())?.is_kind("Follow")),
|
Some(ValidTypes::Undo) => Ok(single_object(input.object())?.is_kind("Follow")),
|
||||||
|
@ -82,32 +84,32 @@ fn valid_without_listener(input: &AcceptedActivities) -> Result<bool, MyError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind_str(base: &AnyBase) -> Result<&str, MyError> {
|
fn kind_str(base: &AnyBase) -> Result<&str, Error> {
|
||||||
base.kind_str().ok_or(MyError::MissingKind)
|
base.kind_str()
|
||||||
|
.ok_or(ErrorKind::MissingKind)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id_string(id: Option<&Url>) -> Result<String, MyError> {
|
fn id_string(id: Option<&Url>) -> Result<String, Error> {
|
||||||
id.map(|s| s.to_string()).ok_or(MyError::MissingId)
|
id.map(|s| s.to_string())
|
||||||
|
.ok_or(ErrorKind::MissingId)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn single_object(o: &OneOrMany<AnyBase>) -> Result<&AnyBase, MyError> {
|
fn single_object(o: &OneOrMany<AnyBase>) -> Result<&AnyBase, Error> {
|
||||||
o.as_one().ok_or(MyError::ObjectCount)
|
o.as_one().ok_or(ErrorKind::ObjectCount).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_accept(config: &Config, input: AcceptedActivities) -> Result<(), MyError> {
|
async fn handle_accept(config: &Config, input: AcceptedActivities) -> Result<(), Error> {
|
||||||
let base = single_object(input.object())?.clone();
|
let base = single_object(input.object())?.clone();
|
||||||
let follow = if let Some(follow) = activity::Follow::from_any_base(base)? {
|
let follow = if let Some(follow) = activity::Follow::from_any_base(base)? {
|
||||||
follow
|
follow
|
||||||
} else {
|
} else {
|
||||||
return Err(MyError::Kind(
|
return Err(ErrorKind::Kind(kind_str(single_object(input.object())?)?.to_owned()).into());
|
||||||
kind_str(single_object(input.object())?)?.to_owned(),
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
|
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
|
||||||
return Err(MyError::WrongActor(id_string(
|
return Err(ErrorKind::WrongActor(id_string(follow.actor()?.as_single_id())?).into());
|
||||||
follow.actor()?.as_single_id(),
|
|
||||||
)?));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -118,20 +120,16 @@ async fn handle_reject(
|
||||||
jobs: &JobServer,
|
jobs: &JobServer,
|
||||||
input: AcceptedActivities,
|
input: AcceptedActivities,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
let base = single_object(input.object())?.clone();
|
let base = single_object(input.object())?.clone();
|
||||||
let follow = if let Some(follow) = activity::Follow::from_any_base(base)? {
|
let follow = if let Some(follow) = activity::Follow::from_any_base(base)? {
|
||||||
follow
|
follow
|
||||||
} else {
|
} else {
|
||||||
return Err(MyError::Kind(
|
return Err(ErrorKind::Kind(kind_str(single_object(input.object())?)?.to_owned()).into());
|
||||||
kind_str(single_object(input.object())?)?.to_owned(),
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
|
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
|
||||||
return Err(MyError::WrongActor(id_string(
|
return Err(ErrorKind::WrongActor(id_string(follow.actor()?.as_single_id())?).into());
|
||||||
follow.actor()?.as_single_id(),
|
|
||||||
)?));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs.queue(Reject(actor))?;
|
jobs.queue(Reject(actor))?;
|
||||||
|
@ -145,26 +143,26 @@ async fn handle_undo(
|
||||||
input: AcceptedActivities,
|
input: AcceptedActivities,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
is_listener: bool,
|
is_listener: bool,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
let any_base = single_object(input.object())?.clone();
|
let any_base = single_object(input.object())?.clone();
|
||||||
let undone_object =
|
let undone_object =
|
||||||
AcceptedUndoObjects::from_any_base(any_base)?.ok_or(MyError::ObjectFormat)?;
|
AcceptedUndoObjects::from_any_base(any_base)?.ok_or(ErrorKind::ObjectFormat)?;
|
||||||
|
|
||||||
if !undone_object.is_kind(&UndoTypes::Follow) {
|
if !undone_object.is_kind(&UndoTypes::Follow) {
|
||||||
if is_listener {
|
if is_listener {
|
||||||
jobs.queue(Forward::new(input, actor))?;
|
jobs.queue(Forward::new(input, actor))?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
return Err(MyError::NotSubscribed(actor.id.to_string()));
|
return Err(ErrorKind::NotSubscribed(actor.id.to_string()).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let my_id: Url = config.generate_url(UrlKind::Actor);
|
let my_id: Url = config.generate_url(UrlKind::Actor);
|
||||||
|
|
||||||
if !undone_object.object_is(&my_id) && !undone_object.object_is(&public()) {
|
if !undone_object.object_is(&my_id) && !undone_object.object_is(&public()) {
|
||||||
return Err(MyError::WrongActor(id_string(
|
return Err(
|
||||||
undone_object.object().as_single_id(),
|
ErrorKind::WrongActor(id_string(undone_object.object().as_single_id())?).into(),
|
||||||
)?));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_listener {
|
if !is_listener {
|
||||||
|
@ -179,7 +177,7 @@ async fn handle_forward(
|
||||||
jobs: &JobServer,
|
jobs: &JobServer,
|
||||||
input: AcceptedActivities,
|
input: AcceptedActivities,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
jobs.queue(Forward::new(input, actor))?;
|
jobs.queue(Forward::new(input, actor))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -190,11 +188,11 @@ async fn handle_announce(
|
||||||
jobs: &JobServer,
|
jobs: &JobServer,
|
||||||
input: AcceptedActivities,
|
input: AcceptedActivities,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
let object_id = input.object().as_single_id().ok_or(MyError::MissingId)?;
|
let object_id = input.object().as_single_id().ok_or(ErrorKind::MissingId)?;
|
||||||
|
|
||||||
if state.is_cached(object_id).await {
|
if state.is_cached(object_id).await {
|
||||||
return Err(MyError::Duplicate);
|
return Err(ErrorKind::Duplicate.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs.queue(Announce::new(object_id.to_owned(), actor))?;
|
jobs.queue(Announce::new(object_id.to_owned(), actor))?;
|
||||||
|
@ -207,13 +205,11 @@ async fn handle_follow(
|
||||||
jobs: &JobServer,
|
jobs: &JobServer,
|
||||||
input: AcceptedActivities,
|
input: AcceptedActivities,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
) -> Result<(), MyError> {
|
) -> Result<(), Error> {
|
||||||
let my_id: Url = config.generate_url(UrlKind::Actor);
|
let my_id: Url = config.generate_url(UrlKind::Actor);
|
||||||
|
|
||||||
if !input.object_is(&my_id) && !input.object_is(&public()) {
|
if !input.object_is(&my_id) && !input.object_is(&public()) {
|
||||||
return Err(MyError::WrongActor(id_string(
|
return Err(ErrorKind::WrongActor(id_string(input.object().as_single_id())?).into());
|
||||||
input.object().as_single_id(),
|
|
||||||
)?));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs.queue(Follow::new(input, actor))?;
|
jobs.queue(Follow::new(input, actor))?;
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
use crate::{config::Config, data::State, error::MyError};
|
use crate::{
|
||||||
|
config::Config,
|
||||||
|
data::State,
|
||||||
|
error::{Error, ErrorKind},
|
||||||
|
};
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use log::error;
|
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Index")]
|
||||||
pub(crate) async fn route(
|
pub(crate) async fn route(
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
) -> Result<HttpResponse, MyError> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let mut nodes = state.node_cache().nodes().await?;
|
let mut nodes = state.node_cache().nodes().await?;
|
||||||
nodes.shuffle(&mut thread_rng());
|
nodes.shuffle(&mut thread_rng());
|
||||||
let mut buf = BufWriter::new(Vec::new());
|
let mut buf = BufWriter::new(Vec::new());
|
||||||
|
@ -15,7 +20,7 @@ pub(crate) async fn route(
|
||||||
crate::templates::index(&mut buf, &nodes, &config)?;
|
crate::templates::index(&mut buf, &nodes, &config)?;
|
||||||
let buf = buf.into_inner().map_err(|e| {
|
let buf = buf.into_inner().map_err(|e| {
|
||||||
error!("Error rendering template, {}", e.error());
|
error!("Error rendering template, {}", e.error());
|
||||||
MyError::FlushBuffer
|
ErrorKind::FlushBuffer
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().content_type("text/html").body(buf))
|
Ok(HttpResponse::Ok().content_type("text/html").body(buf))
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
use crate::{data::MediaCache, error::MyError, requests::Requests};
|
use crate::{data::MediaCache, error::Error, requests::Requests};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
http::header::{CacheControl, CacheDirective},
|
http::header::{CacheControl, CacheDirective},
|
||||||
web, HttpResponse,
|
web, HttpResponse,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Media")]
|
||||||
pub(crate) async fn route(
|
pub(crate) async fn route(
|
||||||
media: web::Data<MediaCache>,
|
media: web::Data<MediaCache>,
|
||||||
requests: web::Data<Requests>,
|
requests: web::Data<Requests>,
|
||||||
uuid: web::Path<Uuid>,
|
uuid: web::Path<Uuid>,
|
||||||
) -> Result<HttpResponse, MyError> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let uuid = uuid.into_inner();
|
let uuid = uuid.into_inner();
|
||||||
|
|
||||||
if let Some((content_type, bytes)) = media.get_bytes(uuid).await? {
|
if let Some((content_type, bytes)) = media.get_bytes(uuid).await? {
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
use actix_web::{web, Responder};
|
use actix_web::{web, Responder};
|
||||||
use actix_webfinger::Link;
|
use actix_webfinger::Link;
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Well Known NodeInfo")]
|
||||||
pub(crate) async fn well_known(config: web::Data<Config>) -> impl Responder {
|
pub(crate) async fn well_known(config: web::Data<Config>) -> impl Responder {
|
||||||
web::Json(Links {
|
web::Json(Links {
|
||||||
links: vec![Link {
|
links: vec![Link {
|
||||||
|
@ -22,6 +23,7 @@ struct Links {
|
||||||
links: Vec<Link>,
|
links: Vec<Link>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "NodeInfo")]
|
||||||
pub(crate) async fn route(
|
pub(crate) async fn route(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use actix_web::{
|
||||||
web, HttpResponse,
|
web, HttpResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "Statistics")]
|
||||||
pub(crate) async fn route(filename: web::Path<String>) -> HttpResponse {
|
pub(crate) async fn route(filename: web::Path<String>) -> HttpResponse {
|
||||||
if let Some(data) = StaticFile::get(&filename.into_inner()) {
|
if let Some(data) = StaticFile::get(&filename.into_inner()) {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
|
Loading…
Add table
Reference in a new issue