lightweight
com.atproto.sync.listReposByCollection
1//! XRPC API server.
2//!
3//! Serves XRPC endpoints via axum routers built with `jacquard-axum`'s
4//! `IntoRouter` helper.
5
6mod admin;
7mod get_repo_status;
8mod hello;
9mod list_repos;
10mod list_repos_by_collection;
11
12use admin::admin_status;
13use get_repo_status::get_repo_status;
14use list_repos::list_repos;
15use list_repos_by_collection::list_repos_by_collection;
16
17use std::net::SocketAddr;
18
19use jacquard_common::url::Host;
20
21use crate::com_atproto::sync::list_repos_by_collection::ListReposByCollectionRequest;
22use jacquard_api::com_atproto::sync::{
23 get_repo_status::GetRepoStatusRequest, list_repos::ListReposRequest,
24};
25use jacquard_axum::IntoRouter;
26
27use crate::error::Result;
28use crate::storage::DbRef;
29
30/// Config for the admin endpoints. Only constructed when `--admin-password` is
31/// set; when absent the `/admin/*` routes are not registered at all.
32#[derive(Clone)]
33pub struct AdminConfig {
34 /// The relay/PDS host that lightrail is subscribed to and backfilling from.
35 pub subscribe_host: Host,
36 /// Required Bearer token for all admin endpoints.
37 pub admin_password: String,
38}
39
40/// Build and serve the axum application on `addr`.
41///
42/// Routes always registered:
43/// GET /xrpc/com.atproto.sync.getRepoStatus
44/// GET /xrpc/com.atproto.sync.listRepos
45/// GET /xrpc/com.atproto.sync.listReposByCollection
46///
47/// Registered only when `admin_config` is `Some`:
48/// GET /admin/status
49pub async fn serve(
50 addr: SocketAddr,
51 db: DbRef,
52 token: tokio_util::sync::CancellationToken,
53 admin_config: Option<AdminConfig>,
54) -> Result<()> {
55 let base = GetRepoStatusRequest::into_router(get_repo_status)
56 .merge(ListReposRequest::into_router(list_repos))
57 .merge(ListReposByCollectionRequest::into_router(
58 list_repos_by_collection,
59 ));
60
61 let app = if let Some(config) = admin_config {
62 base.route("/admin/status", axum::routing::get(admin_status))
63 .route("/", axum::routing::get(hello::hello))
64 .with_state(db)
65 .layer(axum::Extension(config))
66 } else {
67 base.route("/", axum::routing::get(hello::hello))
68 .with_state(db)
69 };
70
71 let listener = tokio::net::TcpListener::bind(addr).await?;
72 axum::serve(listener, app)
73 .with_graceful_shutdown(token.cancelled_owned())
74 .await?;
75 Ok(())
76}