Option to include published timestamp for sent activities

This commit is contained in:
Felix Ableitner 2024-03-19 16:38:23 +01:00
parent 1b46dd6f80
commit 05ab30a1b9
5 changed files with 32 additions and 7 deletions

View file

@ -15,7 +15,7 @@ axum = ["dep:axum", "dep:tower", "dep:hyper", "dep:http-body-util"]
diesel = ["dep:diesel"]
[dependencies]
chrono = { version = "0.4.34", features = ["clock"], default-features = false }
chrono = { version = "0.4.34", features = ["clock", "serde"], default-features = false }
serde = { version = "1.0.197", features = ["derive"] }
async-trait = "0.1.77"
url = { version = "2.5.0", features = ["serde"] }

View file

@ -119,7 +119,7 @@ impl DbUser {
if use_queue {
queue_activity(&activity, self, recipients, data).await?;
} else {
let sends = SendActivityTask::prepare(&activity, self, recipients, data).await?;
let sends = SendActivityTask::prepare(&activity, self, recipients, None, data).await?;
for send in sends {
send.sign_and_send(data).await?;
}

View file

@ -49,7 +49,7 @@ where
ActorType: Actor,
{
let config = &data.config;
let tasks = build_tasks(activity, actor, inboxes, data).await?;
let tasks = build_tasks(activity, actor, inboxes, None, data).await?;
for task in tasks {
// Don't use the activity queue if this is in debug mode, send and wait directly

View file

@ -19,10 +19,10 @@ use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use reqwest_middleware::ClientWithMiddleware;
use serde::Serialize;
use std::{
self,
fmt::{Debug, Display},
time::{Duration, SystemTime},
};
use chrono::{DateTime, Utc};
use tracing::debug;
use url::Url;
@ -51,10 +51,13 @@ impl SendActivityTask {
/// - `inboxes`: List of remote actor inboxes that should receive the activity. Ignores local actor
/// inboxes. Should be built by calling [crate::traits::Actor::shared_inbox_or_inbox]
/// for each target actor.
/// - `published`: Time when the activity was created, so that recipient can apply activities
/// in correct order.
pub async fn prepare<Activity, Datatype, ActorType>(
activity: &Activity,
actor: &ActorType,
inboxes: Vec<Url>,
published: Option<DateTime<Utc>>,
data: &Data<Datatype>,
) -> Result<Vec<SendActivityTask>, Error>
where
@ -62,7 +65,7 @@ impl SendActivityTask {
Datatype: Clone,
ActorType: Actor,
{
build_tasks(activity, actor, inboxes, data).await
build_tasks(activity, actor, inboxes, published, data).await
}
/// convert a sendactivitydata to a request, signing and sending it
@ -117,6 +120,7 @@ pub(crate) async fn build_tasks<'a, Activity, Datatype, ActorType>(
activity: &'a Activity,
actor: &ActorType,
inboxes: Vec<Url>,
published: Option<DateTime<Utc>>,
data: &Data<Datatype>,
) -> Result<Vec<SendActivityTask>, Error>
where
@ -127,7 +131,10 @@ where
let config = &data.config;
let actor_id = activity.actor();
let activity_id = activity.id();
let activity_serialized: Bytes = serde_json::to_vec(activity)
let activity_serialized: Bytes = match published {
Some(published) => serde_json::to_vec(&WithPublished::new(activity, published)),
None => serde_json::to_vec(activity)
}
.map_err(|e| Error::SerializeOutgoingActivity(e, format!("{:?}", activity)))?
.into();
let private_key = get_pkey_cached(data, actor).await?;
@ -210,6 +217,24 @@ pub(crate) fn generate_request_headers(inbox_url: &Url) -> HeaderMap {
headers
}
/// Wrapper struct that adds `published` field with timestamp to outgoing activities. Important that
/// the timestamp includes milliseconds and timezone.
#[derive(Serialize)]
struct WithPublished<T> {
published: DateTime<Utc>,
#[serde(flatten)]
inner: T,
}
impl<T> WithPublished<T> {
pub fn new(inner: T, published: DateTime<Utc>) -> WithPublished<T> {
Self {
published,
inner,
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -343,7 +343,7 @@ pub mod tests {
error::Error,
fetch::object_id::ObjectId,
http_signatures::{generate_actor_keypair, Keypair},
protocol::{public_key::PublicKey, verification::verify_domains_match},
protocol::{verification::verify_domains_match},
};
use activitystreams_kinds::{activity::FollowType, actor::PersonType};
use once_cell::sync::Lazy;