1use std::{io::prelude::*, path::PathBuf};
2
3use atuin_server_database::DbSettings;
4use config::{Config, Environment, File as ConfigFile, FileFormat};
5use eyre::{Result, eyre};
6use fs_err::{File, create_dir_all};
7use serde::{Deserialize, Serialize};
8
9static EXAMPLE_CONFIG: &str = include_str!("../server.toml");
10
11#[derive(Default, Clone, Debug, Deserialize, Serialize)]
12pub struct Mail {
13 #[serde(alias = "enable")]
14 pub enabled: bool,
15
16 #[serde(default)]
19 pub postmark: Postmark,
20
21 #[serde(default)]
22 pub verification: MailVerification,
23}
24
25#[derive(Default, Clone, Debug, Deserialize, Serialize)]
26pub struct Postmark {
27 #[serde(alias = "token")]
28 pub token: Option<String>,
29}
30
31#[derive(Default, Clone, Debug, Deserialize, Serialize)]
32pub struct MailVerification {
33 #[serde(alias = "enable")]
34 pub from: String,
35 pub subject: String,
36}
37
38#[derive(Clone, Debug, Deserialize, Serialize)]
39pub struct Metrics {
40 #[serde(alias = "enabled")]
41 pub enable: bool,
42 pub host: String,
43 pub port: u16,
44}
45
46impl Default for Metrics {
47 fn default() -> Self {
48 Self {
49 enable: false,
50 host: String::from("127.0.0.1"),
51 port: 9001,
52 }
53 }
54}
55
56#[derive(Clone, Debug, Deserialize, Serialize)]
57pub struct Settings {
58 pub host: String,
59 pub port: u16,
60 pub path: String,
61 pub open_registration: bool,
62 pub max_history_length: usize,
63 pub max_record_size: usize,
64 pub page_size: i64,
65 pub register_webhook_url: Option<String>,
66 pub register_webhook_username: String,
67 pub metrics: Metrics,
68 pub tls: Tls,
69 pub mail: Mail,
70
71 pub fake_version: Option<String>,
77
78 #[serde(flatten)]
79 pub db_settings: DbSettings,
80}
81
82impl Settings {
83 pub fn new() -> Result<Self> {
84 let mut config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") {
85 PathBuf::from(p)
86 } else {
87 let mut config_file = PathBuf::new();
88 let config_dir = atuin_common::utils::config_dir();
89 config_file.push(config_dir);
90 config_file
91 };
92
93 config_file.push("server.toml");
94
95 let mut config_builder = Config::builder()
97 .set_default("host", "127.0.0.1")?
98 .set_default("port", 8888)?
99 .set_default("open_registration", false)?
100 .set_default("max_history_length", 8192)?
101 .set_default("max_record_size", 1024 * 1024 * 1024)? .set_default("path", "")?
103 .set_default("register_webhook_username", "")?
104 .set_default("page_size", 1100)?
105 .set_default("metrics.enable", false)?
106 .set_default("metrics.host", "127.0.0.1")?
107 .set_default("metrics.port", 9001)?
108 .set_default("mail.enable", false)?
109 .set_default("tls.enable", false)?
110 .set_default("tls.cert_path", "")?
111 .set_default("tls.pkey_path", "")?
112 .add_source(
113 Environment::with_prefix("atuin")
114 .prefix_separator("_")
115 .separator("__"),
116 );
117
118 config_builder = if config_file.exists() {
119 config_builder.add_source(ConfigFile::new(
120 config_file.to_str().unwrap(),
121 FileFormat::Toml,
122 ))
123 } else {
124 create_dir_all(config_file.parent().unwrap())?;
125 let mut file = File::create(config_file)?;
126 file.write_all(EXAMPLE_CONFIG.as_bytes())?;
127
128 config_builder
129 };
130
131 let config = config_builder.build()?;
132
133 config
134 .try_deserialize()
135 .map_err(|e| eyre!("failed to deserialize: {}", e))
136 }
137}
138
139pub fn example_config() -> &'static str {
140 EXAMPLE_CONFIG
141}
142
143#[derive(Clone, Debug, Default, Deserialize, Serialize)]
144pub struct Tls {
145 #[serde(alias = "enabled")]
146 pub enable: bool,
147
148 pub cert_path: PathBuf,
149 pub pkey_path: PathBuf,
150}