1use std::{
2 collections::BTreeMap,
3 net::{IpAddr, SocketAddr},
4 sync::Arc,
5};
6
7use anyhow::anyhow;
8use axum::{routing::post, Router};
9use holochain::{
10 conductor::chc::EncryptedEntry,
11 core::{hash_type::Action, HoloHash},
12 prelude::{CellId, Signature, SignedActionHashed},
13};
14use parking_lot::RwLock;
15use tokio::net::TcpListener;
16
17use crate::routes::{add_records, get_record_data, not_found};
18
19#[derive(Debug)]
20pub struct ChcService {
21 address: SocketAddr,
22 router: Router,
23}
24
25#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
28pub struct RecordItem {
29 pub action: SignedActionHashed,
30 pub encrypted_entry: Option<(Arc<EncryptedEntry>, Signature)>,
31}
32
33#[derive(Debug, Default)]
34pub struct AppState {
35 pub records: RwLock<BTreeMap<CellId, CellState>>,
36}
37
38#[derive(Debug, Default)]
39pub struct CellState {
40 pub records: Vec<RecordItem>,
41 pub latest_action_seq: u32,
42 pub latest_action_hash: Option<HoloHash<Action>>,
43}
44
45impl ChcService {
46 pub fn new(interface: impl Into<IpAddr>, port: u16) -> Self {
47 let address = SocketAddr::new(interface.into(), port);
48
49 let router = Router::new()
50 .route(
51 "/{dna_hash}/{agent_pubkey}/get_record_data",
52 post(get_record_data),
53 )
54 .route("/{dna_hash}/{agent_pubkey}/add_records", post(add_records))
55 .fallback(not_found)
56 .with_state(Arc::new(AppState::default()));
57
58 ChcService { address, router }
59 }
60
61 pub fn address(&self) -> SocketAddr {
62 self.address
63 }
64
65 pub async fn run(self) -> anyhow::Result<()> {
66 let listerner = TcpListener::bind(self.address)
67 .await
68 .map_err(|e| anyhow!("Failed to bind to address: {}", e))?;
69 axum::serve(listerner, self.router)
70 .await
71 .map_err(|e| anyhow!("Failed to start server: {}", e))?;
72
73 Ok(())
74 }
75}