mirror of
https://github.com/versia-pub/activitypub.git
synced 2025-12-06 14:48:19 +01:00
feat: add missing fields on AP users
This commit is contained in:
parent
692e4bff22
commit
1a741c6420
|
|
@ -5,6 +5,7 @@ mod m20240417_230111_user_table;
|
||||||
mod m20240417_233430_post_user_keys;
|
mod m20240417_233430_post_user_keys;
|
||||||
mod m20240505_002524_user_follow_relation;
|
mod m20240505_002524_user_follow_relation;
|
||||||
mod m20240626_030922_store_ap_json_in_posts;
|
mod m20240626_030922_store_ap_json_in_posts;
|
||||||
|
mod m20240719_235452_user_ap_column;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
|
|
@ -17,6 +18,7 @@ impl MigratorTrait for Migrator {
|
||||||
Box::new(m20240417_233430_post_user_keys::Migration),
|
Box::new(m20240417_233430_post_user_keys::Migration),
|
||||||
Box::new(m20240505_002524_user_follow_relation::Migration),
|
Box::new(m20240505_002524_user_follow_relation::Migration),
|
||||||
Box::new(m20240626_030922_store_ap_json_in_posts::Migration),
|
Box::new(m20240626_030922_store_ap_json_in_posts::Migration),
|
||||||
|
Box::new(m20240719_235452_user_ap_column::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
migration/src/m20240719_235452_user_ap_column.rs
Normal file
35
migration/src/m20240719_235452_user_ap_column.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(User::Table)
|
||||||
|
.add_column_if_not_exists(ColumnDef::new(User::ApJson).string())
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(User::Table)
|
||||||
|
.drop_column(User::ApJson)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
pub enum User {
|
||||||
|
Table,
|
||||||
|
ApJson,
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ pub struct Model {
|
||||||
pub following: Option<String>,
|
pub following: Option<String>,
|
||||||
pub followers: Option<String>,
|
pub followers: Option<String>,
|
||||||
pub inbox: String,
|
pub inbox: String,
|
||||||
|
pub ap_json: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use activitystreams_kinds::public;
|
||||||
use anyhow::{anyhow, Ok};
|
use anyhow::{anyhow, Ok};
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
use reqwest::header;
|
||||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
@ -11,9 +12,13 @@ use url::Url;
|
||||||
use crate::{
|
use crate::{
|
||||||
database::State,
|
database::State,
|
||||||
entities::{self, post, prelude, user},
|
entities::{self, post, prelude, user},
|
||||||
objects::post::Mention,
|
objects::{
|
||||||
|
self,
|
||||||
|
person::{AttachmentType, EndpointType, IconType, Person, TagType},
|
||||||
|
post::Mention,
|
||||||
|
},
|
||||||
utils::{generate_lysand_post_url, generate_object_id, generate_user_id},
|
utils::{generate_lysand_post_url, generate_object_id, generate_user_id},
|
||||||
API_DOMAIN, DB, FEDERATION_CONFIG, LYSAND_DOMAIN,
|
API_DOMAIN, DB, FEDERATION_CONFIG, LOCAL_USER_NAME, LYSAND_DOMAIN, USERNAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -80,6 +85,8 @@ pub async fn lysand_user_from_db(
|
||||||
user: entities::user::Model,
|
user: entities::user::Model,
|
||||||
) -> anyhow::Result<super::objects::User> {
|
) -> anyhow::Result<super::objects::User> {
|
||||||
let url = Url::parse(&user.url)?;
|
let url = Url::parse(&user.url)?;
|
||||||
|
let ap = user.ap_json.unwrap();
|
||||||
|
let serialized_ap: crate::objects::person::Person = serde_json::from_str(&ap)?;
|
||||||
let inbox_url = Url::parse("https://ap.lysand.org/apbridge/lysand/inbox")?;
|
let inbox_url = Url::parse("https://ap.lysand.org/apbridge/lysand/inbox")?;
|
||||||
let outbox_url = Url::parse(
|
let outbox_url = Url::parse(
|
||||||
("https://ap.lysand.org/apbridge/lysand/outbox/".to_string() + &user.id).as_str(),
|
("https://ap.lysand.org/apbridge/lysand/outbox/".to_string() + &user.id).as_str(),
|
||||||
|
|
@ -113,6 +120,59 @@ pub async fn lysand_user_from_db(
|
||||||
"text/html".to_string(),
|
"text/html".to_string(),
|
||||||
ContentEntry::from_string(user.summary.unwrap_or_default()),
|
ContentEntry::from_string(user.summary.unwrap_or_default()),
|
||||||
);
|
);
|
||||||
|
let avatar = match serialized_ap.icon {
|
||||||
|
Some(icon) => {
|
||||||
|
let mut content_format = ContentFormat::default();
|
||||||
|
let content_entry = ContentEntry::from_string(icon.url.to_string());
|
||||||
|
content_format.x.insert(icon.type_, content_entry);
|
||||||
|
Some(content_format)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let header = match serialized_ap.image {
|
||||||
|
Some(image) => {
|
||||||
|
let mut content_format = ContentFormat::default();
|
||||||
|
let content_entry = ContentEntry::from_string(image.url.to_string());
|
||||||
|
content_format.x.insert(image.type_, content_entry);
|
||||||
|
Some(content_format)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let mut fields = Vec::new();
|
||||||
|
if let Some(attachments) = serialized_ap.attachment {
|
||||||
|
for attachment in attachments {
|
||||||
|
let mut key = ContentFormat::default();
|
||||||
|
let mut value = ContentFormat::default();
|
||||||
|
key.x.insert(
|
||||||
|
"text/html".to_string(),
|
||||||
|
ContentEntry::from_string(attachment.name),
|
||||||
|
);
|
||||||
|
value.x.insert(
|
||||||
|
"text/html".to_string(),
|
||||||
|
ContentEntry::from_string(attachment.value),
|
||||||
|
);
|
||||||
|
fields.push(super::objects::FieldKV { key, value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let emojis = match serialized_ap.tag {
|
||||||
|
Some(tags) => {
|
||||||
|
let mut emojis = Vec::new();
|
||||||
|
for tag in tags {
|
||||||
|
let mut content_format = ContentFormat::default();
|
||||||
|
let content_entry = ContentEntry::from_string(tag.id.to_string());
|
||||||
|
content_format.x.insert(tag.type_, content_entry);
|
||||||
|
emojis.push(super::objects::CustomEmoji {
|
||||||
|
name: tag.name,
|
||||||
|
url: content_format,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some(super::objects::CustomEmojis { emojis })
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let extensions = super::objects::ExtensionSpecs {
|
||||||
|
custom_emojis: emojis,
|
||||||
|
};
|
||||||
let user = super::objects::User {
|
let user = super::objects::User {
|
||||||
rtype: super::objects::LysandType::User,
|
rtype: super::objects::LysandType::User,
|
||||||
id: uuid::Uuid::try_parse(&user.id)?,
|
id: uuid::Uuid::try_parse(&user.id)?,
|
||||||
|
|
@ -127,9 +187,9 @@ pub async fn lysand_user_from_db(
|
||||||
likes: likes_url,
|
likes: likes_url,
|
||||||
dislikes: dislikes_url,
|
dislikes: dislikes_url,
|
||||||
bio: Some(bio),
|
bio: Some(bio),
|
||||||
avatar: None,
|
avatar,
|
||||||
header: None,
|
header,
|
||||||
fields: None,
|
fields: Some(fields),
|
||||||
indexable: false,
|
indexable: false,
|
||||||
created_at: OffsetDateTime::from_unix_timestamp(user.created_at.timestamp()).unwrap(),
|
created_at: OffsetDateTime::from_unix_timestamp(user.created_at.timestamp()).unwrap(),
|
||||||
public_key: PublicKey {
|
public_key: PublicKey {
|
||||||
|
|
@ -137,6 +197,7 @@ pub async fn lysand_user_from_db(
|
||||||
public_key: "AAAAC3NzaC1lZDI1NTE5AAAAIMxsX+lEWkHZt9NOvn9yYFP0Z++186LY4b97C4mwj/f2"
|
public_key: "AAAAC3NzaC1lZDI1NTE5AAAAIMxsX+lEWkHZt9NOvn9yYFP0Z++186LY4b97C4mwj/f2"
|
||||||
.to_string(), // dummy key
|
.to_string(), // dummy key
|
||||||
},
|
},
|
||||||
|
extensions: Some(extensions),
|
||||||
};
|
};
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
@ -211,6 +272,101 @@ pub async fn db_user_from_url(url: Url) -> anyhow::Result<entities::user::Model>
|
||||||
} else {
|
} else {
|
||||||
let ls_user = fetch_user_from_url(url).await?;
|
let ls_user = fetch_user_from_url(url).await?;
|
||||||
let keypair = generate_actor_keypair()?;
|
let keypair = generate_actor_keypair()?;
|
||||||
|
let bridge_user_url = generate_user_id(&API_DOMAIN, &ls_user.id.to_string())?;
|
||||||
|
let inbox = Url::parse(&format!(
|
||||||
|
"https://{}/{}/inbox",
|
||||||
|
API_DOMAIN.to_string(),
|
||||||
|
ls_user.username.clone()
|
||||||
|
))?;
|
||||||
|
let icon = if let Some(avatar) = ls_user.avatar {
|
||||||
|
let avatar_url = avatar.select_rich_img_touple().await?;
|
||||||
|
Some(IconType {
|
||||||
|
type_: "Image".to_string(),
|
||||||
|
media_type: avatar_url.0,
|
||||||
|
url: Url::parse(&avatar_url.1).unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let image = if let Some(header) = ls_user.header {
|
||||||
|
let header_url = header.select_rich_img_touple().await?;
|
||||||
|
Some(IconType {
|
||||||
|
type_: "Image".to_string(),
|
||||||
|
media_type: header_url.0,
|
||||||
|
url: Url::parse(&header_url.1).unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let mut attachments: Vec<AttachmentType> = Vec::new();
|
||||||
|
if let Some(fields) = ls_user.fields {
|
||||||
|
for attachment in fields {
|
||||||
|
attachments.push(AttachmentType {
|
||||||
|
type_: "PropertyValue".to_string(),
|
||||||
|
name: attachment.key.select_rich_text().await?,
|
||||||
|
value: attachment.value.select_rich_text().await?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut tags: Vec<TagType> = Vec::new();
|
||||||
|
if let Some(extensions) = ls_user.extensions {
|
||||||
|
if let Some(custom_emojis) = extensions.custom_emojis {
|
||||||
|
for emoji in custom_emojis.emojis {
|
||||||
|
let touple = emoji.url.select_rich_img_touple().await?;
|
||||||
|
tags.push(TagType {
|
||||||
|
id: Url::parse(&touple.1).unwrap(),
|
||||||
|
name: emoji.name,
|
||||||
|
type_: "Emoji".to_string(),
|
||||||
|
updated: Utc::now(),
|
||||||
|
icon: IconType {
|
||||||
|
type_: "Image".to_string(),
|
||||||
|
media_type: touple.0,
|
||||||
|
url: Url::parse(&touple.1).unwrap(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ap_json = Person {
|
||||||
|
kind: Default::default(),
|
||||||
|
id: bridge_user_url.clone().into(),
|
||||||
|
preferred_username: ls_user.username.clone(),
|
||||||
|
inbox,
|
||||||
|
public_key: activitypub_federation::protocol::public_key::PublicKey {
|
||||||
|
owner: bridge_user_url.clone(),
|
||||||
|
public_key_pem: keypair.public_key.clone(),
|
||||||
|
id: format!("{}#main-key", bridge_user_url.clone()),
|
||||||
|
},
|
||||||
|
name: ls_user
|
||||||
|
.display_name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(ls_user.username.clone()),
|
||||||
|
summary: option_content_format_text(ls_user.bio.clone()).await,
|
||||||
|
url: ls_user.uri.clone(),
|
||||||
|
indexable: Some(ls_user.indexable),
|
||||||
|
discoverable: Some(true),
|
||||||
|
manually_approves_followers: Some(false),
|
||||||
|
followers: None,
|
||||||
|
following: None,
|
||||||
|
featured: None,
|
||||||
|
featured_tags: None,
|
||||||
|
outbox: None,
|
||||||
|
endpoints: Some(EndpointType {
|
||||||
|
shared_inbox: Url::parse(
|
||||||
|
&format!(
|
||||||
|
"https://{}/{}/inbox",
|
||||||
|
API_DOMAIN.to_string(),
|
||||||
|
&USERNAME.to_string()
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
}),
|
||||||
|
icon,
|
||||||
|
image,
|
||||||
|
attachment: Some(attachments),
|
||||||
|
tag: Some(tags),
|
||||||
|
};
|
||||||
let user = entities::user::ActiveModel {
|
let user = entities::user::ActiveModel {
|
||||||
id: Set(ls_user.id.to_string()),
|
id: Set(ls_user.id.to_string()),
|
||||||
username: Set(ls_user.username.clone()),
|
username: Set(ls_user.username.clone()),
|
||||||
|
|
@ -230,6 +386,7 @@ pub async fn db_user_from_url(url: Url) -> anyhow::Result<entities::user::Model>
|
||||||
updated_at: Set(Some(Utc::now())),
|
updated_at: Set(Some(Utc::now())),
|
||||||
followers: Set(Some(ls_user.followers.to_string())),
|
followers: Set(Some(ls_user.followers.to_string())),
|
||||||
following: Set(Some(ls_user.following.to_string())),
|
following: Set(Some(ls_user.following.to_string())),
|
||||||
|
ap_json: Set(Some(serde_json::to_string(&ap_json).unwrap())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let db = DB.get().unwrap();
|
let db = DB.get().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
error,
|
error,
|
||||||
lysand::conversion::{lysand_post_from_db, lysand_user_from_db},
|
lysand::conversion::{lysand_post_from_db, lysand_user_from_db},
|
||||||
objects,
|
objects::{self, person::Person},
|
||||||
utils::{base_url_decode, generate_create_id, generate_user_id},
|
utils::{base_url_decode, generate_create_id, generate_user_id},
|
||||||
Response, API_DOMAIN, DB, FEDERATION_CONFIG,
|
Response, API_DOMAIN, DB, FEDERATION_CONFIG,
|
||||||
};
|
};
|
||||||
|
|
@ -134,29 +134,11 @@ async fn fetch_user(
|
||||||
None => return Ok(HttpResponse::NotFound().finish()),
|
None => return Ok(HttpResponse::NotFound().finish()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let bridge_user_url = generate_user_id(&API_DOMAIN, &user.id)?;
|
let deserialized_user: Person = serde_json::from_str(user.ap_json.as_ref().unwrap().as_str())?;
|
||||||
let inbox = Url::parse(&format!(
|
|
||||||
"https://{}/{}/inbox",
|
|
||||||
API_DOMAIN.to_string(),
|
|
||||||
&user.username.clone()
|
|
||||||
))?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.content_type(FEDERATION_CONTENT_TYPE)
|
.content_type(FEDERATION_CONTENT_TYPE)
|
||||||
.json(WithContext::new_default(crate::objects::person::Person {
|
.json(WithContext::new_default(deserialized_user)))
|
||||||
kind: Default::default(),
|
|
||||||
id: bridge_user_url.clone().into(),
|
|
||||||
preferred_username: user.username.clone(),
|
|
||||||
name: user.name.clone(),
|
|
||||||
summary: user.summary.clone(),
|
|
||||||
url: Url::parse(user.url.as_str()).unwrap(),
|
|
||||||
inbox,
|
|
||||||
public_key: PublicKey {
|
|
||||||
owner: bridge_user_url.clone(),
|
|
||||||
public_key_pem: user.public_key,
|
|
||||||
id: format!("{}#main-key", bridge_user_url.clone()),
|
|
||||||
},
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/apbridge/lysand/object/{post}")]
|
#[get("/apbridge/lysand/object/{post}")]
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,34 @@ impl ContentFormat {
|
||||||
|
|
||||||
Ok(self.x.clone().values().next().unwrap().content.clone())
|
Ok(self.x.clone().values().next().unwrap().content.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn select_rich_img_touple(&self) -> anyhow::Result<(String, String)> {
|
||||||
|
if let Some(entry) = self.x.get("image/webp") {
|
||||||
|
return Ok(("image/webp".to_string(), entry.content.clone()));
|
||||||
|
}
|
||||||
|
if let Some(entry) = self.x.get("image/png") {
|
||||||
|
return Ok(("image/png".to_string(), entry.content.clone()));
|
||||||
|
}
|
||||||
|
if let Some(entry) = self.x.get("image/avif") {
|
||||||
|
return Ok(("image/avif".to_string(), entry.content.clone()));
|
||||||
|
}
|
||||||
|
if let Some(entry) = self.x.get("image/jxl") {
|
||||||
|
return Ok(("image/jxl".to_string(), entry.content.clone()));
|
||||||
|
}
|
||||||
|
if let Some(entry) = self.x.get("image/jpeg") {
|
||||||
|
return Ok(("image/jpeg".to_string(), entry.content.clone()));
|
||||||
|
}
|
||||||
|
if let Some(entry) = self.x.get("image/gif") {
|
||||||
|
return Ok(("image/gif".to_string(), entry.content.clone()));
|
||||||
|
}
|
||||||
|
if let Some(entry) = self.x.get("image/bmp") {
|
||||||
|
return Ok(("image/bmp".to_string(), entry.content.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let touple = self.x.iter().next().unwrap();
|
||||||
|
|
||||||
|
Ok((touple.0.clone(), touple.1.content.clone()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for ContentFormat {
|
impl Serialize for ContentFormat {
|
||||||
|
|
@ -182,8 +210,8 @@ impl<'de> Deserialize<'de> for ContentFormat {
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct FieldKV {
|
pub struct FieldKV {
|
||||||
key: ContentFormat,
|
pub key: ContentFormat,
|
||||||
value: ContentFormat,
|
pub value: ContentFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
@ -224,7 +252,6 @@ pub struct User {
|
||||||
#[serde(with = "iso_lysand")]
|
#[serde(with = "iso_lysand")]
|
||||||
pub created_at: OffsetDateTime,
|
pub created_at: OffsetDateTime,
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
// TODO bio: Option<String>,
|
|
||||||
pub inbox: Url,
|
pub inbox: Url,
|
||||||
pub outbox: Url,
|
pub outbox: Url,
|
||||||
pub featured: Url,
|
pub featured: Url,
|
||||||
|
|
@ -238,6 +265,24 @@ pub struct User {
|
||||||
pub header: Option<ContentFormat>,
|
pub header: Option<ContentFormat>,
|
||||||
pub fields: Option<Vec<FieldKV>>,
|
pub fields: Option<Vec<FieldKV>>,
|
||||||
pub indexable: bool,
|
pub indexable: bool,
|
||||||
|
pub extensions: Option<ExtensionSpecs>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ExtensionSpecs {
|
||||||
|
#[serde(rename = "org.lysand:custom_emojis")]
|
||||||
|
pub custom_emojis: Option<CustomEmojis>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct CustomEmojis {
|
||||||
|
pub emojis: Vec<CustomEmoji>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct CustomEmoji {
|
||||||
|
pub name: String,
|
||||||
|
pub url: ContentFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,50 @@ pub struct Person {
|
||||||
pub id: ObjectId<user::Model>,
|
pub id: ObjectId<user::Model>,
|
||||||
pub inbox: Url,
|
pub inbox: Url,
|
||||||
pub public_key: PublicKey,
|
pub public_key: PublicKey,
|
||||||
|
pub indexable: Option<bool>,
|
||||||
|
pub discoverable: Option<bool>,
|
||||||
|
pub manually_approves_followers: Option<bool>,
|
||||||
|
pub followers: Option<Url>,
|
||||||
|
pub following: Option<Url>,
|
||||||
|
pub featured: Option<Url>,
|
||||||
|
pub endpoints: Option<EndpointType>,
|
||||||
|
pub outbox: Option<Url>,
|
||||||
|
pub featured_tags: Option<Url>,
|
||||||
|
pub tag: Option<Vec<TagType>>,
|
||||||
|
pub icon: Option<IconType>,
|
||||||
|
pub image: Option<IconType>,
|
||||||
|
pub attachment: Option<Vec<AttachmentType>>,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct TagType {
|
||||||
|
pub id: Url,
|
||||||
|
pub name: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub type_: String,
|
||||||
|
pub updated: DateTime<Utc>,
|
||||||
|
pub icon: IconType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct EndpointType {
|
||||||
|
pub shared_inbox: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct IconType {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub type_: String, //Always "Image"
|
||||||
|
pub media_type: String,
|
||||||
|
pub url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct AttachmentType {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub type_: String, //Always "PropertyValue"
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
@ -103,16 +147,8 @@ impl Object for user::Model {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
|
||||||
Ok(Person {
|
let serialized = serde_json::from_str(self.ap_json.as_ref().unwrap().as_str())?;
|
||||||
preferred_username: self.username.clone(),
|
Ok(serialized)
|
||||||
kind: Default::default(),
|
|
||||||
id: Url::parse(&self.id).unwrap().into(),
|
|
||||||
inbox: Url::parse(&self.inbox).unwrap(),
|
|
||||||
public_key: self.public_key(),
|
|
||||||
name: self.name.clone(),
|
|
||||||
summary: self.summary.clone(),
|
|
||||||
url: Url::parse(&self.url).unwrap(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify(
|
async fn verify(
|
||||||
|
|
@ -135,6 +171,7 @@ impl Object for user::Model {
|
||||||
if let Some(user) = query {
|
if let Some(user) = query {
|
||||||
return Ok(user);
|
return Ok(user);
|
||||||
}
|
}
|
||||||
|
let copied_json = json.clone();
|
||||||
let model = user::ActiveModel {
|
let model = user::ActiveModel {
|
||||||
id: Set(Uuid::now_v7().to_string()),
|
id: Set(Uuid::now_v7().to_string()),
|
||||||
username: Set(json.preferred_username),
|
username: Set(json.preferred_username),
|
||||||
|
|
@ -148,6 +185,7 @@ impl Object for user::Model {
|
||||||
following_count: Set(0),
|
following_count: Set(0),
|
||||||
created_at: Set(Utc::now()),
|
created_at: Set(Utc::now()),
|
||||||
last_refreshed_at: Set(Utc::now()),
|
last_refreshed_at: Set(Utc::now()),
|
||||||
|
ap_json: Set(Some(serde_json::to_string(&copied_json).unwrap())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let model = model.insert(_data.database_connection.as_ref()).await;
|
let model = model.insert(_data.database_connection.as_ref()).await;
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ impl Object for post::Model {
|
||||||
visibility: Set("public".to_string()), // TODO: make this use the real visibility
|
visibility: Set("public".to_string()), // TODO: make this use the real visibility
|
||||||
sensitive: Set(json.sensitive.clone().unwrap_or_default()),
|
sensitive: Set(json.sensitive.clone().unwrap_or_default()),
|
||||||
url: Set(json.id.clone().to_string()),
|
url: Set(json.id.clone().to_string()),
|
||||||
|
ap_json: Set(Some(serde_json::to_string(&json).unwrap())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let post = post
|
let post = post
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue