Make User follow change of activitystreams
This commit is contained in:
parent
00d31e323f
commit
c37e115470
@ -4,12 +4,15 @@ use crate::{
|
|||||||
safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, Result,
|
safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, Result,
|
||||||
UserEvent::*, CONFIG, ITEMS_PER_PAGE, USER_CHAN,
|
UserEvent::*, CONFIG, ITEMS_PER_PAGE, USER_CHAN,
|
||||||
};
|
};
|
||||||
use activitypub::{
|
use activitystreams::{
|
||||||
activity::Delete,
|
activity::{ActorAndObject, Create, Delete},
|
||||||
actor::Person,
|
actor::{ApActor, Endpoints, Person},
|
||||||
|
base::{AnyBase, AsBase, Base},
|
||||||
collection::{OrderedCollection, OrderedCollectionPage},
|
collection::{OrderedCollection, OrderedCollectionPage},
|
||||||
object::{Image, Tombstone},
|
iri,
|
||||||
Activity, CustomObject, Endpoint,
|
object::{kind::ImageType, ApObject, Image, Tombstone},
|
||||||
|
prelude::*,
|
||||||
|
primitives::OneOrMany, // CustomObject,
|
||||||
};
|
};
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
use diesel::{self, BelongingToDsl, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl};
|
use diesel::{self, BelongingToDsl, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl};
|
||||||
@ -42,7 +45,7 @@ use std::{
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
use webfinger::*;
|
use webfinger::*;
|
||||||
|
|
||||||
pub type CustomPerson = CustomObject<ApSignature, Person>;
|
// pub type CustomPerson = CustomObject<ApSignature, Person>;
|
||||||
|
|
||||||
pub enum Role {
|
pub enum Role {
|
||||||
Admin = 0,
|
Admin = 0,
|
||||||
@ -242,13 +245,16 @@ impl User {
|
|||||||
.ok_or(Error::Webfinger)
|
.ok_or(Error::Webfinger)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(url: &str) -> Result<CustomPerson> {
|
// fn fetch(url: &str) -> Result<CustomPerson> {
|
||||||
|
fn fetch(url: &str) -> Result<Person> {
|
||||||
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
// without this workaround, publicKey is not correctly deserialized
|
// without this workaround, publicKey is not correctly deserialized
|
||||||
let ap_sign = serde_json::from_str::<ApSignature>(text)?;
|
// TODO: Implement
|
||||||
let mut json = serde_json::from_str::<CustomPerson>(text)?;
|
// let ap_sign = serde_json::from_str::<ApSignature>(text)?;
|
||||||
json.custom_props = ap_sign;
|
// let mut json = serde_json::from_str::<CustomPerson>(text)?;
|
||||||
|
let json = serde_json::from_str::<Person>(text)?;
|
||||||
|
// json.custom_props = ap_sign; // TODO: implement
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,35 +266,69 @@ impl User {
|
|||||||
User::fetch(&self.ap_url.clone()).and_then(|json| {
|
User::fetch(&self.ap_url.clone()).and_then(|json| {
|
||||||
let avatar = Media::save_remote(
|
let avatar = Media::save_remote(
|
||||||
conn,
|
conn,
|
||||||
json.object
|
json.icon()
|
||||||
.object_props
|
.and_then(|icon| {
|
||||||
.icon_image()? // FIXME: Fails when icon is not set
|
Some(
|
||||||
.object_props
|
icon.as_one()
|
||||||
.url_string()?,
|
.expect("only")
|
||||||
|
.extend::<Image, ImageType>()
|
||||||
|
.expect("possible")
|
||||||
|
.expect("exists")
|
||||||
|
.url()
|
||||||
|
.ok_or(Error::MissingApProperty)
|
||||||
|
.ok()?
|
||||||
|
.as_one()
|
||||||
|
.expect("one")
|
||||||
|
.as_xsd_string()
|
||||||
|
.expect("possible"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok_or(Error::MissingApProperty)?
|
||||||
|
.into(), // FIXME: Fails when icon is not set,
|
||||||
self,
|
self,
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
|
let person =
|
||||||
|
serde_json::from_value::<ApActor<Person>>(serde_json::to_value(json)?.into())?;
|
||||||
|
|
||||||
diesel::update(self)
|
diesel::update(self)
|
||||||
.set((
|
.set((
|
||||||
users::username.eq(json.object.ap_actor_props.preferred_username_string()?),
|
users::username.eq(person
|
||||||
users::display_name.eq(json.object.object_props.name_string()?),
|
.preferred_username()
|
||||||
users::outbox_url.eq(json.object.ap_actor_props.outbox_string()?),
|
.ok_or(Error::MissingApProperty)?),
|
||||||
users::inbox_url.eq(json.object.ap_actor_props.inbox_string()?),
|
users::display_name.eq(person
|
||||||
|
.name()
|
||||||
|
.ok_or(Error::MissingApProperty)?
|
||||||
|
.as_one()
|
||||||
|
.expect("only")
|
||||||
|
.as_xsd_string()
|
||||||
|
.expect("possible")),
|
||||||
|
users::outbox_url.eq(person.outbox().ok_or(Error::MissingApProperty)?.as_str()),
|
||||||
|
users::inbox_url.eq(person.inbox().as_str()),
|
||||||
users::summary.eq(SafeString::new(
|
users::summary.eq(SafeString::new(
|
||||||
&json
|
&person
|
||||||
.object
|
.summary()
|
||||||
.object_props
|
.and_then(|summary| {
|
||||||
.summary_string()
|
Some(
|
||||||
|
summary
|
||||||
|
.as_one()
|
||||||
|
.expect("only")
|
||||||
|
.as_xsd_string()
|
||||||
|
.expect("possible"),
|
||||||
|
)
|
||||||
|
})
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)),
|
)),
|
||||||
users::followers_endpoint.eq(json.object.ap_actor_props.followers_string()?),
|
users::followers_endpoint
|
||||||
|
.eq(person.followers().ok_or(Error::MissingApProperty)?.as_str()),
|
||||||
users::avatar_id.eq(avatar.map(|a| a.id)),
|
users::avatar_id.eq(avatar.map(|a| a.id)),
|
||||||
users::last_fetched_date.eq(Utc::now().naive_utc()),
|
users::last_fetched_date.eq(Utc::now().naive_utc()),
|
||||||
users::public_key.eq(json
|
// TODO:
|
||||||
.custom_props
|
// users::public_key.eq(json
|
||||||
.public_key_publickey()?
|
// .custom_props
|
||||||
.public_key_pem_string()?),
|
// .public_key_publickey()?
|
||||||
|
// .public_key_pem_string()?),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
@ -428,31 +468,32 @@ impl User {
|
|||||||
.load::<User>(conn)
|
.load::<User>(conn)
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
pub fn outbox(&self, conn: &Connection) -> Result<ActivityStream<OrderedCollection>> {
|
pub fn outbox(&self, conn: &Connection) -> Result<ActivityStream<ApObject<OrderedCollection>>> {
|
||||||
Ok(ActivityStream::new(self.outbox_collection(conn)?))
|
Ok(ActivityStream::new(ApObject::new(
|
||||||
|
self.outbox_collection(conn)?,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
pub fn outbox_collection(&self, conn: &Connection) -> Result<OrderedCollection> {
|
pub fn outbox_collection(&self, conn: &Connection) -> Result<OrderedCollection> {
|
||||||
let mut coll = OrderedCollection::default();
|
let mut coll = OrderedCollection::new();
|
||||||
let first = &format!("{}?page=1", &self.outbox_url);
|
let first = &format!("{}?page=1", &self.outbox_url);
|
||||||
let last = &format!(
|
let last = &format!(
|
||||||
"{}?page={}",
|
"{}?page={}",
|
||||||
&self.outbox_url,
|
&self.outbox_url,
|
||||||
self.get_activities_count(conn) / i64::from(ITEMS_PER_PAGE) + 1
|
self.get_activities_count(conn) / i64::from(ITEMS_PER_PAGE) + 1
|
||||||
);
|
);
|
||||||
coll.collection_props.set_first_link(Id::new(first))?;
|
coll.set_first(iri!(first))
|
||||||
coll.collection_props.set_last_link(Id::new(last))?;
|
.set_last(iri!(last))
|
||||||
coll.collection_props
|
.set_total_items(self.get_activities_count(conn) as u64);
|
||||||
.set_total_items_u64(self.get_activities_count(conn) as u64)?;
|
|
||||||
Ok(coll)
|
Ok(coll)
|
||||||
}
|
}
|
||||||
pub fn outbox_page(
|
pub fn outbox_page(
|
||||||
&self,
|
&self,
|
||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
(min, max): (i32, i32),
|
(min, max): (i32, i32),
|
||||||
) -> Result<ActivityStream<OrderedCollectionPage>> {
|
) -> Result<ActivityStream<ApObject<OrderedCollectionPage>>> {
|
||||||
Ok(ActivityStream::new(
|
Ok(ActivityStream::new(ApObject::new(
|
||||||
self.outbox_collection_page(conn, (min, max))?,
|
self.outbox_collection_page(conn, (min, max))?,
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
pub fn outbox_collection_page(
|
pub fn outbox_collection_page(
|
||||||
&self,
|
&self,
|
||||||
@ -461,27 +502,33 @@ impl User {
|
|||||||
) -> Result<OrderedCollectionPage> {
|
) -> Result<OrderedCollectionPage> {
|
||||||
let acts = self.get_activities_page(conn, (min, max))?;
|
let acts = self.get_activities_page(conn, (min, max))?;
|
||||||
let n_acts = self.get_activities_count(conn);
|
let n_acts = self.get_activities_count(conn);
|
||||||
let mut coll = OrderedCollectionPage::default();
|
let mut coll = OrderedCollectionPage::new();
|
||||||
if n_acts - i64::from(min) >= i64::from(ITEMS_PER_PAGE) {
|
if n_acts - i64::from(min) >= i64::from(ITEMS_PER_PAGE) {
|
||||||
coll.collection_page_props.set_next_link(Id::new(&format!(
|
coll.set_next(iri!(&format!(
|
||||||
"{}?page={}",
|
"{}?page={}",
|
||||||
&self.outbox_url,
|
&self.outbox_url,
|
||||||
min / ITEMS_PER_PAGE + 2
|
min / ITEMS_PER_PAGE + 2
|
||||||
)))?;
|
)));
|
||||||
}
|
}
|
||||||
if min > 0 {
|
if min > 0 {
|
||||||
coll.collection_page_props.set_prev_link(Id::new(&format!(
|
coll.set_prev(iri!(&format!(
|
||||||
"{}?page={}",
|
"{}?page={}",
|
||||||
&self.outbox_url,
|
&self.outbox_url,
|
||||||
min / ITEMS_PER_PAGE
|
min / ITEMS_PER_PAGE
|
||||||
)))?;
|
)));
|
||||||
}
|
}
|
||||||
coll.collection_props.items = serde_json::to_value(acts)?;
|
coll.set_many_items(
|
||||||
coll.collection_page_props
|
acts.iter().map(|create| {
|
||||||
.set_part_of_link(Id::new(&self.outbox_url))?;
|
AnyBase::from_base(create.base_ref().into_generic().expect("possible"))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.set_part_of(iri!(&self.outbox_url));
|
||||||
Ok(coll)
|
Ok(coll)
|
||||||
}
|
}
|
||||||
fn fetch_outbox_page<T: Activity>(&self, url: &str) -> Result<(Vec<T>, Option<String>)> {
|
fn fetch_outbox_page<T: serde::de::DeserializeOwned>(
|
||||||
|
&self,
|
||||||
|
url: &str,
|
||||||
|
) -> Result<(Vec<ActorAndObject<T>>, Option<String>)> {
|
||||||
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
let json: serde_json::Value = serde_json::from_str(text)?;
|
let json: serde_json::Value = serde_json::from_str(text)?;
|
||||||
@ -490,12 +537,12 @@ impl User {
|
|||||||
.unwrap_or(&vec![])
|
.unwrap_or(&vec![])
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|j| serde_json::from_value(j.clone()).ok())
|
.filter_map(|j| serde_json::from_value(j.clone()).ok())
|
||||||
.collect::<Vec<T>>();
|
.collect::<Vec<ActorAndObject<T>>>();
|
||||||
|
|
||||||
let next = json.get("next").map(|x| x.as_str().unwrap().to_owned());
|
let next = json.get("next").map(|x| x.as_str().unwrap().to_owned());
|
||||||
Ok((items, next))
|
Ok((items, next))
|
||||||
}
|
}
|
||||||
pub fn fetch_outbox<T: Activity>(&self) -> Result<Vec<T>> {
|
pub fn fetch_outbox<T: serde::de::DeserializeOwned>(&self) -> Result<Vec<ActorAndObject<T>>> {
|
||||||
let mut res = get(
|
let mut res = get(
|
||||||
&self.outbox_url[..],
|
&self.outbox_url[..],
|
||||||
Self::get_sender(),
|
Self::get_sender(),
|
||||||
@ -504,7 +551,7 @@ impl User {
|
|||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
let json: serde_json::Value = serde_json::from_str(text)?;
|
let json: serde_json::Value = serde_json::from_str(text)?;
|
||||||
if let Some(first) = json.get("first") {
|
if let Some(first) = json.get("first") {
|
||||||
let mut items: Vec<T> = Vec::new();
|
let mut items: Vec<ActorAndObject<T>> = Vec::new();
|
||||||
let mut next = first.as_str().unwrap().to_owned();
|
let mut next = first.as_str().unwrap().to_owned();
|
||||||
while let Ok((mut page, nxt)) = self.fetch_outbox_page(&next) {
|
while let Ok((mut page, nxt)) = self.fetch_outbox_page(&next) {
|
||||||
if page.is_empty() {
|
if page.is_empty() {
|
||||||
@ -527,7 +574,7 @@ impl User {
|
|||||||
.unwrap_or(&vec![])
|
.unwrap_or(&vec![])
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|j| serde_json::from_value(j.clone()).ok())
|
.filter_map(|j| serde_json::from_value(j.clone()).ok())
|
||||||
.collect::<Vec<T>>())
|
.collect::<Vec<ActorAndObject<T>>>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,7 +608,7 @@ impl User {
|
|||||||
&self,
|
&self,
|
||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
(min, max): (i32, i32),
|
(min, max): (i32, i32),
|
||||||
) -> Result<Vec<serde_json::Value>> {
|
) -> Result<Vec<Create>> {
|
||||||
use crate::schema::post_authors;
|
use crate::schema::post_authors;
|
||||||
use crate::schema::posts;
|
use crate::schema::posts;
|
||||||
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
|
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
|
||||||
@ -574,12 +621,8 @@ impl User {
|
|||||||
.load::<Post>(conn)?;
|
.load::<Post>(conn)?;
|
||||||
Ok(posts
|
Ok(posts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|p| {
|
.filter_map(|p| p.create_activity(conn).ok())
|
||||||
p.create_activity(conn)
|
.collect())
|
||||||
.ok()
|
|
||||||
.and_then(|a| serde_json::to_value(a).ok())
|
|
||||||
})
|
|
||||||
.collect::<Vec<serde_json::Value>>())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_followers(&self, conn: &Connection) -> Result<Vec<User>> {
|
pub fn get_followers(&self, conn: &Connection) -> Result<Vec<User>> {
|
||||||
@ -739,72 +782,57 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_activity(&self, conn: &Connection) -> Result<CustomPerson> {
|
// pub fn to_activity(&self, conn: &Connection) -> Result<CustomPerson> {
|
||||||
let mut actor = Person::default();
|
pub fn to_activity(&self, conn: &Connection) -> Result<ApActor<Person>> {
|
||||||
actor.object_props.set_id_string(self.ap_url.clone())?;
|
let mut actor = ApActor::new(
|
||||||
actor
|
iri!(self.inbox_url),
|
||||||
.object_props
|
*Person::new()
|
||||||
.set_name_string(self.display_name.clone())?;
|
.set_name(self.display_name)
|
||||||
actor
|
.set_summary(self.summary_html.get().to_owned())
|
||||||
.object_props
|
.set_url(self.ap_url),
|
||||||
.set_summary_string(self.summary_html.get().clone())?;
|
)
|
||||||
actor.object_props.set_url_string(self.ap_url.clone())?;
|
.set_outbox(iri!(self.outbox_url))
|
||||||
actor
|
.set_preferred_username(self.username)
|
||||||
.ap_actor_props
|
.set_followers(iri!(self.followers_endpoint));
|
||||||
.set_inbox_string(self.inbox_url.clone())?;
|
|
||||||
actor
|
|
||||||
.ap_actor_props
|
|
||||||
.set_outbox_string(self.outbox_url.clone())?;
|
|
||||||
actor
|
|
||||||
.ap_actor_props
|
|
||||||
.set_preferred_username_string(self.username.clone())?;
|
|
||||||
actor
|
|
||||||
.ap_actor_props
|
|
||||||
.set_followers_string(self.followers_endpoint.clone())?;
|
|
||||||
|
|
||||||
if let Some(shared_inbox_url) = self.shared_inbox_url.clone() {
|
if let Some(shared_inbox_url) = self.shared_inbox_url.clone() {
|
||||||
let mut endpoints = Endpoint::default();
|
let endpoints = Endpoints {
|
||||||
endpoints.set_shared_inbox_string(shared_inbox_url)?;
|
shared_inbox: Some(iri!(shared_inbox_url)),
|
||||||
actor.ap_actor_props.set_endpoints_endpoint(endpoints)?;
|
..Endpoints::default()
|
||||||
|
};
|
||||||
|
actor.set_endpoints(endpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut public_key = PublicKey::default();
|
// FIXME
|
||||||
public_key.set_id_string(format!("{}#main-key", self.ap_url))?;
|
// let mut public_key = PublicKey::default();
|
||||||
public_key.set_owner_string(self.ap_url.clone())?;
|
// public_key.set_id_string(format!("{}#main-key", self.ap_url))?;
|
||||||
public_key.set_public_key_pem_string(self.public_key.clone())?;
|
// public_key.set_owner_string(self.ap_url.clone())?;
|
||||||
let mut ap_signature = ApSignature::default();
|
// public_key.set_public_key_pem_string(self.public_key.clone())?;
|
||||||
ap_signature.set_public_key_publickey(public_key)?;
|
// let mut ap_signature = ApSignature::default();
|
||||||
|
// ap_signature.set_public_key_publickey(public_key)?;
|
||||||
|
|
||||||
if let Some(avatar_id) = self.avatar_id {
|
if let Some(avatar_id) = self.avatar_id {
|
||||||
let mut avatar = Image::default();
|
let avatar = Image::new().set_url(iri!(Media::get(conn, avatar_id)?.url()?));
|
||||||
avatar
|
let base = Base::retract(*avatar)?.into_generic()?;
|
||||||
.object_props
|
actor.set_icon(AnyBase::from_base(base));
|
||||||
.set_url_string(Media::get(conn, avatar_id)?.url()?)?;
|
|
||||||
actor.object_props.set_icon_object(avatar)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CustomPerson::new(actor, ap_signature))
|
// Ok(CustomPerson::new(actor, ap_signature))
|
||||||
|
Ok(*actor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_activity(&self, conn: &Connection) -> Result<Delete> {
|
pub fn delete_activity(&self, conn: &Connection) -> Result<Delete> {
|
||||||
let mut del = Delete::default();
|
let followers = self.get_followers(conn)?;
|
||||||
|
let ccs = Vec::with_capacity(followers.len());
|
||||||
let mut tombstone = Tombstone::default();
|
for addr in followers.into_iter() {
|
||||||
tombstone.object_props.set_id_string(self.ap_url.clone())?;
|
ccs.push(iri!(addr.ap_url));
|
||||||
|
}
|
||||||
del.delete_props
|
let tombstone = Tombstone::new()
|
||||||
.set_actor_link(Id::new(self.ap_url.clone()))?;
|
.set_id(iri!(format!("{}#delete", self.ap_url)))
|
||||||
del.delete_props.set_object_object(tombstone)?;
|
.set_many_tos([iri!(PUBLIC_VISIBILITY)])
|
||||||
del.object_props
|
.set_many_ccs(ccs);
|
||||||
.set_id_string(format!("{}#delete", self.ap_url))?;
|
let base = Base::retract(*tombstone)?.into_generic()?;
|
||||||
del.object_props
|
let del = Delete::new::<_, OneOrMany<AnyBase>>(iri!(self.ap_url), base.into());
|
||||||
.set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY)])?;
|
|
||||||
del.object_props.set_cc_link_vec(
|
|
||||||
self.get_followers(conn)?
|
|
||||||
.into_iter()
|
|
||||||
.map(|f| Id::new(f.ap_url))
|
|
||||||
.collect(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(del)
|
Ok(del)
|
||||||
}
|
}
|
||||||
@ -923,14 +951,16 @@ impl Eq for User {}
|
|||||||
|
|
||||||
impl FromId<DbConn> for User {
|
impl FromId<DbConn> for User {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Object = CustomPerson;
|
// type Object = CustomPerson;
|
||||||
|
type Object = Person;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||||
Self::find_by_ap_url(conn, id)
|
Self::find_by_ap_url(conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
|
// fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
|
||||||
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
fn from_activity(conn: &DbConn, acct: Person) -> Result<Self> {
|
||||||
|
let url = Url::parse(&acct.id().ok_or(Error::MissingApProperty)?.as_str())?;
|
||||||
let inst = url.host_str().ok_or(Error::Url)?;
|
let inst = url.host_str().ok_or(Error::Url)?;
|
||||||
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
||||||
Instance::insert(
|
Instance::insert(
|
||||||
@ -949,8 +979,12 @@ impl FromId<DbConn> for User {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
let person = serde_json::from_value::<ApActor<Person>>(serde_json::to_value(acct)?.into())?;
|
||||||
|
|
||||||
let username = acct.object.ap_actor_props.preferred_username_string()?;
|
let username = person
|
||||||
|
.preferred_username()
|
||||||
|
.ok_or(Error::MissingApProperty)?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
|
if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
|
||||||
return Err(Error::InvalidValue);
|
return Err(Error::InvalidValue);
|
||||||
@ -966,50 +1000,77 @@ impl FromId<DbConn> for User {
|
|||||||
conn,
|
conn,
|
||||||
NewUser {
|
NewUser {
|
||||||
display_name: acct
|
display_name: acct
|
||||||
.object
|
.name()
|
||||||
.object_props
|
.map(|name| {
|
||||||
.name_string()
|
name.as_one()
|
||||||
.unwrap_or_else(|_| username.clone()),
|
.expect("only")
|
||||||
|
.as_xsd_string()
|
||||||
|
.expect("exists")
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| &username)
|
||||||
|
.to_string(),
|
||||||
username,
|
username,
|
||||||
outbox_url: acct.object.ap_actor_props.outbox_string()?,
|
outbox_url: person.outbox().ok_or(Error::MissingApProperty)?.to_string(),
|
||||||
inbox_url: acct.object.ap_actor_props.inbox_string()?,
|
inbox_url: person.inbox().to_string(),
|
||||||
role: 2,
|
role: 2,
|
||||||
summary: acct
|
summary: person
|
||||||
.object
|
.summary()
|
||||||
.object_props
|
.map(|summary| {
|
||||||
.summary_string()
|
summary
|
||||||
.unwrap_or_default(),
|
.as_one()
|
||||||
|
.expect("only")
|
||||||
|
.as_xsd_string()
|
||||||
|
.expect("exists")
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string(),
|
||||||
summary_html: SafeString::new(
|
summary_html: SafeString::new(
|
||||||
&acct
|
&person
|
||||||
.object
|
.summary()
|
||||||
.object_props
|
.map(|summary| {
|
||||||
.summary_string()
|
summary
|
||||||
|
.as_one()
|
||||||
|
.expect("only")
|
||||||
|
.as_xsd_string()
|
||||||
|
.expect("exists")
|
||||||
|
})
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
email: None,
|
email: None,
|
||||||
hashed_password: None,
|
hashed_password: None,
|
||||||
instance_id: instance.id,
|
instance_id: instance.id,
|
||||||
ap_url: acct.object.object_props.id_string()?,
|
ap_url: person.id().ok_or(Error::MissingApProperty)?.to_string(),
|
||||||
public_key: acct
|
// public_key: acct
|
||||||
.custom_props
|
// .custom_props
|
||||||
.public_key_publickey()?
|
// .public_key_publickey()?
|
||||||
.public_key_pem_string()?,
|
// .public_key_pem_string()?,
|
||||||
|
public_key: "".to_string(), // FIXME
|
||||||
private_key: None,
|
private_key: None,
|
||||||
shared_inbox_url: acct
|
shared_inbox_url: person
|
||||||
.object
|
.endpoints()
|
||||||
.ap_actor_props
|
.and_then(|e| e.shared_inbox.map(|shared_inbox| shared_inbox.to_string())),
|
||||||
.endpoints_endpoint()
|
followers_endpoint: person
|
||||||
.and_then(|e| e.shared_inbox_string())
|
.followers()
|
||||||
.ok(),
|
.ok_or(Error::MissingApProperty)?
|
||||||
followers_endpoint: acct.object.ap_actor_props.followers_string()?,
|
.to_string(),
|
||||||
fqn,
|
fqn,
|
||||||
avatar_id: None,
|
avatar_id: None,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Ok(icon) = acct.object.object_props.icon_image() {
|
if let Some(icon) = acct.icon() {
|
||||||
if let Ok(url) = icon.object_props.url_string() {
|
let icon_image = icon
|
||||||
let avatar = Media::save_remote(conn, url, &user);
|
.as_one()
|
||||||
|
.expect("only")
|
||||||
|
.extend::<Image, ImageType>()
|
||||||
|
.expect("possible")
|
||||||
|
.expect("exists");
|
||||||
|
if let Some(url) = icon_image.url() {
|
||||||
|
let avatar = Media::save_remote(
|
||||||
|
conn,
|
||||||
|
url.as_single_xsd_string().expect("exists").into(),
|
||||||
|
&user,
|
||||||
|
);
|
||||||
|
|
||||||
if let Ok(avatar) = avatar {
|
if let Ok(avatar) = avatar {
|
||||||
user.set_avatar(conn, avatar.id)?;
|
user.set_avatar(conn, avatar.id)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user