mirror of
https://git.asonix.dog/asonix/relay.git
synced 2024-11-22 01:21:06 +00:00
Update to latest activitystreams
This commit is contained in:
parent
6b0d3298cc
commit
8893895c71
27 changed files with 338 additions and 264 deletions
27
Cargo.lock
generated
27
Cargo.lock
generated
|
@ -4,17 +4,16 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activitystreams"
|
name = "activitystreams"
|
||||||
version = "0.7.0-alpha.14"
|
version = "0.7.0-alpha.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bcc3fbb392890a1942b1e5cca76cba93c8ed24b5ff50004cc3289afaab3f92c"
|
checksum = "31bca51dcfddda6551570371a1c065528050eea2151a29897ac2dad67b5624cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitystreams-kinds",
|
"activitystreams-kinds",
|
||||||
"chrono",
|
"iri-string",
|
||||||
"mime",
|
"mime",
|
||||||
"serde 1.0.133",
|
"serde 1.0.133",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"time 0.3.5",
|
||||||
"url",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -30,12 +29,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activitystreams-kinds"
|
name = "activitystreams-kinds"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0784e99afd032199d3ed70cefb8eb3a8d1aef15f7f2c4e68d033c4e12bb6079e"
|
checksum = "4a762e3441050b51cd0695c1413735cd04195950f50dba8f5da5d7201628fcfc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"iri-string",
|
||||||
"serde 1.0.133",
|
"serde 1.0.133",
|
||||||
"url",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -318,7 +317,6 @@ dependencies = [
|
||||||
"awc",
|
"awc",
|
||||||
"background-jobs",
|
"background-jobs",
|
||||||
"base64",
|
"base64",
|
||||||
"chrono",
|
|
||||||
"config",
|
"config",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
@ -339,6 +337,7 @@ dependencies = [
|
||||||
"sled",
|
"sled",
|
||||||
"structopt",
|
"structopt",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"time 0.3.5",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-actix-web",
|
"tracing-actix-web",
|
||||||
|
@ -1256,6 +1255,15 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iri-string"
|
||||||
|
version = "0.5.0-beta.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08b57553d1be311c9e979117451952d55f1b33366abe5a07f07899dc2f11a4d9"
|
||||||
|
dependencies = [
|
||||||
|
"serde 1.0.133",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
|
@ -3045,7 +3053,6 @@ dependencies = [
|
||||||
"idna",
|
"idna",
|
||||||
"matches",
|
"matches",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde 1.0.133",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -21,13 +21,12 @@ anyhow = "1.0"
|
||||||
actix-rt = "2.0.2"
|
actix-rt = "2.0.2"
|
||||||
actix-web = { version = "4.0.0-beta.7", default-features = false }
|
actix-web = { version = "4.0.0-beta.7", default-features = false }
|
||||||
actix-webfinger = "0.4.0-beta.5"
|
actix-webfinger = "0.4.0-beta.5"
|
||||||
activitystreams = "0.7.0-alpha.10"
|
activitystreams = "0.7.0-alpha.16"
|
||||||
activitystreams-ext = "0.1.0-alpha.2"
|
activitystreams-ext = "0.1.0-alpha.2"
|
||||||
ammonia = "3.1.0"
|
ammonia = "3.1.0"
|
||||||
async-rwlock = "1.3.0"
|
async-rwlock = "1.3.0"
|
||||||
awc = { version = "3.0.0-beta.6", default-features = false, features = ["rustls"] }
|
awc = { version = "3.0.0-beta.6", default-features = false, features = ["rustls"] }
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
chrono = "0.4.19"
|
|
||||||
config = "0.11.0"
|
config = "0.11.0"
|
||||||
console-subscriber = "0.1"
|
console-subscriber = "0.1"
|
||||||
dashmap = "5.0.0"
|
dashmap = "5.0.0"
|
||||||
|
@ -46,6 +45,7 @@ sha2 = "0.10"
|
||||||
sled = "0.34.6"
|
sled = "0.34.6"
|
||||||
structopt = "0.3.12"
|
structopt = "0.3.12"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
time = "0.3.5"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-awc = { version = "0.1.0-beta.10" }
|
tracing-awc = { version = "0.1.0-beta.10" }
|
||||||
tracing-error = "0.2"
|
tracing-error = "0.2"
|
||||||
|
|
|
@ -2,15 +2,15 @@ use activitystreams::{
|
||||||
activity::ActorAndObject,
|
activity::ActorAndObject,
|
||||||
actor::{Actor, ApActor},
|
actor::{Actor, ApActor},
|
||||||
unparsed::UnparsedMutExt,
|
unparsed::UnparsedMutExt,
|
||||||
url::Url,
|
iri_string::types::IriString,
|
||||||
};
|
};
|
||||||
use activitystreams_ext::{Ext1, UnparsedExtension};
|
use activitystreams_ext::{Ext1, UnparsedExtension};
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PublicKeyInner {
|
pub struct PublicKeyInner {
|
||||||
pub id: Url,
|
pub id: IriString,
|
||||||
pub owner: Url,
|
pub owner: IriString,
|
||||||
pub public_key_pem: String,
|
pub public_key_pem: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,13 @@ use crate::{
|
||||||
middleware::MyVerify,
|
middleware::MyVerify,
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
};
|
};
|
||||||
use activitystreams::{uri, url::Url};
|
use activitystreams::{
|
||||||
|
iri,
|
||||||
|
iri_string::{
|
||||||
|
resolve::FixedBaseResolver,
|
||||||
|
types::{IriAbsoluteString, IriFragmentStr, IriRelativeStr, IriString},
|
||||||
|
},
|
||||||
|
};
|
||||||
use config::Environment;
|
use config::Environment;
|
||||||
use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature};
|
use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
@ -22,8 +28,8 @@ pub(crate) struct ParsedConfig {
|
||||||
https: bool,
|
https: bool,
|
||||||
publish_blocks: bool,
|
publish_blocks: bool,
|
||||||
sled_path: PathBuf,
|
sled_path: PathBuf,
|
||||||
source_repo: Url,
|
source_repo: IriString,
|
||||||
opentelemetry_url: Option<Url>,
|
opentelemetry_url: Option<IriString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -35,12 +41,13 @@ pub struct Config {
|
||||||
restricted_mode: bool,
|
restricted_mode: bool,
|
||||||
validate_signatures: bool,
|
validate_signatures: bool,
|
||||||
publish_blocks: bool,
|
publish_blocks: bool,
|
||||||
base_uri: Url,
|
base_uri: IriAbsoluteString,
|
||||||
sled_path: PathBuf,
|
sled_path: PathBuf,
|
||||||
source_repo: Url,
|
source_repo: IriString,
|
||||||
opentelemetry_url: Option<Url>,
|
opentelemetry_url: Option<IriString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum UrlKind {
|
pub enum UrlKind {
|
||||||
Activity,
|
Activity,
|
||||||
Actor,
|
Actor,
|
||||||
|
@ -95,7 +102,7 @@ impl Config {
|
||||||
let config: ParsedConfig = config.try_into()?;
|
let config: ParsedConfig = config.try_into()?;
|
||||||
|
|
||||||
let scheme = if config.https { "https" } else { "http" };
|
let scheme = if config.https { "https" } else { "http" };
|
||||||
let base_uri = uri!(format!("{}://{}", scheme, config.hostname));
|
let base_uri = iri!(format!("{}://{}", scheme, config.hostname)).into_absolute();
|
||||||
|
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
hostname: config.hostname,
|
hostname: config.hostname,
|
||||||
|
@ -210,33 +217,50 @@ impl Config {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn source_code(&self) -> &Url {
|
pub(crate) fn source_code(&self) -> &IriString {
|
||||||
&self.source_repo
|
&self.source_repo
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn opentelemetry_url(&self) -> Option<&Url> {
|
pub(crate) fn opentelemetry_url(&self) -> Option<&IriString> {
|
||||||
self.opentelemetry_url.as_ref()
|
self.opentelemetry_url.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generate_url(&self, kind: UrlKind) -> Url {
|
pub(crate) fn generate_url(&self, kind: UrlKind) -> IriString {
|
||||||
let mut url = self.base_uri.clone();
|
self.do_generate_url(kind).expect("Generated valid IRI")
|
||||||
|
}
|
||||||
|
|
||||||
match kind {
|
#[tracing::instrument(fields(base_uri = tracing::field::debug(&self.base_uri), kind = tracing::field::debug(&kind)))]
|
||||||
UrlKind::Activity => url.set_path(&format!("activity/{}", Uuid::new_v4())),
|
fn do_generate_url(&self, kind: UrlKind) -> Result<IriString, Error> {
|
||||||
UrlKind::Actor => url.set_path("actor"),
|
let iri = match kind {
|
||||||
UrlKind::Followers => url.set_path("followers"),
|
UrlKind::Activity => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
UrlKind::Following => url.set_path("following"),
|
.resolve(IriRelativeStr::new(&format!("activity/{}", Uuid::new_v4()))?.as_ref())?,
|
||||||
UrlKind::Inbox => url.set_path("inbox"),
|
UrlKind::Actor => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
UrlKind::Index => (),
|
.resolve(IriRelativeStr::new("actor")?.as_ref())?,
|
||||||
|
UrlKind::Followers => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
|
.resolve(IriRelativeStr::new("followers")?.as_ref())?,
|
||||||
|
UrlKind::Following => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
|
.resolve(IriRelativeStr::new("following")?.as_ref())?,
|
||||||
|
UrlKind::Inbox => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
|
.resolve(IriRelativeStr::new("inbox")?.as_ref())?,
|
||||||
|
UrlKind::Index => self.base_uri.clone().into(),
|
||||||
UrlKind::MainKey => {
|
UrlKind::MainKey => {
|
||||||
url.set_path("actor");
|
let actor = IriRelativeStr::new("actor")?;
|
||||||
url.set_fragment(Some("main-key"));
|
let fragment = IriFragmentStr::new("main-key")?;
|
||||||
|
|
||||||
|
let mut resolved =
|
||||||
|
FixedBaseResolver::new(self.base_uri.as_ref()).resolve(actor.as_ref())?;
|
||||||
|
|
||||||
|
resolved.set_fragment(Some(fragment));
|
||||||
|
resolved
|
||||||
}
|
}
|
||||||
UrlKind::Media(uuid) => url.set_path(&format!("media/{}", uuid)),
|
UrlKind::Media(uuid) => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
UrlKind::NodeInfo => url.set_path("nodeinfo/2.0.json"),
|
.resolve(IriRelativeStr::new(&format!("media/{}", uuid))?.as_ref())?,
|
||||||
UrlKind::Outbox => url.set_path("outbox"),
|
UrlKind::NodeInfo => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
|
.resolve(IriRelativeStr::new("nodeinfo/2.0.json")?.as_ref())?,
|
||||||
|
UrlKind::Outbox => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
|
.resolve(IriRelativeStr::new("outbox")?.as_ref())?,
|
||||||
};
|
};
|
||||||
|
|
||||||
url
|
Ok(iri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
};
|
};
|
||||||
use activitystreams::{prelude::*, url::Url};
|
use activitystreams::{iri_string::types::IriString, prelude::*};
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
const REFETCH_DURATION: Duration = Duration::from_secs(60 * 30);
|
const REFETCH_DURATION: Duration = Duration::from_secs(60 * 30);
|
||||||
|
@ -40,7 +40,7 @@ impl ActorCache {
|
||||||
#[tracing::instrument(name = "Get Actor", fields(id = id.to_string().as_str(), requests))]
|
#[tracing::instrument(name = "Get Actor", fields(id = id.to_string().as_str(), requests))]
|
||||||
pub(crate) async fn get(
|
pub(crate) async fn get(
|
||||||
&self,
|
&self,
|
||||||
id: &Url,
|
id: &IriString,
|
||||||
requests: &Requests,
|
requests: &Requests,
|
||||||
) -> Result<MaybeCached<Actor>, Error> {
|
) -> Result<MaybeCached<Actor>, Error> {
|
||||||
if let Some(actor) = self.db.actor(id.clone()).await? {
|
if let Some(actor) = self.db.actor(id.clone()).await? {
|
||||||
|
@ -66,12 +66,16 @@ impl ActorCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Fetch remote actor", fields(id = id.to_string().as_str(), requests))]
|
#[tracing::instrument(name = "Fetch remote actor", fields(id = id.to_string().as_str(), requests))]
|
||||||
pub(crate) async fn get_no_cache(&self, id: &Url, requests: &Requests) -> Result<Actor, Error> {
|
pub(crate) async fn get_no_cache(
|
||||||
|
&self,
|
||||||
|
id: &IriString,
|
||||||
|
requests: &Requests,
|
||||||
|
) -> Result<Actor, Error> {
|
||||||
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(ErrorKind::MissingDomain)?;
|
let input_authority = id.authority_components().ok_or(ErrorKind::MissingDomain)?;
|
||||||
let accepted_actor_id = accepted_actor
|
let accepted_actor_id = accepted_actor
|
||||||
.id(input_domain)?
|
.id(input_authority.host(), input_authority.port())?
|
||||||
.ok_or(ErrorKind::MissingId)?;
|
.ok_or(ErrorKind::MissingId)?;
|
||||||
|
|
||||||
let inbox = get_inbox(&accepted_actor)?.clone();
|
let inbox = get_inbox(&accepted_actor)?.clone();
|
||||||
|
@ -90,7 +94,7 @@ impl ActorCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inbox(actor: &AcceptedActors) -> Result<&Url, Error> {
|
fn get_inbox(actor: &AcceptedActors) -> Result<&IriString, Error> {
|
||||||
Ok(actor
|
Ok(actor
|
||||||
.endpoints()?
|
.endpoints()?
|
||||||
.and_then(|e| e.shared_inbox)
|
.and_then(|e| e.shared_inbox)
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
db::{Db, MediaMeta},
|
db::{Db, MediaMeta},
|
||||||
error::Error,
|
error::Error,
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::web::Bytes;
|
use actix_web::web::Bytes;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -20,12 +20,12 @@ impl MediaCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Get media uuid", fields(url = url.to_string().as_str()))]
|
#[tracing::instrument(name = "Get media uuid", fields(url = url.to_string().as_str()))]
|
||||||
pub(crate) async fn get_uuid(&self, url: Url) -> Result<Option<Uuid>, Error> {
|
pub(crate) async fn get_uuid(&self, url: IriString) -> Result<Option<Uuid>, Error> {
|
||||||
self.db.media_id(url).await
|
self.db.media_id(url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Get media url")]
|
#[tracing::instrument(name = "Get media url")]
|
||||||
pub(crate) async fn get_url(&self, uuid: Uuid) -> Result<Option<Url>, Error> {
|
pub(crate) async fn get_url(&self, uuid: Uuid) -> Result<Option<IriString>, Error> {
|
||||||
self.db.media_url(uuid).await
|
self.db.media_url(uuid).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ impl MediaCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Store media url", fields(url = url.to_string().as_str()))]
|
#[tracing::instrument(name = "Store media url", fields(url = url.to_string().as_str()))]
|
||||||
pub(crate) async fn store_url(&self, url: Url) -> Result<Uuid, Error> {
|
pub(crate) async fn store_url(&self, url: IriString) -> 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?;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{Contact, Db, Info, Instance},
|
db::{Contact, Db, Info, Instance},
|
||||||
error::Error,
|
error::{Error, ErrorKind},
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::{iri, iri_string::types::IriString};
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -12,7 +12,7 @@ pub struct NodeCache {
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub(crate) base: Url,
|
pub(crate) base: IriString,
|
||||||
pub(crate) info: Option<Info>,
|
pub(crate) info: Option<Info>,
|
||||||
pub(crate) instance: Option<Instance>,
|
pub(crate) instance: Option<Instance>,
|
||||||
pub(crate) contact: Option<Contact>,
|
pub(crate) contact: Option<Contact>,
|
||||||
|
@ -50,18 +50,15 @@ impl NodeCache {
|
||||||
let instance = instances.get(&actor_id).cloned();
|
let instance = instances.get(&actor_id).cloned();
|
||||||
let contact = contacts.get(&actor_id).cloned();
|
let contact = contacts.get(&actor_id).cloned();
|
||||||
|
|
||||||
Node::new(actor_id)
|
Node::new(actor_id).map(|node| node.info(info).instance(instance).contact(contact))
|
||||||
.info(info)
|
|
||||||
.instance(instance)
|
|
||||||
.contact(contact)
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<Result<Vec<Node>, Error>>()?;
|
||||||
|
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Is NodeInfo Outdated", fields(actor_id = actor_id.to_string().as_str()))]
|
#[tracing::instrument(name = "Is NodeInfo Outdated", fields(actor_id = actor_id.to_string().as_str()))]
|
||||||
pub(crate) async fn is_nodeinfo_outdated(&self, actor_id: Url) -> bool {
|
pub(crate) async fn is_nodeinfo_outdated(&self, actor_id: IriString) -> bool {
|
||||||
self.db
|
self.db
|
||||||
.info(actor_id)
|
.info(actor_id)
|
||||||
.await
|
.await
|
||||||
|
@ -70,7 +67,7 @@ impl NodeCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Is Contact Outdated", fields(actor_id = actor_id.to_string().as_str()))]
|
#[tracing::instrument(name = "Is Contact Outdated", fields(actor_id = actor_id.to_string().as_str()))]
|
||||||
pub(crate) async fn is_contact_outdated(&self, actor_id: Url) -> bool {
|
pub(crate) async fn is_contact_outdated(&self, actor_id: IriString) -> bool {
|
||||||
self.db
|
self.db
|
||||||
.contact(actor_id)
|
.contact(actor_id)
|
||||||
.await
|
.await
|
||||||
|
@ -79,7 +76,7 @@ impl NodeCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Is Instance Outdated", fields(actor_id = actor_id.to_string().as_str()))]
|
#[tracing::instrument(name = "Is Instance Outdated", fields(actor_id = actor_id.to_string().as_str()))]
|
||||||
pub(crate) async fn is_instance_outdated(&self, actor_id: Url) -> bool {
|
pub(crate) async fn is_instance_outdated(&self, actor_id: IriString) -> bool {
|
||||||
self.db
|
self.db
|
||||||
.instance(actor_id)
|
.instance(actor_id)
|
||||||
.await
|
.await
|
||||||
|
@ -90,7 +87,7 @@ impl NodeCache {
|
||||||
#[tracing::instrument(name = "Save node info", fields(actor_id = actor_id.to_string().as_str(), software, version, reg))]
|
#[tracing::instrument(name = "Save node info", fields(actor_id = actor_id.to_string().as_str(), software, version, reg))]
|
||||||
pub(crate) async fn set_info(
|
pub(crate) async fn set_info(
|
||||||
&self,
|
&self,
|
||||||
actor_id: Url,
|
actor_id: IriString,
|
||||||
software: String,
|
software: String,
|
||||||
version: String,
|
version: String,
|
||||||
reg: bool,
|
reg: bool,
|
||||||
|
@ -121,7 +118,7 @@ impl NodeCache {
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn set_instance(
|
pub(crate) async fn set_instance(
|
||||||
&self,
|
&self,
|
||||||
actor_id: Url,
|
actor_id: IriString,
|
||||||
title: String,
|
title: String,
|
||||||
description: String,
|
description: String,
|
||||||
version: String,
|
version: String,
|
||||||
|
@ -155,11 +152,11 @@ impl NodeCache {
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn set_contact(
|
pub(crate) async fn set_contact(
|
||||||
&self,
|
&self,
|
||||||
actor_id: Url,
|
actor_id: IriString,
|
||||||
username: String,
|
username: String,
|
||||||
display_name: String,
|
display_name: String,
|
||||||
url: Url,
|
url: IriString,
|
||||||
avatar: Url,
|
avatar: IriString,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.db
|
self.db
|
||||||
.save_contact(
|
.save_contact(
|
||||||
|
@ -177,17 +174,18 @@ impl NodeCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
fn new(mut url: Url) -> Self {
|
fn new(url: IriString) -> Result<Self, Error> {
|
||||||
url.set_fragment(None);
|
let authority = url.authority_str().ok_or(ErrorKind::MissingDomain)?;
|
||||||
url.set_query(None);
|
let scheme = url.scheme_str();
|
||||||
url.set_path("");
|
|
||||||
|
|
||||||
Node {
|
let base = iri!(format!("{}://{}", scheme, authority));
|
||||||
base: url,
|
|
||||||
|
Ok(Node {
|
||||||
|
base,
|
||||||
info: None,
|
info: None,
|
||||||
instance: None,
|
instance: None,
|
||||||
contact: None,
|
contact: None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn info(mut self, info: Option<Info>) -> Self {
|
fn info(mut self, info: Option<Info>) -> Self {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
requests::{Breakers, Requests},
|
requests::{Breakers, Requests},
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::web;
|
use actix_web::web;
|
||||||
use async_rwlock::RwLock;
|
use async_rwlock::RwLock;
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
|
@ -18,7 +18,7 @@ use tracing::info;
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub(crate) public_key: RsaPublicKey,
|
pub(crate) public_key: RsaPublicKey,
|
||||||
private_key: RsaPrivateKey,
|
private_key: RsaPrivateKey,
|
||||||
object_cache: Arc<RwLock<LruCache<Url, Url>>>,
|
object_cache: Arc<RwLock<LruCache<IriString, IriString>>>,
|
||||||
node_cache: NodeCache,
|
node_cache: NodeCache,
|
||||||
breakers: Breakers,
|
breakers: Breakers,
|
||||||
pub(crate) db: Db,
|
pub(crate) db: Db,
|
||||||
|
@ -52,22 +52,22 @@ impl State {
|
||||||
name = "Get inboxes for other domains",
|
name = "Get inboxes for other domains",
|
||||||
fields(
|
fields(
|
||||||
existing_inbox = existing_inbox.to_string().as_str(),
|
existing_inbox = existing_inbox.to_string().as_str(),
|
||||||
domain
|
authority
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn inboxes_without(
|
pub(crate) async fn inboxes_without(
|
||||||
&self,
|
&self,
|
||||||
existing_inbox: &Url,
|
existing_inbox: &IriString,
|
||||||
domain: &str,
|
authority: &str,
|
||||||
) -> Result<Vec<Url>, Error> {
|
) -> Result<Vec<IriString>, Error> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.db
|
.db
|
||||||
.inboxes()
|
.inboxes()
|
||||||
.await?
|
.await?
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|inbox| {
|
.filter_map(|inbox| {
|
||||||
if let Some(dom) = inbox.domain() {
|
if let Some(authority_str) = inbox.authority_str() {
|
||||||
if inbox != existing_inbox && dom != domain {
|
if inbox != existing_inbox && authority_str != authority {
|
||||||
return Some(inbox.clone());
|
return Some(inbox.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,11 +77,11 @@ impl State {
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn is_cached(&self, object_id: &Url) -> bool {
|
pub(crate) async fn is_cached(&self, object_id: &IriString) -> bool {
|
||||||
self.object_cache.read().await.contains(object_id)
|
self.object_cache.read().await.contains(object_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn cache(&self, object_id: Url, actor_id: Url) {
|
pub(crate) async fn cache(&self, object_id: IriString, actor_id: IriString) {
|
||||||
self.object_cache.write().await.put(object_id, actor_id);
|
self.object_cache.write().await.put(object_id, actor_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
144
src/db.rs
144
src/db.rs
|
@ -1,5 +1,8 @@
|
||||||
use crate::{config::Config, error::Error};
|
use crate::{
|
||||||
use activitystreams::url::Url;
|
config::Config,
|
||||||
|
error::{Error, ErrorKind},
|
||||||
|
};
|
||||||
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::web::Bytes;
|
use actix_web::web::Bytes;
|
||||||
use rsa::{
|
use rsa::{
|
||||||
pkcs8::{FromPrivateKey, ToPrivateKey},
|
pkcs8::{FromPrivateKey, ToPrivateKey},
|
||||||
|
@ -41,10 +44,10 @@ impl std::fmt::Debug for Inner {
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct Actor {
|
pub struct Actor {
|
||||||
pub(crate) id: Url,
|
pub(crate) id: IriString,
|
||||||
pub(crate) public_key: String,
|
pub(crate) public_key: String,
|
||||||
pub(crate) public_key_id: Url,
|
pub(crate) public_key_id: IriString,
|
||||||
pub(crate) inbox: Url,
|
pub(crate) inbox: IriString,
|
||||||
pub(crate) saved_at: SystemTime,
|
pub(crate) saved_at: SystemTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,8 +91,8 @@ pub struct Instance {
|
||||||
pub struct Contact {
|
pub struct Contact {
|
||||||
pub(crate) username: String,
|
pub(crate) username: String,
|
||||||
pub(crate) display_name: String,
|
pub(crate) display_name: String,
|
||||||
pub(crate) url: Url,
|
pub(crate) url: IriString,
|
||||||
pub(crate) avatar: Url,
|
pub(crate) avatar: IriString,
|
||||||
pub(crate) updated: SystemTime,
|
pub(crate) updated: SystemTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +109,10 @@ impl std::fmt::Debug for Contact {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Inner {
|
impl Inner {
|
||||||
fn connected_by_domain(&self, domains: &[String]) -> impl DoubleEndedIterator<Item = Url> {
|
fn connected_by_domain(
|
||||||
|
&self,
|
||||||
|
domains: &[String],
|
||||||
|
) -> impl DoubleEndedIterator<Item = IriString> {
|
||||||
let reversed: Vec<_> = domains.iter().map(|s| domain_key(s.as_str())).collect();
|
let reversed: Vec<_> = domains.iter().map(|s| domain_key(s.as_str())).collect();
|
||||||
|
|
||||||
self.connected_actor_ids
|
self.connected_actor_ids
|
||||||
|
@ -115,7 +121,7 @@ impl Inner {
|
||||||
.filter_map(|res| res.ok())
|
.filter_map(|res| res.ok())
|
||||||
.filter_map(url_from_ivec)
|
.filter_map(url_from_ivec)
|
||||||
.filter_map(move |url| {
|
.filter_map(move |url| {
|
||||||
let connected_domain = url.domain()?;
|
let connected_domain = url.authority_str()?;
|
||||||
let connected_rdnn = domain_key(connected_domain);
|
let connected_rdnn = domain_key(connected_domain);
|
||||||
|
|
||||||
for rdnn in &reversed {
|
for rdnn in &reversed {
|
||||||
|
@ -136,7 +142,7 @@ impl Inner {
|
||||||
.map(|s| String::from_utf8_lossy(&s).to_string())
|
.map(|s| String::from_utf8_lossy(&s).to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connected(&self) -> impl DoubleEndedIterator<Item = Url> {
|
fn connected(&self) -> impl DoubleEndedIterator<Item = IriString> {
|
||||||
self.connected_actor_ids
|
self.connected_actor_ids
|
||||||
.iter()
|
.iter()
|
||||||
.values()
|
.values()
|
||||||
|
@ -156,7 +162,7 @@ impl Inner {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connected_info(&self) -> impl DoubleEndedIterator<Item = (Url, Info)> + '_ {
|
fn connected_info(&self) -> impl DoubleEndedIterator<Item = (IriString, Info)> + '_ {
|
||||||
self.connected_actor_ids
|
self.connected_actor_ids
|
||||||
.iter()
|
.iter()
|
||||||
.values()
|
.values()
|
||||||
|
@ -170,7 +176,7 @@ impl Inner {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connected_instance(&self) -> impl DoubleEndedIterator<Item = (Url, Instance)> + '_ {
|
fn connected_instance(&self) -> impl DoubleEndedIterator<Item = (IriString, Instance)> + '_ {
|
||||||
self.connected_actor_ids
|
self.connected_actor_ids
|
||||||
.iter()
|
.iter()
|
||||||
.values()
|
.values()
|
||||||
|
@ -184,7 +190,7 @@ impl Inner {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connected_contact(&self) -> impl DoubleEndedIterator<Item = (Url, Contact)> + '_ {
|
fn connected_contact(&self) -> impl DoubleEndedIterator<Item = (IriString, Contact)> + '_ {
|
||||||
self.connected_actor_ids
|
self.connected_actor_ids
|
||||||
.iter()
|
.iter()
|
||||||
.values()
|
.values()
|
||||||
|
@ -198,9 +204,9 @@ impl Inner {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_allowed(&self, domain: &str) -> bool {
|
fn is_allowed(&self, authority: &str) -> bool {
|
||||||
let prefix = domain_prefix(domain);
|
let prefix = domain_prefix(authority);
|
||||||
let reverse_domain = domain_key(domain);
|
let reverse_domain = domain_key(authority);
|
||||||
|
|
||||||
if self.restricted_mode {
|
if self.restricted_mode {
|
||||||
self.allowed_domains
|
self.allowed_domains
|
||||||
|
@ -263,11 +269,11 @@ impl Db {
|
||||||
Ok(t)
|
Ok(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn connected_ids(&self) -> Result<Vec<Url>, Error> {
|
pub(crate) async fn connected_ids(&self) -> Result<Vec<IriString>, 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<(), Error> {
|
pub(crate) async fn save_info(&self, actor_id: IriString, 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)?;
|
||||||
|
|
||||||
|
@ -280,7 +286,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn info(&self, actor_id: Url) -> Result<Option<Info>, Error> {
|
pub(crate) async fn info(&self, actor_id: IriString) -> 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)?;
|
||||||
|
@ -292,14 +298,14 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn connected_info(&self) -> Result<HashMap<Url, Info>, Error> {
|
pub(crate) async fn connected_info(&self) -> Result<HashMap<IriString, Info>, Error> {
|
||||||
self.unblock(|inner| Ok(inner.connected_info().collect()))
|
self.unblock(|inner| Ok(inner.connected_info().collect()))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn save_instance(
|
pub(crate) async fn save_instance(
|
||||||
&self,
|
&self,
|
||||||
actor_id: Url,
|
actor_id: IriString,
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
|
@ -314,7 +320,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn instance(&self, actor_id: Url) -> Result<Option<Instance>, Error> {
|
pub(crate) async fn instance(&self, actor_id: IriString) -> 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)?;
|
||||||
|
@ -326,12 +332,16 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn connected_instance(&self) -> Result<HashMap<Url, Instance>, Error> {
|
pub(crate) async fn connected_instance(&self) -> Result<HashMap<IriString, 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(&self, actor_id: Url, contact: Contact) -> Result<(), Error> {
|
pub(crate) async fn save_contact(
|
||||||
|
&self,
|
||||||
|
actor_id: IriString,
|
||||||
|
contact: Contact,
|
||||||
|
) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
let vec = serde_json::to_vec(&contact)?;
|
let vec = serde_json::to_vec(&contact)?;
|
||||||
|
|
||||||
|
@ -344,7 +354,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn contact(&self, actor_id: Url) -> Result<Option<Contact>, Error> {
|
pub(crate) async fn contact(&self, actor_id: IriString) -> 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)?;
|
||||||
|
@ -356,12 +366,12 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn connected_contact(&self) -> Result<HashMap<Url, Contact>, Error> {
|
pub(crate) async fn connected_contact(&self) -> Result<HashMap<IriString, 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<(), Error> {
|
pub(crate) async fn save_url(&self, url: IriString, id: Uuid) -> Result<(), Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
inner
|
inner
|
||||||
.media_id_media_url
|
.media_id_media_url
|
||||||
|
@ -393,7 +403,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn media_id(&self, url: Url) -> Result<Option<Uuid>, Error> {
|
pub(crate) async fn media_id(&self, url: IriString) -> 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))
|
||||||
|
@ -404,7 +414,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn media_url(&self, id: Uuid) -> Result<Option<Url>, Error> {
|
pub(crate) async fn media_url(&self, id: Uuid) -> Result<Option<IriString>, 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))
|
||||||
|
@ -442,24 +452,22 @@ impl Db {
|
||||||
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>, Error> {
|
pub(crate) async fn inboxes(&self) -> Result<Vec<IriString>, 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, Error> {
|
pub(crate) async fn is_connected(&self, base_id: IriString) -> Result<bool, Error> {
|
||||||
id.set_path("");
|
let scheme = base_id.scheme_str();
|
||||||
id.set_query(None);
|
let authority = base_id.authority_str().ok_or(ErrorKind::MissingDomain)?;
|
||||||
id.set_fragment(None);
|
let prefix = format!("{}://{}", scheme, authority);
|
||||||
|
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
let connected = inner
|
let connected = inner
|
||||||
.connected_actor_ids
|
.connected_actor_ids
|
||||||
.scan_prefix(id.as_str().as_bytes())
|
.scan_prefix(prefix.as_bytes())
|
||||||
.values()
|
.values()
|
||||||
.filter_map(|res| res.ok())
|
.any(|res| res.is_ok());
|
||||||
.next()
|
|
||||||
.is_some();
|
|
||||||
|
|
||||||
Ok(connected)
|
Ok(connected)
|
||||||
})
|
})
|
||||||
|
@ -468,8 +476,8 @@ 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: IriString,
|
||||||
) -> Result<Option<Url>, Error> {
|
) -> Result<Option<IriString>, 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
|
||||||
|
@ -483,7 +491,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn actor(&self, actor_id: Url) -> Result<Option<Actor>, Error> {
|
pub(crate) async fn actor(&self, actor_id: IriString) -> 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)?;
|
||||||
|
@ -511,7 +519,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn remove_connection(&self, actor_id: Url) -> Result<(), Error> {
|
pub(crate) async fn remove_connection(&self, actor_id: IriString) -> Result<(), Error> {
|
||||||
tracing::debug!("Removing Connection: {}", actor_id);
|
tracing::debug!("Removing Connection: {}", actor_id);
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
inner
|
inner
|
||||||
|
@ -523,7 +531,7 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn add_connection(&self, actor_id: Url) -> Result<(), Error> {
|
pub(crate) async fn add_connection(&self, actor_id: IriString) -> Result<(), Error> {
|
||||||
tracing::debug!("Adding Connection: {}", actor_id);
|
tracing::debug!("Adding Connection: {}", actor_id);
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
inner
|
inner
|
||||||
|
@ -543,11 +551,11 @@ impl Db {
|
||||||
.remove(connected.as_str().as_bytes())?;
|
.remove(connected.as_str().as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for domain in &domains {
|
for authority in &domains {
|
||||||
inner
|
inner
|
||||||
.blocked_domains
|
.blocked_domains
|
||||||
.insert(domain_key(domain), domain.as_bytes())?;
|
.insert(domain_key(authority), authority.as_bytes())?;
|
||||||
inner.allowed_domains.remove(domain_key(domain))?;
|
inner.allowed_domains.remove(domain_key(authority))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -557,8 +565,8 @@ impl Db {
|
||||||
|
|
||||||
pub(crate) async fn remove_blocks(&self, domains: Vec<String>) -> Result<(), Error> {
|
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 authority in &domains {
|
||||||
inner.blocked_domains.remove(domain_key(domain))?;
|
inner.blocked_domains.remove(domain_key(authority))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -568,10 +576,10 @@ impl Db {
|
||||||
|
|
||||||
pub(crate) async fn add_allows(&self, domains: Vec<String>) -> Result<(), Error> {
|
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 authority in &domains {
|
||||||
inner
|
inner
|
||||||
.allowed_domains
|
.allowed_domains
|
||||||
.insert(domain_key(domain), domain.as_bytes())?;
|
.insert(domain_key(authority), authority.as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -589,8 +597,8 @@ impl Db {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for domain in &domains {
|
for authority in &domains {
|
||||||
inner.allowed_domains.remove(domain_key(domain))?;
|
inner.allowed_domains.remove(domain_key(authority))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -598,10 +606,10 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn is_allowed(&self, url: Url) -> Result<bool, Error> {
|
pub(crate) async fn is_allowed(&self, url: IriString) -> Result<bool, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(domain) = url.domain() {
|
if let Some(authority) = url.authority_str() {
|
||||||
Ok(inner.is_allowed(domain))
|
Ok(inner.is_allowed(authority))
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
@ -639,12 +647,12 @@ impl Db {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn domain_key(domain: &str) -> String {
|
fn domain_key(authority: &str) -> String {
|
||||||
domain.split('.').rev().collect::<Vec<_>>().join(".") + "."
|
authority.split('.').rev().collect::<Vec<_>>().join(".") + "."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn domain_prefix(domain: &str) -> String {
|
fn domain_prefix(authority: &str) -> String {
|
||||||
domain
|
authority
|
||||||
.split('.')
|
.split('.')
|
||||||
.rev()
|
.rev()
|
||||||
.take(2)
|
.take(2)
|
||||||
|
@ -653,8 +661,8 @@ fn domain_prefix(domain: &str) -> String {
|
||||||
+ "."
|
+ "."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn url_from_ivec(ivec: sled::IVec) -> Option<Url> {
|
fn url_from_ivec(ivec: sled::IVec) -> Option<IriString> {
|
||||||
String::from_utf8_lossy(&ivec).parse::<Url>().ok()
|
String::from_utf8_lossy(&ivec).parse::<IriString>().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uuid_from_ivec(ivec: sled::IVec) -> Option<Uuid> {
|
fn uuid_from_ivec(ivec: sled::IVec) -> Option<Uuid> {
|
||||||
|
@ -664,14 +672,14 @@ fn uuid_from_ivec(ivec: sled::IVec) -> Option<Uuid> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Db;
|
use super::Db;
|
||||||
use activitystreams::url::Url;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connect_and_verify() {
|
fn connect_and_verify() {
|
||||||
run(|db| async move {
|
run(|db| async move {
|
||||||
let example_actor: Url = "http://example.com/actor".parse().unwrap();
|
let example_actor: IriString = "http://example.com/actor".parse().unwrap();
|
||||||
let example_sub_actor: Url = "http://example.com/users/fake".parse().unwrap();
|
let example_sub_actor: IriString = "http://example.com/users/fake".parse().unwrap();
|
||||||
db.add_connection(example_actor.clone()).await.unwrap();
|
db.add_connection(example_actor.clone()).await.unwrap();
|
||||||
assert!(db.is_connected(example_sub_actor).await.unwrap());
|
assert!(db.is_connected(example_sub_actor).await.unwrap());
|
||||||
})
|
})
|
||||||
|
@ -680,8 +688,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn disconnect_and_verify() {
|
fn disconnect_and_verify() {
|
||||||
run(|db| async move {
|
run(|db| async move {
|
||||||
let example_actor: Url = "http://example.com/actor".parse().unwrap();
|
let example_actor: IriString = "http://example.com/actor".parse().unwrap();
|
||||||
let example_sub_actor: Url = "http://example.com/users/fake".parse().unwrap();
|
let example_sub_actor: IriString = "http://example.com/users/fake".parse().unwrap();
|
||||||
db.add_connection(example_actor.clone()).await.unwrap();
|
db.add_connection(example_actor.clone()).await.unwrap();
|
||||||
assert!(db.is_connected(example_sub_actor.clone()).await.unwrap());
|
assert!(db.is_connected(example_sub_actor.clone()).await.unwrap());
|
||||||
|
|
||||||
|
@ -693,7 +701,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn connected_actor_in_connected_list() {
|
fn connected_actor_in_connected_list() {
|
||||||
run(|db| async move {
|
run(|db| async move {
|
||||||
let example_actor: Url = "http://example.com/actor".parse().unwrap();
|
let example_actor: IriString = "http://example.com/actor".parse().unwrap();
|
||||||
db.add_connection(example_actor.clone()).await.unwrap();
|
db.add_connection(example_actor.clone()).await.unwrap();
|
||||||
|
|
||||||
assert!(db.connected_ids().await.unwrap().contains(&example_actor));
|
assert!(db.connected_ids().await.unwrap().contains(&example_actor));
|
||||||
|
@ -703,7 +711,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn disconnected_actor_not_in_connected_list() {
|
fn disconnected_actor_not_in_connected_list() {
|
||||||
run(|db| async move {
|
run(|db| async move {
|
||||||
let example_actor: Url = "http://example.com/actor".parse().unwrap();
|
let example_actor: IriString = "http://example.com/actor".parse().unwrap();
|
||||||
db.add_connection(example_actor.clone()).await.unwrap();
|
db.add_connection(example_actor.clone()).await.unwrap();
|
||||||
db.remove_connection(example_actor.clone()).await.unwrap();
|
db.remove_connection(example_actor.clone()).await.unwrap();
|
||||||
|
|
||||||
|
|
13
src/error.rs
13
src/error.rs
|
@ -1,4 +1,4 @@
|
||||||
use activitystreams::{error::DomainError, url::ParseError};
|
use activitystreams::checked::CheckError;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
error::{BlockingError, ResponseError},
|
error::{BlockingError, ResponseError},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
|
@ -56,8 +56,11 @@ pub(crate) enum ErrorKind {
|
||||||
#[error("Couldn't parse key, {0}")]
|
#[error("Couldn't parse key, {0}")]
|
||||||
Pkcs8(#[from] rsa::pkcs8::Error),
|
Pkcs8(#[from] rsa::pkcs8::Error),
|
||||||
|
|
||||||
#[error("Couldn't parse URI, {0}")]
|
#[error("Couldn't parse IRI, {0}")]
|
||||||
Uri(#[from] ParseError),
|
ParseIri(#[from] activitystreams::iri_string::validate::Error),
|
||||||
|
|
||||||
|
#[error("Couldn't resolve IRI, {0}")]
|
||||||
|
ResolveIri(#[from] activitystreams::iri_string::resolve::Error),
|
||||||
|
|
||||||
#[error("Couldn't perform IO, {0}")]
|
#[error("Couldn't perform IO, {0}")]
|
||||||
Io(#[from] io::Error),
|
Io(#[from] io::Error),
|
||||||
|
@ -102,7 +105,7 @@ pub(crate) enum ErrorKind {
|
||||||
CpuCount(#[from] std::num::TryFromIntError),
|
CpuCount(#[from] std::num::TryFromIntError),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
HostMismatch(#[from] DomainError),
|
HostMismatch(#[from] CheckError),
|
||||||
|
|
||||||
#[error("Invalid or missing content type")]
|
#[error("Invalid or missing content type")]
|
||||||
ContentType,
|
ContentType,
|
||||||
|
@ -137,7 +140,7 @@ pub(crate) enum ErrorKind {
|
||||||
#[error("Input is missing a 'id' field")]
|
#[error("Input is missing a 'id' field")]
|
||||||
MissingId,
|
MissingId,
|
||||||
|
|
||||||
#[error("Url is missing a domain")]
|
#[error("IriString is missing a domain")]
|
||||||
MissingDomain,
|
MissingDomain,
|
||||||
|
|
||||||
#[error("URI is missing domain field")]
|
#[error("URI is missing domain field")]
|
||||||
|
|
|
@ -7,13 +7,13 @@ use crate::{
|
||||||
DeliverMany, JobState,
|
DeliverMany, JobState,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::Announce as AsAnnounce, url::Url};
|
use activitystreams::{activity::Announce as AsAnnounce, iri_string::types::IriString};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct Announce {
|
pub(crate) struct Announce {
|
||||||
object_id: Url,
|
object_id: IriString,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ impl std::fmt::Debug for Announce {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Announce {
|
impl Announce {
|
||||||
pub fn new(object_id: Url, actor: Actor) -> Self {
|
pub fn new(object_id: IriString, actor: Actor) -> Self {
|
||||||
Announce { object_id, actor }
|
Announce { object_id, actor }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@ impl Announce {
|
||||||
// Generate a type that says "Look at this object"
|
// Generate a type that says "Look at this object"
|
||||||
fn generate_announce(
|
fn generate_announce(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
activity_id: &Url,
|
activity_id: &IriString,
|
||||||
object_id: &Url,
|
object_id: &IriString,
|
||||||
) -> Result<AsAnnounce, Error> {
|
) -> 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());
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Accept as AsAccept, Follow as AsFollow},
|
activity::{Accept as AsAccept, Follow as AsFollow},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
url::Url,
|
iri_string::types::IriString,
|
||||||
};
|
};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
@ -62,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, Error> {
|
fn generate_follow(config: &Config, actor_id: &IriString, my_id: &IriString) -> 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(
|
||||||
|
@ -75,9 +75,9 @@ fn generate_follow(config: &Config, actor_id: &Url, my_id: &Url) -> Result<AsFol
|
||||||
// Generate a type that says "I accept your follow request"
|
// Generate a type that says "I accept your follow request"
|
||||||
fn generate_accept_follow(
|
fn generate_accept_follow(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
actor_id: &Url,
|
actor_id: &IriString,
|
||||||
input_id: &Url,
|
input_id: &IriString,
|
||||||
my_id: &Url,
|
my_id: &IriString,
|
||||||
) -> Result<AsAccept, Error> {
|
) -> 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());
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl Forward {
|
||||||
async fn perform(self, state: JobState) -> Result<(), Error> {
|
async fn perform(self, state: JobState) -> Result<(), Error> {
|
||||||
let object_id = self
|
let object_id = self
|
||||||
.input
|
.input
|
||||||
.object()
|
.object_unchecked()
|
||||||
.as_single_id()
|
.as_single_id()
|
||||||
.ok_or(ErrorKind::MissingId)?;
|
.ok_or(ErrorKind::MissingId)?;
|
||||||
|
|
||||||
|
@ -31,7 +31,8 @@ impl Forward {
|
||||||
|
|
||||||
state
|
state
|
||||||
.job_server
|
.job_server
|
||||||
.queue(DeliverMany::new(inboxes, self.input)?).await?;
|
.queue(DeliverMany::new(inboxes, self.input)?)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@ use crate::{
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Follow as AsFollow, Undo as AsUndo},
|
activity::{Follow as AsFollow, Undo as AsUndo},
|
||||||
context,
|
context,
|
||||||
|
iri_string::types::IriString,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
security,
|
security,
|
||||||
url::Url,
|
|
||||||
};
|
};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
@ -23,16 +23,23 @@ 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>, Error> {
|
async fn get_inboxes(
|
||||||
let domain = object_id.host().ok_or(ErrorKind::Domain)?.to_string();
|
state: &State,
|
||||||
|
actor: &Actor,
|
||||||
|
object_id: &IriString,
|
||||||
|
) -> Result<Vec<IriString>, Error> {
|
||||||
|
let authority = object_id
|
||||||
|
.authority_str()
|
||||||
|
.ok_or(ErrorKind::Domain)?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
state.inboxes_without(&actor.inbox, &domain).await
|
state.inboxes_without(&actor.inbox, &authority).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_activity<T, U, V, Kind>(
|
fn prepare_activity<T, U, V, Kind>(
|
||||||
mut t: T,
|
mut t: T,
|
||||||
id: impl TryInto<Url, Error = U>,
|
id: impl TryInto<IriString, Error = U>,
|
||||||
to: impl TryInto<Url, Error = V>,
|
to: impl TryInto<IriString, Error = V>,
|
||||||
) -> Result<T, Error>
|
) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: ObjectExt<Kind> + BaseExt<Kind>,
|
T: ObjectExt<Kind> + BaseExt<Kind>,
|
||||||
|
@ -45,7 +52,11 @@ 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, Error> {
|
fn generate_undo_follow(
|
||||||
|
config: &Config,
|
||||||
|
actor_id: &IriString,
|
||||||
|
my_id: &IriString,
|
||||||
|
) -> 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));
|
||||||
|
|
|
@ -3,14 +3,14 @@ use crate::{
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
jobs::JobState,
|
jobs::JobState,
|
||||||
};
|
};
|
||||||
use activitystreams::{object::Image, prelude::*, url::Url};
|
use activitystreams::{object::Image, prelude::*, iri_string::types::IriString};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct QueryContact {
|
pub(crate) struct QueryContact {
|
||||||
actor_id: Url,
|
actor_id: IriString,
|
||||||
contact_id: Url,
|
contact_id: IriString,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for QueryContact {
|
impl std::fmt::Debug for QueryContact {
|
||||||
|
@ -23,7 +23,7 @@ impl std::fmt::Debug for QueryContact {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QueryContact {
|
impl QueryContact {
|
||||||
pub(crate) fn new(actor_id: Url, contact_id: Url) -> Self {
|
pub(crate) fn new(actor_id: IriString, contact_id: IriString) -> Self {
|
||||||
QueryContact {
|
QueryContact {
|
||||||
actor_id,
|
actor_id,
|
||||||
contact_id,
|
contact_id,
|
||||||
|
@ -57,7 +57,7 @@ impl QueryContact {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_contact(contact: AcceptedActors) -> Option<(String, String, Url, Url)> {
|
fn to_contact(contact: AcceptedActors) -> Option<(String, String, IriString, IriString)> {
|
||||||
let username = contact.preferred_username()?.to_owned();
|
let username = contact.preferred_username()?.to_owned();
|
||||||
let display_name = contact.name()?.as_one()?.as_xsd_string()?.to_owned();
|
let display_name = contact.name()?.as_one()?.as_xsd_string()?.to_owned();
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::{error::Error, jobs::JobState};
|
use crate::{error::Error, jobs::JobState};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use background_jobs::{ActixJob, Backoff};
|
use background_jobs::{ActixJob, Backoff};
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct Deliver {
|
pub(crate) struct Deliver {
|
||||||
to: Url,
|
to: IriString,
|
||||||
data: serde_json::Value,
|
data: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ impl std::fmt::Debug for Deliver {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deliver {
|
impl Deliver {
|
||||||
pub(crate) fn new<T>(to: Url, data: T) -> Result<Self, Error>
|
pub(crate) fn new<T>(to: IriString, data: T) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
T: serde::ser::Serialize,
|
T: serde::ser::Serialize,
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,13 +2,13 @@ use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
jobs::{Deliver, JobState},
|
jobs::{Deliver, JobState},
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use futures_util::future::LocalBoxFuture;
|
use futures_util::future::LocalBoxFuture;
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct DeliverMany {
|
pub(crate) struct DeliverMany {
|
||||||
to: Vec<Url>,
|
to: Vec<IriString>,
|
||||||
data: serde_json::Value,
|
data: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ impl std::fmt::Debug for DeliverMany {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeliverMany {
|
impl DeliverMany {
|
||||||
pub(crate) fn new<T>(to: Vec<Url>, data: T) -> Result<Self, Error>
|
pub(crate) fn new<T>(to: Vec<IriString>, data: T) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
T: serde::ser::Serialize,
|
T: serde::ser::Serialize,
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::UrlKind,
|
config::UrlKind,
|
||||||
error::Error,
|
error::{Error, ErrorKind},
|
||||||
jobs::{cache_media::CacheMedia, JobState},
|
jobs::{cache_media::CacheMedia, JobState},
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::{iri, iri_string::types::IriString};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct QueryInstance {
|
pub(crate) struct QueryInstance {
|
||||||
actor_id: Url,
|
actor_id: IriString,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for QueryInstance {
|
impl std::fmt::Debug for QueryInstance {
|
||||||
|
@ -21,7 +21,7 @@ impl std::fmt::Debug for QueryInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QueryInstance {
|
impl QueryInstance {
|
||||||
pub(crate) fn new(actor_id: Url) -> Self {
|
pub(crate) fn new(actor_id: IriString) -> Self {
|
||||||
QueryInstance { actor_id }
|
QueryInstance { actor_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,10 +40,12 @@ impl QueryInstance {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut instance_uri = self.actor_id.clone();
|
let authority = self
|
||||||
instance_uri.set_fragment(None);
|
.actor_id
|
||||||
instance_uri.set_query(None);
|
.authority_str()
|
||||||
instance_uri.set_path("api/v1/instance");
|
.ok_or(ErrorKind::MissingDomain)?;
|
||||||
|
let scheme = self.actor_id.scheme_str();
|
||||||
|
let instance_uri = iri!(format!("{}://{}/api/v1/instance", scheme, authority));
|
||||||
|
|
||||||
let instance = state
|
let instance = state
|
||||||
.requests
|
.requests
|
||||||
|
@ -132,6 +134,6 @@ struct Instance {
|
||||||
struct Contact {
|
struct Contact {
|
||||||
username: String,
|
username: String,
|
||||||
display_name: String,
|
display_name: String,
|
||||||
url: Url,
|
url: IriString,
|
||||||
avatar: Url,
|
avatar: IriString,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::{Error, ErrorKind},
|
||||||
jobs::{JobState, QueryContact},
|
jobs::{JobState, QueryContact},
|
||||||
};
|
};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::{iri, iri_string::types::IriString};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
use std::{fmt::Debug, future::Future, pin::Pin};
|
use std::{fmt::Debug, future::Future, pin::Pin};
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct QueryNodeinfo {
|
pub(crate) struct QueryNodeinfo {
|
||||||
actor_id: Url,
|
actor_id: IriString,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for QueryNodeinfo {
|
impl Debug for QueryNodeinfo {
|
||||||
|
@ -20,7 +20,7 @@ impl Debug for QueryNodeinfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QueryNodeinfo {
|
impl QueryNodeinfo {
|
||||||
pub(crate) fn new(actor_id: Url) -> Self {
|
pub(crate) fn new(actor_id: IriString) -> Self {
|
||||||
QueryNodeinfo { actor_id }
|
QueryNodeinfo { actor_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,10 +34,12 @@ impl QueryNodeinfo {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut well_known_uri = self.actor_id.clone();
|
let authority = self
|
||||||
well_known_uri.set_fragment(None);
|
.actor_id
|
||||||
well_known_uri.set_query(None);
|
.authority_str()
|
||||||
well_known_uri.set_path(".well-known/nodeinfo");
|
.ok_or(ErrorKind::MissingDomain)?;
|
||||||
|
let scheme = self.actor_id.scheme_str();
|
||||||
|
let well_known_uri = iri!(format!("{}://{}/.well-known/nodeinfo", scheme, authority));
|
||||||
|
|
||||||
let well_known = state
|
let well_known = state
|
||||||
.requests
|
.requests
|
||||||
|
@ -100,7 +102,7 @@ struct Nodeinfo {
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct Metadata {
|
struct Metadata {
|
||||||
staff_accounts: Option<Vec<Url>>,
|
staff_accounts: Option<Vec<IriString>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
|
@ -202,7 +204,7 @@ impl<'de> serde::de::Deserialize<'de> for SupportedNodeinfo {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Nodeinfo, WellKnown};
|
use super::{Nodeinfo, WellKnown};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::iri_string::types::IriString;
|
||||||
|
|
||||||
const BANANA_DOG: &'static str = r#"{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"https://banana.dog/nodeinfo/2.0"},{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.1","href":"https://banana.dog/nodeinfo/2.1"}]}"#;
|
const BANANA_DOG: &'static str = r#"{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"https://banana.dog/nodeinfo/2.0"},{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.1","href":"https://banana.dog/nodeinfo/2.1"}]}"#;
|
||||||
const ASONIX_DOG: &'static str = r#"{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"https://asonix.dog/nodeinfo/2.0"}]}"#;
|
const ASONIX_DOG: &'static str = r#"{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"https://asonix.dog/nodeinfo/2.0"}]}"#;
|
||||||
|
@ -224,7 +226,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
accounts[0],
|
accounts[0],
|
||||||
"https://soc.hyena.network/users/HyNET"
|
"https://soc.hyena.network/users/HyNET"
|
||||||
.parse::<Url>()
|
.parse::<IriString>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// need this for ructe
|
// need this for ructe
|
||||||
#![allow(clippy::needless_borrow)]
|
#![allow(clippy::needless_borrow)]
|
||||||
|
|
||||||
use activitystreams::url::Url;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{web, App, HttpServer};
|
||||||
use console_subscriber::ConsoleLayer;
|
use console_subscriber::ConsoleLayer;
|
||||||
use opentelemetry::{sdk::Resource, KeyValue};
|
use opentelemetry::{sdk::Resource, KeyValue};
|
||||||
|
@ -34,7 +34,7 @@ use self::{
|
||||||
|
|
||||||
fn init_subscriber(
|
fn init_subscriber(
|
||||||
software_name: &'static str,
|
software_name: &'static str,
|
||||||
opentelemetry_url: Option<&Url>,
|
opentelemetry_url: Option<&IriString>,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
LogTracer::init()?;
|
LogTracer::init()?;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
};
|
};
|
||||||
use activitystreams::{base::BaseExt, uri, url::Url};
|
use activitystreams::{base::BaseExt, iri, iri_string::types::IriString};
|
||||||
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 rsa::{hash::Hash, padding::PaddingScheme, pkcs8::FromPublicKey, PublicKey, RsaPublicKey};
|
use rsa::{hash::Hash, padding::PaddingScheme, pkcs8::FromPublicKey, PublicKey, RsaPublicKey};
|
||||||
|
@ -23,7 +23,7 @@ impl MyVerify {
|
||||||
signature: String,
|
signature: String,
|
||||||
signing_string: String,
|
signing_string: String,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
let public_key_id = uri!(key_id);
|
let public_key_id = iri!(key_id);
|
||||||
|
|
||||||
let actor_id = if let Some(mut actor_id) = self
|
let actor_id = if let Some(mut actor_id) = self
|
||||||
.2
|
.2
|
||||||
|
@ -85,8 +85,8 @@ impl MyVerify {
|
||||||
enum PublicKeyResponse {
|
enum PublicKeyResponse {
|
||||||
PublicKey {
|
PublicKey {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
id: Url,
|
id: IriString,
|
||||||
owner: Url,
|
owner: IriString,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
public_key_pem: String,
|
public_key_pem: String,
|
||||||
},
|
},
|
||||||
|
@ -94,7 +94,7 @@ enum PublicKeyResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PublicKeyResponse {
|
impl PublicKeyResponse {
|
||||||
fn actor_id(&self) -> Option<Url> {
|
fn actor_id(&self) -> Option<IriString> {
|
||||||
match self {
|
match self {
|
||||||
PublicKeyResponse::PublicKey { owner, .. } => Some(owner.clone()),
|
PublicKeyResponse::PublicKey { owner, .. } => Some(owner.clone()),
|
||||||
PublicKeyResponse::Actor(actor) => actor.id_unchecked().cloned(),
|
PublicKeyResponse::Actor(actor) => actor.id_unchecked().cloned(),
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::error::{Error, ErrorKind};
|
use crate::error::{Error, ErrorKind};
|
||||||
use activitystreams::url::Url;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::{http::header::Date, web::Bytes};
|
use actix_web::{http::header::Date, web::Bytes};
|
||||||
use awc::Client;
|
use awc::Client;
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use http_signature_normalization_actix::prelude::*;
|
use http_signature_normalization_actix::prelude::*;
|
||||||
use rsa::{hash::Hash, padding::PaddingScheme, RsaPrivateKey};
|
use rsa::{hash::Hash, padding::PaddingScheme, RsaPrivateKey};
|
||||||
|
@ -16,6 +15,7 @@ use std::{
|
||||||
},
|
},
|
||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
|
use time::OffsetDateTime;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
use tracing_awc::Tracing;
|
use tracing_awc::Tracing;
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@ impl std::fmt::Debug for Breakers {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Breakers {
|
impl Breakers {
|
||||||
fn should_try(&self, url: &Url) -> bool {
|
fn should_try(&self, url: &IriString) -> bool {
|
||||||
if let Some(domain) = url.domain() {
|
if let Some(authority) = url.authority_str() {
|
||||||
if let Some(breaker) = self.inner.get(domain) {
|
if let Some(breaker) = self.inner.get(authority) {
|
||||||
breaker.should_try()
|
breaker.should_try()
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -43,10 +43,10 @@ impl Breakers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fail(&self, url: &Url) {
|
fn fail(&self, url: &IriString) {
|
||||||
if let Some(domain) = url.domain() {
|
if let Some(authority) = url.authority_str() {
|
||||||
let should_write = {
|
let should_write = {
|
||||||
if let Some(mut breaker) = self.inner.get_mut(domain) {
|
if let Some(mut breaker) = self.inner.get_mut(authority) {
|
||||||
breaker.fail();
|
breaker.fail();
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,16 +55,16 @@ impl Breakers {
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_write {
|
if should_write {
|
||||||
let mut breaker = self.inner.entry(domain.to_owned()).or_default();
|
let mut breaker = self.inner.entry(authority.to_owned()).or_default();
|
||||||
breaker.fail();
|
breaker.fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn succeed(&self, url: &Url) {
|
fn succeed(&self, url: &IriString) {
|
||||||
if let Some(domain) = url.domain() {
|
if let Some(authority) = url.authority_str() {
|
||||||
let should_write = {
|
let should_write = {
|
||||||
if let Some(mut breaker) = self.inner.get_mut(domain) {
|
if let Some(mut breaker) = self.inner.get_mut(authority) {
|
||||||
breaker.succeed();
|
breaker.succeed();
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
@ -73,7 +73,7 @@ impl Breakers {
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_write {
|
if should_write {
|
||||||
let mut breaker = self.inner.entry(domain.to_owned()).or_default();
|
let mut breaker = self.inner.entry(authority.to_owned()).or_default();
|
||||||
breaker.succeed();
|
breaker.succeed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,8 +91,8 @@ impl Default for Breakers {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Breaker {
|
struct Breaker {
|
||||||
failures: usize,
|
failures: usize,
|
||||||
last_attempt: DateTime<Utc>,
|
last_attempt: OffsetDateTime,
|
||||||
last_success: DateTime<Utc>,
|
last_success: OffsetDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Breaker {
|
impl Breaker {
|
||||||
|
@ -100,30 +100,30 @@ impl Breaker {
|
||||||
10
|
10
|
||||||
}
|
}
|
||||||
|
|
||||||
fn failure_wait() -> chrono::Duration {
|
fn failure_wait() -> time::Duration {
|
||||||
chrono::Duration::days(1)
|
time::Duration::days(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_try(&self) -> bool {
|
fn should_try(&self) -> bool {
|
||||||
self.failures < Self::failure_threshold()
|
self.failures < Self::failure_threshold()
|
||||||
|| self.last_attempt + Self::failure_wait() < Utc::now()
|
|| self.last_attempt + Self::failure_wait() < OffsetDateTime::now_utc()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fail(&mut self) {
|
fn fail(&mut self) {
|
||||||
self.failures += 1;
|
self.failures += 1;
|
||||||
self.last_attempt = Utc::now();
|
self.last_attempt = OffsetDateTime::now_utc();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn succeed(&mut self) {
|
fn succeed(&mut self) {
|
||||||
self.failures = 0;
|
self.failures = 0;
|
||||||
self.last_attempt = Utc::now();
|
self.last_attempt = OffsetDateTime::now_utc();
|
||||||
self.last_success = Utc::now();
|
self.last_success = OffsetDateTime::now_utc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Breaker {
|
impl Default for Breaker {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let now = Utc::now();
|
let now = OffsetDateTime::now_utc();
|
||||||
|
|
||||||
Breaker {
|
Breaker {
|
||||||
failures: 0,
|
failures: 0,
|
||||||
|
@ -217,7 +217,7 @@ impl Requests {
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
let parsed_url = url.parse::<Url>()?;
|
let parsed_url = url.parse::<IriString>()?;
|
||||||
|
|
||||||
if !self.breakers.should_try(&parsed_url) {
|
if !self.breakers.should_try(&parsed_url) {
|
||||||
return Err(ErrorKind::Breaker.into());
|
return Err(ErrorKind::Breaker.into());
|
||||||
|
@ -274,7 +274,7 @@ impl Requests {
|
||||||
|
|
||||||
#[tracing::instrument(name = "Fetch Bytes")]
|
#[tracing::instrument(name = "Fetch Bytes")]
|
||||||
pub(crate) async fn fetch_bytes(&self, url: &str) -> Result<(String, Bytes), Error> {
|
pub(crate) async fn fetch_bytes(&self, url: &str) -> Result<(String, Bytes), Error> {
|
||||||
let parsed_url = url.parse::<Url>()?;
|
let parsed_url = url.parse::<IriString>()?;
|
||||||
|
|
||||||
if !self.breakers.should_try(&parsed_url) {
|
if !self.breakers.should_try(&parsed_url) {
|
||||||
return Err(ErrorKind::Breaker.into());
|
return Err(ErrorKind::Breaker.into());
|
||||||
|
@ -346,7 +346,7 @@ impl Requests {
|
||||||
"Deliver to Inbox",
|
"Deliver to Inbox",
|
||||||
fields(self, inbox = inbox.to_string().as_str(), item)
|
fields(self, inbox = inbox.to_string().as_str(), item)
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn deliver<T>(&self, inbox: Url, item: &T) -> Result<(), Error>
|
pub(crate) async fn deliver<T>(&self, inbox: IriString, item: &T) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: serde::ser::Serialize + std::fmt::Debug,
|
T: serde::ser::Serialize + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,7 +10,8 @@ use crate::{
|
||||||
routes::accepted,
|
routes::accepted,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity, base::AnyBase, prelude::*, primitives::OneOrMany, public, url::Url,
|
activity, base::AnyBase, iri_string::types::IriString, prelude::*, primitives::OneOrMany,
|
||||||
|
public,
|
||||||
};
|
};
|
||||||
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};
|
||||||
|
@ -79,7 +80,7 @@ pub(crate) async fn route(
|
||||||
fn valid_without_listener(input: &AcceptedActivities) -> Result<bool, Error> {
|
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_unchecked())?.is_kind("Follow")),
|
||||||
_ => Ok(false),
|
_ => Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +91,7 @@ fn kind_str(base: &AnyBase) -> Result<&str, Error> {
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id_string(id: Option<&Url>) -> Result<String, Error> {
|
fn id_string(id: Option<&IriString>) -> Result<String, Error> {
|
||||||
id.map(|s| s.to_string())
|
id.map(|s| s.to_string())
|
||||||
.ok_or(ErrorKind::MissingId)
|
.ok_or(ErrorKind::MissingId)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
|
@ -101,11 +102,14 @@ fn single_object(o: &OneOrMany<AnyBase>) -> Result<&AnyBase, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_accept(config: &Config, input: AcceptedActivities) -> Result<(), Error> {
|
async fn handle_accept(config: &Config, input: AcceptedActivities) -> Result<(), Error> {
|
||||||
let base = single_object(input.object())?.clone();
|
let base = single_object(input.object_unchecked())?.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(ErrorKind::Kind(kind_str(single_object(input.object())?)?.to_owned()).into());
|
return Err(ErrorKind::Kind(
|
||||||
|
kind_str(single_object(input.object_unchecked())?)?.to_owned(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
|
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
|
||||||
|
@ -121,11 +125,14 @@ async fn handle_reject(
|
||||||
input: AcceptedActivities,
|
input: AcceptedActivities,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let base = single_object(input.object())?.clone();
|
let base = single_object(input.object_unchecked())?.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(ErrorKind::Kind(kind_str(single_object(input.object())?)?.to_owned()).into());
|
return Err(ErrorKind::Kind(
|
||||||
|
kind_str(single_object(input.object_unchecked())?)?.to_owned(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
|
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
|
||||||
|
@ -144,7 +151,7 @@ async fn handle_undo(
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
is_listener: bool,
|
is_listener: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let any_base = single_object(input.object())?.clone();
|
let any_base = single_object(input.object_unchecked())?.clone();
|
||||||
let undone_object =
|
let undone_object =
|
||||||
AcceptedUndoObjects::from_any_base(any_base)?.ok_or(ErrorKind::ObjectFormat)?;
|
AcceptedUndoObjects::from_any_base(any_base)?.ok_or(ErrorKind::ObjectFormat)?;
|
||||||
|
|
||||||
|
@ -157,12 +164,13 @@ async fn handle_undo(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let my_id: Url = config.generate_url(UrlKind::Actor);
|
let my_id: IriString = 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(
|
return Err(ErrorKind::WrongActor(id_string(
|
||||||
ErrorKind::WrongActor(id_string(undone_object.object().as_single_id())?).into(),
|
undone_object.object_unchecked().as_single_id(),
|
||||||
);
|
)?)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_listener {
|
if !is_listener {
|
||||||
|
@ -189,13 +197,17 @@ async fn handle_announce(
|
||||||
input: AcceptedActivities,
|
input: AcceptedActivities,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let object_id = input.object().as_single_id().ok_or(ErrorKind::MissingId)?;
|
let object_id = input
|
||||||
|
.object_unchecked()
|
||||||
|
.as_single_id()
|
||||||
|
.ok_or(ErrorKind::MissingId)?;
|
||||||
|
|
||||||
if state.is_cached(object_id).await {
|
if state.is_cached(object_id).await {
|
||||||
return Err(ErrorKind::Duplicate.into());
|
return Err(ErrorKind::Duplicate.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs.queue(Announce::new(object_id.to_owned(), actor)).await?;
|
jobs.queue(Announce::new(object_id.to_owned(), actor))
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -206,10 +218,12 @@ async fn handle_follow(
|
||||||
input: AcceptedActivities,
|
input: AcceptedActivities,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let my_id: Url = config.generate_url(UrlKind::Actor);
|
let my_id: IriString = 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(ErrorKind::WrongActor(id_string(input.object().as_single_id())?).into());
|
return Err(
|
||||||
|
ErrorKind::WrongActor(id_string(input.object_unchecked().as_single_id())?).into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs.queue(Follow::new(input, actor)).await?;
|
jobs.queue(Follow::new(input, actor)).await?;
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub(crate) async fn route(
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|listener| listener.domain())
|
.filter_map(|listener| listener.authority_str())
|
||||||
.map(|s| s.to_owned())
|
.map(|s| s.to_owned())
|
||||||
.collect(),
|
.collect(),
|
||||||
blocks: if config.publish_blocks() {
|
blocks: if config.publish_blocks() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@use crate::db::Contact;
|
@use crate::db::Contact;
|
||||||
@use activitystreams::url::Url;
|
@use activitystreams::iri_string::types::IriString;
|
||||||
|
|
||||||
@(contact: &Contact, base: &Url)
|
@(contact: &Contact, base: &IriString)
|
||||||
|
|
||||||
<div class="admin">
|
<div class="admin">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<p class="display-name"><a href="@contact.url">@contact.display_name</a></p>
|
<p class="display-name"><a href="@contact.url">@contact.display_name</a></p>
|
||||||
<p class="username">
|
<p class="username">
|
||||||
@@@contact.username@if let Some(domain) = base.domain() {@@@domain}
|
@@@contact.username@if let Some(authority) = base.authority_str() {@@@authority}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
@use crate::db::Info;
|
@use crate::db::Info;
|
||||||
@use activitystreams::url::Url;
|
@use activitystreams::iri_string::types::IriString;
|
||||||
|
|
||||||
@(info: &Info, base: &Url)
|
@(info: &Info, base: &IriString)
|
||||||
|
|
||||||
<article class="info">
|
<article class="info">
|
||||||
@if let Some(domain) = base.domain() {
|
@if let Some(authority) = base.authority_str() {
|
||||||
<h4 class="padded"><a href="@base">@domain</a></h4>
|
<h4 class="padded"><a href="@base">@authority</a></h4>
|
||||||
}
|
}
|
||||||
<p class="padded">
|
<p class="padded">
|
||||||
Running @info.software, version @info.version.
|
Running @info.software, version @info.version.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@use crate::{db::{Contact, Instance}, templates::admin};
|
@use crate::{db::{Contact, Instance}, templates::admin};
|
||||||
@use activitystreams::url::Url;
|
@use activitystreams::iri_string::types::IriString;
|
||||||
|
|
||||||
@(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &Url)
|
@(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &IriString)
|
||||||
|
|
||||||
<article class="instance">
|
<article class="instance">
|
||||||
<h4 class="padded"><a href="@base">@instance.title</a></h4>
|
<h4 class="padded"><a href="@base">@instance.title</a></h4>
|
||||||
|
|
Loading…
Reference in a new issue