Implement outboxes
This commit is contained in:
parent
ca5640b138
commit
91a91b9c16
4 changed files with 113 additions and 12 deletions
|
@ -136,10 +136,13 @@ fn create_activity(
|
|||
instance_url,
|
||||
actor_name,
|
||||
);
|
||||
let activity_id = get_object_url(
|
||||
let mut activity_id = get_object_url(
|
||||
instance_url,
|
||||
internal_activity_id.unwrap_or(&new_uuid()),
|
||||
);
|
||||
if activity_type == CREATE {
|
||||
activity_id.push_str("/create");
|
||||
};
|
||||
Activity {
|
||||
context: json!(AP_CONTEXT),
|
||||
id: activity_id,
|
||||
|
@ -241,7 +244,7 @@ pub fn create_activity_note(
|
|||
instance_url,
|
||||
&post.author.username,
|
||||
CREATE,
|
||||
None,
|
||||
Some(&post.id),
|
||||
object,
|
||||
recipients,
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@ use serde::Serialize;
|
|||
use serde_json::{json, Value};
|
||||
|
||||
use super::constants::AP_CONTEXT;
|
||||
use super::vocabulary::ORDERED_COLLECTION;
|
||||
use super::vocabulary::{ORDERED_COLLECTION, ORDERED_COLLECTION_PAGE};
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -14,14 +14,53 @@ pub struct OrderedCollection {
|
|||
|
||||
#[serde(rename = "type")]
|
||||
pub object_type: String,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
first: Option<String>,
|
||||
}
|
||||
|
||||
impl OrderedCollection {
|
||||
pub fn new(collection_id: String) -> Self {
|
||||
pub fn new(
|
||||
collection_id: String,
|
||||
first_page_id: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
context: json!(AP_CONTEXT),
|
||||
id: collection_id,
|
||||
object_type: ORDERED_COLLECTION.to_string(),
|
||||
first: first_page_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const COLLECTION_PAGE_SIZE: i64 = 10;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OrderedCollectionPage {
|
||||
#[serde(rename = "@context")]
|
||||
pub context: Value,
|
||||
|
||||
pub id: String,
|
||||
|
||||
#[serde(rename = "type")]
|
||||
pub object_type: String,
|
||||
|
||||
ordered_items: Vec<Value>,
|
||||
}
|
||||
|
||||
impl OrderedCollectionPage {
|
||||
pub fn new(
|
||||
collection_page_id: String,
|
||||
items: Vec<impl Serialize>,
|
||||
) -> Self {
|
||||
let ordered_items = items.into_iter()
|
||||
.map(|item| json!(item)).collect();
|
||||
Self {
|
||||
context: json!(AP_CONTEXT),
|
||||
id: collection_page_id,
|
||||
object_type: ORDERED_COLLECTION_PAGE.to_string(),
|
||||
ordered_items,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,15 @@ use crate::database::{Pool, get_database_client};
|
|||
use crate::errors::HttpError;
|
||||
use crate::frontend::{get_post_page_url, get_profile_page_url};
|
||||
use crate::http_signatures::verify::verify_http_signature;
|
||||
use crate::models::posts::queries::get_thread;
|
||||
use crate::models::posts::queries::{get_posts_by_author, get_thread};
|
||||
use crate::models::users::queries::get_user_by_name;
|
||||
use super::activity::create_note;
|
||||
use super::activity::{create_note, create_activity_note};
|
||||
use super::actor::{get_local_actor, get_instance_actor};
|
||||
use super::collections::OrderedCollection;
|
||||
use super::collections::{
|
||||
COLLECTION_PAGE_SIZE,
|
||||
OrderedCollection,
|
||||
OrderedCollectionPage,
|
||||
};
|
||||
use super::constants::ACTIVITY_CONTENT_TYPE;
|
||||
use super::receiver::receive_activity;
|
||||
use super::vocabulary::DELETE;
|
||||
|
@ -116,7 +120,60 @@ async fn inbox(
|
|||
|
||||
#[derive(Deserialize)]
|
||||
struct CollectionQueryParams {
|
||||
page: Option<i32>,
|
||||
page: Option<bool>,
|
||||
}
|
||||
|
||||
#[get("/outbox")]
|
||||
async fn outbox(
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<Pool>,
|
||||
web::Path(username): web::Path<String>,
|
||||
query_params: web::Query<CollectionQueryParams>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
let instance = config.instance();
|
||||
let collection_id = get_outbox_url(&instance.url(), &username);
|
||||
let first_page_id = format!("{}?page=true", collection_id);
|
||||
if query_params.page.is_none() {
|
||||
let collection = OrderedCollection::new(
|
||||
collection_id,
|
||||
Some(first_page_id),
|
||||
);
|
||||
let response = HttpResponse::Ok()
|
||||
.content_type(ACTIVITY_CONTENT_TYPE)
|
||||
.json(collection);
|
||||
return Ok(response);
|
||||
};
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let user = get_user_by_name(db_client, &username).await?;
|
||||
// Post are ordered by creation date
|
||||
let posts = get_posts_by_author(
|
||||
db_client, &user.id,
|
||||
false, false,
|
||||
None, COLLECTION_PAGE_SIZE,
|
||||
).await?;
|
||||
// TODO: include reposts
|
||||
let activities: Vec<_> = posts.iter().filter_map(|post| {
|
||||
match post.repost_of_id {
|
||||
Some(_) => None,
|
||||
None => {
|
||||
let activity = create_activity_note(
|
||||
&instance.host(),
|
||||
&instance.url(),
|
||||
post,
|
||||
None,
|
||||
);
|
||||
Some(activity)
|
||||
},
|
||||
}
|
||||
}).collect();
|
||||
let collection_page = OrderedCollectionPage::new(
|
||||
first_page_id,
|
||||
activities,
|
||||
);
|
||||
let response = HttpResponse::Ok()
|
||||
.content_type(ACTIVITY_CONTENT_TYPE)
|
||||
.json(collection_page);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[get("/followers")]
|
||||
|
@ -129,8 +186,8 @@ async fn followers_collection(
|
|||
// Social graph is not available
|
||||
return Err(HttpError::PermissionError);
|
||||
}
|
||||
let collection_url = get_followers_url(&config.instance_url(), &username);
|
||||
let collection = OrderedCollection::new(collection_url);
|
||||
let collection_id = get_followers_url(&config.instance_url(), &username);
|
||||
let collection = OrderedCollection::new(collection_id, None);
|
||||
let response = HttpResponse::Ok()
|
||||
.content_type(ACTIVITY_CONTENT_TYPE)
|
||||
.json(collection);
|
||||
|
@ -147,8 +204,8 @@ async fn following_collection(
|
|||
// Social graph is not available
|
||||
return Err(HttpError::PermissionError);
|
||||
}
|
||||
let collection_url = get_following_url(&config.instance_url(), &username);
|
||||
let collection = OrderedCollection::new(collection_url);
|
||||
let collection_id = get_following_url(&config.instance_url(), &username);
|
||||
let collection = OrderedCollection::new(collection_id, None);
|
||||
let response = HttpResponse::Ok()
|
||||
.content_type(ACTIVITY_CONTENT_TYPE)
|
||||
.json(collection);
|
||||
|
@ -159,6 +216,7 @@ pub fn actor_scope() -> Scope {
|
|||
web::scope("/users/{username}")
|
||||
.service(actor_view)
|
||||
.service(inbox)
|
||||
.service(outbox)
|
||||
.service(followers_collection)
|
||||
.service(following_collection)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ pub const TOMBSTONE: &str = "Tombstone";
|
|||
|
||||
// Collections
|
||||
pub const ORDERED_COLLECTION: &str = "OrderedCollection";
|
||||
pub const ORDERED_COLLECTION_PAGE: &str = "OrderedCollectionPage";
|
||||
|
||||
// Misc
|
||||
pub const HASHTAG: &str = "Hashtag";
|
||||
|
|
Loading…
Reference in a new issue