feat: v2 rewrite — hexagonal arch, ActivityPub federation, NATS, deployment-ready (#1)
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled

This commit was merged in pull request #1.
This commit is contained in:
2026-05-16 09:42:40 +00:00
parent 071809bc3f
commit 9aee4ceb6d
224 changed files with 35418 additions and 1469 deletions

View File

@@ -0,0 +1,114 @@
use postgres::failed_event::PgFailedEventStore;
use sqlx::PgPool;
use std::sync::Arc;
use activitypub::ThoughtsObjectHandler;
use activitypub_base::ActivityPubService;
use application::services::{FederationEventService, NotificationEventService};
use activitypub_base::{ActivityPubRepository, OutboundFederationPort};
use domain::ports::EventPublisher;
use postgres::activitypub::PgActivityPubRepository;
use postgres_federation::{PostgresApUserRepository, PostgresFederationRepository};
use crate::handlers::{FederationHandler, NotificationHandler};
pub struct WorkerHandlers {
pub notification: NotificationHandler,
pub federation: FederationHandler,
}
pub struct WorkerInfra {
pub pool: PgPool,
pub consumer: event_transport::EventConsumerAdapter<nats::NatsMessageSource>,
pub handlers: WorkerHandlers,
pub dlq_store: Arc<PgFailedEventStore>,
pub event_publisher: Arc<dyn EventPublisher>,
}
pub async fn build(database_url: &str, base_url: &str, nats_url: &str) -> WorkerInfra {
let pool = PgPool::connect(database_url)
.await
.expect("DB connect failed");
// Repos
let thoughts = Arc::new(postgres::thought::PgThoughtRepository::new(pool.clone()));
let users = Arc::new(postgres::user::PgUserRepository::new(pool.clone()));
let notifications = Arc::new(postgres::notification::PgNotificationRepository::new(
pool.clone(),
));
// ActivityPub service (for federation fan-out)
let ap_service = Arc::new(
ActivityPubService::new(
Arc::new(PostgresFederationRepository::new(pool.clone())),
Arc::new(PostgresApUserRepository::new(
pool.clone(),
base_url.to_string(),
)),
Arc::new(ThoughtsObjectHandler::new(
Arc::new(PgActivityPubRepository::new(pool.clone())),
base_url,
None,
Arc::new(postgres::tag::PgTagRepository::new(pool.clone())),
)),
base_url.to_string(),
false,
"thoughts".to_string(),
false,
None,
)
.await
.expect("ActivityPubService build failed"),
);
let ap_outbound = ap_service.clone() as Arc<dyn OutboundFederationPort>;
let ap_repo_worker =
Arc::new(PgActivityPubRepository::new(pool.clone())) as Arc<dyn ActivityPubRepository>;
// Application services
let notification_svc = Arc::new(NotificationEventService {
thoughts: thoughts.clone(),
notifications,
});
let federation_svc = Arc::new(FederationEventService {
thoughts,
users,
ap: ap_outbound,
base_url: base_url.to_string(),
ap_repo: ap_repo_worker,
});
// Thin handlers
let handlers = WorkerHandlers {
notification: NotificationHandler {
service: notification_svc,
},
federation: FederationHandler {
service: federation_svc,
},
};
// DLQ store
let dlq_store = Arc::new(PgFailedEventStore::new(pool.clone()));
// NATS consumer + publisher
let nats_client = async_nats::connect(nats_url)
.await
.expect("NATS connect failed");
nats::ensure_stream(&nats_client)
.await
.expect("JetStream stream setup failed");
let consumer = event_transport::EventConsumerAdapter::new(nats::NatsMessageSource::new(
nats_client.clone(),
));
let event_publisher: Arc<dyn EventPublisher> = Arc::new(
event_transport::EventPublisherAdapter::new(nats::NatsTransport::new(nats_client)),
);
WorkerInfra {
pool,
consumer,
handlers,
dlq_store,
event_publisher,
}
}