iqps_backend/
env.rs

1//! ### Environment Variables
2//!
3//!  Each field in the struct `EnvVars` corresponds to an environment variable. The environment variable name will be in all capitals. The default values are set using the `arg()` macro of the `clap` crate. Check the source code for the defaults.
4
5use std::path::PathBuf;
6
7use hmac::{digest::InvalidLength, Hmac, Mac};
8use sha2::Sha256;
9
10use crate::pathutils::Paths;
11
12pub struct EnvVars {
13    // Database
14    /// Database name
15    pub db_name: String,
16    /// Database hostname
17    pub db_host: String,
18    /// Database port
19    pub db_port: String,
20    /// Database username
21    pub db_user: String,
22    /// Database password
23    pub db_password: String,
24
25    // Auth
26    /// GitHub app client id (public token)
27    pub gh_client_id: String,
28    /// GitHub app client secret
29    pub gh_client_secret: String,
30    /// JWT encryption secret (make it a long, randomized string)
31    jwt_secret: String,
32    /// Github organization name
33    pub gh_org_name: String,
34    /// Github organization team slug (this team has access to admin dashboard)
35    pub gh_org_team_slug: String,
36    /// The usernames of the admins (additional to org team members, comma separated)
37    pub gh_admin_usernames: String,
38    /// URL of Slack webhook for sending notifications
39    pub slack_webhook_url: String,
40
41    // Other configs
42    /// Maximum number of papers that can be uploaded at a time
43    pub max_upload_limit: usize,
44    /// Location where logs are stored
45    pub log_location: PathBuf,
46
47    // Paths
48    /// The URL of the static files server (odin's vault)
49    static_files_url: String,
50    /// The path where static files are served from
51    static_file_storage_location: PathBuf,
52    /// The path where uploaded papers are stored temporarily, relative to the `static_file_storage_location`
53    uploaded_qps_path: PathBuf,
54    /// The path where library papers (scrapped) are stored, relative to the `static_file_storage_location`
55    library_qps_path: PathBuf,
56
57    // Server
58    /// The port the server listens on
59    pub server_port: i32,
60
61    // CORS
62    /// List of origins allowed (as a list of values separated by commas `origin1, origin2`)
63    pub cors_allowed_origins: String,
64
65    /// All paths must be handled using this
66    pub paths: Paths,
67}
68
69impl EnvVars {
70    /// Parses the environment variables into the struct
71    pub fn parse() -> Result<Self, Box<dyn std::error::Error>> {
72        let db_name = std::env::var("DB_NAME")?;
73        let db_host = std::env::var("DB_HOST")?;
74        let db_port = std::env::var("DB_PORT")?;
75        let db_user = std::env::var("DB_USER")?;
76        let db_password = std::env::var("DB_PASSWORD")?;
77        let gh_client_id = std::env::var("GH_CLIENT_ID")?;
78        let jwt_secret = std::env::var("JWT_SECRET")?;
79        let gh_client_secret = std::env::var("GH_CLIENT_SECRET")?;
80        let gh_org_name = std::env::var("GH_ORG_NAME").unwrap_or_default();
81        let gh_org_team_slug = std::env::var("GH_ORG_TEAM_SLUG").unwrap_or_default();
82        let gh_admin_usernames = std::env::var("GH_ADMIN_USERNAMES").unwrap_or_default();
83        let slack_webhook_url = std::env::var("SLACK_WEBHOOK_URL").unwrap_or_default();
84        let max_upload_limit: usize = std::env::var("MAX_UPLOAD_LIMIT")
85            .map(|s| s.parse())
86            .unwrap_or(Ok(10))?;
87        let log_location = std::env::var("LOG_LOCATION")
88            .unwrap_or("./log/application.log".to_string())
89            .into();
90        let static_files_url =
91            std::env::var("STATIC_FILES_URL").unwrap_or("https://static.metakgp.org".to_string());
92        let static_file_storage_location = std::env::var("STATIC_FILE_STORAGE_LOCATION")
93            .unwrap_or("/srv/static".to_string())
94            .into();
95        let uploaded_qps_path = std::env::var("UPLOADED_QPS_PATH")
96            .unwrap_or("/iqps/uploaded".to_string())
97            .into();
98        let library_qps_path = std::env::var("LIBRARY_QPS_PATH")
99            .unwrap_or("/peqp/qp".to_string())
100            .into();
101        let server_port: i32 = std::env::var("SERVER_PORT")
102            .map(|s| s.parse())
103            .unwrap_or(Ok(8080))?;
104        let cors_allowed_origins = std::env::var("CORS_ALLOWED_ORIGINS")
105            .unwrap_or("https://qp.metakgp.org,http://localhost:5173".to_string());
106
107        Ok(Self {
108            db_name,
109            db_host,
110            db_port,
111            db_user,
112            db_password,
113            gh_client_id,
114            jwt_secret,
115            gh_client_secret,
116            gh_org_name,
117            gh_org_team_slug,
118            gh_admin_usernames,
119            slack_webhook_url,
120            max_upload_limit,
121            log_location,
122            static_files_url,
123            static_file_storage_location,
124            uploaded_qps_path,
125            library_qps_path,
126            server_port,
127            cors_allowed_origins,
128            paths: Paths::default(),
129        })
130    }
131
132    /// Processes the environment variables after reading.
133    pub fn process(mut self) -> Result<Self, Box<dyn std::error::Error>> {
134        self.paths = Paths::new(
135            &self.static_files_url,
136            &self.static_file_storage_location,
137            &self.uploaded_qps_path,
138            &self.library_qps_path,
139        )?;
140        self.log_location = std::path::absolute(self.log_location)?;
141
142        Ok(self)
143    }
144
145    /// Returns the JWT signing key
146    pub fn get_jwt_key(&self) -> Result<Hmac<Sha256>, InvalidLength> {
147        Hmac::new_from_slice(self.jwt_secret.as_bytes())
148    }
149}