feat(presentation): ActivityPub routes — WebFinger, NodeInfo, inbox, outbox
This commit is contained in:
@@ -30,6 +30,11 @@ dotenvy = { workspace = true }
|
|||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
|
activitypub = { workspace = true }
|
||||||
|
activitypub-base = { workspace = true }
|
||||||
|
postgres-federation = { workspace = true }
|
||||||
|
url = { workspace = true }
|
||||||
|
activitypub_federation = "0.7.0-beta.11"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
http-body-util = "0.1"
|
http-body-util = "0.1"
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use async_trait::async_trait;
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use domain::{errors::DomainError, events::DomainEvent, ports::EventPublisher};
|
use domain::{errors::DomainError, events::DomainEvent, ports::EventPublisher};
|
||||||
use postgres_search::PgSearchRepository;
|
use postgres_search::PgSearchRepository;
|
||||||
|
use activitypub_base::{ApFederationConfig, FederationData};
|
||||||
|
use activitypub::ThoughtsObjectHandler;
|
||||||
|
use postgres_federation::{PostgresApUserRepository, PostgresFederationRepository};
|
||||||
use state::AppState;
|
use state::AppState;
|
||||||
|
|
||||||
struct NoOpEventPublisher;
|
struct NoOpEventPublisher;
|
||||||
@@ -35,6 +38,28 @@ pub async fn build_state(pool: PgPool, jwt_secret: String) -> AppState {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let base_url = std::env::var("BASE_URL")
|
||||||
|
.unwrap_or_else(|_| "http://localhost:3000".into());
|
||||||
|
let allow_registration = std::env::var("ALLOW_REGISTRATION")
|
||||||
|
.map(|v| v == "true")
|
||||||
|
.unwrap_or(true);
|
||||||
|
let fed_debug = std::env::var("RUST_ENV")
|
||||||
|
.map(|v| v != "production")
|
||||||
|
.unwrap_or(true);
|
||||||
|
|
||||||
|
let fed_data = FederationData::new(
|
||||||
|
Arc::new(PostgresFederationRepository::new(pool.clone())),
|
||||||
|
Arc::new(PostgresApUserRepository::new(pool.clone(), base_url.clone())),
|
||||||
|
Arc::new(ThoughtsObjectHandler::new(pool.clone(), &base_url)),
|
||||||
|
base_url,
|
||||||
|
allow_registration,
|
||||||
|
"thoughts".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let fed_config = ApFederationConfig::new(fed_data, fed_debug).await
|
||||||
|
.expect("federation config failed");
|
||||||
|
|
||||||
AppState {
|
AppState {
|
||||||
users: Arc::new(postgres::user::PgUserRepository::new(pool.clone())),
|
users: Arc::new(postgres::user::PgUserRepository::new(pool.clone())),
|
||||||
thoughts: Arc::new(postgres::thought::PgThoughtRepository::new(pool.clone())),
|
thoughts: Arc::new(postgres::thought::PgThoughtRepository::new(pool.clone())),
|
||||||
@@ -52,5 +77,6 @@ pub async fn build_state(pool: PgPool, jwt_secret: String) -> AppState {
|
|||||||
auth: Arc::new(auth::JwtAuthService::new(jwt_secret, 86400 * 30)),
|
auth: Arc::new(auth::JwtAuthService::new(jwt_secret, 86400 * 30)),
|
||||||
hasher: Arc::new(auth::Argon2PasswordHasher),
|
hasher: Arc::new(auth::Argon2PasswordHasher),
|
||||||
events: event_publisher,
|
events: event_publisher,
|
||||||
|
fed_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ async fn main() {
|
|||||||
sqlx::migrate!("../adapters/postgres/migrations").run(&pool).await.expect("Migrations failed");
|
sqlx::migrate!("../adapters/postgres/migrations").run(&pool).await.expect("Migrations failed");
|
||||||
|
|
||||||
let state = presentation::build_state(pool, jwt_secret).await;
|
let state = presentation::build_state(pool, jwt_secret).await;
|
||||||
let app = presentation::routes::router()
|
let app = presentation::routes::router(&state.fed_config)
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
.layer(CorsLayer::permissive());
|
.layer(CorsLayer::permissive());
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,26 @@ use axum::{
|
|||||||
routing::{delete, get, patch, post, put},
|
routing::{delete, get, patch, post, put},
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
|
use activitypub_base::{
|
||||||
|
actor_handler::actor_handler,
|
||||||
|
followers_handler::{followers_handler, following_handler},
|
||||||
|
inbox::inbox_handler,
|
||||||
|
nodeinfo::{nodeinfo_handler, nodeinfo_well_known_handler},
|
||||||
|
outbox::outbox_handler,
|
||||||
|
webfinger::webfinger_handler,
|
||||||
|
ApFederationConfig,
|
||||||
|
};
|
||||||
|
use activitypub_federation::config::FederationMiddleware;
|
||||||
use crate::{handlers::*, state::AppState};
|
use crate::{handlers::*, state::AppState};
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
pub fn router(fed_config: &ApFederationConfig) -> Router<AppState> {
|
||||||
Router::new()
|
let api_routes = Router::new()
|
||||||
// auth
|
// auth
|
||||||
.route("/auth/register", post(auth::post_register))
|
.route("/auth/register", post(auth::post_register))
|
||||||
.route("/auth/login", post(auth::post_login))
|
.route("/auth/login", post(auth::post_login))
|
||||||
// users — static paths before parameterised
|
// users — static paths before parameterised
|
||||||
.route("/users/me", patch(users::patch_profile))
|
.route("/users/me", patch(users::patch_profile))
|
||||||
.route("/users/me/top-friends", put(social::put_top_friends))
|
.route("/users/me/top-friends", put(social::put_top_friends))
|
||||||
.route("/users/{username}", get(users::get_user))
|
|
||||||
.route("/users/{username}/following", get(feed::get_following_handler))
|
|
||||||
.route("/users/{username}/followers", get(feed::get_followers_handler))
|
|
||||||
.route("/users/{username}/top-friends", get(social::get_top_friends_handler))
|
.route("/users/{username}/top-friends", get(social::get_top_friends_handler))
|
||||||
// follows & blocks (use {id} param)
|
// follows & blocks (use {id} param)
|
||||||
.route(
|
.route(
|
||||||
@@ -56,5 +63,20 @@ pub fn router() -> Router<AppState> {
|
|||||||
"/api-keys",
|
"/api-keys",
|
||||||
get(api_keys::get_api_keys).post(api_keys::post_api_key),
|
get(api_keys::get_api_keys).post(api_keys::post_api_key),
|
||||||
)
|
)
|
||||||
.route("/api-keys/{id}", delete(api_keys::delete_api_key_handler))
|
.route("/api-keys/{id}", delete(api_keys::delete_api_key_handler));
|
||||||
|
|
||||||
|
let ap_routes = Router::new()
|
||||||
|
.route("/.well-known/webfinger", get(webfinger_handler))
|
||||||
|
.route("/.well-known/nodeinfo", get(nodeinfo_well_known_handler))
|
||||||
|
.route("/nodeinfo/2.0", get(nodeinfo_handler))
|
||||||
|
.route("/users/{username}", get(actor_handler))
|
||||||
|
.route("/users/{username}/inbox", post(inbox_handler))
|
||||||
|
.route("/users/{username}/outbox", get(outbox_handler))
|
||||||
|
.route("/users/{username}/followers", get(followers_handler))
|
||||||
|
.route("/users/{username}/following", get(following_handler));
|
||||||
|
|
||||||
|
Router::new()
|
||||||
|
.merge(api_routes)
|
||||||
|
.merge(ap_routes)
|
||||||
|
.layer(FederationMiddleware::new(fed_config.0.clone()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use domain::ports::*;
|
use domain::ports::*;
|
||||||
|
use activitypub_base::ApFederationConfig;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
@@ -19,4 +20,5 @@ pub struct AppState {
|
|||||||
pub auth: Arc<dyn AuthService>,
|
pub auth: Arc<dyn AuthService>,
|
||||||
pub hasher: Arc<dyn PasswordHasher>,
|
pub hasher: Arc<dyn PasswordHasher>,
|
||||||
pub events: Arc<dyn EventPublisher>,
|
pub events: Arc<dyn EventPublisher>,
|
||||||
|
pub fed_config: ApFederationConfig,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user