From a56b12621d9f8c34017995a518e61ef7ee2b0f64 Mon Sep 17 00:00:00 2001 From: rtkay123 Date: Sat, 12 Jul 2025 16:46:32 +0200 Subject: feat: get user --- contrib/bruno/users/folder.bru | 8 ++++++ contrib/bruno/users/get_user.bru | 11 +++++++ src/entity/user.rs | 36 ++++++++++++++++------- src/server.rs | 5 ++-- src/server/routes.rs | 1 + src/server/routes/users.rs | 6 ++++ src/server/routes/users/get_user.rs | 57 +++++++++++++++++++++++++++++++++++++ src/state.rs | 14 ++++----- 8 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 contrib/bruno/users/folder.bru create mode 100644 contrib/bruno/users/get_user.bru create mode 100644 src/server/routes/users.rs create mode 100644 src/server/routes/users/get_user.rs diff --git a/contrib/bruno/users/folder.bru b/contrib/bruno/users/folder.bru new file mode 100644 index 0000000..3659248 --- /dev/null +++ b/contrib/bruno/users/folder.bru @@ -0,0 +1,8 @@ +meta { + name: users + seq: 2 +} + +auth { + mode: inherit +} diff --git a/contrib/bruno/users/get_user.bru b/contrib/bruno/users/get_user.bru new file mode 100644 index 0000000..3981d48 --- /dev/null +++ b/contrib/bruno/users/get_user.bru @@ -0,0 +1,11 @@ +meta { + name: get_user + type: http + seq: 1 +} + +get { + url: http://localhost:3000/users/sellershut + body: none + auth: inherit +} diff --git a/src/entity/user.rs b/src/entity/user.rs index af27ea2..e136cb3 100644 --- a/src/entity/user.rs +++ b/src/entity/user.rs @@ -2,25 +2,27 @@ use activitypub_federation::{ config::Data, fetch::object_id::ObjectId, http_signatures::generate_actor_keypair, + kinds::actor::PersonType, + protocol::public_key::PublicKey, traits::{Actor, Object}, }; use async_trait::async_trait; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use tracing::trace; use url::Url; use crate::{error::AppError, state::AppHandle}; #[derive(PartialEq, Clone, Debug)] -pub(crate) struct LocalUser { +pub(crate) struct User { pub username: String, - pub ap_id: ObjectId, + pub ap_id: ObjectId, pub private_key: Option, pub public_key: String, pub inbox: Url, } -impl LocalUser { +impl User { pub fn new(username: &str) -> Result { trace!("creating a new user"); let keys = generate_actor_keypair()?; @@ -36,17 +38,25 @@ impl LocalUser { } } -#[derive(Deserialize)] -pub struct User {} +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Person { + #[serde(rename = "type")] + kind: PersonType, + preferred_username: String, + id: ObjectId, + inbox: Url, + public_key: PublicKey, +} #[async_trait] -impl Object for LocalUser { +impl Object for User { #[doc = " App data type passed to handlers. Must be identical to"] #[doc = " [crate::config::FederationConfigBuilder::app_data] type."] type DataType = AppHandle; #[doc = " The type of protocol struct which gets sent over network to federate this database struct."] - type Kind = User; + type Kind = Person; #[doc = " Error type returned by handler methods"] type Error = AppError; @@ -69,7 +79,13 @@ impl Object for LocalUser { #[doc = " Called when a local object gets fetched by another instance over HTTP, or when an object"] #[doc = " gets sent in an activity."] async fn into_json(self, data: &Data) -> Result { - todo!() + Ok(Person { + preferred_username: self.username.clone(), + kind: Default::default(), + id: self.ap_id.clone(), + inbox: self.inbox.clone(), + public_key: self.public_key(), + }) } #[doc = " Verifies that the received object is valid."] @@ -94,7 +110,7 @@ impl Object for LocalUser { } } -impl Actor for LocalUser { +impl Actor for User { fn public_key_pem(&self) -> &str { &self.public_key } diff --git a/src/server.rs b/src/server.rs index c6ce02c..bb2b4d0 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,4 @@ -use activitypub_federation::config::FederationConfig; +use activitypub_federation::config::{FederationConfig, FederationMiddleware}; use axum::{Router, routing::get}; use tower_http::trace::TraceLayer; @@ -8,7 +8,8 @@ pub mod routes; pub fn router(state: FederationConfig) -> Router { Router::new() + .merge(routes::users::users_router()) .route("/", get(health_check)) .layer(TraceLayer::new_for_http()) - .with_state(state) + .layer(FederationMiddleware::new(state)) } diff --git a/src/server/routes.rs b/src/server/routes.rs index 5814e0e..7751c95 100644 --- a/src/server/routes.rs +++ b/src/server/routes.rs @@ -1,3 +1,4 @@ +pub(super) mod users; use axum::response::IntoResponse; pub async fn health_check() -> impl IntoResponse { diff --git a/src/server/routes/users.rs b/src/server/routes/users.rs new file mode 100644 index 0000000..ad09c8e --- /dev/null +++ b/src/server/routes/users.rs @@ -0,0 +1,6 @@ +pub mod get_user; +use axum::{Router, routing::get}; + +pub fn users_router() -> Router { + Router::new().route("/users/{usernme}", get(get_user::http_get_user)) +} diff --git a/src/server/routes/users/get_user.rs b/src/server/routes/users/get_user.rs new file mode 100644 index 0000000..8020923 --- /dev/null +++ b/src/server/routes/users/get_user.rs @@ -0,0 +1,57 @@ +use activitypub_federation::{ + axum::json::FederationJson, config::Data, protocol::context::WithContext, traits::Object, +}; +use axum::{debug_handler, extract::Path, http::StatusCode, response::IntoResponse}; + +use crate::{error::AppError, state::AppHandle}; + +#[debug_handler] +pub async fn http_get_user( + Path(name): Path, + data: Data, +) -> Result { + let read = data.users.read().await; + if let Some(a) = read + .iter() + .find(|value| value.username.eq(&name)) + .map(ToOwned::to_owned) + { + let json_user = a.into_json(&data).await?; + Ok(( + StatusCode::OK, + FederationJson(WithContext::new_default(json_user)), + ) + .into_response()) + } else { + Ok((StatusCode::NOT_FOUND, "").into_response()) + } +} + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use tower::ServiceExt; + + use crate::{server, state::AppState}; + + #[tokio::test] + async fn get_user() { + let state = AppState::new().await.unwrap(); + let app = server::router(state); + + let response = app + .oneshot( + Request::builder() + .uri("/users/sellershut") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/src/state.rs b/src/state.rs index 64c2e7c..d7c9136 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,14 +1,12 @@ -use std::{ - ops::Deref, - sync::{Arc, RwLock}, -}; +use std::{ops::Deref, sync::Arc}; use activitypub_federation::config::FederationConfig; +use tokio::sync::RwLock; -use crate::{entity::user::LocalUser, error::AppError}; +use crate::{entity::user::User, error::AppError}; #[derive(Clone)] -pub struct AppHandle(pub Arc); +pub struct AppHandle(Arc); impl Deref for AppHandle { type Target = Arc; @@ -19,12 +17,12 @@ impl Deref for AppHandle { } pub struct AppState { - users: RwLock>, + pub users: RwLock>, } impl AppState { pub async fn new() -> Result, AppError> { - let user = LocalUser::new("sellershut")?; + let user = User::new("sellershut")?; let domain = "localhost"; let config = FederationConfig::builder() -- cgit v1.2.3