headless_lms_utils/
lib.rs1pub mod cache;
4pub mod document_schema_processor;
5pub mod email_processor;
6pub mod error;
7pub mod file_store;
8pub mod folder_checksum;
9pub mod futures;
10pub mod http;
11pub mod icu4x;
12pub mod ip_to_country;
13pub mod language_tag_to_name;
14pub mod merge_edits;
15pub mod numbers;
16pub mod page_visit_hasher;
17pub mod pagination;
18pub mod prelude;
19pub mod strings;
20pub mod tmc;
21pub mod url_encoding;
22pub mod url_to_oembed_endpoint;
23
24#[macro_use]
25extern crate tracing;
26
27use anyhow::Context;
28use std::{env, str::FromStr};
29use url::Url;
30
31#[derive(Clone, PartialEq)]
32pub struct ApplicationConfiguration {
33 pub base_url: String,
34 pub test_mode: bool,
35 pub test_chatbot: bool,
36 pub development_uuid_login: bool,
37 pub azure_configuration: Option<AzureConfiguration>,
38 pub tmc_account_creation_origin: Option<String>,
39}
40
41impl ApplicationConfiguration {
42 pub fn try_from_env() -> anyhow::Result<Self> {
44 let base_url = env::var("BASE_URL").context("BASE_URL must be defined")?;
45 let test_mode = env::var("TEST_MODE").is_ok();
46 let development_uuid_login = env::var("DEVELOPMENT_UUID_LOGIN").is_ok();
47 let test_chatbot = test_mode
48 && (env::var("USE_MOCK_AZURE_CONFIGURATION").is_ok_and(|v| v.as_str() != "false")
49 || env::var("AZURE_CHATBOT_API_KEY").is_err());
50
51 let azure_configuration = if test_chatbot {
52 AzureConfiguration::mock_conf()?
53 } else {
54 AzureConfiguration::try_from_env()?
55 };
56
57 let tmc_account_creation_origin = Some(
58 env::var("TMC_ACCOUNT_CREATION_ORIGIN")
59 .context("TMC_ACCOUNT_CREATION_ORIGIN must be defined")?,
60 );
61
62 Ok(Self {
63 base_url,
64 test_mode,
65 test_chatbot,
66 development_uuid_login,
67 azure_configuration,
68 tmc_account_creation_origin,
69 })
70 }
71}
72
73#[derive(Clone, PartialEq)]
74pub struct AzureChatbotConfiguration {
75 pub api_key: String,
76 pub api_endpoint: Url,
77}
78
79impl AzureChatbotConfiguration {
80 pub fn try_from_env() -> anyhow::Result<Option<Self>> {
85 let api_key = env::var("AZURE_CHATBOT_API_KEY").ok();
86 let api_endpoint_str = env::var("AZURE_CHATBOT_API_ENDPOINT").ok();
87
88 if let (Some(api_key), Some(api_endpoint_str)) = (api_key, api_endpoint_str) {
89 let api_endpoint = Url::parse(&api_endpoint_str)
90 .context("Invalid URL in AZURE_CHATBOT_API_ENDPOINT")?;
91 Ok(Some(AzureChatbotConfiguration {
92 api_key,
93 api_endpoint,
94 }))
95 } else {
96 Ok(None)
97 }
98 }
99}
100
101#[derive(Clone, PartialEq)]
102pub struct AzureSearchConfiguration {
103 pub vectorizer_resource_uri: String,
104 pub vectorizer_deployment_id: String,
105 pub vectorizer_api_key: String,
106 pub vectorizer_model_name: String,
107 pub search_endpoint: Url,
108 pub search_api_key: String,
109}
110
111impl AzureSearchConfiguration {
112 pub fn try_from_env() -> anyhow::Result<Option<Self>> {
117 let vectorizer_resource_uri = env::var("AZURE_VECTORIZER_RESOURCE_URI").ok();
118 let vectorizer_deployment_id = env::var("AZURE_VECTORIZER_DEPLOYMENT_ID").ok();
119 let vectorizer_api_key = env::var("AZURE_VECTORIZER_API_KEY").ok();
120 let vectorizer_model_name = env::var("AZURE_VECTORIZER_MODEL_NAME").ok();
121 let search_endpoint_str = env::var("AZURE_SEARCH_ENDPOINT").ok();
122 let search_api_key = env::var("AZURE_SEARCH_API_KEY").ok();
123
124 if let (
125 Some(vectorizer_resource_uri),
126 Some(vectorizer_deployment_id),
127 Some(vectorizer_api_key),
128 Some(vectorizer_model_name),
129 Some(search_endpoint_str),
130 Some(search_api_key),
131 ) = (
132 vectorizer_resource_uri,
133 vectorizer_deployment_id,
134 vectorizer_api_key,
135 vectorizer_model_name,
136 search_endpoint_str,
137 search_api_key,
138 ) {
139 let search_endpoint =
140 Url::parse(&search_endpoint_str).context("Invalid URL in AZURE_SEARCH_ENDPOINT")?;
141 Ok(Some(AzureSearchConfiguration {
142 vectorizer_resource_uri,
143 vectorizer_deployment_id,
144 vectorizer_api_key,
145 vectorizer_model_name,
146 search_endpoint,
147 search_api_key,
148 }))
149 } else {
150 Ok(None)
151 }
152 }
153}
154
155#[derive(Clone, PartialEq)]
156pub struct AzureBlobStorageConfiguration {
157 pub storage_account: String,
158 pub access_key: String,
159}
160
161impl AzureBlobStorageConfiguration {
162 pub fn try_from_env() -> anyhow::Result<Option<Self>> {
166 let storage_account = env::var("AZURE_BLOB_STORAGE_ACCOUNT").ok();
167 let access_key = env::var("AZURE_BLOB_STORAGE_ACCESS_KEY").ok();
168
169 if let (Some(storage_account), Some(access_key)) = (storage_account, access_key) {
170 Ok(Some(AzureBlobStorageConfiguration {
171 storage_account,
172 access_key,
173 }))
174 } else {
175 Ok(None)
176 }
177 }
178
179 pub fn connection_string(&self) -> anyhow::Result<String> {
180 Ok(format!(
181 "DefaultEndpointsProtocol=https;AccountName={};AccountKey={};EndpointSuffix=core.windows.net",
182 self.storage_account, self.access_key
183 ))
184 }
185}
186
187#[derive(Clone, PartialEq)]
188pub struct AzureConfiguration {
189 pub chatbot_config: Option<AzureChatbotConfiguration>,
190 pub search_config: Option<AzureSearchConfiguration>,
191 pub blob_storage_config: Option<AzureBlobStorageConfiguration>,
192}
193
194impl AzureConfiguration {
195 pub fn try_from_env() -> anyhow::Result<Option<Self>> {
199 let chatbot = AzureChatbotConfiguration::try_from_env()?;
200 let search_config = AzureSearchConfiguration::try_from_env()?;
201 let blob_storage_config = AzureBlobStorageConfiguration::try_from_env()?;
202
203 if chatbot.is_some() || search_config.is_some() || blob_storage_config.is_some() {
204 Ok(Some(AzureConfiguration {
205 chatbot_config: chatbot,
206 search_config,
207 blob_storage_config,
208 }))
209 } else {
210 Ok(None)
211 }
212 }
213
214 pub fn mock_conf() -> anyhow::Result<Option<Self>> {
219 let base_url = env::var("BASE_URL").context("BASE_URL must be defined")?;
220 let chatbot_config = Some(AzureChatbotConfiguration {
221 api_key: "".to_string(),
222 api_endpoint: Url::parse(&base_url)?.join("/api/v0/mock-azure/test/")?,
223 });
224 let search_config = Some(AzureSearchConfiguration {
225 vectorizer_resource_uri: "".to_string(),
226 vectorizer_deployment_id: "".to_string(),
227 vectorizer_api_key: "".to_string(),
228 vectorizer_model_name: "".to_string(),
229 search_api_key: "".to_string(),
230 search_endpoint: Url::from_str("https://example.com/does-not-exist/")?,
231 });
232 let blob_storage_config = Some(AzureBlobStorageConfiguration {
233 storage_account: "".to_string(),
234 access_key: "".to_string(),
235 });
236
237 Ok(Some(AzureConfiguration {
238 chatbot_config,
239 search_config,
240 blob_storage_config,
241 }))
242 }
243}