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 |