mirror of
https://github.com/nullishamy/ferri.git
synced 2025-06-28 09:04:18 +00:00
chore: tidy up ID usage; fmt
This commit is contained in:
parent
244cb8b7e6
commit
2b62948447
27 changed files with 603 additions and 483 deletions
136
ferri-main/src/ap/activity.rs
Normal file
136
ferri-main/src/ap/activity.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
use crate::ap::{Actor, User, http};
|
||||
use chrono::{DateTime, Local};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::Sqlite;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ActivityType {
|
||||
Follow,
|
||||
Accept,
|
||||
Create,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl ActivityType {
|
||||
fn to_raw(self) -> String {
|
||||
match self {
|
||||
ActivityType::Follow => "Follow".to_string(),
|
||||
ActivityType::Accept => "Accept".to_string(),
|
||||
ActivityType::Create => "Create".to_string(),
|
||||
ActivityType::Unknown => "FIXME".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Activity<T: Serialize + Debug> {
|
||||
pub id: String,
|
||||
pub ty: ActivityType,
|
||||
pub object: T,
|
||||
pub published: DateTime<Local>,
|
||||
pub to: Vec<String>,
|
||||
pub cc: Vec<String>,
|
||||
}
|
||||
|
||||
impl<T: Serialize + Debug + Default> Default for Activity<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: Default::default(),
|
||||
ty: ActivityType::Unknown,
|
||||
object: Default::default(),
|
||||
published: Local::now(),
|
||||
to: Default::default(),
|
||||
cc: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type KeyId = String;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutgoingActivity<T: Serialize + Debug> {
|
||||
pub signed_by: KeyId,
|
||||
pub req: Activity<T>,
|
||||
pub to: Actor,
|
||||
}
|
||||
|
||||
impl<T: Serialize + Debug> OutgoingActivity<T> {
|
||||
pub async fn save(&self, conn: impl sqlx::Executor<'_, Database = Sqlite>) {
|
||||
let ty = self.req.ty.clone().to_raw();
|
||||
let actor_id = self.to.id();
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO activity (id, ty, actor_id)
|
||||
VALUES (?1, ?2, ?3)
|
||||
"#,
|
||||
self.req.id,
|
||||
ty,
|
||||
actor_id
|
||||
)
|
||||
.execute(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct RawActivity<T: Serialize + Debug> {
|
||||
#[serde(rename = "@context")]
|
||||
#[serde(skip_deserializing)]
|
||||
context: String,
|
||||
|
||||
id: String,
|
||||
#[serde(rename = "type")]
|
||||
ty: String,
|
||||
|
||||
actor: String,
|
||||
object: T,
|
||||
published: String,
|
||||
}
|
||||
|
||||
type OutboxTransport = http::HttpClient;
|
||||
pub struct Outbox<'a> {
|
||||
user: User,
|
||||
transport: &'a OutboxTransport,
|
||||
}
|
||||
|
||||
impl<'a> Outbox<'a> {
|
||||
pub fn user(&self) -> &User {
|
||||
&self.user
|
||||
}
|
||||
|
||||
pub async fn post<T: Serialize + Debug>(&self, activity: OutgoingActivity<T>) {
|
||||
dbg!(&activity);
|
||||
let raw = RawActivity {
|
||||
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
||||
id: activity.req.id,
|
||||
ty: activity.req.ty.to_raw(),
|
||||
actor: self.user.actor().id().to_string(),
|
||||
object: activity.req.object,
|
||||
published: activity.req.published.to_rfc3339(),
|
||||
};
|
||||
|
||||
dbg!(&raw);
|
||||
|
||||
let follow_res = self
|
||||
.transport
|
||||
.post(activity.to.inbox())
|
||||
.activity()
|
||||
.json(&raw)
|
||||
.sign(&activity.signed_by)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
dbg!(follow_res);
|
||||
}
|
||||
|
||||
pub fn for_user(user: User, transport: &'a OutboxTransport) -> Outbox<'a> {
|
||||
Outbox { user, transport }
|
||||
}
|
||||
}
|
|
@ -51,7 +51,8 @@ impl RequestBuilder {
|
|||
}
|
||||
|
||||
pub fn activity(mut self) -> RequestBuilder {
|
||||
self.inner = self.inner
|
||||
self.inner = self
|
||||
.inner
|
||||
.header("Content-Type", "application/activity+json")
|
||||
.header("Accept", "application/activity+json");
|
||||
self
|
||||
|
@ -66,14 +67,16 @@ impl RequestBuilder {
|
|||
match self.verb {
|
||||
RequestVerb::GET => {
|
||||
let sig = self.sign_get_request(key_id);
|
||||
self.inner = self.inner
|
||||
self.inner = self
|
||||
.inner
|
||||
.header("Date", sig.date)
|
||||
.header("Signature", sig.signature);
|
||||
self
|
||||
}
|
||||
RequestVerb::POST => {
|
||||
let sig = self.sign_post_request(key_id);
|
||||
self.inner = self.inner
|
||||
self.inner = self
|
||||
.inner
|
||||
.header("Date", sig.date)
|
||||
.header("Digest", sig.digest)
|
||||
.header("Signature", sig.signature);
|
||||
|
@ -87,7 +90,8 @@ impl RequestBuilder {
|
|||
let host = url.host_str().unwrap();
|
||||
let path = url.path();
|
||||
|
||||
let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!("../../../private.pem")).unwrap();
|
||||
let private_key =
|
||||
RsaPrivateKey::from_pkcs8_pem(include_str!("../../../private.pem")).unwrap();
|
||||
let signing_key = SigningKey::<Sha256>::new(private_key);
|
||||
|
||||
// UTC=GMT for our purposes, use it
|
||||
|
@ -122,7 +126,8 @@ impl RequestBuilder {
|
|||
let host = url.host_str().unwrap();
|
||||
let path = url.path();
|
||||
|
||||
let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!("../../../private.pem")).unwrap();
|
||||
let private_key =
|
||||
RsaPrivateKey::from_pkcs8_pem(include_str!("../../../private.pem")).unwrap();
|
||||
let signing_key = SigningKey::<Sha256>::new(private_key);
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
|
|
|
@ -1,262 +1,27 @@
|
|||
use chrono::{DateTime, Local};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::Sqlite;
|
||||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
use std::fmt::Debug;
|
||||
pub mod http;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Actor {
|
||||
id: String,
|
||||
inbox: String,
|
||||
outbox: String,
|
||||
mod activity;
|
||||
pub use activity::*;
|
||||
|
||||
mod user;
|
||||
pub use user::*;
|
||||
|
||||
mod post;
|
||||
pub use post::*;
|
||||
|
||||
pub const AS_CONTEXT: &'static str = "https://www.w3.org/ns/activitystreams";
|
||||
|
||||
pub fn new_id() -> String {
|
||||
Uuid::new_v4().to_string()
|
||||
}
|
||||
|
||||
impl Actor {
|
||||
pub fn from_raw(id: String, inbox: String, outbox: String) -> Self {
|
||||
Self { id, inbox, outbox }
|
||||
}
|
||||
pub fn new_ts() -> String {
|
||||
now().to_rfc3339()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct User {
|
||||
id: String,
|
||||
username: String,
|
||||
actor: Actor,
|
||||
display_name: String,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn username(&self) -> &str {
|
||||
&self.username
|
||||
}
|
||||
|
||||
pub fn actor_id(&self) -> &str {
|
||||
&self.actor.id
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> &str {
|
||||
&self.display_name
|
||||
}
|
||||
|
||||
pub fn actor(&self) -> &Actor {
|
||||
&self.actor
|
||||
}
|
||||
|
||||
pub async fn from_id(
|
||||
uuid: &str,
|
||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||
) -> User {
|
||||
let user = sqlx::query!(
|
||||
r#"
|
||||
SELECT u.*, a.id as "actor_own_id", a.inbox, a.outbox
|
||||
FROM user u
|
||||
INNER JOIN actor a ON u.actor_id = a.id
|
||||
WHERE u.id = ?1
|
||||
"#,
|
||||
uuid
|
||||
)
|
||||
.fetch_one(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
User {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
actor: Actor {
|
||||
id: user.actor_own_id,
|
||||
inbox: user.inbox,
|
||||
outbox: user.outbox,
|
||||
},
|
||||
display_name: user.display_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_username(
|
||||
username: &str,
|
||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||
) -> User {
|
||||
let user = sqlx::query!(
|
||||
r#"
|
||||
SELECT u.*, a.id as "actor_own_id", a.inbox, a.outbox
|
||||
FROM user u
|
||||
INNER JOIN actor a ON u.actor_id = a.id
|
||||
WHERE username = ?1
|
||||
"#,
|
||||
username
|
||||
)
|
||||
.fetch_one(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
User {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
actor: Actor {
|
||||
id: user.actor_own_id,
|
||||
inbox: user.inbox,
|
||||
outbox: user.outbox,
|
||||
},
|
||||
display_name: user.display_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_actor_id(
|
||||
actor_id: &str,
|
||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||
) -> User {
|
||||
let user = sqlx::query!(
|
||||
r#"
|
||||
SELECT u.*, a.id as "actor_own_id", a.inbox, a.outbox
|
||||
FROM user u
|
||||
INNER JOIN actor a ON u.actor_id = a.id
|
||||
WHERE actor_id = ?1
|
||||
"#,
|
||||
actor_id
|
||||
)
|
||||
.fetch_one(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
User {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
actor: Actor {
|
||||
id: user.actor_own_id,
|
||||
inbox: user.inbox,
|
||||
outbox: user.outbox,
|
||||
},
|
||||
display_name: user.display_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ActivityType {
|
||||
Follow,
|
||||
Accept,
|
||||
Create,
|
||||
Unknown
|
||||
}
|
||||
|
||||
impl ActivityType {
|
||||
fn to_raw(self) -> String {
|
||||
match self {
|
||||
ActivityType::Follow => "Follow".to_string(),
|
||||
ActivityType::Accept => "Accept".to_string(),
|
||||
ActivityType::Create => "Create".to_string(),
|
||||
ActivityType::Unknown => "FIXME".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Activity<T : Serialize + Debug> {
|
||||
pub id: String,
|
||||
pub ty: ActivityType,
|
||||
pub object: T,
|
||||
pub published: DateTime<Local>,
|
||||
pub to: Vec<String>,
|
||||
pub cc: Vec<String>,
|
||||
}
|
||||
|
||||
impl <T : Serialize + Debug + Default> Default for Activity<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: Default::default(),
|
||||
ty: ActivityType::Unknown,
|
||||
object: Default::default(),
|
||||
published: Local::now(),
|
||||
to: Default::default(),
|
||||
cc: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type KeyId = String;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutgoingActivity<T : Serialize + Debug> {
|
||||
pub signed_by: KeyId,
|
||||
pub req: Activity<T>,
|
||||
pub to: Actor,
|
||||
}
|
||||
|
||||
impl <T : Serialize + Debug> OutgoingActivity<T> {
|
||||
pub async fn save(&self, conn: impl sqlx::Executor<'_, Database = Sqlite>) {
|
||||
let ty = self.req.ty.clone().to_raw();
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO activity (id, ty, actor_id)
|
||||
VALUES (?1, ?2, ?3)
|
||||
"#,
|
||||
self.req.id,
|
||||
ty,
|
||||
self.to.id
|
||||
)
|
||||
.execute(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct RawActivity<T : Serialize + Debug> {
|
||||
#[serde(rename = "@context")]
|
||||
#[serde(skip_deserializing)]
|
||||
context: String,
|
||||
|
||||
id: String,
|
||||
#[serde(rename = "type")]
|
||||
ty: String,
|
||||
|
||||
actor: String,
|
||||
object: T,
|
||||
published: String,
|
||||
}
|
||||
|
||||
type OutboxTransport = http::HttpClient;
|
||||
pub struct Outbox<'a> {
|
||||
user: User,
|
||||
transport: &'a OutboxTransport,
|
||||
}
|
||||
|
||||
impl<'a> Outbox<'a> {
|
||||
pub fn user(&self) -> &User {
|
||||
&self.user
|
||||
}
|
||||
|
||||
pub async fn post<T : Serialize + Debug>(&self, activity: OutgoingActivity<T>) {
|
||||
dbg!(&activity);
|
||||
let raw = RawActivity {
|
||||
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
||||
id: activity.req.id,
|
||||
ty: activity.req.ty.to_raw(),
|
||||
actor: self.user.actor.id.clone(),
|
||||
object: activity.req.object,
|
||||
published: activity.req.published.to_rfc3339(),
|
||||
};
|
||||
|
||||
dbg!(&raw);
|
||||
|
||||
let follow_res = self
|
||||
.transport
|
||||
.post(activity.to.inbox)
|
||||
.activity()
|
||||
.json(&raw)
|
||||
.sign(&activity.signed_by)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
dbg!(follow_res);
|
||||
}
|
||||
|
||||
pub fn for_user(user: User, transport: &'a OutboxTransport) -> Outbox<'a> {
|
||||
Outbox { user, transport }
|
||||
}
|
||||
pub fn now() -> DateTime<Utc> {
|
||||
Utc::now()
|
||||
}
|
||||
|
|
113
ferri-main/src/ap/post.rs
Normal file
113
ferri-main/src/ap/post.rs
Normal file
|
@ -0,0 +1,113 @@
|
|||
use crate::ap;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::Serialize;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
const POST_TYPE: &'static str = "Post";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Post {
|
||||
id: String,
|
||||
from: ap::User,
|
||||
ts: DateTime<Utc>,
|
||||
content: String,
|
||||
|
||||
to: Vec<String>,
|
||||
cc: Vec<String>,
|
||||
}
|
||||
|
||||
impl Post {
|
||||
pub fn from_parts(id: String, content: String, from: ap::User) -> Self {
|
||||
Self {
|
||||
id,
|
||||
content,
|
||||
from,
|
||||
ts: ap::now(),
|
||||
to: vec![],
|
||||
cc: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn content(&self) -> &str {
|
||||
&self.content
|
||||
}
|
||||
|
||||
pub fn created_at(&self) -> String {
|
||||
self.ts.to_rfc3339()
|
||||
}
|
||||
|
||||
pub fn uri(&self) -> String {
|
||||
format!(
|
||||
"https://ferri.amy.mov/users/{}/posts/{}",
|
||||
self.from.id(),
|
||||
self.id
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn save(&self, conn: impl sqlx::Executor<'_, Database = Sqlite>) {
|
||||
let ts = self.ts.to_rfc3339();
|
||||
let user_id = self.from.id();
|
||||
let post_id = self.id();
|
||||
let uri = self.uri();
|
||||
let content = self.content.clone();
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO post (id, uri, user_id, content, created_at)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||
"#,
|
||||
post_id,
|
||||
uri,
|
||||
user_id,
|
||||
content,
|
||||
ts
|
||||
)
|
||||
.execute(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn to(mut self, recipient: String) -> Self {
|
||||
self.to.push(recipient);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cc(mut self, recipient: String) -> Self {
|
||||
self.cc.push(recipient);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn to_ap(self) -> APPost {
|
||||
APPost {
|
||||
context: ap::AS_CONTEXT.to_string(),
|
||||
id: self.uri(),
|
||||
ty: POST_TYPE.to_string(),
|
||||
ts: self.ts.to_rfc3339(),
|
||||
content: self.content,
|
||||
to: self.to,
|
||||
cc: self.cc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Default)]
|
||||
pub struct APPost {
|
||||
#[serde(rename = "@context")]
|
||||
#[serde(skip_deserializing)]
|
||||
context: String,
|
||||
id: String,
|
||||
|
||||
#[serde(rename = "type")]
|
||||
ty: String,
|
||||
|
||||
#[serde(rename = "published")]
|
||||
ts: String,
|
||||
|
||||
content: String,
|
||||
to: Vec<String>,
|
||||
cc: Vec<String>,
|
||||
}
|
138
ferri-main/src/ap/user.rs
Normal file
138
ferri-main/src/ap/user.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
use sqlx::Sqlite;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Actor {
|
||||
id: String,
|
||||
inbox: String,
|
||||
outbox: String,
|
||||
}
|
||||
|
||||
impl Actor {
|
||||
pub fn from_raw(id: String, inbox: String, outbox: String) -> Self {
|
||||
Self { id, inbox, outbox }
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn inbox(&self) -> &str {
|
||||
&self.inbox
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct User {
|
||||
id: String,
|
||||
username: String,
|
||||
actor: Actor,
|
||||
display_name: String,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn username(&self) -> &str {
|
||||
&self.username
|
||||
}
|
||||
|
||||
pub fn actor_id(&self) -> &str {
|
||||
&self.actor.id
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> &str {
|
||||
&self.display_name
|
||||
}
|
||||
|
||||
pub fn actor(&self) -> &Actor {
|
||||
&self.actor
|
||||
}
|
||||
|
||||
pub fn uri(&self) -> String {
|
||||
format!("https://ferri.amy.mov/users/{}", self.id())
|
||||
}
|
||||
|
||||
pub async fn from_id(uuid: &str, conn: impl sqlx::Executor<'_, Database = Sqlite>) -> User {
|
||||
let user = sqlx::query!(
|
||||
r#"
|
||||
SELECT u.*, a.id as "actor_own_id", a.inbox, a.outbox
|
||||
FROM user u
|
||||
INNER JOIN actor a ON u.actor_id = a.id
|
||||
WHERE u.id = ?1
|
||||
"#,
|
||||
uuid
|
||||
)
|
||||
.fetch_one(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
User {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
actor: Actor {
|
||||
id: user.actor_own_id,
|
||||
inbox: user.inbox,
|
||||
outbox: user.outbox,
|
||||
},
|
||||
display_name: user.display_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_username(
|
||||
username: &str,
|
||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||
) -> User {
|
||||
let user = sqlx::query!(
|
||||
r#"
|
||||
SELECT u.*, a.id as "actor_own_id", a.inbox, a.outbox
|
||||
FROM user u
|
||||
INNER JOIN actor a ON u.actor_id = a.id
|
||||
WHERE username = ?1
|
||||
"#,
|
||||
username
|
||||
)
|
||||
.fetch_one(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
User {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
actor: Actor {
|
||||
id: user.actor_own_id,
|
||||
inbox: user.inbox,
|
||||
outbox: user.outbox,
|
||||
},
|
||||
display_name: user.display_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_actor_id(
|
||||
actor_id: &str,
|
||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||
) -> User {
|
||||
let user = sqlx::query!(
|
||||
r#"
|
||||
SELECT u.*, a.id as "actor_own_id", a.inbox, a.outbox
|
||||
FROM user u
|
||||
INNER JOIN actor a ON u.actor_id = a.id
|
||||
WHERE actor_id = ?1
|
||||
"#,
|
||||
actor_id
|
||||
)
|
||||
.fetch_one(conn)
|
||||
.await
|
||||
.unwrap();
|
||||
User {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
actor: Actor {
|
||||
id: user.actor_own_id,
|
||||
inbox: user.inbox,
|
||||
outbox: user.outbox,
|
||||
},
|
||||
display_name: user.display_name,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue