ferri/ferri-main/src/ap/mod.rs

189 lines
4 KiB
Rust
Raw Normal View History

2025-04-11 12:29:29 +01:00
use chrono::{DateTime, Local};
use serde::{Deserialize, Serialize};
use sqlx::Sqlite;
2025-04-10 19:40:50 +01:00
2025-04-11 12:29:29 +01:00
pub mod http;
2025-04-10 19:40:50 +01:00
2025-04-11 12:29:29 +01:00
#[derive(Debug, Clone)]
pub struct Actor {
id: String,
inbox: String,
outbox: String,
2025-04-10 19:40:50 +01:00
}
2025-04-11 12:29:29 +01:00
#[derive(Debug, Clone)]
pub struct User {
id: String,
username: String,
actor: Actor,
display_name: String,
2025-04-10 19:40:50 +01:00
}
2025-04-11 12:29:29 +01:00
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
}
2025-04-10 19:40:50 +01:00
2025-04-11 12:29:29 +01:00
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,
}
}
2025-04-10 19:40:50 +01:00
2025-04-11 12:29:29 +01:00
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,
}
2025-04-10 19:40:50 +01:00
2025-04-11 12:29:29 +01:00
impl ActivityType {
fn to_raw(self) -> String {
match self {
ActivityType::Follow => "Follow".to_string(),
}
}
}
#[derive(Debug, Clone)]
2025-04-10 19:40:50 +01:00
pub struct Activity {
2025-04-11 12:29:29 +01:00
pub id: String,
pub ty: ActivityType,
pub object: String,
pub published: DateTime<Local>,
}
pub type KeyId = String;
#[derive(Debug, Clone)]
pub struct OutgoingActivity {
pub signed_by: KeyId,
pub req: Activity,
pub to: Actor,
}
#[derive(Serialize, Deserialize, Debug)]
struct RawActivity {
#[serde(rename = "@context")]
#[serde(skip_deserializing)]
context: String,
id: String,
#[serde(rename = "type")]
ty: String,
actor: String,
object: String,
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(&self, activity: OutgoingActivity) {
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 }
}
2025-04-10 19:40:50 +01:00
}