- Fix image federation - Enable submission & comment create/update federation - Improve checks to ensure we don't federate when we don't need to
315 lines
9.8 KiB
Rust
315 lines
9.8 KiB
Rust
use super::{ProfileCreated, ProfileDeleted, ProfileUpdated};
|
|
use crate::{
|
|
apub::{ExtendedPerson, PublicKey, PublicKeyInner},
|
|
store::FileSource,
|
|
Context, Error, OnBehalfOf, Outbound, Required,
|
|
};
|
|
use activitystreams::{
|
|
activity::{Create, Delete, Update},
|
|
actor::{ApActor, Endpoints, Person},
|
|
base::AnyBase,
|
|
context,
|
|
object::Image,
|
|
prelude::*,
|
|
public, security,
|
|
};
|
|
use std::collections::HashSet;
|
|
use url::Url;
|
|
use uuid::Uuid;
|
|
|
|
fn build_image(file_id: Uuid, ctx: &Context) -> Result<AnyBase, Error> {
|
|
if let Some(apub_id) = ctx.apub.apub_for_file(file_id)? {
|
|
ctx.apub.object(&apub_id)?.req("image object by id")
|
|
} else {
|
|
let file = ctx.store.files.by_id(file_id)?.req("file by id")?;
|
|
|
|
let FileSource::PictRs(pictrs_file) = file.source();
|
|
|
|
let image_id = ctx.apub.info.gen_id().req("id generation")?;
|
|
ctx.apub.file(&image_id, file_id)?;
|
|
|
|
let mut image = Image::new();
|
|
image
|
|
.set_id(image_id)
|
|
.set_url(ctx.pictrs.image_url(pictrs_file.key()))
|
|
.set_media_type(pictrs_file.media_type().clone());
|
|
|
|
let image = image.into_any_base()?;
|
|
ctx.apub.store_object(&image)?;
|
|
|
|
Ok(image)
|
|
}
|
|
}
|
|
|
|
impl Outbound for ProfileCreated {
|
|
fn id(&self) -> Option<Uuid> {
|
|
Some(self.profile_id)
|
|
}
|
|
|
|
fn behalf(&self, _: &Context) -> Result<OnBehalfOf, Error> {
|
|
Ok(OnBehalfOf::Server)
|
|
}
|
|
|
|
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
|
|
let inboxes = ctx
|
|
.store
|
|
.federated_servers()
|
|
.filter_map(|server_id| {
|
|
let endpoints = ctx.apub.endpoints_for_server(server_id).ok()??;
|
|
Some(endpoints.inbox().to_owned())
|
|
})
|
|
.collect::<HashSet<_>>();
|
|
|
|
Ok(inboxes.into_iter().collect())
|
|
}
|
|
|
|
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
|
|
let profile = ctx
|
|
.store
|
|
.profiles
|
|
.by_id(self.profile_id)?
|
|
.req("profile by id")?;
|
|
|
|
let server_id = ctx.store.servers.get_self()?.req("get self server")?;
|
|
let application_id = ctx
|
|
.apub
|
|
.apub_for_server(server_id)?
|
|
.req("apub for self server")?;
|
|
|
|
let person_id = ctx.apub.info.gen_id().req("id generation")?;
|
|
let public_key_id = ctx.apub.info.public_key(&person_id).req("public key")?;
|
|
let following = ctx.apub.info.following(&person_id).req("following")?;
|
|
let followers = ctx.apub.info.followers(&person_id).req("followers")?;
|
|
let inbox = ctx.apub.info.inbox(&person_id).req("inbox")?;
|
|
let outbox = ctx.apub.info.outbox(&person_id).req("outbox")?;
|
|
let shared_inbox = ctx.apub.info.shared_inbox();
|
|
|
|
ctx.apub.profile(&person_id, profile.id())?;
|
|
ctx.apub.store_profile_endpoints(
|
|
profile.id(),
|
|
&person_id,
|
|
crate::apub::Endpoints {
|
|
inbox: inbox.clone(),
|
|
outbox: outbox.clone(),
|
|
following: Some(following.clone()),
|
|
followers: Some(followers.clone()),
|
|
shared_inbox: Some(shared_inbox.clone()),
|
|
public_key: public_key_id.clone(),
|
|
},
|
|
)?;
|
|
let public_key_pem = ctx.apub.gen_profile_keys(profile.id(), &public_key_id)?;
|
|
|
|
let mut person = ExtendedPerson::new(
|
|
ApActor::new(inbox, Person::new()),
|
|
PublicKey {
|
|
public_key: PublicKeyInner {
|
|
id: public_key_id,
|
|
owner: person_id.clone(),
|
|
public_key_pem,
|
|
},
|
|
},
|
|
);
|
|
|
|
person
|
|
.set_id(person_id.clone())
|
|
.set_following(following)
|
|
.set_followers(followers)
|
|
.set_outbox(outbox)
|
|
.set_endpoints(Endpoints {
|
|
shared_inbox: Some(shared_inbox),
|
|
..Endpoints::default()
|
|
})
|
|
.set_preferred_username(profile.handle())
|
|
.set_published(profile.published().into());
|
|
|
|
if let Some(display_name) = profile.display_name() {
|
|
person.set_name(display_name);
|
|
}
|
|
if let Some(description) = profile.description() {
|
|
person.set_summary(description);
|
|
}
|
|
if let Some(icon) = profile.icon() {
|
|
let image = build_image(icon, ctx)?;
|
|
person.set_icon(image);
|
|
}
|
|
if let Some(banner) = profile.banner() {
|
|
let image = build_image(banner, ctx)?;
|
|
person.set_image(image);
|
|
}
|
|
if !profile.login_required() {
|
|
person.add_to(public());
|
|
}
|
|
|
|
let any_base = person.into_any_base()?;
|
|
ctx.apub.store_object(&any_base)?;
|
|
|
|
let mut create = Create::new(application_id, any_base);
|
|
create
|
|
.set_id(ctx.apub.info.gen_id().req("id generation")?)
|
|
.add_context(context())
|
|
.add_context(security());
|
|
|
|
let any_base = create.into_any_base()?;
|
|
ctx.apub.store_object(&any_base)?;
|
|
|
|
Ok(any_base)
|
|
}
|
|
}
|
|
|
|
impl Outbound for ProfileUpdated {
|
|
fn id(&self) -> Option<Uuid> {
|
|
Some(self.profile_id)
|
|
}
|
|
|
|
fn behalf(&self, _: &Context) -> Result<OnBehalfOf, Error> {
|
|
Ok(OnBehalfOf::Profile(self.profile_id))
|
|
}
|
|
|
|
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
|
|
let inboxes = ctx
|
|
.store
|
|
.federated_servers()
|
|
.filter_map(|server_id| {
|
|
let endpoints = ctx.apub.endpoints_for_server(server_id).ok()??;
|
|
Some(endpoints.inbox)
|
|
})
|
|
.collect::<HashSet<_>>();
|
|
|
|
Ok(inboxes.into_iter().collect())
|
|
}
|
|
|
|
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
|
|
let profile = ctx
|
|
.store
|
|
.profiles
|
|
.by_id(self.profile_id)?
|
|
.req("profile by id")?;
|
|
let endpoints = ctx
|
|
.apub
|
|
.endpoints_for_profile(self.profile_id)?
|
|
.req("endpoints for profile")?;
|
|
let public_key_id = ctx
|
|
.apub
|
|
.key_for_profile(profile.id())?
|
|
.req("key for profile")?;
|
|
let public_key_pem = ctx
|
|
.apub
|
|
.public_key_for_id(&public_key_id)?
|
|
.req("public key for id")?;
|
|
let person_id = ctx
|
|
.apub
|
|
.apub_for_profile(self.profile_id)?
|
|
.req("apub id for profile")?;
|
|
|
|
let mut person = ExtendedPerson::new(
|
|
ApActor::new(endpoints.inbox, Person::new()),
|
|
PublicKey {
|
|
public_key: PublicKeyInner {
|
|
id: public_key_id,
|
|
owner: person_id.clone(),
|
|
public_key_pem,
|
|
},
|
|
},
|
|
);
|
|
|
|
person
|
|
.set_id(person_id.clone())
|
|
.set_outbox(endpoints.outbox)
|
|
.set_preferred_username(profile.handle())
|
|
.set_published(profile.published().into());
|
|
|
|
if let Some(following) = endpoints.following {
|
|
person.set_following(following);
|
|
}
|
|
if let Some(followers) = endpoints.followers {
|
|
person.set_followers(followers);
|
|
}
|
|
if let Some(shared_inbox) = endpoints.shared_inbox {
|
|
person.set_endpoints(Endpoints {
|
|
shared_inbox: Some(shared_inbox),
|
|
..Endpoints::default()
|
|
});
|
|
}
|
|
|
|
if let Some(display_name) = profile.display_name() {
|
|
person.set_name(display_name);
|
|
}
|
|
if let Some(description) = profile.description() {
|
|
person.set_summary(description);
|
|
}
|
|
if let Some(icon) = profile.icon() {
|
|
let image = build_image(icon, ctx)?;
|
|
person.set_icon(image);
|
|
}
|
|
if let Some(banner) = profile.banner() {
|
|
let image = build_image(banner, ctx)?;
|
|
person.set_image(image);
|
|
}
|
|
if !profile.login_required() {
|
|
person.add_to(public());
|
|
}
|
|
|
|
let any_base = person.into_any_base()?;
|
|
ctx.apub.store_object(&any_base)?;
|
|
|
|
let mut update = Update::new(person_id, any_base);
|
|
update
|
|
.set_id(ctx.apub.info.gen_id().req("id generation")?)
|
|
.add_context(context())
|
|
.add_context(security());
|
|
|
|
let any_base = update.into_any_base()?;
|
|
ctx.apub.store_object(&any_base)?;
|
|
|
|
Ok(any_base)
|
|
}
|
|
}
|
|
|
|
impl Outbound for ProfileDeleted {
|
|
fn id(&self) -> Option<Uuid> {
|
|
None
|
|
}
|
|
|
|
fn behalf(&self, _: &Context) -> Result<OnBehalfOf, Error> {
|
|
Ok(OnBehalfOf::Server)
|
|
}
|
|
|
|
fn inboxes(&self, ctx: &Context) -> Result<Vec<Url>, Error> {
|
|
let inboxes = ctx
|
|
.store
|
|
.federated_servers()
|
|
.filter_map(|server_id| {
|
|
let endpoints = ctx.apub.endpoints_for_server(server_id).ok()??;
|
|
Some(endpoints.inbox().to_owned())
|
|
})
|
|
.collect::<HashSet<_>>();
|
|
|
|
Ok(inboxes.into_iter().collect())
|
|
}
|
|
|
|
fn to_apub(&self, ctx: &Context) -> Result<AnyBase, Error> {
|
|
let server_id = ctx.store.servers.get_self()?.req("get self server")?;
|
|
let application_id = ctx
|
|
.apub
|
|
.apub_for_server(server_id)?
|
|
.req("apub for self server")?;
|
|
|
|
let person_id = ctx
|
|
.apub
|
|
.apub_for_profile(self.profile_id)?
|
|
.req("apub id for profile")?;
|
|
let person = ctx.apub.object(&person_id)?.req("apub for person")?;
|
|
|
|
let mut delete = Delete::new(application_id, person);
|
|
delete
|
|
.set_id(ctx.apub.info.gen_id().req("id generation")?)
|
|
.add_context(context())
|
|
.add_context(security());
|
|
|
|
let any_base = delete.into_any_base()?;
|
|
ctx.apub.store_object(&any_base)?;
|
|
|
|
Ok(any_base)
|
|
}
|
|
}
|