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:
parent
49eeb48b72
commit
ab8aa2cbe1
|
@ -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"
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
})))
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?;
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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()?;
|
||||
|
|
|
@ -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(¬e_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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()?;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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()?;
|
||||
|
|
|
@ -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()?;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue