ceramic_config/
lib.rs

1mod convert;
2mod daemon;
3
4pub use convert::convert_network_identifier;
5pub use daemon::DaemonConfig;
6
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::path::PathBuf;
10
11#[derive(Clone, Debug, Deserialize, Serialize)]
12pub struct IpfsRemote {
13    pub host: String,
14}
15
16impl Default for IpfsRemote {
17    fn default() -> Self {
18        Self {
19            host: "/ipfs".to_string(),
20        }
21    }
22}
23
24#[derive(Clone, Debug, Deserialize, Serialize)]
25pub enum Ipfs {
26    Bundled,
27    Remote(IpfsRemote),
28}
29
30impl std::fmt::Display for Ipfs {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            Self::Bundled => write!(f, "Bundled"),
34            Self::Remote(_) => write!(f, "Remote"),
35        }
36    }
37}
38
39impl Default for Ipfs {
40    fn default() -> Self {
41        Self::Bundled
42    }
43}
44
45#[derive(Clone, Debug, Deserialize, Serialize)]
46pub struct S3StateStore {
47    pub bucket: String,
48    pub endpoint: String,
49}
50
51#[derive(Clone, Debug, Deserialize, Serialize)]
52pub enum StateStore {
53    S3(S3StateStore),
54    LocalDirectory(PathBuf),
55}
56
57impl Default for StateStore {
58    fn default() -> Self {
59        Self::LocalDirectory(PathBuf::from("/etc/ceramic/data"))
60    }
61}
62
63#[derive(Clone, Debug, Deserialize, Serialize)]
64pub struct HttpApi {
65    pub hostname: String,
66    pub port: u16,
67    pub cors_allowed_origins: Vec<String>,
68    pub admin_dids: Vec<String>,
69}
70
71impl Default for HttpApi {
72    fn default() -> Self {
73        Self {
74            hostname: std::net::Ipv4Addr::LOCALHOST.to_string(),
75            port: 7007,
76            cors_allowed_origins: vec![],
77            admin_dids: vec![],
78        }
79    }
80}
81
82#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
83pub enum NetworkIdentifier {
84    InMemory,
85    Local,
86    Dev,
87    Clay,
88    Mainnet,
89}
90
91impl std::fmt::Display for NetworkIdentifier {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        match self {
94            Self::InMemory => write!(f, "InMemory"),
95            Self::Local => write!(f, "Local"),
96            Self::Dev => write!(f, "Dev"),
97            Self::Clay => write!(f, "Clay"),
98            Self::Mainnet => write!(f, "Mainnet"),
99        }
100    }
101}
102
103impl Default for NetworkIdentifier {
104    fn default() -> Self {
105        Self::Clay
106    }
107}
108
109#[derive(Clone, Debug, Deserialize, Serialize)]
110pub struct Network {
111    pub id: NetworkIdentifier,
112    pub pubsub_topic: Option<String>,
113}
114
115impl Default for Network {
116    fn default() -> Self {
117        Self {
118            id: NetworkIdentifier::default(),
119            pubsub_topic: None,
120        }
121    }
122}
123
124impl Network {
125    pub fn new(id: &NetworkIdentifier, name: &str) -> Self {
126        let topic = if NetworkIdentifier::Local == *id {
127            Some(format!("/ceramic/local-topic-{}", name))
128        } else {
129            None
130        };
131        Self {
132            id: *id,
133            pubsub_topic: topic,
134        }
135    }
136}
137
138#[derive(Clone, Debug, Deserialize, Serialize)]
139pub enum Anchor {
140    None,
141    Ip {
142        url: String,
143    },
144    RemoteDid {
145        url: String,
146        private_seed_url: String,
147    },
148}
149
150impl Default for Anchor {
151    fn default() -> Self {
152        Self::None
153    }
154}
155
156impl Anchor {
157    pub fn url_for_network(id: &NetworkIdentifier) -> Option<String> {
158        match id {
159            NetworkIdentifier::InMemory => None,
160            NetworkIdentifier::Local | NetworkIdentifier::Dev => {
161                Some("https://cas-qa.3boxlabs.com/".to_string())
162            }
163            NetworkIdentifier::Clay => Some("https://cas-clay.3boxlabs.com/".to_string()),
164            NetworkIdentifier::Mainnet => Some("https://cas.3boxlabs.com/".to_string()),
165        }
166    }
167}
168
169#[derive(Clone, Debug, Deserialize, Serialize)]
170pub struct Indexing {
171    pub db: String,
172    pub allow_queries_before_historical_sync: bool,
173    pub enable_historical_sync: bool,
174}
175
176impl Default for Indexing {
177    fn default() -> Self {
178        Self {
179            db: Indexing::postgres_default().to_string(),
180            allow_queries_before_historical_sync: true,
181            enable_historical_sync: false,
182        }
183    }
184}
185
186impl Indexing {
187    pub fn postgres_default() -> &'static str {
188        "postgres://ceramic:password@localhost:5432/ceramic"
189    }
190
191    pub fn is_sqlite(&self) -> bool {
192        self.db.starts_with("sqlite")
193    }
194}
195
196#[derive(Clone, Debug, Deserialize, Serialize)]
197pub enum DidResolvers {
198    Ethr(HashMap<String, serde_json::Value>),
199}
200
201impl Default for DidResolvers {
202    fn default() -> Self {
203        Self::Ethr(HashMap::default())
204    }
205}
206
207#[derive(Clone, Debug, Deserialize, Serialize)]
208pub struct Node {
209    pub gateway: bool,
210    pub sync_override: bool,
211    pub stream_cache_limit: usize,
212}
213
214impl Default for Node {
215    fn default() -> Self {
216        Self {
217            gateway: false,
218            sync_override: false,
219            stream_cache_limit: 100,
220        }
221    }
222}
223
224#[derive(Clone, Debug, Deserialize, Serialize)]
225pub struct FileLogger {
226    pub enabled: bool,
227    pub directory: PathBuf,
228}
229
230impl Default for FileLogger {
231    fn default() -> Self {
232        Self {
233            enabled: true,
234            directory: PathBuf::from("./log/ceramic"),
235        }
236    }
237}
238
239#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
240pub enum LogLevel {
241    Trace,
242    Debug,
243    Info,
244    Warn,
245    Error,
246}
247
248impl Default for LogLevel {
249    fn default() -> Self {
250        Self::Info
251    }
252}
253
254#[derive(Clone, Debug, Deserialize, Serialize)]
255pub struct Logger {
256    pub file: Option<FileLogger>,
257    pub level: LogLevel,
258}
259
260impl Default for Logger {
261    fn default() -> Self {
262        Self {
263            file: Some(FileLogger::default()),
264            level: LogLevel::default(),
265        }
266    }
267}
268
269#[derive(Clone, Debug, Deserialize, Serialize)]
270pub enum Metrics {
271    Disabled,
272    Enabled(String),
273}
274
275impl Default for Metrics {
276    fn default() -> Self {
277        Self::Disabled
278    }
279}
280
281#[derive(Clone, Debug, Default, Deserialize, Serialize)]
282pub struct Config {
283    pub ipfs: Ipfs,
284    pub state_store: StateStore,
285    pub http_api: HttpApi,
286    pub network: Network,
287    pub anchor: Anchor,
288    pub indexing: Indexing,
289    pub did_resolvers: DidResolvers,
290    pub node: Node,
291    pub logger: Logger,
292    pub metrics: Metrics,
293}
294
295pub struct CasAuth {
296    pub url: String,
297    pub pk: Option<String>,
298}
299
300impl Config {
301    pub fn new(id: &NetworkIdentifier, name: &str, cas_auth: Option<CasAuth>) -> Self {
302        let mut cfg = Self::default();
303        cfg.initialize(id, name, cas_auth);
304        cfg
305    }
306
307    pub fn initialize(
308        &mut self,
309        id: &NetworkIdentifier,
310        name: &str,
311        cas_auth: Option<CasAuth>,
312    ) -> &mut Self {
313        self.network = Network::new(id, name);
314        self.anchor = if let Some(auth) = cas_auth {
315            if let Some(p) = auth.pk {
316                Anchor::RemoteDid {
317                    url: auth.url,
318                    private_seed_url: p,
319                }
320            } else {
321                Anchor::Ip { url: auth.url }
322            }
323        } else {
324            Anchor::None
325        };
326        if NetworkIdentifier::Mainnet == *id {
327            self.indexing.enable_historical_sync = true;
328        }
329        self
330    }
331}
332
333impl Config {
334    pub fn eth_resolver_options(&self) -> Option<String> {
335        let DidResolvers::Ethr(m) = &self.did_resolvers;
336        Some(serde_json::to_string(m).unwrap_or_else(|_| String::default()))
337    }
338
339    pub fn allows_sqlite(&self) -> bool {
340        self.network.id != NetworkIdentifier::Mainnet
341    }
342}
343
344pub fn from_file_err(file: String) -> anyhow::Result<Config> {
345    let data = std::fs::read(PathBuf::from(file))?;
346    Ok(serde_json::from_slice(data.as_slice())?)
347}
348
349pub fn from_string_err(json: &str) -> anyhow::Result<Config> {
350    Ok(serde_json::from_str(json)?)
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356
357    #[test]
358    fn should_roundtrip_default_config() {
359        let js = serde_json::to_string(&Config::default()).unwrap();
360        let _: Config = serde_json::from_str(&js).unwrap();
361    }
362}