Compare commits
No commits in common. "36becee07865d64042908de5516d8da96ec3f0fa" and "07963da3ba4affa9045231443018889c42369d47" have entirely different histories.
36becee078
...
07963da3ba
7 changed files with 30 additions and 572 deletions
43
Cargo.lock
generated
43
Cargo.lock
generated
|
@ -28,7 +28,6 @@ dependencies = [
|
||||||
"actix-codec",
|
"actix-codec",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
"actix-tls",
|
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"ahash 0.7.6",
|
"ahash 0.7.6",
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
|
@ -120,24 +119,6 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "actix-tls"
|
|
||||||
version = "3.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297"
|
|
||||||
dependencies = [
|
|
||||||
"actix-codec",
|
|
||||||
"actix-rt",
|
|
||||||
"actix-service",
|
|
||||||
"actix-utils",
|
|
||||||
"futures-core",
|
|
||||||
"log",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio-rustls",
|
|
||||||
"tokio-util",
|
|
||||||
"webpki-roots 0.22.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-utils"
|
name = "actix-utils"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
|
@ -161,7 +142,6 @@ dependencies = [
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-server",
|
"actix-server",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
"actix-tls",
|
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"actix-web-codegen",
|
"actix-web-codegen",
|
||||||
"ahash 0.7.6",
|
"ahash 0.7.6",
|
||||||
|
@ -1741,6 +1721,7 @@ dependencies = [
|
||||||
"locspan",
|
"locspan",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
|
"openssl",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rand",
|
"rand",
|
||||||
"rdf-types",
|
"rdf-types",
|
||||||
|
@ -2516,7 +2497,7 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"webpki-roots 0.24.0",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2844,17 +2825,6 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-rustls"
|
|
||||||
version = "0.23.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
|
||||||
dependencies = [
|
|
||||||
"rustls 0.20.7",
|
|
||||||
"tokio",
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
|
@ -3116,15 +3086,6 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki-roots"
|
|
||||||
version = "0.22.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
|
||||||
dependencies = [
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-rt = "2.7"
|
actix-rt = "2.7"
|
||||||
actix-web = { version = "4", features = ["rustls"] }
|
actix-web = "4"
|
||||||
argon2 = "0.5.1"
|
argon2 = "0.5.1"
|
||||||
async-trait = "0.1.59"
|
async-trait = "0.1.59"
|
||||||
base64 = "0.21"
|
base64 = "0.21"
|
||||||
|
@ -20,6 +20,7 @@ jsonwebtoken = { version = "8", default-features = false }
|
||||||
locspan = "0.7"
|
locspan = "0.7"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
|
openssl = "0.10"
|
||||||
pretty_env_logger = "0.5.0"
|
pretty_env_logger = "0.5.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rdf-types = "0.15.4"
|
rdf-types = "0.15.4"
|
||||||
|
|
|
@ -96,15 +96,13 @@ impl<I: Clone + Eq + Hash + Send + Sync, M: Send, T: Clone + Send> CachedLoader<
|
||||||
) -> Result<RemoteDocument<I, M, T>> {
|
) -> Result<RemoteDocument<I, M, T>> {
|
||||||
const MAX_REDIRECTS: u32 = 8;
|
const MAX_REDIRECTS: u32 = 8;
|
||||||
const ACCEPT_ACTIVITY_PUB: &str =
|
const ACCEPT_ACTIVITY_PUB: &str =
|
||||||
"application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", application/json";
|
"application/activity+json, application/ld+json, application/json";
|
||||||
|
|
||||||
let mut redirect_count = 0;
|
let mut redirect_count = 0;
|
||||||
|
|
||||||
'next_url: loop {
|
'next_url: loop {
|
||||||
if redirect_count > MAX_REDIRECTS {
|
if redirect_count > MAX_REDIRECTS {
|
||||||
return Err(Error::MalformedApub(format!(
|
return Err(Error::NotFound);
|
||||||
"Refusing to follow more than {MAX_REDIRECTS} redirects"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let iri = vocabulary
|
let iri = vocabulary
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
pub mod processor;
|
pub mod processor;
|
||||||
pub mod vocab;
|
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
use json_ld::{
|
use json_ld::{
|
||||||
syntax::{Parse, Value},
|
syntax::{Parse, Value},
|
||||||
Expand, IndexedObject, RemoteDocument,
|
Expand, RemoteDocument,
|
||||||
};
|
};
|
||||||
use locspan::Span;
|
use rdf_types::{IndexVocabulary, IriVocabulary};
|
||||||
use rdf_types::vocabulary::BlankIdIndex;
|
|
||||||
use rdf_types::{vocabulary::IriIndex, IndexVocabulary, Vocabulary};
|
|
||||||
|
|
||||||
use crate::ap::{loader::CachedLoader, vocab::Ids};
|
use crate::ap::loader::CachedLoader;
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
/// Main API for handling ActivityPub ingress, called by [`crate::job::inbox::InboxWorker`].
|
/// Main API for handling ActivityPub ingress, called by [`crate::job::inbox::InboxWorker`].
|
||||||
pub async fn process_document(state: &AppState, raw: &str) -> Result<()> {
|
pub async fn process_document(state: &AppState, raw: &str) -> Result<()> {
|
||||||
let mut vocab: IndexVocabulary = IndexVocabulary::new();
|
let mut vocab: IndexVocabulary = IndexVocabulary::new();
|
||||||
let indices = Ids::populate(&mut vocab);
|
|
||||||
|
|
||||||
let document = Value::parse_str(raw, |span| span)
|
let document = Value::parse_str(raw, |span| span)
|
||||||
.map_err(|e| Error::MalformedApub(format!("Could not parse document: {e}")))?;
|
.map_err(|e| Error::MalformedApub(format!("Could not parse document: {e}")))?;
|
||||||
|
@ -25,54 +22,27 @@ pub async fn process_document(state: &AppState, raw: &str) -> Result<()> {
|
||||||
);
|
);
|
||||||
let mut loader = CachedLoader::new(state.clone());
|
let mut loader = CachedLoader::new(state.clone());
|
||||||
let rd = rd.expand_with(&mut vocab, &mut loader).await.unwrap();
|
let rd = rd.expand_with(&mut vocab, &mut loader).await.unwrap();
|
||||||
let vocab = vocab;
|
|
||||||
|
|
||||||
// this loop will usually only run once (one object per request)
|
// this loop will usually only run once (one object per request)
|
||||||
for object in rd.into_value() {
|
for object in rd.into_value() {
|
||||||
if let Err(e) = process_object(object, &vocab, &indices).await {
|
let id = object
|
||||||
error!("Error in remote document: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn process_object(
|
|
||||||
obj: IndexedObject<IriIndex, BlankIdIndex, Span>,
|
|
||||||
vocab: &impl Vocabulary<Iri = IriIndex>,
|
|
||||||
ids: &Ids,
|
|
||||||
) -> Result<()> {
|
|
||||||
let id = obj
|
|
||||||
.id()
|
.id()
|
||||||
.and_then(|id| id.as_iri())
|
.and_then(|i| i.as_iri())
|
||||||
.and_then(|index| vocab.iri(index))
|
.and_then(|index| vocab.iri(index))
|
||||||
.ok_or_else(apub_err("Object does not have an id"))?;
|
.ok_or(Error::MalformedApub(String::from(
|
||||||
let typ = obj
|
"Document does not have an id",
|
||||||
.types()
|
)))?;
|
||||||
.next()
|
let mut typ = None;
|
||||||
.ok_or_else(apub_err("Object does not have a type"))?
|
for t in object.types() {
|
||||||
.as_iri()
|
typ = Some(t);
|
||||||
.and_then(|index| vocab.iri(index))
|
}
|
||||||
.ok_or_else(apub_err("Object has invalid type"))?;
|
let typ = typ.ok_or(Error::MalformedApub(String::from(
|
||||||
|
"Document does not have a type",
|
||||||
// just for testing: extract and print `object.content`
|
)))?;
|
||||||
// to see if the expansion actually works as expected
|
// just for testing some stuff
|
||||||
let properties = obj
|
let typ = typ.as_iri().and_then(|index| vocab.iri(index)).unwrap();
|
||||||
.as_node()
|
debug!("Object id=\"{id}\" type=\"{typ}\"");
|
||||||
.ok_or_else(apub_err("Object is not a node"))?
|
}
|
||||||
.properties();
|
|
||||||
let object = properties
|
|
||||||
.get_any(&ids.apub.property.object)
|
|
||||||
.and_then(|prop| prop.as_node())
|
|
||||||
.and_then(|node| node.properties().get_any(&ids.apub.property.content))
|
|
||||||
.and_then(|prop| prop.as_value())
|
|
||||||
.and_then(|val| val.as_str())
|
|
||||||
.unwrap();
|
|
||||||
debug!("content = \"{object}\"");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn apub_err(msg: impl Into<String>) -> impl FnOnce() -> Error {
|
|
||||||
move || Error::MalformedApub(msg.into())
|
|
||||||
}
|
|
||||||
|
|
468
src/ap/vocab.rs
468
src/ap/vocab.rs
|
@ -1,468 +0,0 @@
|
||||||
//! An annoyingly huge collection of all known IRIs, from all supported namespaces.
|
|
||||||
//!
|
|
||||||
//! This might be replaced with an entirely custom implementation of [`Vocabulary`]
|
|
||||||
//! in the future because reinitializing the entire AP vocabulary from scratch for
|
|
||||||
//! every single post to the inbox is probably a bit inefficient. I hate my life.
|
|
||||||
|
|
||||||
use rdf_types::{
|
|
||||||
vocabulary::{BlankIdIndex, IriIndex},
|
|
||||||
Subject, VocabularyMut,
|
|
||||||
};
|
|
||||||
use static_iref::iri;
|
|
||||||
|
|
||||||
pub struct Ids {
|
|
||||||
/// IRI identifiers for the base ActivityStreams and ActivityPub
|
|
||||||
/// namespace (`https://www.w3.org/ns/activitystreams#`).
|
|
||||||
pub apub: ApubIds,
|
|
||||||
/// IRI identifiers for Mastodon's extension namespace
|
|
||||||
/// (`http://joinmastodon.org/ns#`).
|
|
||||||
pub toot: TootIds,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Id = Subject<IriIndex, BlankIdIndex>;
|
|
||||||
|
|
||||||
/// Transform
|
|
||||||
/// `key => iri!("...")`
|
|
||||||
/// into
|
|
||||||
/// `key: Id::Iri(vocab.insert(iri!("..."))`
|
|
||||||
/// so the lines don't exceed five trillion characters.
|
|
||||||
macro_rules! populate_ids {
|
|
||||||
($vocab:ident, $($name:ident => $iri:expr),* $(,)?) => {
|
|
||||||
Self {
|
|
||||||
$($name: Id::Iri($vocab.insert($iri)),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ids {
|
|
||||||
pub fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
Self {
|
|
||||||
apub: ApubIds::populate(vocab),
|
|
||||||
toot: TootIds::populate(vocab),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ApubIds {
|
|
||||||
pub object: ApubObjectIds,
|
|
||||||
pub activity: ApubActivityIds,
|
|
||||||
pub link: ApubLinkIds,
|
|
||||||
pub property: ApubPropertyIds,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApubIds {
|
|
||||||
fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
Self {
|
|
||||||
object: ApubObjectIds::populate(vocab),
|
|
||||||
activity: ApubActivityIds::populate(vocab),
|
|
||||||
link: ApubLinkIds::populate(vocab),
|
|
||||||
property: ApubPropertyIds::populate(vocab),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ApubObjectIds {
|
|
||||||
pub activity: Id,
|
|
||||||
pub application: Id,
|
|
||||||
pub article: Id,
|
|
||||||
pub audio: Id,
|
|
||||||
pub collection: Id,
|
|
||||||
pub collection_page: Id,
|
|
||||||
pub relationship: Id,
|
|
||||||
pub document: Id,
|
|
||||||
pub event: Id,
|
|
||||||
pub group: Id,
|
|
||||||
pub image: Id,
|
|
||||||
pub intransitive_activity: Id,
|
|
||||||
pub note: Id,
|
|
||||||
pub object: Id,
|
|
||||||
pub ordered_collection: Id,
|
|
||||||
pub ordered_collection_page: Id,
|
|
||||||
pub organization: Id,
|
|
||||||
pub page: Id,
|
|
||||||
pub person: Id,
|
|
||||||
pub place: Id,
|
|
||||||
pub profile: Id,
|
|
||||||
pub question: Id,
|
|
||||||
pub service: Id,
|
|
||||||
pub tombstone: Id,
|
|
||||||
pub video: Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApubObjectIds {
|
|
||||||
fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
populate_ids! {
|
|
||||||
vocab,
|
|
||||||
|
|
||||||
activity => iri!("https://www.w3.org/ns/activitystreams#Activity"),
|
|
||||||
application => iri!("https://www.w3.org/ns/activitystreams#Application"),
|
|
||||||
article => iri!("https://www.w3.org/ns/activitystreams#Article"),
|
|
||||||
audio => iri!("https://www.w3.org/ns/activitystreams#Audio"),
|
|
||||||
collection => iri!("https://www.w3.org/ns/activitystreams#Collection"),
|
|
||||||
collection_page => iri!("https://www.w3.org/ns/activitystreams#CollectionPage"),
|
|
||||||
relationship => iri!("https://www.w3.org/ns/activitystreams#Relationship"),
|
|
||||||
document => iri!("https://www.w3.org/ns/activitystreams#Document"),
|
|
||||||
event => iri!("https://www.w3.org/ns/activitystreams#Event"),
|
|
||||||
group => iri!("https://www.w3.org/ns/activitystreams#Group"),
|
|
||||||
image => iri!("https://www.w3.org/ns/activitystreams#Image"),
|
|
||||||
intransitive_activity => iri!("https://www.w3.org/ns/activitystreams#IntransitiveActivity"),
|
|
||||||
note => iri!("https://www.w3.org/ns/activitystreams#Note"),
|
|
||||||
object => iri!("https://www.w3.org/ns/activitystreams#Object"),
|
|
||||||
ordered_collection => iri!("https://www.w3.org/ns/activitystreams#OrderedCollection"),
|
|
||||||
ordered_collection_page => iri!("https://www.w3.org/ns/activitystreams#OrderedCollectionPage"),
|
|
||||||
organization => iri!("https://www.w3.org/ns/activitystreams#Organization"),
|
|
||||||
page => iri!("https://www.w3.org/ns/activitystreams#Page"),
|
|
||||||
person => iri!("https://www.w3.org/ns/activitystreams#Person"),
|
|
||||||
place => iri!("https://www.w3.org/ns/activitystreams#Place"),
|
|
||||||
profile => iri!("https://www.w3.org/ns/activitystreams#Profile"),
|
|
||||||
question => iri!("https://www.w3.org/ns/activitystreams#Question"),
|
|
||||||
service => iri!("https://www.w3.org/ns/activitystreams#Service"),
|
|
||||||
tombstone => iri!("https://www.w3.org/ns/activitystreams#Tombstone"),
|
|
||||||
video => iri!("https://www.w3.org/ns/activitystreams#Video"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ApubActivityIds {
|
|
||||||
pub accept: Id,
|
|
||||||
pub add: Id,
|
|
||||||
pub announce: Id,
|
|
||||||
pub arrive: Id,
|
|
||||||
pub block: Id,
|
|
||||||
pub create: Id,
|
|
||||||
pub delete: Id,
|
|
||||||
pub dislike: Id,
|
|
||||||
pub follow: Id,
|
|
||||||
pub flag: Id,
|
|
||||||
pub ignore: Id,
|
|
||||||
pub invite: Id,
|
|
||||||
pub join: Id,
|
|
||||||
pub leave: Id,
|
|
||||||
pub like: Id,
|
|
||||||
pub listen: Id,
|
|
||||||
pub mov: Id, // move
|
|
||||||
pub offer: Id,
|
|
||||||
pub read: Id,
|
|
||||||
pub reject: Id,
|
|
||||||
pub tentative_accept: Id,
|
|
||||||
pub tentative_reject: Id,
|
|
||||||
pub travel: Id,
|
|
||||||
pub undo: Id,
|
|
||||||
pub update: Id,
|
|
||||||
pub view: Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApubActivityIds {
|
|
||||||
fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
populate_ids! {
|
|
||||||
vocab,
|
|
||||||
|
|
||||||
accept => iri!("https://www.w3.org/ns/activitystreams#Accept"),
|
|
||||||
add => iri!("https://www.w3.org/ns/activitystreams#Add"),
|
|
||||||
announce => iri!("https://www.w3.org/ns/activitystreams#Announce"),
|
|
||||||
arrive => iri!("https://www.w3.org/ns/activitystreams#Arrive"),
|
|
||||||
block => iri!("https://www.w3.org/ns/activitystreams#Block"),
|
|
||||||
create => iri!("https://www.w3.org/ns/activitystreams#Create"),
|
|
||||||
delete => iri!("https://www.w3.org/ns/activitystreams#Delete"),
|
|
||||||
dislike => iri!("https://www.w3.org/ns/activitystreams#Dislike"),
|
|
||||||
follow => iri!("https://www.w3.org/ns/activitystreams#Follow"),
|
|
||||||
flag => iri!("https://www.w3.org/ns/activitystreams#Flag"),
|
|
||||||
ignore => iri!("https://www.w3.org/ns/activitystreams#Ignore"),
|
|
||||||
invite => iri!("https://www.w3.org/ns/activitystreams#Invite"),
|
|
||||||
join => iri!("https://www.w3.org/ns/activitystreams#Join"),
|
|
||||||
leave => iri!("https://www.w3.org/ns/activitystreams#Leave"),
|
|
||||||
like => iri!("https://www.w3.org/ns/activitystreams#Like"),
|
|
||||||
listen => iri!("https://www.w3.org/ns/activitystreams#Listen"),
|
|
||||||
mov => iri!("https://www.w3.org/ns/activitystreams#Move"),
|
|
||||||
offer => iri!("https://www.w3.org/ns/activitystreams#Offer"),
|
|
||||||
read => iri!("https://www.w3.org/ns/activitystreams#Read"),
|
|
||||||
reject => iri!("https://www.w3.org/ns/activitystreams#Reject"),
|
|
||||||
tentative_accept => iri!("https://www.w3.org/ns/activitystreams#TentativeAccept"),
|
|
||||||
tentative_reject => iri!("https://www.w3.org/ns/activitystreams#TentativeReject"),
|
|
||||||
travel => iri!("https://www.w3.org/ns/activitystreams#Travel"),
|
|
||||||
undo => iri!("https://www.w3.org/ns/activitystreams#Undo"),
|
|
||||||
update => iri!("https://www.w3.org/ns/activitystreams#Update"),
|
|
||||||
view => iri!("https://www.w3.org/ns/activitystreams#View"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ApubLinkIds {
|
|
||||||
pub link: Id,
|
|
||||||
pub mention: Id,
|
|
||||||
pub is_following: Id,
|
|
||||||
pub is_followed_by: Id,
|
|
||||||
pub is_contact: Id,
|
|
||||||
pub is_member: Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApubLinkIds {
|
|
||||||
fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
populate_ids! {
|
|
||||||
vocab,
|
|
||||||
|
|
||||||
link => iri!("https://www.w3.org/ns/activitystreams#Link"),
|
|
||||||
mention => iri!("https://www.w3.org/ns/activitystreams#Mention"),
|
|
||||||
is_following => iri!("https://www.w3.org/ns/activitystreams#IsFollowing"),
|
|
||||||
is_followed_by => iri!("https://www.w3.org/ns/activitystreams#IsFollowedBy"),
|
|
||||||
is_contact => iri!("https://www.w3.org/ns/activitystreams#IsContact"),
|
|
||||||
is_member => iri!("https://www.w3.org/ns/activitystreams#IsMember"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ApubPropertyIds {
|
|
||||||
pub subject: Id,
|
|
||||||
pub relationship: Id,
|
|
||||||
pub actor: Id,
|
|
||||||
pub attributed_to: Id,
|
|
||||||
pub attachment: Id,
|
|
||||||
pub attachments: Id,
|
|
||||||
pub author: Id,
|
|
||||||
pub bcc: Id,
|
|
||||||
pub bto: Id,
|
|
||||||
pub cc: Id,
|
|
||||||
pub context: Id,
|
|
||||||
pub current: Id,
|
|
||||||
pub first: Id,
|
|
||||||
pub generator: Id,
|
|
||||||
pub icon: Id,
|
|
||||||
pub image: Id,
|
|
||||||
pub in_reply_to: Id,
|
|
||||||
pub items: Id,
|
|
||||||
pub instrument: Id,
|
|
||||||
pub ordered_items: Id,
|
|
||||||
pub last: Id,
|
|
||||||
pub location: Id,
|
|
||||||
pub next: Id,
|
|
||||||
pub object: Id,
|
|
||||||
pub one_of: Id,
|
|
||||||
pub any_of: Id,
|
|
||||||
pub closed: Id,
|
|
||||||
pub origin: Id,
|
|
||||||
pub accuracy: Id,
|
|
||||||
pub prev: Id,
|
|
||||||
pub preview: Id,
|
|
||||||
pub provider: Id,
|
|
||||||
pub replies: Id,
|
|
||||||
pub result: Id,
|
|
||||||
pub audience: Id,
|
|
||||||
pub part_of: Id,
|
|
||||||
pub tag: Id,
|
|
||||||
pub tags: Id,
|
|
||||||
pub target: Id,
|
|
||||||
pub to: Id,
|
|
||||||
pub url: Id,
|
|
||||||
pub altitude: Id,
|
|
||||||
pub content: Id,
|
|
||||||
pub content_map: Id,
|
|
||||||
pub name: Id,
|
|
||||||
pub name_map: Id,
|
|
||||||
pub downstream_duplicates: Id,
|
|
||||||
pub duration: Id,
|
|
||||||
pub end_time: Id,
|
|
||||||
pub height: Id,
|
|
||||||
pub href: Id,
|
|
||||||
pub hreflang: Id,
|
|
||||||
pub latitude: Id,
|
|
||||||
pub longitude: Id,
|
|
||||||
pub media_type: Id,
|
|
||||||
pub published: Id,
|
|
||||||
pub radius: Id,
|
|
||||||
pub rating: Id,
|
|
||||||
pub rel: Id,
|
|
||||||
pub start_index: Id,
|
|
||||||
pub start_time: Id,
|
|
||||||
pub summary: Id,
|
|
||||||
pub summary_map: Id,
|
|
||||||
pub total_items: Id,
|
|
||||||
pub units: Id,
|
|
||||||
pub updated: Id,
|
|
||||||
pub upstream_duplicates: Id,
|
|
||||||
pub verb: Id,
|
|
||||||
pub width: Id,
|
|
||||||
pub describes: Id,
|
|
||||||
pub former_type: Id,
|
|
||||||
pub deleted: Id,
|
|
||||||
|
|
||||||
// ActivityPub extensions
|
|
||||||
pub endpoints: Id,
|
|
||||||
pub following: Id,
|
|
||||||
pub followers: Id,
|
|
||||||
pub inbox: Id,
|
|
||||||
pub liked: Id,
|
|
||||||
pub shares: Id,
|
|
||||||
pub likes: Id,
|
|
||||||
pub oauth_authorization_endpoint: Id,
|
|
||||||
pub oauth_token_endpoint: Id,
|
|
||||||
pub outbox: Id,
|
|
||||||
pub preferred_username: Id,
|
|
||||||
pub provide_client_key: Id,
|
|
||||||
pub proxy_url: Id,
|
|
||||||
pub shared_inbox: Id,
|
|
||||||
pub sign_client_key: Id,
|
|
||||||
pub source: Id,
|
|
||||||
pub streams: Id,
|
|
||||||
pub upload_media: Id,
|
|
||||||
|
|
||||||
// DID Core extensions
|
|
||||||
pub also_known_as: Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApubPropertyIds {
|
|
||||||
fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
populate_ids! {
|
|
||||||
vocab,
|
|
||||||
|
|
||||||
subject => iri!("https://www.w3.org/ns/activitystreams#subject"),
|
|
||||||
relationship => iri!("https://www.w3.org/ns/activitystreams#relationship"),
|
|
||||||
actor => iri!("https://www.w3.org/ns/activitystreams#actor"),
|
|
||||||
attributed_to => iri!("https://www.w3.org/ns/activitystreams#attributedTo"),
|
|
||||||
attachment => iri!("https://www.w3.org/ns/activitystreams#attachment"),
|
|
||||||
attachments => iri!("https://www.w3.org/ns/activitystreams#attachments"),
|
|
||||||
author => iri!("https://www.w3.org/ns/activitystreams#author"),
|
|
||||||
bcc => iri!("https://www.w3.org/ns/activitystreams#bcc"),
|
|
||||||
bto => iri!("https://www.w3.org/ns/activitystreams#bto"),
|
|
||||||
cc => iri!("https://www.w3.org/ns/activitystreams#cc"),
|
|
||||||
context => iri!("https://www.w3.org/ns/activitystreams#context"),
|
|
||||||
current => iri!("https://www.w3.org/ns/activitystreams#current"),
|
|
||||||
first => iri!("https://www.w3.org/ns/activitystreams#first"),
|
|
||||||
generator => iri!("https://www.w3.org/ns/activitystreams#generator"),
|
|
||||||
icon => iri!("https://www.w3.org/ns/activitystreams#icon"),
|
|
||||||
image => iri!("https://www.w3.org/ns/activitystreams#image"),
|
|
||||||
in_reply_to => iri!("https://www.w3.org/ns/activitystreams#inReplyTo"),
|
|
||||||
items => iri!("https://www.w3.org/ns/activitystreams#items"),
|
|
||||||
instrument => iri!("https://www.w3.org/ns/activitystreams#instrument"),
|
|
||||||
ordered_items => iri!("https://www.w3.org/ns/activitystreams#orderedItems"),
|
|
||||||
last => iri!("https://www.w3.org/ns/activitystreams#last"),
|
|
||||||
location => iri!("https://www.w3.org/ns/activitystreams#location"),
|
|
||||||
next => iri!("https://www.w3.org/ns/activitystreams#next"),
|
|
||||||
object => iri!("https://www.w3.org/ns/activitystreams#object"),
|
|
||||||
one_of => iri!("https://www.w3.org/ns/activitystreams#oneOf"),
|
|
||||||
any_of => iri!("https://www.w3.org/ns/activitystreams#anyOf"),
|
|
||||||
closed => iri!("https://www.w3.org/ns/activitystreams#closed"),
|
|
||||||
origin => iri!("https://www.w3.org/ns/activitystreams#origin"),
|
|
||||||
accuracy => iri!("https://www.w3.org/ns/activitystreams#accuracy"),
|
|
||||||
prev => iri!("https://www.w3.org/ns/activitystreams#prev"),
|
|
||||||
preview => iri!("https://www.w3.org/ns/activitystreams#preview"),
|
|
||||||
provider => iri!("https://www.w3.org/ns/activitystreams#provider"),
|
|
||||||
replies => iri!("https://www.w3.org/ns/activitystreams#replies"),
|
|
||||||
result => iri!("https://www.w3.org/ns/activitystreams#result"),
|
|
||||||
audience => iri!("https://www.w3.org/ns/activitystreams#audience"),
|
|
||||||
part_of => iri!("https://www.w3.org/ns/activitystreams#partOf"),
|
|
||||||
tag => iri!("https://www.w3.org/ns/activitystreams#tag"),
|
|
||||||
tags => iri!("https://www.w3.org/ns/activitystreams#tags"),
|
|
||||||
target => iri!("https://www.w3.org/ns/activitystreams#taget"),
|
|
||||||
to => iri!("https://www.w3.org/ns/activitystreams#to"),
|
|
||||||
url => iri!("https://www.w3.org/ns/activitystreams#url"),
|
|
||||||
altitude => iri!("https://www.w3.org/ns/activitystreams#altitude"),
|
|
||||||
content => iri!("https://www.w3.org/ns/activitystreams#content"),
|
|
||||||
content_map => iri!("https://www.w3.org/ns/activitystreams#contentMap"),
|
|
||||||
name => iri!("https://www.w3.org/ns/activitystreams#name"),
|
|
||||||
name_map => iri!("https://www.w3.org/ns/activitystreams#nameMap"),
|
|
||||||
downstream_duplicates => iri!("https://www.w3.org/ns/activitystreams#downstreamDuplicates"),
|
|
||||||
duration => iri!("https://www.w3.org/ns/activitystreams#duration"),
|
|
||||||
end_time => iri!("https://www.w3.org/ns/activitystreams#endTime"),
|
|
||||||
height => iri!("https://www.w3.org/ns/activitystreams#height"),
|
|
||||||
href => iri!("https://www.w3.org/ns/activitystreams#href"),
|
|
||||||
hreflang => iri!("https://www.w3.org/ns/activitystreams#hreflang"),
|
|
||||||
latitude => iri!("https://www.w3.org/ns/activitystreams#latitude"),
|
|
||||||
longitude => iri!("https://www.w3.org/ns/activitystreams#longitude"),
|
|
||||||
media_type => iri!("https://www.w3.org/ns/activitystreams#mediaType"),
|
|
||||||
published => iri!("https://www.w3.org/ns/activitystreams#published"),
|
|
||||||
radius => iri!("https://www.w3.org/ns/activitystreams#radius"),
|
|
||||||
rating => iri!("https://www.w3.org/ns/activitystreams#ratine"),
|
|
||||||
rel => iri!("https://www.w3.org/ns/activitystreams#rel"),
|
|
||||||
start_index => iri!("https://www.w3.org/ns/activitystreams#startIndex"),
|
|
||||||
start_time => iri!("https://www.w3.org/ns/activitystreams#startTime"),
|
|
||||||
summary => iri!("https://www.w3.org/ns/activitystreams#summary"),
|
|
||||||
summary_map => iri!("https://www.w3.org/ns/activitystreams#summaryMap"),
|
|
||||||
total_items => iri!("https://www.w3.org/ns/activitystreams#totalItems"),
|
|
||||||
units => iri!("https://www.w3.org/ns/activitystreams#units"),
|
|
||||||
updated => iri!("https://www.w3.org/ns/activitystreams#updated"),
|
|
||||||
upstream_duplicates => iri!("https://www.w3.org/ns/activitystreams#upstreamDuplicates"),
|
|
||||||
verb => iri!("https://www.w3.org/ns/activitystreams#verb"),
|
|
||||||
width => iri!("https://www.w3.org/ns/activitystreams#width"),
|
|
||||||
describes => iri!("https://www.w3.org/ns/activitystreams#describes"),
|
|
||||||
former_type => iri!("https://www.w3.org/ns/activitystreams#formerType"),
|
|
||||||
deleted => iri!("https://www.w3.org/ns/activitystreams#deleted"),
|
|
||||||
|
|
||||||
// ActivityPub extensions
|
|
||||||
endpoints => iri!("https://www.w3.org/ns/activitystreams#endpoints"),
|
|
||||||
following => iri!("https://www.w3.org/ns/activitystreams#following"),
|
|
||||||
followers => iri!("https://www.w3.org/ns/activitystreams#followers"),
|
|
||||||
inbox => iri!("https://www.w3.org/ns/activitystreams#inbox"),
|
|
||||||
liked => iri!("https://www.w3.org/ns/activitystreams#liked"),
|
|
||||||
shares => iri!("https://www.w3.org/ns/activitystreams#shares"),
|
|
||||||
likes => iri!("https://www.w3.org/ns/activitystreams#likes"),
|
|
||||||
oauth_authorization_endpoint => iri!("https://www.w3.org/ns/activitystreams#oauthAuthorizationEndpoint"),
|
|
||||||
oauth_token_endpoint => iri!("https://www.w3.org/ns/activitystreams#oauthTokenEndpoint"),
|
|
||||||
outbox => iri!("https://www.w3.org/ns/activitystreams#outbox"),
|
|
||||||
preferred_username => iri!("https://www.w3.org/ns/activitystreams#preferredUsername"),
|
|
||||||
provide_client_key => iri!("https://www.w3.org/ns/activitystreams#provideClientKey"),
|
|
||||||
proxy_url => iri!("https://www.w3.org/ns/activitystreams#proxyUrl"),
|
|
||||||
shared_inbox => iri!("https://www.w3.org/ns/activitystreams#sharedInbox"),
|
|
||||||
sign_client_key => iri!("https://www.w3.org/ns/activitystreams#signClientKey"),
|
|
||||||
source => iri!("https://www.w3.org/ns/activitystreams#source"),
|
|
||||||
streams => iri!("https://www.w3.org/ns/activitystreams#streams"),
|
|
||||||
upload_media => iri!("https://www.w3.org/ns/activitystreams#uploadMedia"),
|
|
||||||
also_known_as => iri!("https://www.w3.org/ns/activitystreams#alsoKnownAs"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TootIds {
|
|
||||||
pub class: TootClassIds,
|
|
||||||
pub props: TootPropIds,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TootIds {
|
|
||||||
fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
Self {
|
|
||||||
class: TootClassIds::populate(vocab),
|
|
||||||
props: TootPropIds::populate(vocab),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TootClassIds {
|
|
||||||
pub emoji: Id,
|
|
||||||
pub identity_proof: Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TootClassIds {
|
|
||||||
fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
populate_ids! {
|
|
||||||
vocab,
|
|
||||||
|
|
||||||
emoji => iri!("http://joinmastodon.org/ns#Emoji"),
|
|
||||||
identity_proof => iri!("http://joinmastodon.org/ns#IdentityProof"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TootPropIds {
|
|
||||||
pub blurhash: Id,
|
|
||||||
pub focal_point: Id,
|
|
||||||
pub featured: Id,
|
|
||||||
pub featured_tags: Id,
|
|
||||||
pub discoverable: Id,
|
|
||||||
pub suspended: Id,
|
|
||||||
pub voters_count: Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TootPropIds {
|
|
||||||
fn populate(vocab: &mut impl VocabularyMut<Iri = IriIndex>) -> Self {
|
|
||||||
populate_ids! {
|
|
||||||
vocab,
|
|
||||||
|
|
||||||
blurhash => iri!("http://joinmastodon.org/ns#blurhash"),
|
|
||||||
focal_point => iri!("http://joinmastodon.org/ns#focalPoint"),
|
|
||||||
featured => iri!("http://joinmastodon.org/ns#featured"),
|
|
||||||
featured_tags => iri!("http://joinmastodon.org/ns#featuredTags"),
|
|
||||||
discoverable => iri!("http://joinmastodon.org/ns#discoverable"),
|
|
||||||
suspended => iri!("http://joinmastodon.org/ns#suspended"),
|
|
||||||
voters_count => iri!("http://joinmastodon.org/ns#votersCount"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
use actix_web::http::header::{ACCEPT, CONTENT_TYPE};
|
|
||||||
use actix_web::{post, web, HttpRequest, HttpResponse};
|
use actix_web::{post, web, HttpRequest, HttpResponse};
|
||||||
|
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
|
@ -7,24 +6,22 @@ use crate::state::AppState;
|
||||||
|
|
||||||
#[post("")]
|
#[post("")]
|
||||||
async fn post_inbox(body: String, request: HttpRequest, state: AppState) -> Result<HttpResponse> {
|
async fn post_inbox(body: String, request: HttpRequest, state: AppState) -> Result<HttpResponse> {
|
||||||
const CONTENT_TYPES: &[&str] = &[
|
const CONTENT_TYPES: &[&'static str] = &[
|
||||||
"application/activity+json",
|
"application/activity+json",
|
||||||
"application/ld+json",
|
"application/ld+json",
|
||||||
"application/json",
|
"application/json",
|
||||||
];
|
];
|
||||||
let content_type = request
|
let content_type = request
|
||||||
.headers()
|
.headers()
|
||||||
.get(CONTENT_TYPE)
|
.get("Content-Type")
|
||||||
.ok_or(Error::BadRequest)?
|
.ok_or(Error::BadRequest)?
|
||||||
.to_str()?;
|
.to_str()?;
|
||||||
if CONTENT_TYPES.iter().all(|&typ| typ != content_type) {
|
if CONTENT_TYPES.iter().all(|typ| *typ != content_type) {
|
||||||
return Ok(HttpResponse::UnsupportedMediaType()
|
return Ok(HttpResponse::BadRequest().finish());
|
||||||
.append_header((ACCEPT, "application/activity+json, application/ld+json"))
|
|
||||||
.finish());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.sched.schedule(InboxWorker::new(body));
|
state.sched.schedule(InboxWorker::new(body));
|
||||||
Ok(HttpResponse::Accepted().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure(cfg: &mut web::ServiceConfig) {
|
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||||
|
|
Loading…
Reference in a new issue