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}