1use std::{
8 net::SocketAddr,
9 str::FromStr,
10 sync::{Arc, LazyLock},
11};
12
13use axum::{
14 Json, Router,
15 extract::{Path, State},
16 http::StatusCode,
17 routing::put,
18};
19use axum_macros::debug_handler;
20use directories::ProjectDirs;
21use dwn::{Dwn, core::message::Message};
22use tokio::net::TcpListener;
23use tracing::{debug, error, info};
24use xdid::core::did::Did;
25
26pub use dwn::core::reply::Reply;
27
28pub static DIRS: LazyLock<ProjectDirs> = LazyLock::new(|| {
29 let dirs = ProjectDirs::from("", "UNAVI", "dwn-server").expect("project dirs");
30 std::fs::create_dir_all(dirs.data_dir()).expect("data dir");
31 dirs
32});
33
34const DB_FILE: &str = "data.db";
35
36pub async fn run_server(addr: SocketAddr) -> anyhow::Result<()> {
37 let path = {
38 let mut dir = DIRS.data_dir().to_path_buf();
39 dir.push(DB_FILE);
40 dir
41 };
42 let store = Arc::new(dwn_native_db::NativeDbStore::new(path)?);
43 let dwn = Dwn::new(store.clone(), store);
44
45 let listener = TcpListener::bind(addr).await?;
46 let router = create_router(dwn);
47
48 info!("DWN server running on {addr}");
49
50 axum::serve(listener, router).await?;
51
52 Ok(())
53}
54
55pub fn create_router(dwn: Dwn) -> Router {
56 Router::new()
57 .route("/{target}", put(handle_put))
58 .with_state(dwn)
59}
60
61#[debug_handler]
62async fn handle_put(
63 Path(mut target): Path<String>,
64 State(dwn): State<Dwn>,
65 Json(msg): Json<Message>,
66) -> Result<Json<serde_json::Value>, StatusCode> {
67 if target.starts_with("did:web:") {
68 let (_, rest) = target
71 .split_once("did:web:")
72 .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;
73
74 let mut parts = rest.split(':');
75
76 if let Some(first) = parts.next()
77 && let Some(second) = parts.next()
78 && second.len() <= 5
79 && second.chars().all(|c| c.is_numeric())
80 {
81 let parts_vec = parts.collect::<Vec<_>>();
83 let parts_str = if parts_vec.is_empty() {
84 String::new()
85 } else {
86 format!(":{}", parts_vec.join(":"))
87 };
88 target = format!("did:web:{first}%3A{second}{parts_str}");
89 }
90 }
91
92 let target = Did::from_str(&target).map_err(|e| {
95 debug!("Failed to parse DID: {:?}", e);
96 StatusCode::BAD_REQUEST
97 })?;
98
99 let reply = dwn.process_message(&target, msg).await?;
100
101 let res = serde_json::to_value(reply).map_err(|e| {
102 error!("Error serializing response: {e:?}");
103 StatusCode::INTERNAL_SERVER_ERROR
104 })?;
105
106 Ok(Json(res))
109}