feat: separated apb types into crate, reworked
no more "impl ..." hell, each trait has associated types so that we know it's a "Self::Link" or a "Self::Actor", but in practice they can both be a "serde_json::Value" and thus we can change its type. also Node::Array is now a Vec<T> rather than Vec<Node<T>> because it makes more sense. Node is Iterable and will yield zero (Empty|Link), one (Object) or many (Array) Ts
This commit is contained in:
parent
b5867b90ac
commit
520c8eff3a
51 changed files with 3593 additions and 829 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/apb/target
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[workspace]
|
||||
members = ["apb"]
|
||||
|
||||
[package]
|
||||
name = "upub"
|
||||
version = "0.1.0"
|
||||
|
@ -10,7 +13,7 @@ axum = "0.7.3"
|
|||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
clap = { version = "4.5.3", features = ["derive"] }
|
||||
paste = "1.0.14"
|
||||
reqwest = { version = "0.11.26", features = ["json"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
sea-orm = { version = "0.12.14", features = ["macros", "sqlx-sqlite", "runtime-tokio-rustls"] }
|
||||
sea-orm-migration = "0.12.15"
|
||||
serde = { version = "1.0.193", features = ["derive"] }
|
||||
|
@ -21,6 +24,7 @@ tracing = "0.1.40"
|
|||
tracing-subscriber = "0.3.18"
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
jrd = "0.1"
|
||||
apb = { path = "apb", features = ["dict", "fetch", "orm"] }
|
||||
# nodeinfo = "0.0.2" # the version on crates.io doesn't re-export necessary types to build the struct!!!
|
||||
nodeinfo = { git = "https://codeberg.org/thefederationinfo/nodeinfo-rs", rev = "e865094804" }
|
||||
rand = "0.8.5"
|
||||
|
|
2747
apb/Cargo.lock
generated
Normal file
2747
apb/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
23
apb/Cargo.toml
Normal file
23
apb/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "apb"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
thiserror = "1.0"
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
reqwest = { version = "0.12", features = ["json"], optional = true }
|
||||
paste = "1.0.14"
|
||||
sea-orm = { version = "0.12", optional = true }
|
||||
tracing = "0.1.40"
|
||||
|
||||
[features]
|
||||
default = ["fetch", "dict", "orm"]
|
||||
fetch = ["dep:reqwest"]
|
||||
dict = ["dep:serde_json"]
|
||||
orm = ["dep:sea-orm"]
|
41
apb/src/base.rs
Normal file
41
apb/src/base.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use crate::{getter, setter, strenum, LinkType, ObjectType};
|
||||
|
||||
strenum! {
|
||||
pub enum BaseType {
|
||||
;
|
||||
Object(ObjectType),
|
||||
Link(LinkType)
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Base {
|
||||
fn id(&self) -> Option<&str> { None }
|
||||
fn base_type(&self) -> Option<BaseType> { None }
|
||||
}
|
||||
|
||||
|
||||
pub trait BaseMut {
|
||||
fn set_id(self, val: Option<&str>) -> Self;
|
||||
fn set_base_type(self, val: Option<BaseType>) -> Self;
|
||||
}
|
||||
|
||||
|
||||
impl Base for String {
|
||||
fn id(&self) -> Option<&str> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn base_type(&self) -> Option<BaseType> {
|
||||
Some(BaseType::Link(LinkType::Link))
|
||||
}
|
||||
}
|
||||
|
||||
impl Base for serde_json::Value {
|
||||
getter! { id -> &str }
|
||||
getter! { base_type -> type BaseType }
|
||||
}
|
||||
|
||||
impl BaseMut for serde_json::Value {
|
||||
setter! { id -> &str }
|
||||
setter! { base_type -> type BaseType }
|
||||
}
|
36
apb/src/lib.rs
Normal file
36
apb/src/lib.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
mod macros;
|
||||
|
||||
mod node;
|
||||
pub use node::Node;
|
||||
|
||||
mod link;
|
||||
pub use link::{Link, LinkMut, LinkType};
|
||||
|
||||
pub mod key;
|
||||
pub use key::{PublicKey, PublicKeyMut};
|
||||
|
||||
mod base;
|
||||
pub use base::{Base, BaseMut, BaseType};
|
||||
|
||||
mod object;
|
||||
pub use object::{
|
||||
Object, ObjectMut, ObjectType,
|
||||
activity::{
|
||||
Activity, ActivityMut, ActivityType,
|
||||
accept::{Accept, AcceptMut, AcceptType},
|
||||
ignore::{Ignore, IgnoreMut, IgnoreType},
|
||||
intransitive::{IntransitiveActivity, IntransitiveActivityMut, IntransitiveActivityType},
|
||||
offer::{Offer, OfferMut, OfferType},
|
||||
reject::{Reject, RejectMut, RejectType},
|
||||
},
|
||||
actor::{Actor, ActorMut, ActorType},
|
||||
collection::{
|
||||
Collection, CollectionMut, CollectionType,
|
||||
page::{CollectionPage, CollectionPageMut}
|
||||
},
|
||||
document::{Document, DocumentMut, DocumentType},
|
||||
place::{Place, PlaceMut},
|
||||
// profile::Profile,
|
||||
relationship::{Relationship, RelationshipMut},
|
||||
tombstone::{Tombstone, TombstoneMut},
|
||||
};
|
|
@ -37,7 +37,7 @@ macro_rules! strenum {
|
|||
}
|
||||
|
||||
impl TryFrom<&str> for $enum_name {
|
||||
type Error = $crate::activitystream::macros::TypeValueError;
|
||||
type Error = $crate::macros::TypeValueError;
|
||||
|
||||
fn try_from(value:&str) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
|
@ -48,7 +48,7 @@ macro_rules! strenum {
|
|||
return Ok(Self::$deep(x));
|
||||
}
|
||||
)*
|
||||
Err($crate::activitystream::macros::TypeValueError)
|
||||
Err($crate::macros::TypeValueError)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -171,19 +171,19 @@ macro_rules! getter {
|
|||
};
|
||||
|
||||
($name:ident -> node $t:ty) => {
|
||||
fn $name(&self) -> $crate::activitystream::Node<$t> {
|
||||
fn $name(&self) -> $crate::Node<$t> {
|
||||
match self.get(stringify!($name)) {
|
||||
Some(x) => $crate::activitystream::Node::from(x.clone()),
|
||||
None => $crate::activitystream::Node::Empty,
|
||||
Some(x) => $crate::Node::from(x.clone()),
|
||||
None => $crate::Node::Empty,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident::$rename:ident -> node $t:ty) => {
|
||||
fn $name(&self) -> $crate::activitystream::Node<$t> {
|
||||
fn $name(&self) -> $crate::Node<$t> {
|
||||
match self.get(stringify!($rename)) {
|
||||
Some(x) => $crate::activitystream::Node::from(x.clone()),
|
||||
None => $crate::activitystream::Node::Empty,
|
||||
Some(x) => $crate::Node::from(x.clone()),
|
||||
None => $crate::Node::Empty,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -194,7 +194,7 @@ macro_rules! setter {
|
|||
($name:ident -> bool) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: Option<bool>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_value(
|
||||
$crate::macros::set_maybe_value(
|
||||
&mut self, stringify!($name), val.map(|x| serde_json::Value::Bool(x))
|
||||
);
|
||||
self
|
||||
|
@ -205,7 +205,7 @@ macro_rules! setter {
|
|||
($name:ident -> &str) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: Option<&str>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_value(
|
||||
$crate::macros::set_maybe_value(
|
||||
&mut self, stringify!($name), val.map(|x| serde_json::Value::String(x.to_string()))
|
||||
);
|
||||
self
|
||||
|
@ -216,7 +216,7 @@ macro_rules! setter {
|
|||
($name:ident::$rename:ident -> &str) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: Option<&str>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_value(
|
||||
$crate::macros::set_maybe_value(
|
||||
&mut self, stringify!($rename), val.map(|x| serde_json::Value::String(x.to_string()))
|
||||
);
|
||||
self
|
||||
|
@ -227,7 +227,7 @@ macro_rules! setter {
|
|||
($name:ident -> u64) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: Option<u64>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_value(
|
||||
$crate::macros::set_maybe_value(
|
||||
&mut self, stringify!($name), val.map(|x| serde_json::Value::Number(serde_json::Number::from(x)))
|
||||
);
|
||||
self
|
||||
|
@ -238,7 +238,7 @@ macro_rules! setter {
|
|||
($name:ident::$rename:ident -> u64) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: Option<u64>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_value(
|
||||
$crate::macros::set_maybe_value(
|
||||
&mut self, stringify!($rename), val.map(|x| serde_json::Value::Number(serde_json::Number::from(x)))
|
||||
);
|
||||
self
|
||||
|
@ -249,7 +249,7 @@ macro_rules! setter {
|
|||
($name:ident -> chrono::DateTime<chrono::Utc>) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_value(
|
||||
$crate::macros::set_maybe_value(
|
||||
&mut self, stringify!($name), val.map(|x| serde_json::Value::String(x.to_rfc3339()))
|
||||
);
|
||||
self
|
||||
|
@ -260,7 +260,7 @@ macro_rules! setter {
|
|||
($name:ident::$rename:ident -> chrono::DateTime<chrono::Utc>) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_value(
|
||||
$crate::macros::set_maybe_value(
|
||||
&mut self, stringify!($rename), val.map(|x| serde_json::Value::String(x.to_rfc3339()))
|
||||
);
|
||||
self
|
||||
|
@ -270,8 +270,8 @@ macro_rules! setter {
|
|||
|
||||
($name:ident -> node $t:ty ) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: $crate::activitystream::Node<$t>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_node(
|
||||
fn [< set_$name >](mut self, val: $crate::Node<$t>) -> Self {
|
||||
$crate::macros::set_maybe_node(
|
||||
&mut self, stringify!($name), val
|
||||
);
|
||||
self
|
||||
|
@ -281,8 +281,8 @@ macro_rules! setter {
|
|||
|
||||
($name:ident::$rename:ident -> node $t:ty ) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: $crate::activitystream::Node<$t>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_node(
|
||||
fn [< set_$name >](mut self, val: $crate::Node<$t>) -> Self {
|
||||
$crate::macros::set_maybe_node(
|
||||
&mut self, stringify!($rename), val
|
||||
);
|
||||
self
|
||||
|
@ -293,7 +293,7 @@ macro_rules! setter {
|
|||
($name:ident -> type $t:ty ) => {
|
||||
paste::item! {
|
||||
fn [< set_$name >](mut self, val: Option<$t>) -> Self {
|
||||
$crate::activitystream::macros::set_maybe_value(
|
||||
$crate::macros::set_maybe_value(
|
||||
&mut self, "type", val.map(|x| serde_json::Value::String(x.as_ref().to_string()))
|
||||
);
|
||||
self
|
||||
|
@ -302,11 +302,11 @@ macro_rules! setter {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn set_maybe_node<T : super::Base>(obj: &mut serde_json::Value, key: &str, node: super::Node<T>) {
|
||||
pub fn set_maybe_node(obj: &mut serde_json::Value, key: &str, node: super::Node<serde_json::Value>) {
|
||||
match node {
|
||||
super::Node::Object(x) => {
|
||||
set_maybe_value(
|
||||
obj, key, Some(x.underlying_json_object()),
|
||||
obj, key, Some(*x),
|
||||
);
|
||||
},
|
||||
super::Node::Link(l) => {
|
||||
|
@ -316,7 +316,7 @@ pub fn set_maybe_node<T : super::Base>(obj: &mut serde_json::Value, key: &str, n
|
|||
},
|
||||
super::Node::Array(_) => {
|
||||
set_maybe_value(
|
||||
obj, key, Some(serde_json::Value::Array(node.flat())),
|
||||
obj, key, Some(serde_json::Value::Array(node.into_iter().collect())),
|
||||
);
|
||||
},
|
||||
super::Node::Empty => {
|
||||
|
@ -357,7 +357,7 @@ impl InsertValue for serde_json::Map<String, serde_json::Value> {
|
|||
Node::Array(ref _arr) => {
|
||||
self.insert(
|
||||
k.to_string(),
|
||||
serde_json::Value::Array(node.flat()),
|
||||
serde_json::Value::Array(node.into_iter().collect()),
|
||||
);
|
||||
},
|
||||
Node::Link(l) => {
|
167
apb/src/node.rs
Normal file
167
apb/src/node.rs
Normal file
|
@ -0,0 +1,167 @@
|
|||
pub enum Node<T : super::Base> {
|
||||
Array(Vec<T>), // TODO would be cool to make it Box<[T]> so that Node is just a ptr
|
||||
Object(Box<T>),
|
||||
Link(Box<dyn super::Link>),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl<T : super::Base> From<Option<T>> for Node<T> {
|
||||
fn from(value: Option<T>) -> Self {
|
||||
match value {
|
||||
Some(x) => Node::Object(Box::new(x)),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T : super::Base + Clone> Iterator for Node<T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let x = match self {
|
||||
Self::Empty => return None,
|
||||
Self::Link(_) => return None,
|
||||
Self::Array(arr) => return arr.pop(), // TODO weird that we iter in reverse
|
||||
Self::Object(x) => *x.clone(), // TODO needed because next() on object can't get value without owning
|
||||
};
|
||||
*self = Self::Empty;
|
||||
Some(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T : super::Base> Node<T> {
|
||||
pub fn get(&self) -> Option<&T> {
|
||||
match self {
|
||||
Node::Empty | Node::Link(_) => None,
|
||||
Node::Object(x) => Some(x),
|
||||
Node::Array(v) => v.last(), // TODO so it's coherent with next(), still weird tho!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract(self) -> Option<T> {
|
||||
match self {
|
||||
Node::Empty | Node::Link(_) => None,
|
||||
Node::Object(x) => Some(*x),
|
||||
Node::Array(mut v) => v.pop(), // TODO so it's coherent with next(), still weird tho!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
matches!(self, Node::Empty)
|
||||
}
|
||||
|
||||
pub fn is_link(&self) -> bool {
|
||||
matches!(self, Node::Link(_))
|
||||
}
|
||||
|
||||
pub fn is_object(&self) -> bool {
|
||||
matches!(self, Node::Object(_))
|
||||
}
|
||||
|
||||
pub fn is_array(&self) -> bool {
|
||||
matches!(self, Node::Array(_))
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Node::Empty => 0,
|
||||
Node::Link(_) => 1,
|
||||
Node::Object(_) => 1,
|
||||
Node::Array(v) => v.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<String> {
|
||||
match self {
|
||||
Node::Empty | Node::Array(_) => None,
|
||||
Node::Link(uri) => Some(uri.href().to_string()),
|
||||
Node::Object(obj) => obj.id().map(|x| x.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dict")]
|
||||
impl Node<serde_json::Value> {
|
||||
pub fn link(uri: String) -> Self {
|
||||
Node::Link(Box::new(uri))
|
||||
}
|
||||
|
||||
pub fn links(uris: Vec<String>) -> Self {
|
||||
Node::Array(
|
||||
uris
|
||||
.into_iter()
|
||||
.map(serde_json::Value::String)
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn maybe_link(uri: Option<String>) -> Self {
|
||||
match uri {
|
||||
Some(uri) => Node::Link(Box::new(uri)),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn object(x: serde_json::Value) -> Self {
|
||||
Node::Object(Box::new(x))
|
||||
}
|
||||
|
||||
pub fn maybe_object(x: Option<serde_json::Value>) -> Self {
|
||||
match x {
|
||||
Some(x) => Node::Object(Box::new(x)),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn array(values: Vec<serde_json::Value>) -> Self {
|
||||
Node::Array(values)
|
||||
}
|
||||
|
||||
#[cfg(feature = "fetch")]
|
||||
pub async fn fetch(&mut self) -> reqwest::Result<&mut Self> {
|
||||
if let Node::Link(link) = self {
|
||||
*self = reqwest::Client::new()
|
||||
.get(link.href())
|
||||
.header("Accept", "application/json")
|
||||
.send()
|
||||
.await?
|
||||
.json::<serde_json::Value>()
|
||||
.await?
|
||||
.into();
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dict")]
|
||||
impl From<Option<&str>> for Node<serde_json::Value> {
|
||||
fn from(value: Option<&str>) -> Self {
|
||||
match value {
|
||||
Some(x) => Node::Link(Box::new(x.to_string())),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dict")]
|
||||
impl From<&str> for Node<serde_json::Value> {
|
||||
fn from(value: &str) -> Self {
|
||||
Node::Link(Box::new(value.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dict")]
|
||||
impl From<serde_json::Value> for Node<serde_json::Value> {
|
||||
fn from(value: serde_json::Value) -> Self {
|
||||
match value {
|
||||
serde_json::Value::String(uri) => Node::Link(Box::new(uri)),
|
||||
serde_json::Value::Array(arr) => Node::Array(arr),
|
||||
serde_json::Value::Object(_) => match value.get("href") {
|
||||
None => Node::Object(Box::new(value)),
|
||||
Some(_) => Node::Link(Box::new(value)),
|
||||
},
|
||||
_ => Node::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
81
apb/src/object/activity/mod.rs
Normal file
81
apb/src/object/activity/mod.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
pub mod accept;
|
||||
pub mod ignore;
|
||||
pub mod intransitive;
|
||||
pub mod offer;
|
||||
pub mod reject;
|
||||
|
||||
use crate::{Node, object::{Object, ObjectMut}, getter, setter, strenum};
|
||||
use accept::AcceptType;
|
||||
use reject::RejectType;
|
||||
use offer::OfferType;
|
||||
use intransitive::IntransitiveActivityType;
|
||||
use ignore::IgnoreType;
|
||||
|
||||
strenum! {
|
||||
pub enum ActivityType {
|
||||
Activity,
|
||||
Add,
|
||||
Announce,
|
||||
Create,
|
||||
Delete,
|
||||
Dislike,
|
||||
Flag,
|
||||
Follow,
|
||||
Join,
|
||||
Leave,
|
||||
Like,
|
||||
Listen,
|
||||
Move,
|
||||
Read,
|
||||
Remove,
|
||||
Undo,
|
||||
Update,
|
||||
View;
|
||||
|
||||
IntransitiveActivity(IntransitiveActivityType),
|
||||
Accept(AcceptType),
|
||||
Ignore(IgnoreType),
|
||||
Offer(OfferType),
|
||||
Reject(RejectType)
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Activity : Object {
|
||||
fn activity_type(&self) -> Option<ActivityType> { None }
|
||||
fn actor(&self) -> Node<Self::Actor> { Node::Empty }
|
||||
fn object(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn target(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn result(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn origin(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn instrument(&self) -> Node<Self::Object> { Node::Empty }
|
||||
}
|
||||
|
||||
pub trait ActivityMut : ObjectMut {
|
||||
fn set_activity_type(self, val: Option<ActivityType>) -> Self;
|
||||
fn set_actor(self, val: Node<Self::Actor>) -> Self;
|
||||
fn set_object(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_target(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_result(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_origin(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_instrument(self, val: Node<Self::Object>) -> Self;
|
||||
}
|
||||
|
||||
impl Activity for serde_json::Value {
|
||||
getter! { activity_type -> type ActivityType }
|
||||
getter! { actor -> node Self::Actor }
|
||||
getter! { object -> node <Self as Object>::Object }
|
||||
getter! { target -> node <Self as Object>::Object }
|
||||
getter! { result -> node <Self as Object>::Object }
|
||||
getter! { origin -> node <Self as Object>::Object }
|
||||
getter! { instrument -> node <Self as Object>::Object }
|
||||
}
|
||||
|
||||
impl ActivityMut for serde_json::Value {
|
||||
setter! { activity_type -> type ActivityType }
|
||||
setter! { actor -> node Self::Actor }
|
||||
setter! { object -> node <Self as Object>::Object }
|
||||
setter! { target -> node <Self as Object>::Object }
|
||||
setter! { result -> node <Self as Object>::Object }
|
||||
setter! { origin -> node <Self as Object>::Object }
|
||||
setter! { instrument -> node <Self as Object>::Object }
|
||||
}
|
84
apb/src/object/actor.rs
Normal file
84
apb/src/object/actor.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use crate::{Node, getter, setter, strenum};
|
||||
|
||||
use super::{Object, ObjectMut, super::key::PublicKey};
|
||||
|
||||
strenum! {
|
||||
pub enum ActorType {
|
||||
Application,
|
||||
Group,
|
||||
Organization,
|
||||
Person;
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Actor : Object {
|
||||
type PublicKey : PublicKey;
|
||||
|
||||
fn actor_type(&self) -> Option<ActorType> { None }
|
||||
fn preferred_username(&self) -> Option<&str> { None }
|
||||
fn inbox(&self) -> Node<Self::Collection>;
|
||||
fn outbox(&self) -> Node<Self::Collection>;
|
||||
fn following(&self) -> Node<Self::Collection> { todo!() }
|
||||
fn followers(&self) -> Node<Self::Collection> { todo!() }
|
||||
fn liked(&self) -> Node<Self::Collection> { todo!() }
|
||||
fn streams(&self) -> Node<Self::Collection> { todo!() }
|
||||
fn endpoints(&self) -> Option<serde_json::Map<String, String>> { None }
|
||||
fn public_key(&self) -> Node<Self::PublicKey> { todo!() }
|
||||
// idk about this? everyone has it but AP doesn't mention it
|
||||
fn discoverable(&self) -> Option<bool> { None }
|
||||
}
|
||||
|
||||
pub trait ActorMut : ObjectMut {
|
||||
type PublicKey : PublicKey;
|
||||
|
||||
fn set_actor_type(self, val: Option<ActorType>) -> Self;
|
||||
fn set_preferred_username(self, val: Option<&str>) -> Self;
|
||||
fn set_inbox(self, val: Node<Self::Collection>) -> Self;
|
||||
fn set_outbox(self, val: Node<Self::Collection>) -> Self;
|
||||
fn set_following(self, val: Node<Self::Collection>) -> Self;
|
||||
fn set_followers(self, val: Node<Self::Collection>) -> Self;
|
||||
fn set_liked(self, val: Node<Self::Collection>) -> Self;
|
||||
fn set_streams(self, val: Node<Self::Collection>) -> Self;
|
||||
fn set_endpoints(self, val: Option<serde_json::Map<String, String>>) -> Self;
|
||||
fn set_public_key(self, val: Node<Self::PublicKey>) -> Self;
|
||||
fn set_discoverable(self, val: Option<bool>) -> Self;
|
||||
}
|
||||
|
||||
impl Actor for serde_json::Value {
|
||||
type PublicKey = serde_json::Value;
|
||||
|
||||
getter! { actor_type -> type ActorType }
|
||||
getter! { preferred_username::preferredUsername -> &str }
|
||||
getter! { inbox -> node Self::Collection }
|
||||
getter! { outbox -> node Self::Collection }
|
||||
getter! { following -> node Self::Collection }
|
||||
getter! { followers -> node Self::Collection }
|
||||
getter! { liked -> node Self::Collection }
|
||||
getter! { streams -> node Self::Collection }
|
||||
getter! { public_key::publicKey -> node Self::PublicKey }
|
||||
getter! { discoverable -> bool }
|
||||
|
||||
fn endpoints(&self) -> Option<serde_json::Map<String, String>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActorMut for serde_json::Value {
|
||||
type PublicKey = serde_json::Value;
|
||||
|
||||
setter! { actor_type -> type ActorType }
|
||||
setter! { preferred_username::preferredUsername -> &str }
|
||||
setter! { inbox -> node Self::Collection }
|
||||
setter! { outbox -> node Self::Collection }
|
||||
setter! { following -> node Self::Collection }
|
||||
setter! { followers -> node Self::Collection }
|
||||
setter! { liked -> node Self::Collection }
|
||||
setter! { streams -> node Self::Collection }
|
||||
setter! { public_key::publicKey -> node Self::PublicKey }
|
||||
setter! { discoverable -> bool }
|
||||
|
||||
fn set_endpoints(mut self, _val: Option<serde_json::Map<String, String>>) -> Self {
|
||||
self.as_object_mut().unwrap().insert("endpoints".to_string(), serde_json::Value::Object(serde_json::Map::default()));
|
||||
self
|
||||
}
|
||||
}
|
62
apb/src/object/collection/mod.rs
Normal file
62
apb/src/object/collection/mod.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
pub mod page;
|
||||
pub use page::CollectionPage;
|
||||
|
||||
use crate::{Node, Object, object::ObjectMut, getter, setter, strenum};
|
||||
|
||||
strenum! {
|
||||
pub enum CollectionType {
|
||||
Collection,
|
||||
CollectionPage,
|
||||
OrderedCollection,
|
||||
OrderedCollectionPage;
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Collection : Object {
|
||||
type CollectionPage : CollectionPage;
|
||||
|
||||
fn collection_type(&self) -> Option<CollectionType> { None }
|
||||
|
||||
fn total_items(&self) -> Option<u64> { None }
|
||||
fn current(&self) -> Node<Self::CollectionPage> { Node::Empty }
|
||||
fn first(&self) -> Node<Self::CollectionPage> { Node::Empty }
|
||||
fn last(&self) -> Node<Self::CollectionPage> { Node::Empty }
|
||||
fn items(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn ordered_items(&self) -> Node<Self::Object> { Node::Empty }
|
||||
}
|
||||
|
||||
pub trait CollectionMut : ObjectMut {
|
||||
type CollectionPage : CollectionPage;
|
||||
|
||||
fn set_collection_type(self, val: Option<CollectionType>) -> Self;
|
||||
fn set_total_items(self, val: Option<u64>) -> Self;
|
||||
fn set_current(self, val: Node<Self::CollectionPage>) -> Self;
|
||||
fn set_first(self, val: Node<Self::CollectionPage>) -> Self;
|
||||
fn set_last(self, val: Node<Self::CollectionPage>) -> Self;
|
||||
fn set_items(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_ordered_items(self, val: Node<Self::Object>) -> Self;
|
||||
}
|
||||
|
||||
impl Collection for serde_json::Value {
|
||||
type CollectionPage = serde_json::Value;
|
||||
|
||||
getter! { collection_type -> type CollectionType }
|
||||
getter! { total_items::totalItems -> u64 }
|
||||
getter! { current -> node Self::CollectionPage }
|
||||
getter! { first -> node Self::CollectionPage }
|
||||
getter! { last -> node Self::CollectionPage }
|
||||
getter! { items -> node <Self as Object>::Object }
|
||||
getter! { ordered_items::orderedItems -> node <Self as Object>::Object }
|
||||
}
|
||||
|
||||
impl CollectionMut for serde_json::Value {
|
||||
type CollectionPage = serde_json::Value;
|
||||
|
||||
setter! { collection_type -> type CollectionType }
|
||||
setter! { total_items::totalItems -> u64 }
|
||||
setter! { current -> node Self::CollectionPage }
|
||||
setter! { first -> node Self::CollectionPage }
|
||||
setter! { last -> node Self::CollectionPage }
|
||||
setter! { items -> node <Self as Object>::Object }
|
||||
setter! { ordered_items::orderedItems -> node <Self as Object>::Object }
|
||||
}
|
25
apb/src/object/collection/page.rs
Normal file
25
apb/src/object/collection/page.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use crate::{Node, getter, setter};
|
||||
|
||||
pub trait CollectionPage : super::Collection {
|
||||
fn part_of(&self) -> Node<Self::Collection> { Node::Empty }
|
||||
fn next(&self) -> Node<Self::CollectionPage> { Node::Empty }
|
||||
fn prev(&self) -> Node<Self::CollectionPage> { Node::Empty }
|
||||
}
|
||||
|
||||
pub trait CollectionPageMut : super::CollectionMut {
|
||||
fn set_part_of(self, val: Node<Self::Collection>) -> Self;
|
||||
fn set_next(self, val: Node<Self::CollectionPage>) -> Self;
|
||||
fn set_prev(self, val: Node<Self::CollectionPage>) -> Self;
|
||||
}
|
||||
|
||||
impl CollectionPage for serde_json::Value {
|
||||
getter! { part_of::partOf -> node Self::Collection }
|
||||
getter! { next -> node Self::CollectionPage }
|
||||
getter! { prev -> node Self::CollectionPage }
|
||||
}
|
||||
|
||||
impl CollectionPageMut for serde_json::Value {
|
||||
setter! { part_of::partOf -> node Self::Collection }
|
||||
setter! { next -> node Self::CollectionPage }
|
||||
setter! { prev -> node Self::CollectionPage }
|
||||
}
|
|
@ -26,8 +26,3 @@ impl Document for serde_json::Value {
|
|||
impl DocumentMut for serde_json::Value {
|
||||
setter! { document_type -> type DocumentType }
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub trait Image : Document {}
|
||||
impl Image for serde_json::Value {}
|
202
apb/src/object/mod.rs
Normal file
202
apb/src/object/mod.rs
Normal file
|
@ -0,0 +1,202 @@
|
|||
pub mod activity;
|
||||
pub mod actor;
|
||||
pub mod collection;
|
||||
pub mod document;
|
||||
pub mod tombstone;
|
||||
pub mod place;
|
||||
pub mod profile;
|
||||
pub mod relationship;
|
||||
|
||||
use crate::{getter, setter, strenum};
|
||||
|
||||
use super::{Base, BaseMut, Link, Node};
|
||||
|
||||
use actor::{Actor, ActorType};
|
||||
use document::{Document, DocumentType};
|
||||
use activity::ActivityType;
|
||||
use collection::{Collection, CollectionType};
|
||||
|
||||
strenum! {
|
||||
pub enum ObjectType {
|
||||
Object,
|
||||
Article,
|
||||
Event,
|
||||
Note,
|
||||
Place,
|
||||
Profile,
|
||||
Relationship,
|
||||
Tombstone;
|
||||
|
||||
Activity(ActivityType),
|
||||
Actor(ActorType),
|
||||
Collection(CollectionType),
|
||||
Document(DocumentType)
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Object : Base {
|
||||
type Link : Link;
|
||||
type Actor : Actor;
|
||||
type Object : Object;
|
||||
type Collection : Collection;
|
||||
type Document : Document;
|
||||
|
||||
fn object_type(&self) -> Option<ObjectType> { None }
|
||||
fn attachment(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn attributed_to(&self) -> Node<Self::Actor> { Node::Empty }
|
||||
fn audience(&self) -> Node<Self::Actor> { Node::Empty }
|
||||
fn content(&self) -> Option<&str> { None } // TODO handle language maps
|
||||
fn context(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn name(&self) -> Option<&str> { None } // also in link // TODO handle language maps
|
||||
fn end_time(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
fn generator(&self) -> Node<Self::Actor> { Node::Empty }
|
||||
fn icon(&self) -> Node<Self::Document> { Node::Empty }
|
||||
fn image(&self) -> Node<Self::Document> { Node::Empty }
|
||||
fn in_reply_to(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn location(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn preview(&self) -> Node<Self::Object> { Node::Empty } // also in link
|
||||
fn published(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
fn replies(&self) -> Node<Self::Collection> { Node::Empty }
|
||||
fn start_time(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
fn summary(&self) -> Option<&str> { None }
|
||||
fn tag(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn updated(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
fn url(&self) -> Node<Self::Link> { Node::Empty }
|
||||
fn to(&self) -> Node<Self::Link> { Node::Empty }
|
||||
fn bto(&self) -> Node<Self::Link> { Node::Empty }
|
||||
fn cc(&self) -> Node<Self::Link> { Node::Empty }
|
||||
fn bcc(&self) -> Node<Self::Link> { Node::Empty }
|
||||
fn media_type(&self) -> Option<&str> { None } // also in link
|
||||
fn duration(&self) -> Option<&str> { None } // TODO how to parse xsd:duration ?
|
||||
}
|
||||
|
||||
pub trait ObjectMut : BaseMut {
|
||||
type Link : Link;
|
||||
type Actor : Actor;
|
||||
type Object : Object;
|
||||
type Collection : Collection;
|
||||
type Document : Document;
|
||||
|
||||
fn set_object_type(self, val: Option<ObjectType>) -> Self;
|
||||
fn set_attachment(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_attributed_to(self, val: Node<Self::Actor>) -> Self;
|
||||
fn set_audience(self, val: Node<Self::Actor>) -> Self;
|
||||
fn set_content(self, val: Option<&str>) -> Self; // TODO handle language maps
|
||||
fn set_context(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_name(self, val: Option<&str>) -> Self; // also in link // TODO handle language maps
|
||||
fn set_end_time(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
fn set_generator(self, val: Node<Self::Actor>) -> Self;
|
||||
fn set_icon(self, val: Node<Self::Document>) -> Self;
|
||||
fn set_image(self, val: Node<Self::Document>) -> Self;
|
||||
fn set_in_reply_to(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_location(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_preview(self, val: Node<Self::Object>) -> Self; // also in link
|
||||
fn set_published(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
fn set_replies(self, val: Node<Self::Collection>) -> Self;
|
||||
fn set_start_time(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
fn set_summary(self, val: Option<&str>) -> Self;
|
||||
fn set_tag(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_updated(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
fn set_url(self, val: Node<Self::Link>) -> Self;
|
||||
fn set_to(self, val: Node<Self::Link>) -> Self;
|
||||
fn set_bto(self, val: Node<Self::Link>) -> Self;
|
||||
fn set_cc(self, val: Node<Self::Link>) -> Self;
|
||||
fn set_bcc(self, val: Node<Self::Link>) -> Self;
|
||||
fn set_media_type(self, val: Option<&str>) -> Self; // also in link
|
||||
fn set_duration(self, val: Option<&str>) -> Self; // TODO how to parse xsd:duration ?
|
||||
}
|
||||
|
||||
impl Object for serde_json::Value {
|
||||
type Link = serde_json::Value;
|
||||
type Actor = serde_json::Value;
|
||||
type Object = serde_json::Value;
|
||||
type Document = serde_json::Value;
|
||||
type Collection = serde_json::Value;
|
||||
|
||||
getter! { object_type -> type ObjectType }
|
||||
getter! { attachment -> node <Self as Object>::Object }
|
||||
getter! { attributed_to::attributedTo -> node Self::Actor }
|
||||
getter! { audience -> node Self::Actor }
|
||||
getter! { content -> &str }
|
||||
getter! { name -> &str }
|
||||
getter! { end_time::endTime -> chrono::DateTime<chrono::Utc> }
|
||||
getter! { generator -> node Self::Actor }
|
||||
getter! { icon -> node Self::Document }
|
||||
getter! { image -> node Self::Document }
|
||||
getter! { in_reply_to::inReplyTo -> node <Self as Object>::Object }
|
||||
getter! { location -> node <Self as Object>::Object }
|
||||
getter! { preview -> node <Self as Object>::Object }
|
||||
getter! { published -> chrono::DateTime<chrono::Utc> }
|
||||
getter! { replies -> node Self::Collection }
|
||||
getter! { start_time::startTime -> chrono::DateTime<chrono::Utc> }
|
||||
getter! { summary -> &str }
|
||||
getter! { tag -> node <Self as Object>::Object }
|
||||
getter! { updated -> chrono::DateTime<chrono::Utc> }
|
||||
getter! { to -> node Self::Link }
|
||||
getter! { bto -> node Self::Link }
|
||||
getter! { cc -> node Self::Link }
|
||||
getter! { bcc -> node Self::Link }
|
||||
getter! { media_type -> &str }
|
||||
getter! { duration -> &str }
|
||||
getter! { url -> node Self::Link }
|
||||
|
||||
// TODO Mastodon doesn't use a "context" field on the object but makes up a new one!!
|
||||
fn context(&self) -> Node<<Self as Object>::Object> {
|
||||
match self.get("context") {
|
||||
Some(x) => Node::from(x.clone()),
|
||||
None => match self.get("conversation") {
|
||||
Some(x) => Node::from(x.clone()),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectMut for serde_json::Value {
|
||||
type Link = serde_json::Value;
|
||||
type Actor = serde_json::Value;
|
||||
type Object = serde_json::Value;
|
||||
type Document = serde_json::Value;
|
||||
type Collection = serde_json::Value;
|
||||
|
||||
setter! { object_type -> type ObjectType }
|
||||
setter! { attachment -> node <Self as Object>::Object }
|
||||
setter! { attributed_to::attributedTo -> node Self::Actor }
|
||||
setter! { audience -> node Self::Actor }
|
||||
setter! { content -> &str }
|
||||
setter! { name -> &str }
|
||||
setter! { end_time::endTime -> chrono::DateTime<chrono::Utc> }
|
||||
setter! { generator -> node Self::Actor }
|
||||
setter! { icon -> node Self::Document }
|
||||
setter! { image -> node Self::Document }
|
||||
setter! { in_reply_to::inReplyTo -> node <Self as Object>::Object }
|
||||
setter! { location -> node <Self as Object>::Object }
|
||||
setter! { preview -> node <Self as Object>::Object }
|
||||
setter! { published -> chrono::DateTime<chrono::Utc> }
|
||||
setter! { replies -> node Self::Collection }
|
||||
setter! { start_time::startTime -> chrono::DateTime<chrono::Utc> }
|
||||
setter! { summary -> &str }
|
||||
setter! { tag -> node <Self as Object>::Object }
|
||||
setter! { updated -> chrono::DateTime<chrono::Utc> }
|
||||
setter! { to -> node Self::Link }
|
||||
setter! { bto -> node Self::Link}
|
||||
setter! { cc -> node Self::Link }
|
||||
setter! { bcc -> node Self::Link }
|
||||
setter! { media_type -> &str }
|
||||
setter! { duration -> &str }
|
||||
setter! { url -> node Self::Link }
|
||||
|
||||
// TODO Mastodon doesn't use a "context" field on the object but makes up a new one!!
|
||||
fn set_context(mut self, ctx: Node<<Self as Object>::Object>) -> Self {
|
||||
if let Some(conversation) = ctx.id() {
|
||||
crate::macros::set_maybe_value(
|
||||
&mut self, "conversation", Some(serde_json::Value::String(conversation)),
|
||||
);
|
||||
}
|
||||
crate::macros::set_maybe_node(
|
||||
&mut self, "context", ctx
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
}
|
|
@ -1,17 +1,17 @@
|
|||
use crate::activitystream::Node;
|
||||
use crate::Node;
|
||||
|
||||
pub trait Relationship : super::Object {
|
||||
fn subject(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
fn subject(&self) -> Node<Self::Object> { Node::Empty }
|
||||
fn relationship(&self) -> Option<&str> { None } // TODO what does this mean???
|
||||
// TODO was just object but clashes with Activity
|
||||
fn relationship_object(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
fn relationship_object(&self) -> Node<Self::Object> { Node::Empty }
|
||||
}
|
||||
|
||||
pub trait RelationshipMut : super::ObjectMut {
|
||||
fn set_subject(self, val: Node<impl super::Object>) -> Self;
|
||||
fn set_subject(self, val: Node<Self::Object>) -> Self;
|
||||
fn set_relationship(self, val: Option<&str>) -> Self; // TODO what does this mean???
|
||||
// TODO was just object but clashes with Activity
|
||||
fn set_relationship_object(self, val: Node<impl super::Object>) -> Self;
|
||||
fn set_relationship_object(self, val: Node<Self::Object>) -> Self;
|
||||
}
|
||||
|
||||
impl Relationship for serde_json::Value {
|
|
@ -1,10 +1,10 @@
|
|||
pub trait Tombstone : super::Object {
|
||||
fn former_type(&self) -> Option<super::super::BaseType> { None }
|
||||
fn former_type(&self) -> Option<crate::BaseType> { None }
|
||||
fn deleted(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
}
|
||||
|
||||
pub trait TombstoneMut : super::ObjectMut {
|
||||
fn set_former_type(self, val: Option<super::super::BaseType>) -> Self;
|
||||
fn set_former_type(self, val: Option<crate::BaseType>) -> Self;
|
||||
fn set_deleted(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use axum::{extract::{Path, State}, http::StatusCode};
|
||||
use sea_orm::EntityTrait;
|
||||
use crate::{activitystream::{object::{activity::ActivityMut, ObjectMut}, BaseMut, Node}, model::{self, activity, object}, server::Context};
|
||||
use crate::{model::{self, activity, object}, server::Context};
|
||||
use apb::{ActivityMut, ObjectMut, BaseMut, Node};
|
||||
|
||||
use super::{jsonld::LD, JsonLD};
|
||||
|
||||
|
@ -13,9 +14,9 @@ pub fn ap_activity(activity: model::activity::Model) -> serde_json::Value {
|
|||
.set_target(Node::maybe_link(activity.target))
|
||||
.set_published(Some(activity.published))
|
||||
.set_to(Node::links(activity.to.0.clone()))
|
||||
.set_bto(Node::empty())
|
||||
.set_bto(Node::Empty)
|
||||
.set_cc(Node::links(activity.cc.0.clone()))
|
||||
.set_bcc(Node::empty())
|
||||
.set_bcc(Node::Empty)
|
||||
}
|
||||
|
||||
pub async fn view(State(ctx) : State<Context>, Path(id): Path<String>) -> Result<JsonLD<serde_json::Value>, StatusCode> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use axum::{extract::{Query, State}, http::StatusCode};
|
||||
use sea_orm::{ColumnTrait, Condition, EntityTrait, Order, QueryFilter, QueryOrder, QuerySelect};
|
||||
|
||||
use crate::{activitystream::Node, auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url};
|
||||
use crate::{auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url};
|
||||
|
||||
use super::{activity::ap_activity, jsonld::LD, JsonLD, Pagination, PUBLIC_TARGET};
|
||||
|
||||
|
@ -39,8 +39,8 @@ pub async fn page(
|
|||
offset, limit,
|
||||
activities
|
||||
.into_iter()
|
||||
.filter_map(|(_, a)| Some(Node::object(ap_activity(a?))))
|
||||
.collect::<Vec<Node<serde_json::Value>>>()
|
||||
.filter_map(|(_, a)| Some(ap_activity(a?)))
|
||||
.collect::<Vec<serde_json::Value>>()
|
||||
).ld_context()
|
||||
))
|
||||
}
|
||||
|
|
|
@ -12,10 +12,25 @@ use axum::{extract::State, http::StatusCode, response::IntoResponse, Json};
|
|||
use rand::Rng;
|
||||
use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter};
|
||||
|
||||
use crate::{activitystream::{key::PublicKeyMut, object::{actor::{ActorMut, ActorType}, ObjectMut}, BaseMut, Node}, model, server::Context, url};
|
||||
use apb::{PublicKeyMut, ActorMut, ActorType, Link, Object, ObjectMut, BaseMut, Node};
|
||||
use crate::{model, server::Context, url};
|
||||
|
||||
use self::jsonld::LD;
|
||||
|
||||
pub trait Addressed : Object {
|
||||
fn addressed(&self) -> Vec<String>;
|
||||
}
|
||||
|
||||
impl Addressed for serde_json::Value {
|
||||
fn addressed(&self) -> Vec<String> {
|
||||
let mut to : Vec<String> = self.to().map(|x| x.href().to_string()).collect();
|
||||
to.append(&mut self.bto().map(|x| x.href().to_string()).collect());
|
||||
to.append(&mut self.cc().map(|x| x.href().to_string()).collect());
|
||||
to.append(&mut self.bcc().map(|x| x.href().to_string()).collect());
|
||||
to
|
||||
}
|
||||
}
|
||||
|
||||
pub const PUBLIC_TARGET : &str = "https://www.w3.org/ns/activitystreams#Public";
|
||||
|
||||
pub fn split_id(id: &str) -> (String, String) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use axum::{extract::{Path, State}, http::StatusCode};
|
||||
use sea_orm::EntityTrait;
|
||||
|
||||
use crate::{activitystream::{object::ObjectMut, BaseMut, Node}, model::{self, object}, server::Context};
|
||||
use apb::{ObjectMut, BaseMut, Node};
|
||||
use crate::{model::{self, object}, server::Context};
|
||||
|
||||
use super::{jsonld::LD, JsonLD};
|
||||
|
||||
|
@ -16,9 +17,9 @@ pub fn ap_object(object: model::object::Model) -> serde_json::Value {
|
|||
.set_context(Node::maybe_link(object.context.clone()))
|
||||
.set_published(Some(object.published))
|
||||
.set_to(Node::links(object.to.0.clone()))
|
||||
.set_bto(Node::empty())
|
||||
.set_bto(Node::Empty)
|
||||
.set_cc(Node::links(object.cc.0.clone()))
|
||||
.set_bcc(Node::empty())
|
||||
.set_bcc(Node::Empty)
|
||||
}
|
||||
|
||||
pub async fn view(State(ctx) : State<Context>, Path(id): Path<String>) -> Result<JsonLD<serde_json::Value>, StatusCode> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use axum::{extract::{Path, Query, State}, http::StatusCode};
|
||||
use sea_orm::{ColumnTrait, Condition, EntityTrait, PaginatorTrait, QueryFilter, QuerySelect, SelectColumns};
|
||||
|
||||
use crate::{activitypub::{jsonld::LD, JsonLD, Pagination}, activitystream::Node, model, server::Context, url};
|
||||
use crate::{activitypub::{jsonld::LD, JsonLD, Pagination}, model, server::Context, url};
|
||||
|
||||
use model::relation::Column::{Following, Follower};
|
||||
|
||||
|
@ -51,7 +51,7 @@ pub async fn page<const OUTGOING: bool>(
|
|||
&url!(ctx, "/users/{id}/{follow___}"),
|
||||
offset,
|
||||
limit,
|
||||
following.into_iter().map(Node::link).collect()
|
||||
following.into_iter().map(serde_json::Value::String).collect()
|
||||
).ld_context()
|
||||
))
|
||||
},
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use axum::{extract::{Path, Query, State}, http::StatusCode, Json};
|
||||
use sea_orm::{sea_query::Expr, ColumnTrait, Condition, EntityTrait, IntoActiveModel, Order, QueryFilter, QueryOrder, QuerySelect, Set};
|
||||
|
||||
use crate::{activitypub::{activity::ap_activity, jsonld::LD, JsonLD, Pagination}, activitystream::{object::{activity::{Activity, ActivityType}, Addressed, Object, ObjectType}, Base, BaseType, Node}, auth::{AuthIdentity, Identity}, errors::{LoggableError, UpubError}, model, server::Context, url};
|
||||
use apb::{Activity, ActivityType, Object, ObjectType, Base, BaseType};
|
||||
use crate::{activitypub::{activity::ap_activity, jsonld::LD, Addressed, JsonLD, Pagination}, auth::{AuthIdentity, Identity}, errors::{LoggableError, UpubError}, model, server::Context, url};
|
||||
|
||||
pub async fn get(
|
||||
State(ctx): State<Context>,
|
||||
|
@ -48,8 +49,8 @@ pub async fn page(
|
|||
offset, limit,
|
||||
activities
|
||||
.into_iter()
|
||||
.filter_map(|(_, a)| Some(Node::object(ap_activity(a?))))
|
||||
.collect::<Vec<Node<serde_json::Value>>>()
|
||||
.filter_map(|(_, a)| Some(ap_activity(a?)))
|
||||
.collect::<Vec<serde_json::Value>>()
|
||||
).ld_context()
|
||||
))
|
||||
},
|
||||
|
@ -84,7 +85,7 @@ pub async fn post(
|
|||
|
||||
Some(BaseType::Object(ObjectType::Activity(ActivityType::Delete))) => {
|
||||
// TODO verify the signature before just deleting lmao
|
||||
let oid = object.object().id().ok_or(StatusCode::BAD_REQUEST)?.to_string();
|
||||
let oid = object.object().id().ok_or(StatusCode::BAD_REQUEST)?;
|
||||
// TODO maybe we should keep the tombstone?
|
||||
model::user::Entity::delete_by_id(&oid).exec(ctx.db()).await.info_failed("failed deleting from users");
|
||||
model::activity::Entity::delete_by_id(&oid).exec(ctx.db()).await.info_failed("failed deleting from activities");
|
||||
|
@ -152,8 +153,8 @@ pub async fn post(
|
|||
},
|
||||
|
||||
Some(BaseType::Object(ObjectType::Activity(ActivityType::Like))) => {
|
||||
let aid = object.actor().id().ok_or(StatusCode::BAD_REQUEST)?.to_string();
|
||||
let oid = object.object().id().ok_or(StatusCode::BAD_REQUEST)?.to_string();
|
||||
let aid = object.actor().id().ok_or(StatusCode::BAD_REQUEST)?;
|
||||
let oid = object.object().id().ok_or(StatusCode::BAD_REQUEST)?;
|
||||
let like = model::like::ActiveModel {
|
||||
id: sea_orm::ActiveValue::NotSet,
|
||||
actor: sea_orm::Set(aid.clone()),
|
||||
|
@ -182,7 +183,7 @@ pub async fn post(
|
|||
Some(BaseType::Object(ObjectType::Activity(ActivityType::Create))) => {
|
||||
let activity_model = model::activity::Model::new(&object)?;
|
||||
let activity_targets = object.addressed();
|
||||
let Some(object_node) = object.object().get() else {
|
||||
let Some(object_node) = object.object().extract() else {
|
||||
// TODO we could process non-embedded activities or arrays but im lazy rn
|
||||
tracing::error!("refusing to process activity without embedded object: {}", serde_json::to_string_pretty(&object).unwrap());
|
||||
return Err(StatusCode::UNPROCESSABLE_ENTITY.into());
|
||||
|
@ -200,7 +201,7 @@ pub async fn post(
|
|||
Some(BaseType::Object(ObjectType::Activity(ActivityType::Update))) => {
|
||||
let activity_model = model::activity::Model::new(&object)?;
|
||||
let activity_targets = object.addressed();
|
||||
let Some(object_node) = object.object().get() else {
|
||||
let Some(object_node) = object.object().extract() else {
|
||||
// TODO we could process non-embedded activities or arrays but im lazy rn
|
||||
tracing::error!("refusing to process activity without embedded object: {}", serde_json::to_string_pretty(&object).unwrap());
|
||||
return Err(StatusCode::UNPROCESSABLE_ENTITY.into());
|
||||
|
@ -214,7 +215,7 @@ pub async fn post(
|
|||
Some(ObjectType::Actor(_)) => {
|
||||
// TODO oof here is an example of the weakness of this model, we have to go all the way
|
||||
// back up to serde_json::Value because impl Object != impl Actor
|
||||
let actor_model = model::user::Model::new(&object_node.underlying_json_object())?;
|
||||
let actor_model = model::user::Model::new(&object_node)?;
|
||||
model::user::Entity::update(actor_model.into_active_model())
|
||||
.exec(ctx.db()).await?;
|
||||
},
|
||||
|
|
|
@ -7,7 +7,8 @@ pub mod following;
|
|||
use axum::{extract::{Path, State}, http::StatusCode};
|
||||
use sea_orm::EntityTrait;
|
||||
|
||||
use crate::{activitystream::{key::PublicKeyMut, object::{actor::ActorMut, document::{DocumentMut, DocumentType}, ObjectMut}, BaseMut, Node}, model::{self, user}, server::Context, url};
|
||||
use apb::{PublicKeyMut, ActorMut, DocumentMut, DocumentType, ObjectMut, BaseMut, Node};
|
||||
use crate::{model::{self, user}, server::Context, url};
|
||||
|
||||
use super::{jsonld::LD, JsonLD};
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use axum::{extract::{Path, Query, State}, http::StatusCode, Json};
|
||||
use sea_orm::{EntityTrait, IntoActiveModel, Order, QueryOrder, QuerySelect, Set};
|
||||
|
||||
use crate::{activitypub::{jsonld::LD, CreationResult, JsonLD, Pagination}, activitystream::{object::{activity::{accept::AcceptType, Activity, ActivityMut, ActivityType}, Addressed, ObjectMut}, Base, BaseMut, BaseType, Node, ObjectType}, auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url};
|
||||
use apb::{AcceptType, Activity, ActivityMut, ActivityType, ObjectMut, Base, BaseMut, BaseType, Node, ObjectType};
|
||||
use crate::{activitypub::{jsonld::LD, Addressed, CreationResult, JsonLD, Pagination}, auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url};
|
||||
|
||||
pub async fn get(
|
||||
State(ctx): State<Context>,
|
||||
|
@ -49,13 +50,11 @@ pub async fn page(
|
|||
.into_iter()
|
||||
.map(|(a, o)| {
|
||||
let oid = a.object.clone();
|
||||
Node::object(
|
||||
super::super::activity::ap_activity(a)
|
||||
.set_object(match o {
|
||||
Some(o) => Node::object(super::super::object::ap_object(o)),
|
||||
None => Node::maybe_link(oid),
|
||||
})
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
).ld_context()
|
||||
|
@ -114,7 +113,7 @@ pub async fn post(
|
|||
},
|
||||
|
||||
Some(BaseType::Object(ObjectType::Activity(ActivityType::Create))) => {
|
||||
let Some(object) = activity.object().get().map(|x| x.underlying_json_object()) else {
|
||||
let Some(object) = activity.object().extract() else {
|
||||
return Err(StatusCode::BAD_REQUEST.into());
|
||||
};
|
||||
let oid = ctx.oid(uuid::Uuid::new_v4().to_string());
|
||||
|
@ -152,7 +151,7 @@ pub async fn post(
|
|||
Some(BaseType::Object(ObjectType::Activity(ActivityType::Like))) => {
|
||||
let aid = ctx.aid(uuid::Uuid::new_v4().to_string());
|
||||
let activity_targets = activity.addressed();
|
||||
let Some(oid) = activity.object().id().map(|x| x.to_string()) else {
|
||||
let Some(oid) = activity.object().id() else {
|
||||
return Err(StatusCode::BAD_REQUEST.into());
|
||||
};
|
||||
let activity_model = model::activity::Model::new(
|
||||
|
@ -164,7 +163,7 @@ pub async fn post(
|
|||
|
||||
let like_model = model::like::ActiveModel {
|
||||
actor: Set(uid.clone()),
|
||||
likes: Set(oid.clone()),
|
||||
likes: Set(oid),
|
||||
date: Set(chrono::Utc::now()),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
pub mod link;
|
||||
pub use link::{Link, LinkType};
|
||||
|
||||
pub mod object;
|
||||
pub use object::{Object, ObjectType};
|
||||
|
||||
pub mod node;
|
||||
pub use node::Node;
|
||||
|
||||
pub mod macros;
|
||||
pub mod prelude;
|
||||
|
||||
pub mod key;
|
||||
|
||||
use crate::{getter, setter, strenum};
|
||||
|
||||
strenum! {
|
||||
pub enum BaseType {
|
||||
;
|
||||
Object(ObjectType),
|
||||
Link(LinkType)
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Base {
|
||||
fn id(&self) -> Option<&str> { None }
|
||||
fn base_type(&self) -> Option<BaseType> { None }
|
||||
|
||||
// TODO this is a dirty fix because my trait model is flawed and leads to circular resolution
|
||||
// errors, basically can't downcast back to serde_json::Value once i've updasted it to
|
||||
// impl Object/Actor/whatever... ask me to infodump+bikeshed about this!!! :3
|
||||
fn underlying_json_object(self) -> serde_json::Value;
|
||||
}
|
||||
|
||||
|
||||
pub trait BaseMut {
|
||||
fn set_id(self, val: Option<&str>) -> Self;
|
||||
fn set_base_type(self, val: Option<BaseType>) -> Self;
|
||||
}
|
||||
|
||||
|
||||
impl Base for String {
|
||||
fn id(&self) -> Option<&str> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn base_type(&self) -> Option<BaseType> {
|
||||
Some(BaseType::Link(LinkType::Link))
|
||||
}
|
||||
|
||||
fn underlying_json_object(self) -> serde_json::Value {
|
||||
serde_json::Value::String(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Base for serde_json::Value {
|
||||
fn underlying_json_object(self) -> serde_json::Value {
|
||||
self
|
||||
}
|
||||
|
||||
getter! { id -> &str }
|
||||
getter! { base_type -> type BaseType }
|
||||
}
|
||||
|
||||
impl BaseMut for serde_json::Value {
|
||||
setter! { id -> &str }
|
||||
setter! { base_type -> type BaseType }
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
pub enum Node<T : super::Base> {
|
||||
Array(Vec<Node<T>>), // TODO would be cool to make it Box<[Node<T>]> so that Node is just a ptr
|
||||
Object(Box<T>),
|
||||
Link(Box<dyn super::Link>),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl<T : super::Base> From<Option<T>> for Node<T> {
|
||||
fn from(value: Option<T>) -> Self {
|
||||
match value {
|
||||
Some(x) => Node::Object(Box::new(x)),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T : super::Base> Node<T> {
|
||||
pub fn get(self) -> Option<T> {
|
||||
match self {
|
||||
Node::Empty | Node::Link(_) => None,
|
||||
Node::Object(x) => Some(*x),
|
||||
Node::Array(v) => v.into_iter().find_map(|x| match x {
|
||||
Node::Object(x) => Some(*x),
|
||||
_ => None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO extremely unforgiving, is this even useful?
|
||||
pub fn get_items(&self) -> Option<Vec<&T>> {
|
||||
match self {
|
||||
Node::Empty | Node::Link(_) => None,
|
||||
Node::Object(x) => Some(vec![x]),
|
||||
Node::Array(v) =>
|
||||
Some(v.iter().filter_map(|x| match x {
|
||||
Node::Object(x) => Some(&**x),
|
||||
_ => None,
|
||||
}).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_links(&self) -> Vec<String> {
|
||||
match self {
|
||||
Node::Empty => vec![],
|
||||
Node::Link(x) => vec![x.href().to_string()],
|
||||
Node::Object(x) => match x.id() {
|
||||
Some(x) => vec![x.to_string()],
|
||||
None => vec![],
|
||||
},
|
||||
Node::Array(v) =>
|
||||
v.iter().filter_map(|x| match x {
|
||||
Node::Link(x) => Some(x.href().to_string()),
|
||||
Node::Object(x) => x.id().map(|x| x.to_string()),
|
||||
// TODO handle array of arrays maybe?
|
||||
_ => None,
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
Node::Empty | Node::Link(_) => true,
|
||||
Node::Object(_) | Node::Array(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Node::Empty => 0,
|
||||
Node::Link(_) => 0,
|
||||
Node::Object(_) => 1,
|
||||
Node::Array(v) => v.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flat(self) -> Vec<serde_json::Value> {
|
||||
match self {
|
||||
Node::Empty => vec![],
|
||||
Node::Link(l) => vec![serde_json::Value::String(l.href().to_string())],
|
||||
Node::Object(x) => vec![x.underlying_json_object()],
|
||||
Node::Array(arr) => {
|
||||
arr
|
||||
.into_iter()
|
||||
.filter_map(|node| match node {
|
||||
Node::Empty => None,
|
||||
Node::Link(l) => Some(serde_json::Value::String(l.href().to_string())),
|
||||
Node::Object(o) => Some(o.underlying_json_object()),
|
||||
Node::Array(_) => Some(serde_json::Value::Array(node.flat())),
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<String> {
|
||||
match self {
|
||||
Node::Empty => None,
|
||||
Node::Link(uri) => Some(uri.href().to_string()),
|
||||
Node::Object(obj) => obj.id().map(|x| x.to_string()),
|
||||
Node::Array(arr) => arr.first()?.id().map(|x| x.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<serde_json::Value>{
|
||||
pub fn empty() -> Self {
|
||||
Node::Empty
|
||||
}
|
||||
|
||||
pub fn link(uri: String) -> Self {
|
||||
Node::Link(Box::new(uri))
|
||||
}
|
||||
|
||||
pub fn links(uris: Vec<String>) -> Self {
|
||||
Node::Array(
|
||||
uris
|
||||
.into_iter()
|
||||
.map(Node::link)
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn maybe_link(uri: Option<String>) -> Self {
|
||||
match uri {
|
||||
Some(uri) => Node::Link(Box::new(uri)),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn object(x: impl super::Base) -> Self {
|
||||
Node::Object(Box::new(x.underlying_json_object()))
|
||||
}
|
||||
|
||||
pub fn maybe_object(x: Option<impl super::Base>) -> Self {
|
||||
match x {
|
||||
Some(x) => Node::Object(Box::new(x.underlying_json_object())),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn array(x: Vec<impl super::Base>) -> Self {
|
||||
Node::Array(x.into_iter().map(|x| Node::object(x.underlying_json_object())).collect())
|
||||
}
|
||||
|
||||
pub async fn fetch(&mut self) -> reqwest::Result<()> {
|
||||
if let Node::Link(link) = self {
|
||||
*self = reqwest::Client::new()
|
||||
.get(link.href())
|
||||
.header("Accept", "application/json")
|
||||
.send()
|
||||
.await?
|
||||
.json::<serde_json::Value>()
|
||||
.await?
|
||||
.into();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<&str>> for Node<serde_json::Value> {
|
||||
fn from(value: Option<&str>) -> Self {
|
||||
match value {
|
||||
Some(x) => Node::Link(Box::new(x.to_string())),
|
||||
None => Node::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Node<serde_json::Value> {
|
||||
fn from(value: &str) -> Self {
|
||||
Node::Link(Box::new(value.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Value> for Node<serde_json::Value> {
|
||||
fn from(value: serde_json::Value) -> Self {
|
||||
match value {
|
||||
serde_json::Value::String(uri) => Node::Link(Box::new(uri)),
|
||||
serde_json::Value::Object(_) => match value.get("href") {
|
||||
None => Node::Object(Box::new(value)),
|
||||
Some(_) => Node::Link(Box::new(value)),
|
||||
},
|
||||
serde_json::Value::Array(arr) => Node::Array(
|
||||
arr
|
||||
.into_iter()
|
||||
.map(Self::from)
|
||||
.collect()
|
||||
),
|
||||
_ => Node::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
pub mod accept;
|
||||
pub mod ignore;
|
||||
pub mod intransitive;
|
||||
pub mod offer;
|
||||
pub mod reject;
|
||||
|
||||
use crate::activitystream::Node;
|
||||
use crate::{getter, setter, strenum};
|
||||
use accept::AcceptType;
|
||||
use reject::RejectType;
|
||||
use offer::OfferType;
|
||||
use intransitive::IntransitiveActivityType;
|
||||
use ignore::IgnoreType;
|
||||
|
||||
strenum! {
|
||||
pub enum ActivityType {
|
||||
Activity,
|
||||
Add,
|
||||
Announce,
|
||||
Create,
|
||||
Delete,
|
||||
Dislike,
|
||||
Flag,
|
||||
Follow,
|
||||
Join,
|
||||
Leave,
|
||||
Like,
|
||||
Listen,
|
||||
Move,
|
||||
Read,
|
||||
Remove,
|
||||
Undo,
|
||||
Update,
|
||||
View;
|
||||
|
||||
IntransitiveActivity(IntransitiveActivityType),
|
||||
Accept(AcceptType),
|
||||
Ignore(IgnoreType),
|
||||
Offer(OfferType),
|
||||
Reject(RejectType)
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Activity : super::Object {
|
||||
fn activity_type(&self) -> Option<ActivityType> { None }
|
||||
fn actor(&self) -> Node<impl super::Actor> { Node::Empty::<serde_json::Value> }
|
||||
fn object(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
fn target(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
fn result(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
fn origin(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
fn instrument(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
}
|
||||
|
||||
pub trait ActivityMut : super::ObjectMut {
|
||||
fn set_activity_type(self, val: Option<ActivityType>) -> Self;
|
||||
fn set_actor(self, val: Node<impl super::Actor>) -> Self;
|
||||
fn set_object(self, val: Node<impl super::Object>) -> Self;
|
||||
fn set_target(self, val: Node<impl super::Object>) -> Self;
|
||||
fn set_result(self, val: Node<impl super::Object>) -> Self;
|
||||
fn set_origin(self, val: Node<impl super::Object>) -> Self;
|
||||
fn set_instrument(self, val: Node<impl super::Object>) -> Self;
|
||||
}
|
||||
|
||||
impl Activity for serde_json::Value {
|
||||
getter! { activity_type -> type ActivityType }
|
||||
getter! { actor -> node impl super::Actor }
|
||||
getter! { object -> node impl super::Object }
|
||||
getter! { target -> node impl super::Object }
|
||||
getter! { result -> node impl super::Object }
|
||||
getter! { origin -> node impl super::Object }
|
||||
getter! { instrument -> node impl super::Object }
|
||||
}
|
||||
|
||||
impl ActivityMut for serde_json::Value {
|
||||
setter! { activity_type -> type ActivityType }
|
||||
setter! { actor -> node impl super::Actor }
|
||||
setter! { object -> node impl super::Object }
|
||||
setter! { target -> node impl super::Object }
|
||||
setter! { result -> node impl super::Object }
|
||||
setter! { origin -> node impl super::Object }
|
||||
setter! { instrument -> node impl super::Object }
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
use crate::{activitystream::Node, getter, setter, strenum};
|
||||
|
||||
use super::collection::Collection;
|
||||
use super::super::key::PublicKey;
|
||||
|
||||
strenum! {
|
||||
pub enum ActorType {
|
||||
Application,
|
||||
Group,
|
||||
Organization,
|
||||
Person;
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Actor : super::Object {
|
||||
fn actor_type(&self) -> Option<ActorType> { None }
|
||||
fn preferred_username(&self) -> Option<&str> { None }
|
||||
fn inbox(&self) -> Node<impl Collection>;
|
||||
fn outbox(&self) -> Node<impl Collection>;
|
||||
fn following(&self) -> Node<impl Collection> { Node::empty() }
|
||||
fn followers(&self) -> Node<impl Collection> { Node::empty() }
|
||||
fn liked(&self) -> Node<impl Collection> { Node::empty() }
|
||||
fn streams(&self) -> Node<impl Collection> { Node::empty() }
|
||||
fn endpoints(&self) -> Option<serde_json::Map<String, String>> { None }
|
||||
fn public_key(&self) -> Node<impl PublicKey> { Node::empty() }
|
||||
// idk about this? everyone has it but AP doesn't mention it
|
||||
fn discoverable(&self) -> Option<bool> { None }
|
||||
}
|
||||
|
||||
pub trait ActorMut : super::ObjectMut {
|
||||
fn set_actor_type(self, val: Option<ActorType>) -> Self;
|
||||
fn set_preferred_username(self, val: Option<&str>) -> Self;
|
||||
fn set_inbox(self, val: Node<impl Collection>) -> Self;
|
||||
fn set_outbox(self, val: Node<impl Collection>) -> Self;
|
||||
fn set_following(self, val: Node<impl Collection>) -> Self;
|
||||
fn set_followers(self, val: Node<impl Collection>) -> Self;
|
||||
fn set_liked(self, val: Node<impl Collection>) -> Self;
|
||||
fn set_streams(self, val: Node<impl Collection>) -> Self;
|
||||
fn set_endpoints(self, val: Option<serde_json::Map<String, String>>) -> Self;
|
||||
fn set_public_key(self, val: Node<impl PublicKey>) -> Self;
|
||||
fn set_discoverable(self, val: Option<bool>) -> Self;
|
||||
}
|
||||
|
||||
impl Actor for serde_json::Value {
|
||||
getter! { actor_type -> type ActorType }
|
||||
getter! { preferred_username::preferredUsername -> &str }
|
||||
getter! { inbox -> node impl Collection }
|
||||
getter! { outbox -> node impl Collection }
|
||||
getter! { following -> node impl Collection }
|
||||
getter! { followers -> node impl Collection }
|
||||
getter! { liked -> node impl Collection }
|
||||
getter! { streams -> node impl Collection }
|
||||
getter! { public_key::publicKey -> node impl PublicKey }
|
||||
getter! { discoverable -> bool }
|
||||
|
||||
fn endpoints(&self) -> Option<serde_json::Map<String, String>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActorMut for serde_json::Value {
|
||||
setter! { actor_type -> type ActorType }
|
||||
setter! { preferred_username::preferredUsername -> &str }
|
||||
setter! { inbox -> node impl Collection }
|
||||
setter! { outbox -> node impl Collection }
|
||||
setter! { following -> node impl Collection }
|
||||
setter! { followers -> node impl Collection }
|
||||
setter! { liked -> node impl Collection }
|
||||
setter! { streams -> node impl Collection }
|
||||
setter! { public_key::publicKey -> node impl PublicKey }
|
||||
setter! { discoverable -> bool }
|
||||
|
||||
fn set_endpoints(mut self, _val: Option<serde_json::Map<String, String>>) -> Self {
|
||||
self.as_object_mut().unwrap().insert("endpoints".to_string(), serde_json::Value::Object(serde_json::Map::default()));
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
pub mod page;
|
||||
pub use page::CollectionPage;
|
||||
|
||||
use crate::activitystream::Node;
|
||||
use crate::{getter, setter, strenum};
|
||||
|
||||
strenum! {
|
||||
pub enum CollectionType {
|
||||
Collection,
|
||||
CollectionPage,
|
||||
OrderedCollection,
|
||||
OrderedCollectionPage;
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Collection : super::Object {
|
||||
fn collection_type(&self) -> Option<CollectionType> { None }
|
||||
|
||||
fn total_items(&self) -> Option<u64> { None }
|
||||
fn current(&self) -> Node<impl CollectionPage> { Node::Empty::<serde_json::Value> }
|
||||
fn first(&self) -> Node<impl CollectionPage> { Node::Empty::<serde_json::Value> }
|
||||
fn last(&self) -> Node<impl CollectionPage> { Node::Empty::<serde_json::Value> }
|
||||
fn items(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
fn ordered_items(&self) -> Node<impl super::Object> { Node::Empty::<serde_json::Value> }
|
||||
}
|
||||
|
||||
pub trait CollectionMut : super::ObjectMut {
|
||||
fn set_collection_type(self, val: Option<CollectionType>) -> Self;
|
||||
fn set_total_items(self, val: Option<u64>) -> Self;
|
||||
fn set_current(self, val: Node<impl CollectionPage>) -> Self;
|
||||
fn set_first(self, val: Node<impl CollectionPage>) -> Self;
|
||||
fn set_last(self, val: Node<impl CollectionPage>) -> Self;
|
||||
fn set_items(self, val: Node<impl super::Object>) -> Self;
|
||||
fn set_ordered_items(self, val: Node<impl super::Object>) -> Self;
|
||||
}
|
||||
|
||||
impl Collection for serde_json::Value {
|
||||
getter! { collection_type -> type CollectionType }
|
||||
getter! { total_items::totalItems -> u64 }
|
||||
getter! { current -> node impl CollectionPage }
|
||||
getter! { first -> node impl CollectionPage }
|
||||
getter! { last -> node impl CollectionPage }
|
||||
getter! { items -> node impl super::Object }
|
||||
getter! { ordered_items::orderedItems -> node impl super::Object }
|
||||
}
|
||||
|
||||
impl CollectionMut for serde_json::Value {
|
||||
setter! { collection_type -> type CollectionType }
|
||||
setter! { total_items::totalItems -> u64 }
|
||||
setter! { current -> node impl CollectionPage }
|
||||
setter! { first -> node impl CollectionPage }
|
||||
setter! { last -> node impl CollectionPage }
|
||||
setter! { items -> node impl super::Object }
|
||||
setter! { ordered_items::orderedItems -> node impl super::Object }
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
use crate::{activitystream::Node, getter, setter};
|
||||
|
||||
pub trait CollectionPage : super::Collection {
|
||||
fn part_of(&self) -> Node<impl super::Collection> { Node::Empty::<serde_json::Value> }
|
||||
fn next(&self) -> Node<impl CollectionPage> { Node::Empty::<serde_json::Value> }
|
||||
fn prev(&self) -> Node<impl CollectionPage> { Node::Empty::<serde_json::Value> }
|
||||
}
|
||||
|
||||
pub trait CollectionPageMut : super::CollectionMut {
|
||||
fn set_part_of(self, val: Node<impl super::Collection>) -> Self;
|
||||
fn set_next(self, val: Node<impl CollectionPage>) -> Self;
|
||||
fn set_prev(self, val: Node<impl CollectionPage>) -> Self;
|
||||
}
|
||||
|
||||
impl CollectionPage for serde_json::Value {
|
||||
getter! { part_of::partOf -> node impl super::Collection }
|
||||
getter! { next -> node impl CollectionPage }
|
||||
getter! { prev -> node impl CollectionPage }
|
||||
}
|
||||
|
||||
impl CollectionPageMut for serde_json::Value {
|
||||
setter! { part_of::partOf -> node impl super::Collection }
|
||||
setter! { next -> node impl CollectionPage }
|
||||
setter! { prev -> node impl CollectionPage }
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
pub mod activity;
|
||||
pub mod actor;
|
||||
pub mod collection;
|
||||
pub mod document;
|
||||
pub mod tombstone;
|
||||
pub mod place;
|
||||
pub mod profile;
|
||||
pub mod relationship;
|
||||
|
||||
use crate::{getter, setter, strenum};
|
||||
|
||||
use super::{Link, Node};
|
||||
|
||||
use actor::{Actor, ActorType};
|
||||
use document::{Image, DocumentType};
|
||||
use activity::ActivityType;
|
||||
use collection::{Collection, CollectionType};
|
||||
|
||||
strenum! {
|
||||
pub enum ObjectType {
|
||||
Object,
|
||||
Article,
|
||||
Event,
|
||||
Note,
|
||||
Place,
|
||||
Profile,
|
||||
Relationship,
|
||||
Tombstone;
|
||||
|
||||
Activity(ActivityType),
|
||||
Actor(ActorType),
|
||||
Collection(CollectionType),
|
||||
Document(DocumentType)
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Object : super::Base {
|
||||
fn object_type(&self) -> Option<ObjectType> { None }
|
||||
fn attachment(&self) -> Node<impl Object> { Node::Empty::<serde_json::Value> }
|
||||
fn attributed_to(&self) -> Node<impl Actor> { Node::Empty::<serde_json::Value> }
|
||||
fn audience(&self) -> Node<impl Actor> { Node::Empty::<serde_json::Value> }
|
||||
fn content(&self) -> Option<&str> { None } // TODO handle language maps
|
||||
fn context(&self) -> Node<impl Object> { Node::Empty::<serde_json::Value> }
|
||||
fn name(&self) -> Option<&str> { None } // also in link // TODO handle language maps
|
||||
fn end_time(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
fn generator(&self) -> Node<impl Actor> { Node::Empty::<serde_json::Value> }
|
||||
fn icon(&self) -> Node<impl Image> { Node::Empty::<serde_json::Value> }
|
||||
fn image(&self) -> Node<impl Image> { Node::Empty::<serde_json::Value> }
|
||||
fn in_reply_to(&self) -> Node<impl Object> { Node::Empty::<serde_json::Value> }
|
||||
fn location(&self) -> Node<impl Object> { Node::Empty::<serde_json::Value> }
|
||||
fn preview(&self) -> Node<impl Object> { Node::Empty::<serde_json::Value> } // also in link
|
||||
fn published(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
fn replies(&self) -> Node<impl Collection> { Node::Empty::<serde_json::Value> }
|
||||
fn start_time(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
fn summary(&self) -> Option<&str> { None }
|
||||
fn tag(&self) -> Node<impl Object> { Node::Empty::<serde_json::Value> }
|
||||
fn updated(&self) -> Option<chrono::DateTime<chrono::Utc>> { None }
|
||||
fn url(&self) -> Node<impl super::Link> { Node::empty() }
|
||||
fn to(&self) -> Node<impl Link> { Node::Empty::<serde_json::Value> }
|
||||
fn bto(&self) -> Node<impl Link> { Node::Empty::<serde_json::Value> }
|
||||
fn cc(&self) -> Node<impl Link> { Node::Empty::<serde_json::Value> }
|
||||
fn bcc(&self) -> Node<impl Link> { Node::Empty::<serde_json::Value> }
|
||||
fn media_type(&self) -> Option<&str> { None } // also in link
|
||||
fn duration(&self) -> Option<&str> { None } // TODO how to parse xsd:duration ?
|
||||
}
|
||||
|
||||
pub trait Addressed : Object {
|
||||
fn addressed(&self) -> Vec<String> {
|
||||
let mut to = self.to().get_links();
|
||||
to.append(&mut self.bto().get_links());
|
||||
to.append(&mut self.cc().get_links());
|
||||
to.append(&mut self.bcc().get_links());
|
||||
to
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ObjectMut : super::BaseMut {
|
||||
fn set_object_type(self, val: Option<ObjectType>) -> Self;
|
||||
fn set_attachment(self, val: Node<impl Object>) -> Self;
|
||||
fn set_attributed_to(self, val: Node<impl Actor>) -> Self;
|
||||
fn set_audience(self, val: Node<impl Actor>) -> Self;
|
||||
fn set_content(self, val: Option<&str>) -> Self; // TODO handle language maps
|
||||
fn set_context(self, val: Node<impl Object>) -> Self;
|
||||
fn set_name(self, val: Option<&str>) -> Self; // also in link // TODO handle language maps
|
||||
fn set_end_time(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
fn set_generator(self, val: Node<impl Actor>) -> Self;
|
||||
fn set_icon(self, val: Node<impl Image>) -> Self;
|
||||
fn set_image(self, val: Node<impl Image>) -> Self;
|
||||
fn set_in_reply_to(self, val: Node<impl Object>) -> Self;
|
||||
fn set_location(self, val: Node<impl Object>) -> Self;
|
||||
fn set_preview(self, val: Node<impl Object>) -> Self; // also in link
|
||||
fn set_published(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
fn set_replies(self, val: Node<impl Collection>) -> Self;
|
||||
fn set_start_time(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
fn set_summary(self, val: Option<&str>) -> Self;
|
||||
fn set_tag(self, val: Node<impl Object>) -> Self;
|
||||
fn set_updated(self, val: Option<chrono::DateTime<chrono::Utc>>) -> Self;
|
||||
fn set_url(self, val: Node<impl super::Link>) -> Self;
|
||||
fn set_to(self, val: Node<impl Link>) -> Self;
|
||||
fn set_bto(self, val: Node<impl Link>) -> Self;
|
||||
fn set_cc(self, val: Node<impl Link>) -> Self;
|
||||
fn set_bcc(self, val: Node<impl Link>) -> Self;
|
||||
fn set_media_type(self, val: Option<&str>) -> Self; // also in link
|
||||
fn set_duration(self, val: Option<&str>) -> Self; // TODO how to parse xsd:duration ?
|
||||
}
|
||||
|
||||
impl Object for serde_json::Value {
|
||||
|
||||
getter! { object_type -> type ObjectType }
|
||||
getter! { attachment -> node impl Object }
|
||||
getter! { attributed_to::attributedTo -> node impl Actor }
|
||||
getter! { audience -> node impl Actor }
|
||||
getter! { content -> &str }
|
||||
getter! { name -> &str }
|
||||
getter! { end_time::endTime -> chrono::DateTime<chrono::Utc> }
|
||||
getter! { generator -> node impl Actor }
|
||||
getter! { icon -> node impl Image }
|
||||
getter! { image -> node impl Image }
|
||||
getter! { in_reply_to::inReplyTo -> node impl Object }
|
||||
getter! { location -> node impl Object }
|
||||
getter! { preview -> node impl Object }
|
||||
getter! { published -> chrono::DateTime<chrono::Utc> }
|
||||
getter! { replies -> node impl Collection }
|
||||
getter! { start_time::startTime -> chrono::DateTime<chrono::Utc> }
|
||||
getter! { summary -> &str }
|
||||
getter! { tag -> node impl Object }
|
||||
getter! { updated -> chrono::DateTime<chrono::Utc> }
|
||||
getter! { to -> node impl Link }
|
||||
getter! { bto -> node impl Link }
|
||||
getter! { cc -> node impl Link }
|
||||
getter! { bcc -> node impl Link }
|
||||
getter! { media_type -> &str }
|
||||
getter! { duration -> &str }
|
||||
getter! { url -> node impl super::Link }
|
||||
|
||||
// TODO Mastodon doesn't use a "context" field on the object but makes up a new one!!
|
||||
fn context(&self) -> Node<impl Object> {
|
||||
match self.get("context") {
|
||||
Some(x) => Node::from(x.clone()),
|
||||
None => match self.get("conversation") {
|
||||
Some(x) => Node::from(x.clone()),
|
||||
None => Node::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Addressed for serde_json::Value {}
|
||||
|
||||
impl ObjectMut for serde_json::Value {
|
||||
setter! { object_type -> type ObjectType }
|
||||
setter! { attachment -> node impl Object }
|
||||
setter! { attributed_to::attributedTo -> node impl Actor }
|
||||
setter! { audience -> node impl Actor }
|
||||
setter! { content -> &str }
|
||||
setter! { name -> &str }
|
||||
setter! { end_time::endTime -> chrono::DateTime<chrono::Utc> }
|
||||
setter! { generator -> node impl Actor }
|
||||
setter! { icon -> node impl Image }
|
||||
setter! { image -> node impl Image }
|
||||
setter! { in_reply_to::inReplyTo -> node impl Object }
|
||||
setter! { location -> node impl Object }
|
||||
setter! { preview -> node impl Object }
|
||||
setter! { published -> chrono::DateTime<chrono::Utc> }
|
||||
setter! { replies -> node impl Collection }
|
||||
setter! { start_time::startTime -> chrono::DateTime<chrono::Utc> }
|
||||
setter! { summary -> &str }
|
||||
setter! { tag -> node impl Object }
|
||||
setter! { updated -> chrono::DateTime<chrono::Utc> }
|
||||
setter! { to -> node impl Link }
|
||||
setter! { bto -> node impl Link}
|
||||
setter! { cc -> node impl Link }
|
||||
setter! { bcc -> node impl Link }
|
||||
setter! { media_type -> &str }
|
||||
setter! { duration -> &str }
|
||||
setter! { url -> node impl super::Link }
|
||||
|
||||
// TODO Mastodon doesn't use a "context" field on the object but makes up a new one!!
|
||||
fn set_context(mut self, ctx: Node<impl Object>) -> Self {
|
||||
if let Some(conversation) = ctx.id() {
|
||||
crate::activitystream::macros::set_maybe_value(
|
||||
&mut self, "conversation", Some(serde_json::Value::String(conversation.to_string())),
|
||||
);
|
||||
}
|
||||
crate::activitystream::macros::set_maybe_node(
|
||||
&mut self, "context", ctx
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
pub use super::{
|
||||
Base as _, BaseMut as _,
|
||||
key::{PublicKey as _, PublicKeyMut as _},
|
||||
link::{Link as _, LinkMut as _},
|
||||
object::{
|
||||
Object as _, ObjectMut as _,
|
||||
tombstone::{Tombstone as _, TombstoneMut as _},
|
||||
relationship::{Relationship as _, RelationshipMut as _},
|
||||
profile::{Profile as _, /* ProfileMut as _ */}, // TODO!
|
||||
place::{Place as _, PlaceMut as _},
|
||||
actor::{Actor as _, ActorMut as _},
|
||||
document::{
|
||||
Document as _, DocumentMut as _, Image as _,
|
||||
},
|
||||
collection::{
|
||||
Collection as _, CollectionMut as _,
|
||||
page::{CollectionPage as _, CollectionPageMut as _},
|
||||
},
|
||||
activity::{
|
||||
Activity as _, ActivityMut as _,
|
||||
reject::{Reject as _, RejectMut as _},
|
||||
offer::{Offer as _, OfferMut as _},
|
||||
intransitive::{IntransitiveActivity as _, IntransitiveActivityMut as _},
|
||||
ignore::{Ignore as _, IgnoreMut as _},
|
||||
accept::{Accept as _, AcceptMut as _},
|
||||
},
|
||||
}
|
||||
};
|
|
@ -4,7 +4,8 @@ use reqwest::header::{CONTENT_TYPE, USER_AGENT};
|
|||
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, Order, QueryFilter, QueryOrder};
|
||||
use tokio::{sync::broadcast, task::JoinHandle};
|
||||
|
||||
use crate::{activitypub::{activity::ap_activity, object::ap_object}, activitystream::{object::activity::ActivityMut, Node}, errors::UpubError, model, server::Context, VERSION};
|
||||
use apb::{ActivityMut, Node};
|
||||
use crate::{activitypub::{activity::ap_activity, object::ap_object}, errors::UpubError, model, server::Context, VERSION};
|
||||
|
||||
pub struct Dispatcher {
|
||||
waker: broadcast::Sender<()>,
|
||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -1,4 +1,3 @@
|
|||
pub mod activitystream;
|
||||
pub mod activitypub;
|
||||
|
||||
mod model;
|
||||
|
@ -16,8 +15,6 @@ use sea_orm_migration::MigratorTrait;
|
|||
|
||||
pub use errors::UpubResult as Result;
|
||||
|
||||
use crate::activitystream::{BaseType, ObjectType};
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
#[derive(Parser)]
|
||||
|
@ -102,9 +99,9 @@ async fn main() {
|
|||
|
||||
|
||||
async fn fetch(db: &sea_orm::DatabaseConnection, uri: &str, save: bool) -> reqwest::Result<()> {
|
||||
use crate::activitystream::{Base, Object};
|
||||
use apb::{Base, Object};
|
||||
|
||||
let mut node = activitystream::Node::from(uri);
|
||||
let mut node = apb::Node::from(uri);
|
||||
tracing::info!("fetching object");
|
||||
node.fetch().await?;
|
||||
tracing::info!("fetched node");
|
||||
|
@ -115,23 +112,23 @@ async fn fetch(db: &sea_orm::DatabaseConnection, uri: &str, save: bool) -> reqwe
|
|||
|
||||
if save {
|
||||
match obj.base_type() {
|
||||
Some(BaseType::Object(ObjectType::Actor(_))) => {
|
||||
Some(apb::BaseType::Object(apb::ObjectType::Actor(_))) => {
|
||||
model::user::Entity::insert(
|
||||
model::user::Model::new(&obj).unwrap().into_active_model()
|
||||
model::user::Model::new(obj).unwrap().into_active_model()
|
||||
).exec(db).await.unwrap();
|
||||
},
|
||||
Some(BaseType::Object(ObjectType::Activity(_))) => {
|
||||
Some(apb::BaseType::Object(apb::ObjectType::Activity(_))) => {
|
||||
model::activity::Entity::insert(
|
||||
model::activity::Model::new(&obj).unwrap().into_active_model()
|
||||
model::activity::Model::new(obj).unwrap().into_active_model()
|
||||
).exec(db).await.unwrap();
|
||||
},
|
||||
Some(BaseType::Object(ObjectType::Note)) => {
|
||||
Some(apb::BaseType::Object(apb::ObjectType::Note)) => {
|
||||
model::object::Entity::insert(
|
||||
model::object::Model::new(&obj).unwrap().into_active_model()
|
||||
model::object::Model::new(obj).unwrap().into_active_model()
|
||||
).exec(db).await.unwrap();
|
||||
},
|
||||
Some(BaseType::Object(t)) => tracing::warn!("not implemented: {:?}", t),
|
||||
Some(BaseType::Link(_)) => tracing::error!("fetched another link?"),
|
||||
Some(apb::BaseType::Object(t)) => tracing::warn!("not implemented: {:?}", t),
|
||||
Some(apb::BaseType::Link(_)) => tracing::error!("fetched another link?"),
|
||||
None => tracing::error!("no type on object"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use sea_orm::entity::prelude::*;
|
||||
|
||||
use crate::activitystream::object::activity::{Activity, ActivityType};
|
||||
|
||||
use super::Audience;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
|
@ -10,7 +8,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key)]
|
||||
pub id: String,
|
||||
|
||||
pub activity_type: ActivityType,
|
||||
pub activity_type: apb::ActivityType,
|
||||
pub actor: String,
|
||||
pub object: Option<String>,
|
||||
|
||||
|
@ -25,13 +23,13 @@ pub struct Model {
|
|||
}
|
||||
|
||||
impl Model {
|
||||
pub fn new(activity: &impl Activity) -> Result<Self, super::FieldError> {
|
||||
pub fn new(activity: &impl apb::Activity) -> Result<Self, super::FieldError> {
|
||||
Ok(Model {
|
||||
id: activity.id().ok_or(super::FieldError("id"))?.to_string(),
|
||||
activity_type: activity.activity_type().ok_or(super::FieldError("type"))?,
|
||||
actor: activity.actor().id().ok_or(super::FieldError("actor"))?.to_string(),
|
||||
object: activity.object().id().map(|x| x.to_string()),
|
||||
target: activity.target().id().map(|x| x.to_string()),
|
||||
actor: activity.actor().id().ok_or(super::FieldError("actor"))?,
|
||||
object: activity.object().id(),
|
||||
target: activity.target().id(),
|
||||
published: activity.published().unwrap_or(chrono::Utc::now()),
|
||||
to: activity.to().into(),
|
||||
bto: activity.bto().into(),
|
||||
|
|
|
@ -22,7 +22,7 @@ pub async fn faker(db: &sea_orm::DatabaseConnection, domain: String, count: u64)
|
|||
inbox: None,
|
||||
shared_inbox: None,
|
||||
outbox: None,
|
||||
actor_type: crate::activitystream::object::actor::ActorType::Person,
|
||||
actor_type: apb::ActorType::Person,
|
||||
created: chrono::Utc::now(),
|
||||
updated: chrono::Utc::now(),
|
||||
private_key: Some(std::str::from_utf8(&key.private_key_to_pem().unwrap()).unwrap().to_string()),
|
||||
|
@ -55,7 +55,7 @@ pub async fn faker(db: &sea_orm::DatabaseConnection, domain: String, count: u64)
|
|||
object::Entity::insert(object::ActiveModel {
|
||||
id: Set(format!("{domain}/objects/{oid}")),
|
||||
name: Set(None),
|
||||
object_type: Set(crate::activitystream::object::ObjectType::Note),
|
||||
object_type: Set(apb::ObjectType::Note),
|
||||
attributed_to: Set(Some(format!("{domain}/users/test"))),
|
||||
summary: Set(None),
|
||||
context: Set(Some(context.clone())),
|
||||
|
@ -72,7 +72,7 @@ pub async fn faker(db: &sea_orm::DatabaseConnection, domain: String, count: u64)
|
|||
|
||||
activity::Entity::insert(activity::ActiveModel {
|
||||
id: Set(format!("{domain}/activities/{aid}")),
|
||||
activity_type: Set(crate::activitystream::object::activity::ActivityType::Create),
|
||||
activity_type: Set(apb::ActivityType::Create),
|
||||
actor: Set(format!("{domain}/users/test")),
|
||||
object: Set(Some(format!("{domain}/objects/{oid}"))),
|
||||
target: Set(None),
|
||||
|
|
|
@ -28,7 +28,7 @@ impl From<FieldError> for axum::http::StatusCode {
|
|||
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, sea_orm::FromJsonQueryResult)]
|
||||
pub struct Audience(pub Vec<String>);
|
||||
|
||||
use crate::activitystream::{Link, Node};
|
||||
use apb::{Link, Node};
|
||||
impl<T : Link> From<Node<T>> for Audience {
|
||||
fn from(value: Node<T>) -> Self {
|
||||
Audience(
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use sea_orm::entity::prelude::*;
|
||||
|
||||
use crate::activitystream::object::ObjectType;
|
||||
|
||||
use super::Audience;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
|
@ -9,7 +7,7 @@ use super::Audience;
|
|||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: String,
|
||||
pub object_type: ObjectType,
|
||||
pub object_type: apb::ObjectType,
|
||||
pub attributed_to: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub summary: Option<String>,
|
||||
|
@ -26,15 +24,15 @@ pub struct Model {
|
|||
}
|
||||
|
||||
impl Model {
|
||||
pub fn new(object: &impl crate::activitystream::object::Object) -> Result<Self, super::FieldError> {
|
||||
pub fn new(object: &impl apb::Object) -> Result<Self, super::FieldError> {
|
||||
Ok(Model {
|
||||
id: object.id().ok_or(super::FieldError("id"))?.to_string(),
|
||||
object_type: object.object_type().ok_or(super::FieldError("type"))?,
|
||||
attributed_to: object.attributed_to().id().map(|x| x.to_string()),
|
||||
attributed_to: object.attributed_to().id(),
|
||||
name: object.name().map(|x| x.to_string()),
|
||||
summary: object.summary().map(|x| x.to_string()),
|
||||
content: object.content().map(|x| x.to_string()),
|
||||
context: object.context().id().map(|x| x.to_string()),
|
||||
context: object.context().id(),
|
||||
published: object.published().ok_or(super::FieldError("published"))?,
|
||||
comments: 0,
|
||||
likes: 0,
|
||||
|
|
|
@ -11,5 +11,6 @@ pub struct Model {
|
|||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
// TODO how to represent this User-to-User relation in sea orm??
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use sea_orm::entity::prelude::*;
|
||||
use crate::activitystream::key::PublicKey as _;
|
||||
|
||||
use crate::{activitypub, activitystream::object::{collection::Collection, actor::{Actor, ActorType}}};
|
||||
use apb::{Collection, Actor, PublicKey, ActorType};
|
||||
use crate::activitypub;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "users")]
|
||||
|
@ -46,13 +46,13 @@ impl Model {
|
|||
actor_type: object.actor_type().ok_or(super::FieldError("type"))?,
|
||||
name: object.name().map(|x| x.to_string()),
|
||||
summary: object.summary().map(|x| x.to_string()),
|
||||
icon: object.icon().id().map(|x| x.to_string()),
|
||||
image: object.image().id().map(|x| x.to_string()),
|
||||
inbox: object.inbox().id().map(|x| x.to_string()),
|
||||
outbox: object.inbox().id().map(|x| x.to_string()),
|
||||
icon: object.icon().id(),
|
||||
image: object.image().id(),
|
||||
inbox: object.inbox().id(),
|
||||
outbox: object.inbox().id(),
|
||||
shared_inbox: None, // TODO!!! parse endpoints
|
||||
followers: object.followers().id().map(|x| x.to_string()),
|
||||
following: object.following().id().map(|x| x.to_string()),
|
||||
followers: object.followers().id(),
|
||||
following: object.following().id(),
|
||||
created: object.published().unwrap_or(chrono::Utc::now()),
|
||||
updated: chrono::Utc::now(),
|
||||
following_count: object.following().get().map(|f| f.total_items().unwrap_or(0)).unwrap_or(0) as i64,
|
||||
|
|
|
@ -3,7 +3,8 @@ use std::{str::Utf8Error, sync::Arc};
|
|||
use openssl::rsa::Rsa;
|
||||
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QuerySelect, SelectColumns, Set};
|
||||
|
||||
use crate::{activitypub::{jsonld::LD, PUBLIC_TARGET}, activitystream::{object::collection::{page::CollectionPageMut, CollectionMut, CollectionType}, BaseMut, Node}, dispatcher::Dispatcher, fetcher::Fetcher, model};
|
||||
use crate::{activitypub::{jsonld::LD, PUBLIC_TARGET}, dispatcher::Dispatcher, fetcher::Fetcher, model};
|
||||
use apb::{CollectionPageMut, CollectionMut, CollectionType, BaseMut, Node};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Context(Arc<ContextInner>);
|
||||
|
@ -215,7 +216,7 @@ impl Context {
|
|||
.set_total_items(total_items)
|
||||
}
|
||||
|
||||
pub fn ap_collection_page(&self, id: &str, offset: u64, limit: u64, items: Vec<Node<serde_json::Value>>) -> serde_json::Value {
|
||||
pub fn ap_collection_page(&self, id: &str, offset: u64, limit: u64, items: Vec<serde_json::Value>) -> serde_json::Value {
|
||||
serde_json::Value::new_object()
|
||||
.set_id(Some(&format!("{id}?offset={offset}")))
|
||||
.set_collection_type(Some(CollectionType::OrderedCollectionPage))
|
||||
|
|
Loading…
Reference in a new issue