forked from
nonbinary.computer/jacquard
A better Rust ATProto crate
1use std::sync::Arc;
2
3use axum::{Json, Router, extract::State, http::StatusCode, response::IntoResponse};
4use jacquard::{
5 CowStr, DefaultStr,
6 api::com_atproto::identity::resolve_did::{ResolveDidOutput, ResolveDidRequest},
7 identity::{JacquardResolver, resolver::IdentityResolver},
8 types::value::to_data,
9};
10use jacquard_axum::{ExtractXrpc, IntoRouter};
11use miette::{IntoDiagnostic, Result};
12use tracing_subscriber::EnvFilter;
13
14#[axum_macros::debug_handler]
15async fn resolve_did(
16 State(state): State<Arc<AppState>>,
17 ExtractXrpc(args): ExtractXrpc<ResolveDidRequest, DefaultStr>,
18) -> Result<Json<ResolveDidOutput<CowStr<'static>>>, XrpcErrorResponse> {
19 let doc = state
20 .resolver
21 .resolve_did_doc_owned(&args.did)
22 .await
23 .map_err(|_| XrpcErrorResponse::internal_server_error())?;
24 Ok(ResolveDidOutput {
25 did_doc: to_data(&doc).map_err(|_| XrpcErrorResponse::internal_server_error())?,
26 extra_data: Default::default(),
27 }
28 .into())
29}
30
31#[tokio::main]
32async fn main() -> Result<()> {
33 tracing_subscriber::fmt()
34 .with_timer(tracing_subscriber::fmt::time::UtcTime::rfc_3339())
35 .with_env_filter(EnvFilter::from_env("QDPDS_LOG"))
36 .init();
37 let app = Router::new()
38 .route("/", axum::routing::get(|| async { "hello world!" }))
39 .merge(ResolveDidRequest::into_router(resolve_did))
40 .with_state(Arc::new(AppState::new()))
41 .layer(tower_http::trace::TraceLayer::new_for_http());
42 let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
43 .await
44 .into_diagnostic()?;
45 axum::serve(listener, app).await.into_diagnostic()?;
46 Ok(())
47}
48
49pub struct XrpcErrorResponse {
50 error: XrpcError,
51 pub status: StatusCode,
52}
53
54impl XrpcErrorResponse {
55 pub fn internal_server_error() -> Self {
56 Self {
57 error: XrpcError {
58 error: "InternalServerError".to_string(),
59 message: None,
60 },
61 status: StatusCode::INTERNAL_SERVER_ERROR,
62 }
63 }
64}
65
66#[derive(serde::Deserialize, serde::Serialize)]
67pub struct XrpcError {
68 pub error: String,
69 #[serde(skip_serializing_if = "std::option::Option::is_none")]
70 pub message: Option<String>,
71}
72
73impl IntoResponse for XrpcErrorResponse {
74 fn into_response(self) -> axum::response::Response {
75 Json(self.error).into_response()
76 }
77}
78
79pub struct AppState {
80 pub resolver: JacquardResolver,
81}
82
83impl AppState {
84 pub fn new() -> Self {
85 Self {
86 resolver: jacquard::identity::slingshot_resolver_default(),
87 }
88 }
89}