use log::{error, info, warn}; use rusoto_core::credential::StaticProvider; use rusoto_core::{HttpClient, Region, RusotoError}; use rusoto_s3::{PutObjectError, S3Client}; use rusoto_s3::{PutObjectRequest, S3}; use std::time::Duration; mod fabric; mod forge; mod minecraft; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("{0}")] DaedalusError(#[from] daedalus::Error), #[error("Error while deserializing JSON")] SerdeError(#[from] serde_json::Error), #[error("Unable to fetch {item}")] FetchError { inner: reqwest::Error, item: String }, #[error("Error while managing asynchronous tasks")] TaskError(#[from] tokio::task::JoinError), #[error("Error while uploading file to S3")] S3Error { inner: RusotoError, file: String, }, #[error("Error while parsing version as semver: {0}")] SemVerError(#[from] semver::Error), #[error("Error while reading zip file: {0}")] ZipError(#[from] zip::result::ZipError), #[error("Error while reading zip file: {0}")] IoError(#[from] std::io::Error), } #[tokio::main] async fn main() { if check_env_vars() { error!("Some environment variables are missing!"); return; } let mut timer = tokio::time::interval(Duration::from_secs(10 * 60)); loop { timer.tick().await; tokio::spawn(async { match fabric::retrieve_data().await { Ok(..) => {} Err(err) => error!("{:?}", err), }; match minecraft::retrieve_data().await { Ok(..) => {} Err(err) => error!("{:?}", err), }; match forge::retrieve_data().await { Ok(..) => {} Err(err) => error!("{:?}", err), }; }); } } fn check_env_vars() -> bool { let mut failed = false; fn check_var(var: &str) -> bool { if dotenv::var(var) .ok() .and_then(|s| s.parse::().ok()) .is_none() { warn!( "Variable `{}` missing in dotenv or not of type `{}`", var, std::any::type_name::() ); true } else { false } } failed |= check_var::("BASE_URL"); failed |= check_var::("BASE_FOLDER"); failed |= check_var::("S3_ACCESS_TOKEN"); failed |= check_var::("S3_SECRET"); failed |= check_var::("S3_URL"); failed |= check_var::("S3_REGION"); failed |= check_var::("S3_BUCKET_NAME"); failed |= check_var::("DO_INTEGRATION"); let do_integration = dotenv::var("DO_INTEGRATION") .ok() .map(|x| x.parse::().ok()) .flatten(); if do_integration.unwrap_or(false) { failed |= check_var::("DO_ACCESS_KEY"); } failed } lazy_static::lazy_static! { static ref CLIENT : S3Client = S3Client::new_with( HttpClient::new().unwrap(), StaticProvider::new( dotenv::var("S3_ACCESS_TOKEN").unwrap(), dotenv::var("S3_SECRET").unwrap(), None, None, ), Region::Custom { name: dotenv::var("S3_REGION").unwrap(), endpoint: dotenv::var("S3_URL").unwrap(), }, ); } pub async fn upload_file_to_bucket( path: String, bytes: Vec, content_type: Option, ) -> Result<(), Error> { CLIENT .put_object(PutObjectRequest { bucket: dotenv::var("S3_BUCKET_NAME").unwrap(), key: format!("{}/{}", &*dotenv::var("BASE_FOLDER").unwrap(), path), body: Some(bytes.into()), acl: Some("public-read".to_string()), content_type, ..Default::default() }) .await .map_err(|err| Error::S3Error { inner: err, file: format!("{}/{}", &*dotenv::var("BASE_FOLDER").unwrap(), path), })?; Ok(()) } pub fn format_url(path: &str) -> String { format!( "{}/{}/{}", &*dotenv::var("BASE_URL").unwrap(), &*dotenv::var("BASE_FOLDER").unwrap(), path ) }