refactor: cleanup; add missing APIs; reorganise

This commit is contained in:
nullishamy 2025-05-06 22:25:15 +01:00
parent fafaf243c5
commit ab9836293e
Signed by: amy
SSH key fingerprint: SHA256:WmV0uk6WgAQvDJlM8Ld4mFPHZo02CLXXP5VkwQ5xtyk
22 changed files with 529 additions and 870 deletions

View file

@ -1,12 +1,12 @@
use rocket::{
get, serde::json::Json, FromFormField, State,
};
use main::types::{api, get};
use main::{federation::http::HttpWrapper, types::{api, get}};
use rocket_db_pools::Connection;
use serde::{Deserialize, Serialize};
use tracing::{info, error};
use crate::{http_wrapper::HttpWrapper, AuthenticatedUser, Db};
use crate::{AuthenticatedUser, Db};
#[derive(Serialize, Deserialize, FromFormField, Debug)]
#[serde(rename_all = "lowercase")]

View file

@ -43,7 +43,7 @@ fn to_db_post(req: &CreateStatus, user: &AuthenticatedUser, config: &Config) ->
uri: ObjectUri(config.post_url(&user.id.0, &post_id)),
user: user.user.clone(),
content: req.status.clone(),
created_at: main::ap::now(),
created_at: main::now(),
boosted_post: None,
attachments: vec![]
}

View file

@ -1,4 +1,5 @@
use main::ap;
use main::federation::outbox::OutboxRequest;
use main::federation::QueueMessage;
use main::types::{api, get, ObjectUuid};
use rocket::response::status::NotFound;
use rocket::{
@ -6,10 +7,9 @@ use rocket::{
serde::{Deserialize, Serialize, json::Json},
};
use rocket_db_pools::Connection;
use uuid::Uuid;
use tracing::info;
use crate::{AuthenticatedUser, Db};
use crate::{AuthenticatedUser, Db, OutboundQueue};
#[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
@ -62,39 +62,38 @@ pub async fn verify_credentials(user: AuthenticatedUser) -> Json<CredentialAcoun
#[post("/accounts/<uuid>/follow")]
pub async fn new_follow(
mut db: Connection<Db>,
helpers: &State<crate::Helpers>,
outbound: &State<OutboundQueue>,
uuid: &str,
user: AuthenticatedUser,
) -> Result<(), NotFound<String>> {
let http = &helpers.http;
let follower = ap::User::from_actor_id(&user.actor_id.0, &mut **db).await;
let followed = ap::User::from_id(uuid, &mut **db)
let follower = user.user;
let followed = get::user_by_id(ObjectUuid(uuid.to_string()), &mut **db)
.await
.map_err(|e| NotFound(e.to_string()))?;
.unwrap();
let outbox = ap::Outbox::for_user(follower.clone(), http);
let activity = ap::Activity {
id: format!("https://ferri.amy.mov/activities/{}", Uuid::new_v4()),
ty: ap::ActivityType::Follow,
object: followed.actor_id().to_string(),
..Default::default()
};
let req = ap::OutgoingActivity {
signed_by: format!("{}#main-key", follower.uri()),
req: activity,
to: followed.actor().clone(),
};
req.save(&mut **db).await;
outbox.post(req).await;
let conn = db.into_inner();
let conn = conn.detach();
let msg = QueueMessage::Outbound(OutboxRequest::Follow {
follower,
followed,
conn
});
outbound.0.send(msg).await;
Ok(())
}
#[get("/accounts/relationships?<id>")]
pub async fn relationships(
id: Vec<String>,
user: AuthenticatedUser
) -> Result<Json<Vec<api::Relationship>>, ()> {
info!("{} looking up relationships for {:#?}", user.username, id);
Ok(Json(vec![]))
}
#[get("/accounts/<uuid>")]
pub async fn account(
mut db: Connection<Db>,

View file

@ -50,23 +50,14 @@ pub async fn followers(
mut db: Connection<Db>,
uuid: &str,
) -> Result<ActivityResponse<Json<OrderedCollection>>, NotFound<String>> {
let target = main::ap::User::from_id(uuid, &mut **db)
let user = get::user_by_id(ObjectUuid(uuid.to_string()), &mut **db)
.await
.map_err(|e| NotFound(e.to_string()))?;
let actor_id = target.actor_id();
let followers = sqlx::query!(
r#"
SELECT follower_id FROM follow
WHERE followed_id = ?
"#,
actor_id
)
.fetch_all(&mut **db)
.await
.unwrap();
.unwrap();
let followers = get::followers_for_user(user.id.clone(), &mut **db)
.await
.unwrap();
ap_ok(Json(OrderedCollection {
context: as_context(),
ty: "OrderedCollection".to_string(),
@ -74,8 +65,8 @@ pub async fn followers(
id: format!("https://ferri.amy.mov/users/{}/followers", uuid),
ordered_items: followers
.into_iter()
.map(|f| f.follower_id)
.collect::<Vec<_>>(),
.map(|f| f.follower.0)
.collect(),
}))
}
@ -84,32 +75,23 @@ pub async fn following(
mut db: Connection<Db>,
uuid: &str,
) -> Result<ActivityResponse<Json<OrderedCollection>>, NotFound<String>> {
let target = main::ap::User::from_id(uuid, &mut **db)
let user = get::user_by_id(ObjectUuid(uuid.to_string()), &mut **db)
.await
.map_err(|e| NotFound(e.to_string()))?;
let actor_id = target.actor_id();
let following = sqlx::query!(
r#"
SELECT followed_id FROM follow
WHERE follower_id = ?
"#,
actor_id
)
.fetch_all(&mut **db)
.await
.unwrap();
.unwrap();
let followers = get::following_for_user(user.id.clone(), &mut **db)
.await
.unwrap();
ap_ok(Json(OrderedCollection {
context: as_context(),
ty: "OrderedCollection".to_string(),
total_items: 1,
id: format!("https://ferri.amy.mov/users/{}/following", uuid),
ordered_items: following
ordered_items: followers
.into_iter()
.map(|f| f.followed_id)
.collect::<Vec<_>>(),
.map(|f| f.followed.0)
.collect(),
}))
}

View file

@ -1,7 +1,6 @@
use crate::Db;
use main::ap;
use main::types::api;
use rocket::{State, get, serde::json::Json};
use main::types::{api, get};
use rocket::{get, serde::json::Json, State};
use rocket_db_pools::Connection;
use tracing::info;
@ -27,24 +26,26 @@ pub async fn webfinger(
let acct = resource.strip_prefix("acct:").unwrap();
let (user, _) = acct.split_once("@").unwrap();
let user = ap::User::from_username(user, &mut **db).await;
let user = get::user_by_username(user, &mut **db)
.await
.unwrap();
Json(api::WebfingerHit {
subject: resource.to_string(),
aliases: vec![
config.user_url(user.id()),
config.user_web_url(user.username()),
config.user_url(&user.id.0),
config.user_web_url(&user.username),
],
links: vec![
api::WebfingerLink {
rel: "http://webfinger.net/rel/profile-page".to_string(),
ty: Some("text/html".to_string()),
href: Some(config.user_web_url(user.username())),
href: Some(config.user_web_url(&user.username)),
},
api::WebfingerLink {
rel: "self".to_string(),
ty: Some("application/activity+json".to_string()),
href: Some(config.user_url(user.id())),
href: Some(config.user_url(&user.id.0)),
},
],
})

View file

@ -1,70 +0,0 @@
use crate::http::HttpClient;
use main::types::ap;
use std::fmt::Debug;
use thiserror::Error;
use tracing::{Level, error, event, info};
pub struct HttpWrapper<'a> {
client: &'a HttpClient,
key_id: &'a str,
}
#[derive(Error, Debug)]
pub enum HttpError {
#[error("entity of type `{0}` @ URL `{1}` could not be loaded")]
LoadFailure(String, String),
#[error("entity of type `{0}` @ URL `{1}` could not be parsed ({2})")]
ParseFailure(String, String, String),
}
impl<'a> HttpWrapper<'a> {
pub fn new(client: &'a HttpClient, key_id: &'a str) -> HttpWrapper<'a> {
Self { client, key_id }
}
async fn get<T: serde::de::DeserializeOwned + Debug>(
&self,
ty: &str,
url: &str,
) -> Result<T, HttpError> {
let ty = ty.to_string();
event!(Level::INFO, url, "loading {}", ty);
let http_result = self
.client
.get(url)
.sign(self.key_id)
.activity()
.send()
.await;
if let Err(e) = http_result {
error!("could not load url {}: {:#?}", url, e);
return Err(HttpError::LoadFailure(ty, url.to_string()));
}
let raw_body = http_result.unwrap().text().await;
if let Err(e) = raw_body {
error!("could not get text for url {}: {:#?}", url, e);
return Err(HttpError::LoadFailure(ty, url.to_string()));
}
let raw_body = raw_body.unwrap();
info!("raw body {}", raw_body);
let decoded = serde_json::from_str::<T>(&raw_body);
if let Err(e) = decoded {
error!(
"could not parse {} for url {}: {:#?} {}",
ty, url, e, &raw_body
);
return Err(HttpError::ParseFailure(ty, url.to_string(), e.to_string()));
}
Ok(decoded.unwrap())
}
pub async fn get_person(&self, url: &str) -> Result<ap::Person, HttpError> {
self.get("Person", url).await
}
}

View file

@ -4,10 +4,8 @@ use endpoints::{
};
use tracing_subscriber::fmt;
use main::{federation::{self, http}, types::{db, get, ObjectUri, ObjectUuid}};
use main::{federation, types::{db, get, ObjectUri, ObjectUuid}};
use main::ap::http;
use main::config::Config;
use rocket::{
Build, Request, Rocket, build, get,
@ -170,6 +168,7 @@ pub fn launch(cfg: Config) -> Rocket<Build> {
api::user::new_follow,
api::user::statuses,
api::user::account,
api::user::relationships,
api::apps::new_app,
api::preferences::preferences,
api::user::verify_credentials,