mirror of
https://github.com/nullishamy/ferri.git
synced 2025-08-18 10:39:14 +00:00
Compare commits
No commits in common. "8cf7834cfedd7ad2efd7ea56445446d3e7851779" and "ecb706e93fcf50b99d48b91dd95d2898d5da4328" have entirely different histories.
8cf7834cfe
...
ecb706e93f
12 changed files with 28 additions and 706 deletions
|
@ -75,10 +75,6 @@ impl<'a> HttpWrapper<'a> {
|
||||||
self.get("Person", url).await
|
self.get("Person", url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_note(&self, url: &str) -> Result<ap::Post, HttpError> {
|
|
||||||
self.get("Note", url).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn post_activity<T : Serialize + Debug>(
|
pub async fn post_activity<T : Serialize + Debug>(
|
||||||
&self,
|
&self,
|
||||||
inbox: &str,
|
inbox: &str,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use super::outbox::OutboxRequest;
|
||||||
use super::QueueMessage;
|
use super::QueueMessage;
|
||||||
|
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use tracing::{warn, error, Level, event};
|
use tracing::warn;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum InboxRequest {
|
pub enum InboxRequest {
|
||||||
|
@ -19,7 +19,7 @@ pub enum InboxRequest {
|
||||||
},
|
},
|
||||||
Create(ap::CreateActivity, db::User, sqlx::SqliteConnection),
|
Create(ap::CreateActivity, db::User, sqlx::SqliteConnection),
|
||||||
Like(ap::LikeActivity, db::User),
|
Like(ap::LikeActivity, db::User),
|
||||||
Boost(ap::BoostActivity, db::User, sqlx::SqliteConnection)
|
Boost(ap::BoostActivity, db::User)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_id(user: &db::User) -> String {
|
fn key_id(user: &db::User) -> String {
|
||||||
|
@ -32,7 +32,7 @@ pub async fn handle_inbox_request(
|
||||||
) {
|
) {
|
||||||
match req {
|
match req {
|
||||||
InboxRequest::Delete(_, _) => {
|
InboxRequest::Delete(_, _) => {
|
||||||
warn!("unimplemented Delete");
|
todo!()
|
||||||
},
|
},
|
||||||
InboxRequest::Follow { activity, followed, mut conn, outbound } => {
|
InboxRequest::Follow { activity, followed, mut conn, outbound } => {
|
||||||
let kid = key_id(&followed);
|
let kid = key_id(&followed);
|
||||||
|
@ -123,7 +123,6 @@ pub async fn handle_inbox_request(
|
||||||
user,
|
user,
|
||||||
content: post.content,
|
content: post.content,
|
||||||
created_at,
|
created_at,
|
||||||
attachments: vec![],
|
|
||||||
boosted_post: None
|
boosted_post: None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,165 +134,8 @@ pub async fn handle_inbox_request(
|
||||||
InboxRequest::Like(_, _) => {
|
InboxRequest::Like(_, _) => {
|
||||||
warn!("unimplemented Like in inbox");
|
warn!("unimplemented Like in inbox");
|
||||||
},
|
},
|
||||||
InboxRequest::Boost(activity, target, mut conn) => {
|
InboxRequest::Boost(_, _) => {
|
||||||
let id = key_id(&target);
|
warn!("unimplemented Boost in inbox");
|
||||||
let http = HttpWrapper::new(http, &id);
|
|
||||||
let person = http.get_person(&activity.actor).await.unwrap();
|
|
||||||
let rmt = person.remote_info();
|
|
||||||
|
|
||||||
let boosted_note = http.get_note(&activity.object).await.unwrap();
|
|
||||||
let boosted_author = if let Some(attributed_to) = &boosted_note.attributed_to {
|
|
||||||
http.get_person(attributed_to).await.map_err(|e| {
|
|
||||||
error!("failed to fetch attributed_to {}: {}",
|
|
||||||
attributed_to,
|
|
||||||
e.to_string()
|
|
||||||
);
|
|
||||||
()
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}.unwrap();
|
|
||||||
|
|
||||||
let boosted_rmt = boosted_author.remote_info();
|
|
||||||
|
|
||||||
event!(Level::INFO,
|
|
||||||
boosted_by = rmt.acct,
|
|
||||||
op = boosted_rmt.acct,
|
|
||||||
"recording boost"
|
|
||||||
);
|
|
||||||
|
|
||||||
let boosted_post = {
|
|
||||||
let actor_uri = boosted_author.obj.id;
|
|
||||||
let actor = db::Actor {
|
|
||||||
id: actor_uri,
|
|
||||||
inbox: boosted_author.inbox,
|
|
||||||
outbox: boosted_author.outbox
|
|
||||||
};
|
|
||||||
|
|
||||||
make::new_actor(actor.clone(), &mut conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let user = get::user_by_actor_uri(actor.id.clone(), &mut conn)
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
db::User {
|
|
||||||
id: ObjectUuid(crate::new_id()),
|
|
||||||
actor,
|
|
||||||
username: boosted_author.preferred_username,
|
|
||||||
display_name: boosted_author.name,
|
|
||||||
acct: boosted_rmt.acct,
|
|
||||||
remote: boosted_rmt.is_remote,
|
|
||||||
url: boosted_rmt.web_url,
|
|
||||||
// FIXME: Come from boosted_author
|
|
||||||
created_at: crate::ap::now(),
|
|
||||||
icon_url: boosted_author.icon.map(|ic| ic.url)
|
|
||||||
.unwrap_or("https//ferri.amy.mov/assets/pfp.png".to_string()),
|
|
||||||
posts: db::UserPosts {
|
|
||||||
last_post_at: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
make::new_user(user.clone(), &mut conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let id = crate::new_id();
|
|
||||||
let created_at = DateTime::parse_from_rfc3339(&boosted_note.ts)
|
|
||||||
.map(|dt| dt.to_utc())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let attachments = boosted_note.attachment
|
|
||||||
.into_iter()
|
|
||||||
.map(|at| {
|
|
||||||
db::Attachment {
|
|
||||||
id: ObjectUuid(crate::new_id()),
|
|
||||||
post_id: ObjectUuid(id.clone()),
|
|
||||||
url: at.url,
|
|
||||||
media_type: Some(at.media_type),
|
|
||||||
sensitive: at.sensitive,
|
|
||||||
alt: at.summary
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
db::Post {
|
|
||||||
id: ObjectUuid(id),
|
|
||||||
uri: boosted_note.obj.id,
|
|
||||||
user,
|
|
||||||
attachments,
|
|
||||||
content: boosted_note.content,
|
|
||||||
created_at,
|
|
||||||
boosted_post: None
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
make::new_post(boosted_post.clone(), &mut conn).await.unwrap();
|
|
||||||
|
|
||||||
let base_note = {
|
|
||||||
let actor_uri = person.obj.id.clone();
|
|
||||||
let actor = db::Actor {
|
|
||||||
id: actor_uri,
|
|
||||||
inbox: person.inbox,
|
|
||||||
outbox: person.outbox
|
|
||||||
};
|
|
||||||
|
|
||||||
make::new_actor(actor.clone(), &mut conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let user = get::user_by_actor_uri(actor.id.clone(), &mut conn)
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
db::User {
|
|
||||||
id: ObjectUuid(crate::new_id()),
|
|
||||||
actor,
|
|
||||||
username: person.preferred_username,
|
|
||||||
display_name: person.name,
|
|
||||||
acct: rmt.acct,
|
|
||||||
remote: rmt.is_remote,
|
|
||||||
url: rmt.web_url,
|
|
||||||
created_at: crate::ap::now(),
|
|
||||||
icon_url: person.icon.map(|ic| ic.url)
|
|
||||||
.unwrap_or("https//ferri.amy.mov/assets/pfp.png".to_string()),
|
|
||||||
posts: db::UserPosts {
|
|
||||||
last_post_at: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
make::new_user(user.clone(), &mut conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let id = crate::new_id();
|
|
||||||
let created_at = DateTime::parse_from_rfc3339(&activity.published)
|
|
||||||
.map(|dt| dt.to_utc())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db::Post {
|
|
||||||
id: ObjectUuid(id.clone()),
|
|
||||||
uri: ObjectUri(
|
|
||||||
format!("https://ferri.amy.mov/users/{}/posts/{}",
|
|
||||||
person.obj.id.0,
|
|
||||||
id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
user,
|
|
||||||
attachments: vec![],
|
|
||||||
content: String::new(),
|
|
||||||
created_at,
|
|
||||||
boosted_post: Some(boosted_post.id.clone())
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
make::new_post(base_note, &mut conn).await.unwrap();
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,27 +46,22 @@ impl RequestQueue {
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
info!("using config {:#?}, queue is up", config);
|
info!("using config {:#?}, queue is up", config);
|
||||||
let mut recv = self.recv;
|
let mut recv = self.recv;
|
||||||
|
let http = HttpClient::new();
|
||||||
|
|
||||||
while let Some(req) = recv.recv().await {
|
while let Some(req) = recv.recv().await {
|
||||||
info!(?req, "got a message into the queue");
|
info!(?req, "got a message into the queue");
|
||||||
|
|
||||||
// Spawn up a new task so that we can run concurrently and also so we can not die if it panics
|
match req {
|
||||||
tokio::spawn(async {
|
QueueMessage::Heartbeat => {
|
||||||
let http = HttpClient::new();
|
info!("heartbeat on queue");
|
||||||
|
},
|
||||||
match req {
|
QueueMessage::Inbound(inbox_request) => {
|
||||||
QueueMessage::Heartbeat => {
|
handle_inbox_request(inbox_request, &http).await;
|
||||||
info!("heartbeat on queue");
|
},
|
||||||
},
|
QueueMessage::Outbound(outbox_request) => {
|
||||||
QueueMessage::Inbound(inbox_request) => {
|
handle_outbox_request(outbox_request, &http).await;
|
||||||
handle_inbox_request(inbox_request, &http).await;
|
},
|
||||||
},
|
}
|
||||||
QueueMessage::Outbound(outbox_request) => {
|
|
||||||
handle_outbox_request(outbox_request, &http).await;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}.instrument(span);
|
}.instrument(span);
|
||||||
|
|
||||||
|
|
|
@ -209,27 +209,6 @@ pub async fn posts_for_user_id(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
for record in posts {
|
for record in posts {
|
||||||
let attachments = sqlx::query!(
|
|
||||||
"SELECT * FROM attachment WHERE post_id = ?",
|
|
||||||
record.post_id
|
|
||||||
)
|
|
||||||
.fetch_all(&mut *conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let attachments = attachments.into_iter()
|
|
||||||
.map(|at| {
|
|
||||||
db::Attachment {
|
|
||||||
id: ObjectUuid(at.id),
|
|
||||||
post_id: ObjectUuid(at.post_id),
|
|
||||||
url: at.url,
|
|
||||||
media_type: Some(at.media_type),
|
|
||||||
sensitive: at.marked_sensitive,
|
|
||||||
alt: at.alt
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let user_created = parse_ts(record.user_created)
|
let user_created = parse_ts(record.user_created)
|
||||||
.expect("no db corruption");
|
.expect("no db corruption");
|
||||||
|
|
||||||
|
@ -254,7 +233,6 @@ pub async fn posts_for_user_id(
|
||||||
last_post_at: None
|
last_post_at: None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
attachments,
|
|
||||||
content: record.content,
|
content: record.content,
|
||||||
created_at: parse_ts(record.post_created).unwrap(),
|
created_at: parse_ts(record.post_created).unwrap(),
|
||||||
boosted_post: None
|
boosted_post: None
|
||||||
|
|
|
@ -68,28 +68,6 @@ pub async fn new_follow(
|
||||||
Ok(follow)
|
Ok(follow)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_attachment(
|
|
||||||
attachment: db::Attachment,
|
|
||||||
conn: &mut SqliteConnection
|
|
||||||
) -> Result<db::Attachment, DbError> {
|
|
||||||
sqlx::query!(
|
|
||||||
r#"
|
|
||||||
INSERT INTO attachment (id, post_id, url, media_type, marked_sensitive, alt)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
|
||||||
"#,
|
|
||||||
attachment.id.0,
|
|
||||||
attachment.post_id.0,
|
|
||||||
attachment.url,
|
|
||||||
attachment.media_type,
|
|
||||||
attachment.sensitive,
|
|
||||||
attachment.alt
|
|
||||||
)
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| DbError::CreationError(e.to_string()))?;
|
|
||||||
|
|
||||||
Ok(attachment)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new_post(
|
pub async fn new_post(
|
||||||
post: db::Post,
|
post: db::Post,
|
||||||
|
@ -110,14 +88,11 @@ pub async fn new_post(
|
||||||
post.content,
|
post.content,
|
||||||
ts,
|
ts,
|
||||||
boosted
|
boosted
|
||||||
)
|
|
||||||
.execute(&mut *conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| DbError::CreationError(e.to_string()))?;
|
|
||||||
|
|
||||||
for attachment in post.attachments.clone() {
|
)
|
||||||
new_attachment(attachment, &mut *conn).await?;
|
.execute(conn)
|
||||||
}
|
.await
|
||||||
|
.map_err(|e| DbError::CreationError(e.to_string()))?;
|
||||||
|
|
||||||
Ok(post)
|
Ok(post)
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,16 +98,6 @@ pub mod db {
|
||||||
pub posts: UserPosts,
|
pub posts: UserPosts,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
|
||||||
pub struct Attachment {
|
|
||||||
pub id: ObjectUuid,
|
|
||||||
pub post_id: ObjectUuid,
|
|
||||||
pub url: String,
|
|
||||||
pub media_type: Option<String>,
|
|
||||||
pub sensitive: bool,
|
|
||||||
pub alt: Option<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
pub id: ObjectUuid,
|
pub id: ObjectUuid,
|
||||||
|
@ -115,8 +105,7 @@ pub mod db {
|
||||||
pub user: User,
|
pub user: User,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub boosted_post: Option<ObjectUuid>,
|
pub boosted_post: Option<ObjectUuid>
|
||||||
pub attachments: Vec<Attachment>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,25 +203,6 @@ pub mod ap {
|
||||||
pub object: String,
|
pub object: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub enum PostAttachmentType {
|
|
||||||
Document
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct PostAttachment {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub ty: PostAttachmentType,
|
|
||||||
|
|
||||||
pub media_type: String,
|
|
||||||
pub url: String,
|
|
||||||
pub name: String,
|
|
||||||
pub summary: Option<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub sensitive: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -247,8 +217,6 @@ pub mod ap {
|
||||||
pub to: Vec<String>,
|
pub to: Vec<String>,
|
||||||
pub cc: Vec<String>,
|
pub cc: Vec<String>,
|
||||||
|
|
||||||
pub attachment: Vec<PostAttachment>,
|
|
||||||
|
|
||||||
#[serde(rename = "attributedTo")]
|
#[serde(rename = "attributedTo")]
|
||||||
pub attributed_to: Option<String>,
|
pub attributed_to: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{AuthenticatedUser, Db, endpoints::api::user::CredentialAcount};
|
use crate::{AuthenticatedUser, Db, endpoints::api::user::CredentialAcount};
|
||||||
use main::types::ObjectUuid;
|
|
||||||
use rocket::{
|
use rocket::{
|
||||||
get,
|
get,
|
||||||
serde::{Deserialize, Serialize, json::Json},
|
serde::{Deserialize, Serialize, json::Json},
|
||||||
|
@ -8,16 +7,6 @@ use rocket_db_pools::Connection;
|
||||||
|
|
||||||
pub type TimelineAccount = CredentialAcount;
|
pub type TimelineAccount = CredentialAcount;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
pub struct TimelineStatusAttachment {
|
|
||||||
id: ObjectUuid,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
ty: String,
|
|
||||||
url: String,
|
|
||||||
description: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct TimelineStatus {
|
pub struct TimelineStatus {
|
||||||
|
@ -39,14 +28,14 @@ pub struct TimelineStatus {
|
||||||
pub muted: bool,
|
pub muted: bool,
|
||||||
pub bookmarked: bool,
|
pub bookmarked: bool,
|
||||||
pub reblog: Option<Box<TimelineStatus>>,
|
pub reblog: Option<Box<TimelineStatus>>,
|
||||||
pub media_attachments: Vec<TimelineStatusAttachment>,
|
pub media_attachments: Vec<()>,
|
||||||
pub account: TimelineAccount,
|
pub account: TimelineAccount,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/timelines/home")]
|
#[get("/timelines/home")]
|
||||||
pub async fn home(
|
pub async fn home(
|
||||||
mut db: Connection<Db>,
|
mut db: Connection<Db>,
|
||||||
user: AuthenticatedUser,
|
_user: AuthenticatedUser,
|
||||||
) -> Json<Vec<TimelineStatus>> {
|
) -> Json<Vec<TimelineStatus>> {
|
||||||
#[derive(sqlx::FromRow, Debug)]
|
#[derive(sqlx::FromRow, Debug)]
|
||||||
struct Post {
|
struct Post {
|
||||||
|
@ -91,7 +80,7 @@ pub async fn home(
|
||||||
JOIN user u ON u.id = p.user_id;
|
JOIN user u ON u.id = p.user_id;
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(user.actor_id.0)
|
.bind("https://ferri.amy.mov/users/9b9d497b-2731-435f-a929-e609ca69dac9")
|
||||||
.fetch_all(&mut **db)
|
.fetch_all(&mut **db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -101,23 +90,6 @@ pub async fn home(
|
||||||
let mut boost: Option<Box<TimelineStatus>> = None;
|
let mut boost: Option<Box<TimelineStatus>> = None;
|
||||||
if let Some(ref boosted_id) = record.boosted_post_id {
|
if let Some(ref boosted_id) = record.boosted_post_id {
|
||||||
let record = posts.iter().find(|p| &p.post_id == boosted_id).unwrap();
|
let record = posts.iter().find(|p| &p.post_id == boosted_id).unwrap();
|
||||||
let attachments = sqlx::query!(
|
|
||||||
"SELECT * FROM attachment WHERE post_id = ?1",
|
|
||||||
boosted_id
|
|
||||||
)
|
|
||||||
.fetch_all(&mut **db)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.map(|at| {
|
|
||||||
TimelineStatusAttachment {
|
|
||||||
id: ObjectUuid(at.id),
|
|
||||||
url: at.url,
|
|
||||||
ty: "image".to_string(),
|
|
||||||
description: at.alt.unwrap_or(String::new())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
boost = Some(Box::new(TimelineStatus {
|
boost = Some(Box::new(TimelineStatus {
|
||||||
id: record.post_id.clone(),
|
id: record.post_id.clone(),
|
||||||
|
@ -138,7 +110,7 @@ pub async fn home(
|
||||||
reblog: boost,
|
reblog: boost,
|
||||||
muted: false,
|
muted: false,
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
media_attachments: attachments,
|
media_attachments: vec![],
|
||||||
account: CredentialAcount {
|
account: CredentialAcount {
|
||||||
id: record.user_id.clone(),
|
id: record.user_id.clone(),
|
||||||
username: record.username.clone(),
|
username: record.username.clone(),
|
||||||
|
@ -162,7 +134,6 @@ pub async fn home(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't send the empty boost source posts
|
|
||||||
if !record.is_boost_source {
|
if !record.is_boost_source {
|
||||||
out.push(TimelineStatus {
|
out.push(TimelineStatus {
|
||||||
id: record.post_id.clone(),
|
id: record.post_id.clone(),
|
||||||
|
|
|
@ -143,7 +143,6 @@ pub async fn statuses(
|
||||||
FROM post p
|
FROM post p
|
||||||
INNER JOIN user u on p.user_id = u.id
|
INNER JOIN user u on p.user_id = u.id
|
||||||
WHERE u.id = ?1
|
WHERE u.id = ?1
|
||||||
ORDER BY p.created_at DESC
|
|
||||||
"#, uid)
|
"#, uid)
|
||||||
.fetch_all(&mut **db)
|
.fetch_all(&mut **db)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -83,7 +83,7 @@ pub async fn inbox(
|
||||||
ap::ActivityType::Announce => {
|
ap::ActivityType::Announce => {
|
||||||
let activity = deser::<ap::BoostActivity>(&body);
|
let activity = deser::<ap::BoostActivity>(&body);
|
||||||
let msg = QueueMessage::Inbound(
|
let msg = QueueMessage::Inbound(
|
||||||
InboxRequest::Boost(activity, user, conn)
|
InboxRequest::Boost(activity, user)
|
||||||
);
|
);
|
||||||
|
|
||||||
queue.0.send(msg).await;
|
queue.0.send(msg).await;
|
||||||
|
|
|
@ -1,388 +0,0 @@
|
||||||
fn handle_delete_activity(activity: ap::DeleteActivity) {
|
|
||||||
warn!(?activity, "unimplemented delete activity");
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_actor(
|
|
||||||
user: &ap::Person,
|
|
||||||
actor: &str,
|
|
||||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
|
||||||
) {
|
|
||||||
sqlx::query!(
|
|
||||||
r#"
|
|
||||||
INSERT INTO actor (id, inbox, outbox)
|
|
||||||
VALUES ( ?1, ?2, ?3 )
|
|
||||||
ON CONFLICT(id) DO NOTHING;
|
|
||||||
"#,
|
|
||||||
actor,
|
|
||||||
user.inbox,
|
|
||||||
user.outbox
|
|
||||||
)
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_user(
|
|
||||||
user: &ap::Person,
|
|
||||||
actor: &str,
|
|
||||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
|
||||||
) {
|
|
||||||
// HACK: Allow us to formulate a `user@host` username by assuming the actor is on the same host as the user
|
|
||||||
let url = Url::parse(actor).unwrap();
|
|
||||||
let host = url.host_str().unwrap();
|
|
||||||
info!(
|
|
||||||
"creating user '{}'@'{}' ({:#?})",
|
|
||||||
user.preferred_username, host, user
|
|
||||||
);
|
|
||||||
|
|
||||||
let (acct, remote) = if host != "ferri.amy.mov" {
|
|
||||||
(format!("{}@{}", user.preferred_username, host), true)
|
|
||||||
} else {
|
|
||||||
(user.preferred_username.clone(), false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let url = format!("https://ferri.amy.mov/{}", acct);
|
|
||||||
let icon_url = user.icon.as_ref().map(|ic| ic.url.clone()).unwrap_or(
|
|
||||||
"https://ferri.amy.mov/assets/pfp.png".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
let uuid = Uuid::new_v4().to_string();
|
|
||||||
// FIXME: Pull from user
|
|
||||||
let ts = main::ap::new_ts();
|
|
||||||
sqlx::query!(
|
|
||||||
r#"
|
|
||||||
INSERT INTO user (
|
|
||||||
id, acct, url, remote, username,
|
|
||||||
actor_id, display_name, created_at, icon_url
|
|
||||||
)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
|
|
||||||
ON CONFLICT(actor_id) DO NOTHING;
|
|
||||||
"#,
|
|
||||||
uuid,
|
|
||||||
acct,
|
|
||||||
url,
|
|
||||||
remote,
|
|
||||||
user.preferred_username,
|
|
||||||
actor,
|
|
||||||
user.name,
|
|
||||||
ts,
|
|
||||||
icon_url
|
|
||||||
)
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_follow(
|
|
||||||
activity: &ap::FollowActivity,
|
|
||||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
|
||||||
) {
|
|
||||||
sqlx::query!(
|
|
||||||
r#"
|
|
||||||
INSERT INTO follow (id, follower_id, followed_id)
|
|
||||||
VALUES ( ?1, ?2, ?3 )
|
|
||||||
ON CONFLICT(id) DO NOTHING;
|
|
||||||
"#,
|
|
||||||
activity.obj.id.0,
|
|
||||||
activity.actor,
|
|
||||||
activity.object
|
|
||||||
)
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RemoteInfo {
|
|
||||||
acct: String,
|
|
||||||
web_url: String,
|
|
||||||
is_remote: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_remote_info(actor_url: &str, person: &ap::Person) -> RemoteInfo {
|
|
||||||
let url = Url::parse(actor_url).unwrap();
|
|
||||||
let host = url.host_str().unwrap();
|
|
||||||
|
|
||||||
let (acct, remote) = if host != "ferri.amy.mov" {
|
|
||||||
(format!("{}@{}", person.preferred_username, host), true)
|
|
||||||
} else {
|
|
||||||
(person.preferred_username.clone(), false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let url = format!("https://ferri.amy.mov/{}", acct);
|
|
||||||
|
|
||||||
RemoteInfo {
|
|
||||||
acct: acct.to_string(),
|
|
||||||
web_url: url,
|
|
||||||
is_remote: remote,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn resolve_actor<'a>(
|
|
||||||
actor_url: &str,
|
|
||||||
http: &HttpWrapper<'a>,
|
|
||||||
conn: &mut SqliteConnection,
|
|
||||||
) -> Result<db::User, DbError> {
|
|
||||||
let person = {
|
|
||||||
let res = http.get_person(actor_url).await;
|
|
||||||
if let Err(e) = res {
|
|
||||||
error!("could not load user {}: {}", actor_url, e.to_string());
|
|
||||||
return Err(DbError::FetchError(format!(
|
|
||||||
"could not load user {}: {}",
|
|
||||||
actor_url, e
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
res.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = ObjectUuid::new();
|
|
||||||
let remote_info = get_remote_info(actor_url, &person);
|
|
||||||
|
|
||||||
let actor = db::Actor {
|
|
||||||
id: ObjectUri(actor_url.to_string()),
|
|
||||||
inbox: person.inbox.clone(),
|
|
||||||
outbox: person.outbox.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("creating actor {}", actor_url);
|
|
||||||
|
|
||||||
let actor = make::new_actor(actor.clone(), conn).await.unwrap_or(actor);
|
|
||||||
|
|
||||||
info!("creating user {} ({:#?})", remote_info.acct, person);
|
|
||||||
|
|
||||||
let user = db::User {
|
|
||||||
id: user_id,
|
|
||||||
actor,
|
|
||||||
username: person.name,
|
|
||||||
display_name: person.preferred_username,
|
|
||||||
acct: remote_info.acct,
|
|
||||||
remote: remote_info.is_remote,
|
|
||||||
url: remote_info.web_url,
|
|
||||||
created_at: main::ap::now(),
|
|
||||||
icon_url: person.icon.map(|ic| ic.url).unwrap_or(
|
|
||||||
"https://ferri.amy.mov/assets/pfp.png".to_string()
|
|
||||||
),
|
|
||||||
|
|
||||||
posts: db::UserPosts { last_post_at: None },
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(make::new_user(user.clone(), conn).await.unwrap_or(user))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_follow_activity<'a>(
|
|
||||||
followed_account: &str,
|
|
||||||
activity: ap::FollowActivity,
|
|
||||||
http: HttpWrapper<'a>,
|
|
||||||
mut db: Connection<Db>,
|
|
||||||
) {
|
|
||||||
let actor = resolve_actor(&activity.actor, &http, &mut db)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
info!("{:?} follows {}", actor, followed_account);
|
|
||||||
|
|
||||||
create_follow(&activity, &mut **db).await;
|
|
||||||
|
|
||||||
let follower = main::ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
|
||||||
let followed = main::ap::User::from_id(followed_account, &mut **db)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let outbox = main::ap::Outbox::for_user(followed.clone(), http.client());
|
|
||||||
|
|
||||||
let activity = main::ap::Activity {
|
|
||||||
id: format!("https://ferri.amy.mov/activities/{}", Uuid::new_v4()),
|
|
||||||
ty: main::ap::ActivityType::Accept,
|
|
||||||
object: activity.obj.id.0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let req = main::ap::OutgoingActivity {
|
|
||||||
signed_by: format!(
|
|
||||||
"https://ferri.amy.mov/users/{}#main-key",
|
|
||||||
followed.username()
|
|
||||||
),
|
|
||||||
req: activity,
|
|
||||||
to: follower.actor().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
req.save(&mut **db).await;
|
|
||||||
outbox.post(req).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_like_activity(activity: ap::LikeActivity, mut db: Connection<Db>) {
|
|
||||||
warn!(?activity, "unimplemented like activity");
|
|
||||||
|
|
||||||
let target_post = sqlx::query!("SELECT * FROM post WHERE uri = ?1", activity.object)
|
|
||||||
.fetch_one(&mut **db)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if let Ok(post) = target_post {
|
|
||||||
warn!(?post, "tried to like post");
|
|
||||||
} else {
|
|
||||||
warn!(post = ?activity.object, "could not find post");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_boost_activity<'a>(
|
|
||||||
activity: ap::BoostActivity,
|
|
||||||
http: HttpWrapper<'a>,
|
|
||||||
mut db: Connection<Db>,
|
|
||||||
) {
|
|
||||||
let key_id = "https://ferri.amy.mov/users/9b9d497b-2731-435f-a929-e609ca69dac9#main-key";
|
|
||||||
dbg!(&activity);
|
|
||||||
let post = http
|
|
||||||
.client()
|
|
||||||
.get(&activity.object)
|
|
||||||
.activity()
|
|
||||||
.sign(key_id)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.text()
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
info!("{}", post);
|
|
||||||
|
|
||||||
let post = serde_json::from_str::<ap::Post>(&post);
|
|
||||||
if let Err(e) = post {
|
|
||||||
error!(?e, "when decoding post");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let post = post.unwrap();
|
|
||||||
|
|
||||||
info!("{:#?}", post);
|
|
||||||
let attribution = post.attributed_to.unwrap();
|
|
||||||
|
|
||||||
let post_user = http.get_person(&attribution).await;
|
|
||||||
if let Err(e) = post_user {
|
|
||||||
error!(
|
|
||||||
"could not load post_user {}: {}",
|
|
||||||
attribution,
|
|
||||||
e.to_string()
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let post_user = post_user.unwrap();
|
|
||||||
|
|
||||||
let user = http.get_person(&activity.actor).await;
|
|
||||||
if let Err(e) = user {
|
|
||||||
error!("could not load actor {}: {}", activity.actor, e.to_string());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let user = user.unwrap();
|
|
||||||
|
|
||||||
debug!("creating actor {}", activity.actor);
|
|
||||||
create_actor(&user, &activity.actor, &mut **db).await;
|
|
||||||
|
|
||||||
debug!("creating user {}", activity.actor);
|
|
||||||
create_user(&user, &activity.actor, &mut **db).await;
|
|
||||||
|
|
||||||
debug!("creating actor {}", attribution);
|
|
||||||
create_actor(&post_user, &attribution, &mut **db).await;
|
|
||||||
|
|
||||||
debug!("creating user {}", attribution);
|
|
||||||
create_user(&post_user, &attribution, &mut **db).await;
|
|
||||||
|
|
||||||
let attributed_user = main::ap::User::from_actor_id(&attribution, &mut **db).await;
|
|
||||||
let actor_user = main::ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
|
||||||
|
|
||||||
let base_id = main::ap::new_id();
|
|
||||||
let now = main::ap::new_ts();
|
|
||||||
|
|
||||||
let reblog_id = main::ap::new_id();
|
|
||||||
|
|
||||||
let attr_id = attributed_user.id();
|
|
||||||
// HACK: ON CONFLICT is to avoid duplicate remote posts coming in
|
|
||||||
// check this better in future
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
INSERT INTO post (id, uri, user_id, content, created_at)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
|
||||||
ON CONFLICT(uri) DO NOTHING
|
|
||||||
",
|
|
||||||
reblog_id,
|
|
||||||
post.obj.id.0,
|
|
||||||
attr_id,
|
|
||||||
post.content,
|
|
||||||
post.ts
|
|
||||||
)
|
|
||||||
.execute(&mut **db)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let uri = format!(
|
|
||||||
"https://ferri.amy.mov/users/{}/posts/{}",
|
|
||||||
actor_user.id(),
|
|
||||||
base_id
|
|
||||||
);
|
|
||||||
let user_id = actor_user.id();
|
|
||||||
|
|
||||||
info!("inserting post with id {} uri {}", base_id, uri);
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
INSERT INTO post (id, uri, user_id, content, created_at, boosted_post_id)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
|
||||||
",
|
|
||||||
base_id,
|
|
||||||
uri,
|
|
||||||
user_id,
|
|
||||||
"",
|
|
||||||
now,
|
|
||||||
reblog_id
|
|
||||||
)
|
|
||||||
.execute(&mut **db)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_create_activity<'a>(
|
|
||||||
activity: ap::CreateActivity,
|
|
||||||
http: HttpWrapper<'a>,
|
|
||||||
mut db: Connection<Db>,
|
|
||||||
) {
|
|
||||||
assert!(activity.object.ty == ap::ActivityType::Note);
|
|
||||||
debug!("resolving user {}", activity.actor);
|
|
||||||
|
|
||||||
let user = http.get_person(&activity.actor).await;
|
|
||||||
if let Err(e) = user {
|
|
||||||
error!("could not load user {}: {}", activity.actor, e.to_string());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let user = user.unwrap();
|
|
||||||
|
|
||||||
debug!("creating actor {}", activity.actor);
|
|
||||||
create_actor(&user, &activity.actor, &mut **db).await;
|
|
||||||
|
|
||||||
debug!("creating user {}", activity.actor);
|
|
||||||
create_user(&user, &activity.actor, &mut **db).await;
|
|
||||||
|
|
||||||
let user = main::ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
|
||||||
debug!("user created {:?}", user);
|
|
||||||
|
|
||||||
let user_id = user.id();
|
|
||||||
let now = Local::now().to_rfc3339();
|
|
||||||
let content = activity.object.content.clone();
|
|
||||||
let post_id = Uuid::new_v4().to_string();
|
|
||||||
let uri = activity.obj.id.0;
|
|
||||||
|
|
||||||
info!(post_id, "creating post");
|
|
||||||
|
|
||||||
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,
|
|
||||||
now
|
|
||||||
)
|
|
||||||
.execute(&mut **db)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
|
@ -126,7 +126,6 @@ pub async fn post(
|
||||||
context: as_context(),
|
context: as_context(),
|
||||||
id: ObjectUri(config.post_url(uuid, &post.id)),
|
id: ObjectUri(config.post_url(uuid, &post.id)),
|
||||||
},
|
},
|
||||||
attachment: vec![],
|
|
||||||
attributed_to: Some(config.user_url(uuid)),
|
attributed_to: Some(config.user_url(uuid)),
|
||||||
ty: ap::ActivityType::Note,
|
ty: ap::ActivityType::Note,
|
||||||
content: post.content,
|
content: post.content,
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS attachment
|
|
||||||
(
|
|
||||||
-- UUID
|
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
|
||||||
post_id TEXT NOT NULL,
|
|
||||||
url TEXT NOT NULL,
|
|
||||||
media_type TEXT NOT NULL,
|
|
||||||
marked_sensitive BOOL NOT NULL,
|
|
||||||
alt TEXT,
|
|
||||||
|
|
||||||
FOREIGN KEY(post_id) REFERENCES post(id)
|
|
||||||
);
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue