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