Profiles: expose needed information for non-apub profile creation

- Add image purge to profile update
- Add outbound processing in apub ingest
- Add follower + follow request cleanup in profile delete
- Add inboxes + id to Outbound trait
This commit is contained in:
asonix 2021-01-06 02:21:17 -06:00
parent 49eeb48b72
commit ab8aa2cbe1
22 changed files with 721 additions and 143 deletions

View file

@ -12,7 +12,6 @@ actix-web = "3.3.2"
activitystreams = "0.7.0-alpha.9"
activitystreams-ext = "0.1.0-alpha.2"
chrono = { version = "0.4.19", features = ["serde"] }
hyaenidae-toolkit = { version = "0.1.0", path = "../toolkit" }
log = "0.4.11"
mime = "0.3.16"
rand = "0.7.0"

View file

@ -25,7 +25,9 @@ pub(crate) fn ingest(
match parse(any_base.clone(), context)? {
Ok(action) => {
action.perform(context)?;
if let Some(outbound) = action.perform(context)? {
context.deliver(&*outbound);
}
if let Some(any_base) = stack.pop() {
context.spawner.process(any_base, stack);
}
@ -39,6 +41,7 @@ pub(crate) fn ingest(
context.spawner.download_images(urls, stack);
}
}
Ok(())
}

View file

@ -1,5 +1,5 @@
use crate::{
apub::actions::{CreateProfile, DeleteProfile, UpdateProfile},
apub::actions::{CreateProfile, CreateProfileApub, DeleteProfile, UpdateProfile},
apub::ExtendedPerson,
recover,
store::OwnerSource,
@ -82,7 +82,11 @@ pub(crate) fn person(
.unwrap_or(true);
Ok(Ok(Box::new(CreateProfile {
person_apub_id: Some(id.to_owned()),
apub: Some(CreateProfileApub {
person_apub_id: id.to_owned(),
public_key_id,
public_key,
}),
owner_source: OwnerSource::Remote(id.to_string()),
handle,
domain,
@ -91,8 +95,6 @@ pub(crate) fn person(
login_required,
icon,
banner,
public_key_id,
public_key,
published,
})))
}
@ -153,11 +155,11 @@ pub(crate) fn update_person(
profile_id,
display_name,
description,
login_required,
login_required: Some(login_required),
icon,
banner,
public_key_id,
public_key,
public_key_id: Some(public_key_id),
public_key: Some(public_key),
})))
}

View file

@ -1,10 +1,10 @@
use crate::{
apub::actions::{CreateBlock, DeleteBlock, RejectFollowRequest, UndoFollow},
Action, Completed, Context, Error, Required,
Action, Context, Error, Outbound, Required,
};
impl Action for CreateBlock {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let block = context.store.view.blocks.new(
self.blocked_profile,
self.blocked_by_profile,
@ -88,7 +88,7 @@ impl Action for CreateBlock {
}
impl Action for DeleteBlock {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let opt = context.store.view.blocks.remove(self.block_id)?;
let block_apub_id = context.apub.apub_for_block(self.block_id)?.req()?;
context.apub.delete_object(&block_apub_id)?;
@ -98,6 +98,7 @@ impl Action for DeleteBlock {
return Ok(Some(Box::new(crate::apub::results::UndoBlock {
block_apub_id,
profile_id: undo_block.0.right,
blocked_id: undo_block.0.left,
})));
}
}

View file

@ -1,10 +1,10 @@
use crate::{
apub::actions::{AnnounceComment, CreateComment, DeleteComment, DeleteReact, UpdateComment},
Action, Completed, Context, Error, Required,
Action, Outbound, Context, Error, Required,
};
impl Action for CreateComment {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let submissioner_id = context
.store
.submissions
@ -79,13 +79,13 @@ impl Action for CreateComment {
}
impl Action for AnnounceComment {
fn perform(&self, _: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, _: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
Ok(None)
}
}
impl Action for UpdateComment {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let comment = context.store.comments.by_id(self.comment_id)?.req()?;
let mut changes = comment.update();
@ -126,7 +126,7 @@ fn delete_react(react_id: uuid::Uuid, context: &Context) -> Result<(), Error> {
}
impl Action for DeleteComment {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let opt = context.store.comments.delete(self.comment_id)?;
context.store.view.comments.remove(self.comment_id);
context.apub.delete_object(&self.note_apub_id)?;

View file

@ -1,10 +1,10 @@
use crate::{
apub::actions::{UndoAcceptFollow, UndoFollow},
Action, Completed, Context, Error, Required,
Action, Context, Error, Outbound, Required,
};
impl Action for UndoFollow {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let opt = context.store.view.follows.remove(self.follow_id)?;
let accept_apub_id = context.apub.apub_for_follow(self.follow_id)?.req()?;
context.apub.delete_object(&accept_apub_id)?;
@ -15,6 +15,7 @@ impl Action for UndoFollow {
return Ok(Some(Box::new(crate::apub::results::UndoFollow {
follow_apub_id: self.follow_apub_id.clone(),
profile_id: undo_follow.0.right,
followed_id: undo_follow.0.left,
})));
}
}
@ -24,7 +25,7 @@ impl Action for UndoFollow {
}
impl Action for UndoAcceptFollow {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let opt = context.store.view.follows.remove(self.follow_id)?;
let accept_apub_id = context.apub.apub_for_follow(self.follow_id)?.req()?;
context.apub.delete_object(&accept_apub_id)?;
@ -34,7 +35,8 @@ impl Action for UndoAcceptFollow {
if context.is_local(undo_follow.0.right)? {
return Ok(Some(Box::new(crate::apub::results::UndoAcceptFollow {
accept_apub_id,
profile_id: undo_follow.0.right,
profile_id: undo_follow.0.left,
requester_id: undo_follow.0.right,
})));
}
}

View file

@ -2,11 +2,11 @@ use crate::{
apub::actions::{
AcceptFollowRequest, CreateFollowRequest, RejectFollowRequest, UndoFollowRequest,
},
Action, Completed, Context, Error, Required,
Action, Context, Error, Outbound, Required,
};
impl Action for CreateFollowRequest {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
context.check_block(self.followed_by_profile, self.followed_profile)?;
let follow_request = context.store.view.follow_requests.new(
@ -40,7 +40,7 @@ impl Action for CreateFollowRequest {
}
impl Action for AcceptFollowRequest {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let opt = context
.store
.view
@ -82,7 +82,7 @@ impl Action for AcceptFollowRequest {
}
impl Action for RejectFollowRequest {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let opt = context
.store
.view
@ -108,6 +108,7 @@ impl Action for RejectFollowRequest {
return Ok(Some(Box::new(crate::apub::results::RejectFollow {
follow_apub_id,
profile_id: undo_follow_request.0.left,
requester_id: undo_follow_request.0.right,
})));
}
}
@ -117,7 +118,7 @@ impl Action for RejectFollowRequest {
}
impl Action for UndoFollowRequest {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let opt = context
.store
.view
@ -143,6 +144,7 @@ impl Action for UndoFollowRequest {
return Ok(Some(Box::new(crate::apub::results::UndoFollow {
follow_apub_id,
profile_id: undo_follow_request.0.right,
followed_id: undo_follow_request.0.left,
})));
}
}

View file

@ -72,8 +72,14 @@ pub struct DeleteSubmission {
submission_id: Uuid,
}
pub struct CreateProfileApub {
person_apub_id: Url,
public_key_id: Url,
public_key: String,
}
pub struct CreateProfile {
person_apub_id: Option<Url>,
apub: Option<CreateProfileApub>,
owner_source: OwnerSource,
handle: String,
domain: String,
@ -82,20 +88,89 @@ pub struct CreateProfile {
login_required: bool,
icon: Option<Uuid>,
banner: Option<Uuid>,
public_key_id: Url,
public_key: String,
published: DateTime<Utc>,
}
impl CreateProfile {
pub fn from_local(owner_id: Uuid, handle: String, domain: String) -> Self {
CreateProfile {
apub: None,
owner_source: OwnerSource::Local(owner_id),
handle,
domain,
display_name: None,
description: None,
login_required: true,
icon: None,
banner: None,
published: Utc::now(),
}
}
}
pub struct UpdateProfile {
profile_id: Uuid,
display_name: Option<String>,
description: Option<String>,
login_required: bool,
login_required: Option<bool>,
icon: Option<Uuid>,
banner: Option<Uuid>,
public_key_id: Url,
public_key: String,
public_key_id: Option<Url>,
public_key: Option<String>,
}
impl UpdateProfile {
pub fn from_text(profile_id: Uuid, display_name: String, description: String) -> Self {
UpdateProfile {
profile_id,
display_name: Some(display_name),
description: Some(description),
login_required: None,
icon: None,
banner: None,
public_key_id: None,
public_key: None,
}
}
pub fn from_icon(profile_id: Uuid, icon: Uuid) -> Self {
UpdateProfile {
profile_id,
display_name: None,
description: None,
login_required: None,
icon: Some(icon),
banner: None,
public_key_id: None,
public_key: None,
}
}
pub fn from_banner(profile_id: Uuid, banner: Uuid) -> Self {
UpdateProfile {
profile_id,
display_name: None,
description: None,
login_required: None,
icon: None,
banner: Some(banner),
public_key_id: None,
public_key: None,
}
}
pub fn from_login_required(profile_id: Uuid, login_required: bool) -> Self {
UpdateProfile {
profile_id,
display_name: None,
description: None,
login_required: Some(login_required),
icon: None,
banner: None,
public_key_id: None,
public_key: None,
}
}
}
pub struct DeleteProfile {

View file

@ -1,11 +1,11 @@
use crate::{
apub::actions::{CreateProfile, DeleteProfile, DeleteSubmission, UpdateProfile},
Action, Completed, Context, Error, Required,
Action, Context, Error, Outbound, Required,
};
impl Action for CreateProfile {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
let profile = context.store.profiles.create(
fn perform(&self, ctx: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let profile = ctx.store.profiles.create(
self.owner_source.clone(),
&self.handle,
&self.domain,
@ -20,27 +20,26 @@ impl Action for CreateProfile {
if let Some(description) = &self.description {
changes.description(description);
}
let profile = context.store.profiles.update(&changes)?;
let profile = ctx.store.profiles.update(&changes)?;
let mut changes = profile.update_images();
if let Some(banner) = self.banner {
if let Ok(Some(file)) = context.store.files.by_id(banner) {
if let Ok(Some(file)) = ctx.store.files.by_id(banner) {
changes.banner(&file);
}
}
if let Some(icon) = self.icon {
if let Ok(Some(file)) = context.store.files.by_id(icon) {
if let Ok(Some(file)) = ctx.store.files.by_id(icon) {
changes.banner(&file);
}
}
let profile = context.store.profiles.update_images(&changes)?;
let profile = ctx.store.profiles.update_images(&changes)?;
context
.apub
.store_public_key(profile.id(), &self.public_key_id, &self.public_key)?;
if let Some(apub) = &self.apub {
ctx.apub.profile(&apub.person_apub_id, profile.id())?;
if let Some(apub_id) = &self.person_apub_id {
context.apub.profile(apub_id, profile.id())?;
ctx.apub
.store_public_key(profile.id(), &apub.public_key_id, &apub.public_key)?;
}
if profile.owner_source().is_local() {
@ -54,34 +53,51 @@ impl Action for CreateProfile {
}
impl Action for UpdateProfile {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
let profile = context.store.profiles.by_id(self.profile_id)?.req()?;
fn perform(&self, ctx: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let profile = ctx.store.profiles.by_id(self.profile_id)?.req()?;
let mut changes = profile.update();
changes.login_required(self.login_required);
if let Some(login_required) = self.login_required {
changes.login_required(login_required);
}
if let Some(display_name) = &self.display_name {
changes.display_name(display_name);
}
if let Some(description) = &self.description {
changes.description(description);
}
let profile = context.store.profiles.update(&changes)?;
let profile = ctx.store.profiles.update(&changes)?;
let previous_banner = profile.banner();
let previous_icon = profile.icon();
let mut changes = profile.update_images();
if let Some(banner) = self.banner {
if let Ok(Some(file)) = context.store.files.by_id(banner) {
if let Ok(Some(file)) = ctx.store.files.by_id(banner) {
changes.banner(&file);
if let Some(old_banner) = previous_banner {
if old_banner != banner {
ctx.spawner.purge_file(old_banner);
}
}
}
}
if let Some(icon) = self.icon {
if let Ok(Some(file)) = context.store.files.by_id(icon) {
changes.banner(&file);
if let Ok(Some(file)) = ctx.store.files.by_id(icon) {
changes.icon(&file);
if let Some(old_icon) = previous_icon {
if old_icon != icon {
ctx.spawner.purge_file(old_icon);
}
}
}
}
let profile = ctx.store.profiles.update_images(&changes)?;
if let Some(id) = &self.public_key_id {
if let Some(key) = &self.public_key {
ctx.apub.store_public_key(profile.id(), id, key)?;
}
}
let profile = context.store.profiles.update_images(&changes)?;
context
.apub
.store_public_key(profile.id(), &self.public_key_id, &self.public_key)?;
if profile.owner_source().is_local() {
return Ok(Some(Box::new(crate::apub::results::ProfileUpdated {
@ -93,30 +109,29 @@ impl Action for UpdateProfile {
}
}
fn delete_submission(submission_id: uuid::Uuid, context: &Context) -> Result<(), Error> {
Action::perform(&DeleteSubmission { submission_id }, context)?;
fn delete_submission(submission_id: uuid::Uuid, ctx: &Context) -> Result<(), Error> {
Action::perform(&DeleteSubmission { submission_id }, ctx)?;
Ok(())
}
impl Action for DeleteProfile {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, ctx: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let profile_id = self.profile_id;
let opt = context.store.profiles.delete(profile_id)?;
let opt = ctx.store.profiles.delete(profile_id)?;
if let Some(undo_profile) = opt {
let person_apub_id = context.apub.apub_for_profile(profile_id)?.req()?;
context.apub.delete_object(&person_apub_id)?;
let person_apub_id = ctx.apub.apub_for_profile(profile_id)?.req()?;
ctx.apub.delete_object(&person_apub_id)?;
if let Some(banner) = undo_profile.0.banner() {
context.spawner.purge_file(banner);
ctx.spawner.purge_file(banner);
}
if let Some(icon) = undo_profile.0.icon() {
context.spawner.purge_file(icon);
ctx.spawner.purge_file(icon);
}
let context_clone = context.clone();
context.spawn_blocking(move || {
let context_clone = ctx.clone();
ctx.spawn_blocking(move || {
for submission_id in context_clone.store.submissions.for_profile(profile_id) {
if let Err(e) = delete_submission(submission_id, &context_clone) {
log::error!("Failed to delete submission {}: {}", submission_id, e);
@ -124,9 +139,46 @@ impl Action for DeleteProfile {
}
});
let follower_ids = ctx
.store
.view
.follows
.forward_iter(profile_id)
.filter_map(|follow_id| ctx.store.view.follows.right(follow_id).ok()?)
.collect::<Vec<_>>();
let context_clone = ctx.clone();
ctx.spawn_blocking(move || {
for follow_id in context_clone.store.view.follows.forward_iter(profile_id) {
if let Err(e) = context_clone.store.view.follows.remove(follow_id) {
log::error!("Failed to delete follow {}: {}", follow_id, e);
}
}
});
let context_clone = ctx.clone();
ctx.spawn_blocking(move || {
for follow_request_id in context_clone
.store
.view
.follow_requests
.forward_iter(profile_id)
{
if let Err(e) = context_clone
.store
.view
.follow_requests
.remove(follow_request_id)
{
log::error!("Failed to delete follow {}: {}", follow_request_id, e);
}
}
});
if undo_profile.0.owner_source().is_local() {
return Ok(Some(Box::new(crate::apub::results::ProfileDeleted {
profile_id,
follower_ids,
})));
}
}

View file

@ -1,10 +1,10 @@
use crate::{
apub::actions::{CreateReact, DeleteReact},
Action, Completed, Context, Error, Required,
Action, Context, Error, Outbound, Required,
};
impl Action for CreateReact {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let submissioner_id = context
.store
.submissions
@ -62,7 +62,7 @@ impl Action for CreateReact {
}
impl Action for DeleteReact {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let react_id = self.react_id;
let opt = context.store.reacts.delete(react_id)?;
@ -78,6 +78,8 @@ impl Action for DeleteReact {
return Ok(Some(Box::new(crate::apub::results::UndoReact {
like_apub_id,
profile_id,
submission_id: undo_react.0.submission_id(),
comment_id: undo_react.0.comment_id(),
})));
}
}

View file

@ -2,12 +2,12 @@ use crate::{
apub::actions::{
AnnounceSubmission, CreateSubmission, DeleteComment, DeleteSubmission, UpdateSubmission,
},
Action, Completed, Context, Error, Required,
Action, Outbound, Context, Error, Required,
};
use std::collections::HashSet;
impl Action for CreateSubmission {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let submission =
context
.store
@ -60,13 +60,13 @@ impl Action for CreateSubmission {
}
impl Action for AnnounceSubmission {
fn perform(&self, _: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, _: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
Ok(None)
}
}
impl Action for UpdateSubmission {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let submission_id = self.submission_id;
let submission = context.store.submissions.by_id(submission_id)?.req()?;
@ -116,7 +116,7 @@ fn delete_comment(comment_id: uuid::Uuid, context: &Context) -> Result<(), Error
}
impl Action for DeleteSubmission {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error> {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error> {
let submission_id = self.submission_id;
let opt = context.store.submissions.delete(submission_id)?;

View file

@ -4,15 +4,15 @@ use std::{fmt, sync::Arc};
use url::Url;
use uuid::Uuid;
mod actions;
pub mod actions;
mod keys;
pub mod results;
mod results;
pub(crate) use actions::ingest;
use keys::{ExtendedPerson, PublicKey, PublicKeyInner};
pub trait ApubIds {
fn gen_id(&self) -> Url;
fn gen_id(&self) -> Option<Url>;
fn public_key(&self, id: &Url) -> Option<Url>;
fn following(&self, id: &Url) -> Option<Url>;
@ -237,6 +237,13 @@ impl Store {
Ok(())
}
pub fn serve_object(&self, id: &Url) -> Result<Option<AnyBase>, crate::Error> {
if self.deleted(id)? {
return Ok(None);
}
Ok(self.object(id)?)
}
fn deleted(&self, id: &Url) -> Result<bool, StoreError> {
Ok(self.deleted.get(id.as_str())?.is_some())
}

View file

@ -1,5 +1,5 @@
use super::{Block as Blocked, UndoBlock};
use crate::{Completed, Context, Error, Required};
use crate::{Context, Error, Outbound, Required};
use activitystreams::{
activity::{Block, Undo},
base::AnyBase,
@ -7,8 +7,21 @@ use activitystreams::{
prelude::*,
security,
};
use url::Url;
use uuid::Uuid;
impl Outbound for Blocked {
fn id(&self) -> Option<Uuid> {
Some(self.block_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let block = ctx.store.view.blocks.by_id(self.block_id)?.req()?;
let inbox = ctx.apub.endpoints_for_profile(block.left)?.req()?.inbox;
Ok(vec![inbox])
}
impl Completed for Blocked {
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let block = ctx.store.view.blocks.by_id(self.block_id)?.req()?;
let actor_id = ctx.apub.apub_for_profile(block.right)?.req()?;
@ -17,7 +30,7 @@ impl Completed for Blocked {
let mut block = Block::new(actor_id, object_id);
block
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.set_published(published.into())
.add_context(context())
.add_context(security());
@ -28,13 +41,26 @@ impl Completed for Blocked {
}
}
impl Completed for UndoBlock {
impl Outbound for UndoBlock {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let inbox = ctx
.apub
.endpoints_for_profile(self.blocked_id)?
.req()?
.inbox;
Ok(vec![inbox])
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let block = ctx.apub.object(&self.block_apub_id)?.req()?;
let mut undo = Undo::new(person_id, block);
undo.set_id(ctx.apub.info.gen_id())
undo.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let undo = undo.into_any_base()?;

View file

@ -1,7 +1,8 @@
use super::{
CommentCreated, CommentDeleted, CommentUpdated, RemoteCommentCreated, RemoteCommentUpdated,
RemoteCommenteDeleted,
};
use crate::{store::Comment, Completed, Context, Error, Required};
use crate::{store::Comment, Context, Error, Outbound, Required};
use activitystreams::{
activity::{Announce, Create, Delete, Update},
base::AnyBase,
@ -10,7 +11,62 @@ use activitystreams::{
prelude::*,
public, security,
};
use std::collections::HashSet;
use url::Url;
use uuid::Uuid;
fn local_inboxes(
submission_id: Uuid,
comment_id: Option<Uuid>,
ctx: &Context,
) -> Result<Vec<Url>, Error> {
let mut urls = vec![];
let submissioner_id = ctx
.store
.submissions
.by_id(submission_id)?
.req()?
.profile_id();
let submissioner_inbox = ctx
.apub
.endpoints_for_profile(submissioner_id)?
.req()?
.inbox;
urls.push(submissioner_inbox);
if let Some(comment_id) = comment_id {
let commenter_id = ctx.store.comments.by_id(comment_id)?.req()?.profile_id();
let commenter_inbox = ctx.apub.endpoints_for_profile(commenter_id)?.req()?.inbox;
urls.push(commenter_inbox);
}
Ok(urls)
}
fn remote_inboxes(submissioner_id: Uuid, ctx: &Context) -> Result<Vec<Url>, Error> {
let follower_inboxes = ctx
.store
.view
.follows
.forward_iter(submissioner_id)
.filter_map(|follow_id| ctx.store.view.follows.right(follow_id).ok())
.filter_map(|opt| opt)
.filter(|follower_id| ctx.is_local(*follower_id).unwrap_or(false))
.filter_map(|follower_id| {
Some(
ctx.apub
.endpoints_for_profile(follower_id)
.ok()??
.shared_inbox,
)
})
.collect::<HashSet<_>>();
Ok(follower_inboxes.into_iter().collect())
}
fn build_comment(
comment: Comment,
@ -42,17 +98,27 @@ fn build_comment(
Ok(any_base)
}
impl Completed for CommentCreated {
impl Outbound for CommentCreated {
fn id(&self) -> Option<Uuid> {
Some(self.comment_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let comment = ctx.store.comments.by_id(self.comment_id)?.req()?;
local_inboxes(comment.submission_id(), comment.comment_id(), ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let comment = ctx.store.comments.by_id(self.comment_id)?.req()?;
let person_id = ctx.apub.apub_for_profile(comment.profile_id())?.req()?;
let note_id = ctx.apub.info.gen_id();
let note_id = ctx.apub.info.gen_id().req()?;
ctx.apub.comment(&note_id, self.comment_id)?;
let note = build_comment(comment, note_id, person_id.clone(), ctx)?;
let mut create = Create::new(person_id, note);
create
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let any_base = create.into_any_base()?;
@ -62,14 +128,22 @@ impl Completed for CommentCreated {
}
}
impl Completed for RemoteCommentCreated {
impl Outbound for RemoteCommentCreated {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
remote_inboxes(self.profile_id, ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let comment = ctx.apub.object(&self.note_apub_id)?.req()?;
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let mut announce = Announce::new(person_id, comment);
announce
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let announce = announce.into_any_base()?;
@ -79,7 +153,17 @@ impl Completed for RemoteCommentCreated {
}
}
impl Completed for CommentUpdated {
impl Outbound for CommentUpdated {
fn id(&self) -> Option<Uuid> {
Some(self.comment_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let comment = ctx.store.comments.by_id(self.comment_id)?.req()?;
local_inboxes(comment.submission_id(), comment.comment_id(), ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let comment = ctx.store.comments.by_id(self.comment_id)?.req()?;
let person_id = ctx.apub.apub_for_profile(comment.profile_id())?.req()?;
@ -88,7 +172,7 @@ impl Completed for CommentUpdated {
let mut update = Update::new(person_id, note);
update
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let any_base = update.into_any_base()?;
@ -98,14 +182,22 @@ impl Completed for CommentUpdated {
}
}
impl Completed for RemoteCommentUpdated {
impl Outbound for RemoteCommentUpdated {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
remote_inboxes(self.profile_id, ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let update = ctx.apub.object(&self.update_apub_id)?.req()?;
let mut announce = Announce::new(person_id, update);
announce
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let announce = announce.into_any_base()?;
@ -115,14 +207,22 @@ impl Completed for RemoteCommentUpdated {
}
}
impl Completed for CommentDeleted {
impl Outbound for CommentDeleted {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
local_inboxes(self.submission_id, self.reply_to_id, ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let note = ctx.apub.object(&self.note_apub_id)?.req()?;
let mut delete = Delete::new(person_id, note);
delete
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let any_base = delete.into_any_base()?;
@ -131,3 +231,28 @@ impl Completed for CommentDeleted {
Ok(any_base)
}
}
impl Outbound for RemoteCommenteDeleted {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
remote_inboxes(self.profile_id, ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let delete = ctx.apub.object(&self.delete_apub_id)?.req()?;
let mut announce = Announce::new(person_id, delete);
announce
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let announce = announce.into_any_base()?;
ctx.apub.store_object(&announce)?;
Ok(announce)
}
}

View file

@ -1,5 +1,5 @@
use super::{AcceptFollow, Follow as FollowRequested, RejectFollow, UndoAcceptFollow, UndoFollow};
use crate::{Completed, Context, Error, Required};
use crate::{Context, Error, Outbound, Required};
use activitystreams::{
activity::{Accept, Follow, Reject, Undo},
base::AnyBase,
@ -7,8 +7,26 @@ use activitystreams::{
prelude::*,
security,
};
use url::Url;
use uuid::Uuid;
impl Outbound for FollowRequested {
fn id(&self) -> Option<Uuid> {
Some(self.follow_request_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let follow = ctx
.store
.view
.follow_requests
.by_id(self.follow_request_id)?
.req()?;
let inbox = ctx.apub.endpoints_for_profile(follow.left)?.req()?.inbox;
Ok(vec![inbox])
}
impl Completed for FollowRequested {
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let follow = ctx
.store
@ -20,7 +38,7 @@ impl Completed for FollowRequested {
let object_id = ctx.apub.apub_for_profile(follow.left)?.req()?;
let published = follow.published;
let follow_id = ctx.apub.info.gen_id();
let follow_id = ctx.apub.info.gen_id().req()?;
ctx.apub
.follow_request(&follow_id, self.follow_request_id)?;
@ -37,14 +55,28 @@ impl Completed for FollowRequested {
}
}
impl Completed for RejectFollow {
impl Outbound for RejectFollow {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let inbox = ctx
.apub
.endpoints_for_profile(self.requester_id)?
.req()?
.inbox;
Ok(vec![inbox])
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let follow_request = ctx.apub.object(&self.follow_apub_id)?.req()?;
let mut reject = Reject::new(person_id, follow_request);
reject
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let reject = reject.into_any_base()?;
@ -54,12 +86,24 @@ impl Completed for RejectFollow {
}
}
impl Completed for AcceptFollow {
impl Outbound for AcceptFollow {
fn id(&self) -> Option<Uuid> {
Some(self.follow_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let requester_id = ctx.store.view.follows.right(self.follow_id)?.req()?;
let inbox = ctx.apub.endpoints_for_profile(requester_id)?.req()?.inbox;
Ok(vec![inbox])
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let follow_request = ctx.apub.object(&self.follow_apub_id)?.req()?;
let accept_id = ctx.apub.info.gen_id();
let accept_id = ctx.apub.info.gen_id().req()?;
ctx.apub.follow(&accept_id, self.follow_id)?;
let mut accept = Accept::new(person_id, follow_request);
@ -74,13 +118,27 @@ impl Completed for AcceptFollow {
}
}
impl Completed for UndoAcceptFollow {
impl Outbound for UndoAcceptFollow {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let inbox = ctx
.apub
.endpoints_for_profile(self.requester_id)?
.req()?
.inbox;
Ok(vec![inbox])
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let accept = ctx.apub.object(&self.accept_apub_id)?.req()?;
let mut undo = Undo::new(person_id, accept);
undo.set_id(ctx.apub.info.gen_id())
undo.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let undo = undo.into_any_base()?;
@ -90,13 +148,27 @@ impl Completed for UndoAcceptFollow {
}
}
impl Completed for UndoFollow {
impl Outbound for UndoFollow {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let inbox = ctx
.apub
.endpoints_for_profile(self.followed_id)?
.req()?
.inbox;
Ok(vec![inbox])
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let follow = ctx.apub.object(&self.follow_apub_id)?.req()?;
let mut undo = Undo::new(person_id, follow);
undo.set_id(ctx.apub.info.gen_id())
undo.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let undo = undo.into_any_base()?;

View file

@ -18,6 +18,7 @@ pub(super) struct ProfileUpdated {
pub(super) struct ProfileDeleted {
pub(super) profile_id: Uuid,
pub(super) follower_ids: Vec<Uuid>,
}
pub(super) struct SubmissionCreated {
@ -54,6 +55,13 @@ pub(super) struct RemoteCommentUpdated {
pub(super) struct CommentDeleted {
pub(super) note_apub_id: Url,
pub(super) profile_id: Uuid,
pub(super) submission_id: Uuid,
pub(super) reply_to_id: Option<Uuid>,
}
pub(super) struct RemoteCommenteDeleted {
pub(super) delete_apub_id: Url,
pub(super) profile_id: Uuid,
}
pub(super) struct React {
@ -63,6 +71,8 @@ pub(super) struct React {
pub(super) struct UndoReact {
pub(super) like_apub_id: Url,
pub(super) profile_id: Uuid,
pub(super) submission_id: Uuid,
pub(super) comment_id: Option<Uuid>,
}
pub(super) struct Follow {
@ -72,6 +82,7 @@ pub(super) struct Follow {
pub(super) struct RejectFollow {
pub(super) follow_apub_id: Url,
pub(super) profile_id: Uuid,
pub(super) requester_id: Uuid,
}
pub(super) struct AcceptFollow {
@ -83,11 +94,13 @@ pub(super) struct AcceptFollow {
pub(super) struct UndoAcceptFollow {
pub(super) accept_apub_id: Url,
pub(super) profile_id: Uuid,
pub(super) requester_id: Uuid,
}
pub(super) struct UndoFollow {
pub(super) follow_apub_id: Url,
pub(super) profile_id: Uuid,
pub(super) followed_id: Uuid,
}
pub(super) struct Block {
@ -97,4 +110,5 @@ pub(super) struct Block {
pub(super) struct UndoBlock {
pub(super) block_apub_id: Url,
pub(super) profile_id: Uuid,
pub(super) blocked_id: Uuid,
}

View file

@ -2,7 +2,7 @@ use super::{ProfileCreated, ProfileDeleted, ProfileUpdated};
use crate::{
apub::{ExtendedPerson, PublicKey, PublicKeyInner},
store::FileSource,
Completed, Context, Error, Required,
Context, Error, Outbound, Required,
};
use activitystreams::{
activity::{Create, Delete, Update},
@ -12,12 +12,24 @@ use activitystreams::{
prelude::*,
public, security,
};
use std::collections::HashSet;
use url::Url;
use uuid::Uuid;
impl Outbound for ProfileCreated {
fn id(&self) -> Option<Uuid> {
Some(self.profile_id)
}
fn inboxes(&self, _: &Context) -> Result<Vec<Url>, Error> {
// TODO: Potentially send to Server Actors of federated servers
Ok(vec![])
}
impl Completed for ProfileCreated {
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let profile = ctx.store.profiles.by_id(self.profile_id)?.req()?;
let person_id = ctx.apub.info.gen_id();
let person_id = ctx.apub.info.gen_id().req()?;
let public_key_id = ctx.apub.info.public_key(&person_id).req()?;
let following = ctx.apub.info.following(&person_id).req()?;
let followers = ctx.apub.info.followers(&person_id).req()?;
@ -88,7 +100,7 @@ impl Completed for ProfileCreated {
let mut create = Create::new(person_id, any_base);
create
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
@ -99,7 +111,33 @@ impl Completed for ProfileCreated {
}
}
impl Completed for ProfileUpdated {
impl Outbound for ProfileUpdated {
fn id(&self) -> Option<Uuid> {
Some(self.profile_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let follower_inboxes = ctx
.store
.view
.follows
.forward_iter(self.profile_id)
.filter_map(|follow_id| ctx.store.view.follows.right(follow_id).ok())
.filter_map(|opt| opt)
.filter(|follower_id| ctx.is_local(*follower_id).unwrap_or(false))
.filter_map(|follower_id| {
Some(
ctx.apub
.endpoints_for_profile(follower_id)
.ok()??
.shared_inbox,
)
})
.collect::<HashSet<_>>();
Ok(follower_inboxes.into_iter().collect())
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let profile = ctx.store.profiles.by_id(self.profile_id)?.req()?;
let endpoints = ctx.apub.endpoints_for_profile(self.profile_id)?.req()?;
@ -155,7 +193,7 @@ impl Completed for ProfileUpdated {
let mut update = Update::new(person_id, any_base);
update
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
@ -166,14 +204,35 @@ impl Completed for ProfileUpdated {
}
}
impl Completed for ProfileDeleted {
impl Outbound for ProfileDeleted {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let inboxes = self
.follower_ids
.iter()
.filter_map(|follower_id| {
Some(
ctx.apub
.endpoints_for_profile(*follower_id)
.ok()??
.shared_inbox,
)
})
.collect::<HashSet<_>>();
Ok(inboxes.into_iter().collect())
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let person = ctx.apub.object(&person_id)?.req()?;
let mut delete = Delete::new(person_id, person);
delete
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());

View file

@ -1,5 +1,5 @@
use super::{React, UndoReact};
use crate::{Completed, Context, Error, Required};
use crate::{Context, Error, Outbound, Required};
use activitystreams::{
activity::{Create, Delete, Like},
base::AnyBase,
@ -7,8 +7,32 @@ use activitystreams::{
prelude::*,
public, security,
};
use url::Url;
use uuid::Uuid;
impl Outbound for React {
fn id(&self) -> Option<Uuid> {
Some(self.react_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let react = ctx.store.reacts.by_id(self.react_id)?.req()?;
let notifier_id = if let Some(comment_id) = react.comment_id() {
ctx.store.comments.by_id(comment_id)?.req()?.profile_id()
} else {
ctx.store
.submissions
.by_id(react.submission_id())?
.req()?
.profile_id()
};
let inbox = ctx.apub.endpoints_for_profile(notifier_id)?.req()?.inbox;
Ok(vec![inbox])
}
impl Completed for React {
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let react = ctx.store.reacts.by_id(self.react_id)?.req()?;
let person_id = ctx.apub.apub_for_profile(react.profile_id())?.req()?;
@ -23,7 +47,7 @@ impl Completed for React {
let endpoints = ctx.apub.endpoints_for_profile(react.profile_id())?.req()?;
let mut like = Like::new(person_id.clone(), note_id);
like.set_id(ctx.apub.info.gen_id())
like.set_id(ctx.apub.info.gen_id().req()?)
.set_content(react.react())
.set_published(published.into())
.set_attributed_to(person_id.clone())
@ -35,7 +59,7 @@ impl Completed for React {
let mut create = Create::new(person_id, like);
create
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
@ -46,14 +70,34 @@ impl Completed for React {
}
}
impl Completed for UndoReact {
impl Outbound for UndoReact {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let notifier_id = if let Some(comment_id) = self.comment_id {
ctx.store.comments.by_id(comment_id)?.req()?.profile_id()
} else {
ctx.store
.submissions
.by_id(self.submission_id)?
.req()?
.profile_id()
};
let inbox = ctx.apub.endpoints_for_profile(notifier_id)?.req()?.inbox;
Ok(vec![inbox])
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let like = ctx.apub.object(&self.like_apub_id)?.req()?;
let mut delete = Delete::new(person_id, like);
delete
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let delete = delete.into_any_base()?;

View file

@ -1,7 +1,7 @@
use super::{SubmissionCreated, SubmissionDeleted, SubmissionUpdated};
use crate::{
store::{FileSource, Submission, Visibility},
Completed, Context, Error, Required,
Context, Error, Outbound, Required,
};
use activitystreams::{
activity::{Create, Delete, Update},
@ -11,7 +11,30 @@ use activitystreams::{
prelude::*,
public, security,
};
use std::collections::HashSet;
use url::Url;
use uuid::Uuid;
fn remote_inboxes(submissioner_id: Uuid, ctx: &Context) -> Result<Vec<Url>, Error> {
let follower_inboxes = ctx
.store
.view
.follows
.forward_iter(submissioner_id)
.filter_map(|follow_id| ctx.store.view.follows.right(follow_id).ok())
.filter_map(|opt| opt)
.filter_map(|follower_id| {
Some(
ctx.apub
.endpoints_for_profile(follower_id)
.ok()??
.shared_inbox,
)
})
.collect::<HashSet<_>>();
Ok(follower_inboxes.into_iter().collect())
}
fn build_submission(
submission: Submission,
@ -58,16 +81,31 @@ fn build_submission(
Ok(any_base)
}
impl Completed for SubmissionCreated {
impl Outbound for SubmissionCreated {
fn id(&self) -> Option<Uuid> {
Some(self.submission_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let profile_id = ctx
.store
.submissions
.by_id(self.submission_id)?
.req()?
.profile_id();
remote_inboxes(profile_id, ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let submission = ctx.store.submissions.by_id(self.submission_id)?.req()?;
let person_id = ctx.apub.apub_for_profile(submission.profile_id())?.req()?;
let note_id = ctx.apub.info.gen_id();
let note_id = ctx.apub.info.gen_id().req()?;
let note = build_submission(submission, note_id, person_id.clone(), ctx)?;
let mut create = Create::new(person_id, note);
create
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let any_base = create.into_any_base()?;
@ -77,7 +115,22 @@ impl Completed for SubmissionCreated {
}
}
impl Completed for SubmissionUpdated {
impl Outbound for SubmissionUpdated {
fn id(&self) -> Option<Uuid> {
Some(self.submission_id)
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
let profile_id = ctx
.store
.submissions
.by_id(self.submission_id)?
.req()?
.profile_id();
remote_inboxes(profile_id, ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let submission = ctx.store.submissions.by_id(self.submission_id)?.req()?;
let person_id = ctx.apub.apub_for_profile(submission.profile_id())?.req()?;
@ -86,7 +139,7 @@ impl Completed for SubmissionUpdated {
let mut update = Update::new(person_id, note);
update
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let any_base = update.into_any_base()?;
@ -96,7 +149,15 @@ impl Completed for SubmissionUpdated {
}
}
impl Completed for SubmissionDeleted {
impl Outbound for SubmissionDeleted {
fn id(&self) -> Option<Uuid> {
None
}
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
remote_inboxes(self.profile_id, ctx)
}
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
let person_id = ctx.apub.apub_for_profile(self.profile_id)?.req()?;
let note_id = ctx.apub.apub_for_submission(self.submission_id)?.req()?;
@ -104,7 +165,7 @@ impl Completed for SubmissionDeleted {
let mut delete = Delete::new(person_id, note);
delete
.set_id(ctx.apub.info.gen_id())
.set_id(ctx.apub.info.gen_id().req()?)
.add_context(context())
.add_context(security());
let any_base = delete.into_any_base()?;

View file

@ -44,6 +44,17 @@ impl Context {
}
}
fn deliver(&self, completed: &dyn Outbound) {
let res = completed
.inboxes(self)
.and_then(|inboxes| completed.to_apub(self).map(|any_base| (inboxes, any_base)));
match res {
Ok((inboxes, any_base)) => self.spawner.deliver(any_base, inboxes),
Err(e) => log::error!("Error spawning deliver task: {}", e),
}
}
fn is_local(&self, id: Uuid) -> Result<bool, Error> {
Ok(self.store.profiles.is_local(id)?.req()?)
}
@ -100,7 +111,7 @@ pub trait Spawner {
fn download_images(&self, images: Vec<Url>, stack: Vec<AnyBase>);
fn purge_file(&self, file_id: Uuid);
fn process(&self, any_base: AnyBase, stack: Vec<AnyBase>);
fn deliver(&self, completed: &dyn Completed);
fn deliver(&self, any_base: AnyBase, inboxes: Vec<Url>);
}
enum RecoverableError {
@ -108,11 +119,13 @@ enum RecoverableError {
MissingImages(Vec<Url>),
}
trait Action {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Completed>>, Error>;
pub trait Action {
fn perform(&self, context: &Context) -> Result<Option<Box<dyn Outbound>>, Error>;
}
pub trait Completed {
pub trait Outbound {
fn id(&self) -> Option<Uuid>;
fn inboxes(&self, context: &Context) -> Result<Vec<Url>, Error>;
fn to_apub(&self, context: &Context) -> Result<AnyBase, Error>;
}
@ -153,6 +166,18 @@ impl State {
})
}
pub async fn run(&self, action: &dyn Action) -> Result<Option<Uuid>, Error> {
let ctx = Context::from_state(self).await;
match action.perform(&ctx)? {
Some(outbound) => {
ctx.deliver(&*outbound);
Ok(outbound.id())
}
None => Ok(None),
}
}
pub async fn ingest(&self, any_base: AnyBase, stack: Vec<AnyBase>) -> Result<(), Error> {
let context = Context::from_state(self).await;

View file

@ -1,5 +1,5 @@
use actix_web::{body::BodyStream, client::Client, dev::Payload, HttpRequest, HttpResponse};
use std::{fmt, sync::Arc};
use std::{fmt, sync::Arc, time::Duration};
use url::Url;
#[derive(Debug, Clone)]
@ -190,7 +190,10 @@ impl State {
url: Url,
req: &HttpRequest,
) -> Result<HttpResponse, actix_web::Error> {
let client_req = self.client.request_from(url.as_str(), req.head());
let client_req = self
.client
.request_from(url.as_str(), req.head())
.timeout(Duration::from_secs(30));
let client_req = if let Some(addr) = req.head().peer_addr {
client_req.header("X-Forwarded-For", addr.to_string())
} else {
@ -231,7 +234,10 @@ impl State {
req: HttpRequest,
body: Payload,
) -> Result<Images, UploadError> {
let client_req = self.client.request_from(self.upload().as_str(), req.head());
let client_req = self
.client
.request_from(self.upload().as_str(), req.head())
.timeout(Duration::from_secs(30));
let client_req = if let Some(addr) = req.head().peer_addr {
client_req.header("X-Forwarded-For", addr.to_string())
@ -257,6 +263,7 @@ impl State {
let mut res = self
.client
.get(self.download(url).as_str())
.timeout(Duration::from_secs(30))
.send()
.await
.map_err(|e| {

View file

@ -85,31 +85,31 @@ impl Profile {
&self.owner_source
}
pub(crate) fn handle(&self) -> &str {
pub fn handle(&self) -> &str {
&self.handle
}
pub(crate) fn display_name(&self) -> Option<&str> {
pub fn display_name(&self) -> Option<&str> {
self.display_name.as_ref().map(|dn| dn.as_str())
}
pub(crate) fn description(&self) -> Option<&str> {
pub fn description(&self) -> Option<&str> {
self.description.as_ref().map(|d| d.as_str())
}
pub(crate) fn icon(&self) -> Option<Uuid> {
pub fn icon(&self) -> Option<Uuid> {
self.icon
}
pub(crate) fn banner(&self) -> Option<Uuid> {
pub fn banner(&self) -> Option<Uuid> {
self.banner
}
pub(crate) fn published(&self) -> DateTime<Utc> {
pub fn published(&self) -> DateTime<Utc> {
self.published
}
pub(crate) fn login_required(&self) -> bool {
pub fn login_required(&self) -> bool {
self.login_required
}
}
@ -140,7 +140,7 @@ impl PictRsFile {
}
}
pub(crate) fn key(&self) -> &str {
pub fn key(&self) -> &str {
&self.key
}
@ -165,7 +165,7 @@ impl File {
self.id
}
pub(crate) fn source(&self) -> &FileSource {
pub fn source(&self) -> &FileSource {
&self.source
}
}