You've already forked pages
forked from didirus/AstralRinth
219 lines
6.1 KiB
Rust
219 lines
6.1 KiB
Rust
use crate::util::{
|
|
format_url, upload_file_to_bucket, upload_url_to_bucket_mirrors,
|
|
REQWEST_CLIENT,
|
|
};
|
|
use daedalus::get_path_from_artifact;
|
|
use dashmap::{DashMap, DashSet};
|
|
use std::sync::Arc;
|
|
use tokio::sync::Semaphore;
|
|
use tracing_error::ErrorLayer;
|
|
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
|
|
|
mod error;
|
|
mod fabric;
|
|
mod forge;
|
|
mod minecraft;
|
|
pub mod util;
|
|
|
|
pub use error::{Error, ErrorKind, Result};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
dotenvy::dotenv().ok();
|
|
|
|
let subscriber = tracing_subscriber::registry()
|
|
.with(fmt::layer())
|
|
.with(EnvFilter::from_default_env())
|
|
.with(ErrorLayer::default());
|
|
|
|
tracing::subscriber::set_global_default(subscriber)?;
|
|
|
|
tracing::info!("Initialized tracing. Starting Daedalus!");
|
|
|
|
if check_env_vars() {
|
|
tracing::error!("Some environment variables are missing!");
|
|
|
|
return Ok(());
|
|
}
|
|
|
|
let semaphore = Arc::new(Semaphore::new(
|
|
dotenvy::var("CONCURRENCY_LIMIT")
|
|
.ok()
|
|
.and_then(|x| x.parse().ok())
|
|
.unwrap_or(10),
|
|
));
|
|
|
|
// path, upload file
|
|
let upload_files: DashMap<String, UploadFile> = DashMap::new();
|
|
// path, mirror artifact
|
|
let mirror_artifacts: DashMap<String, MirrorArtifact> = DashMap::new();
|
|
|
|
minecraft::fetch(semaphore.clone(), &upload_files, &mirror_artifacts)
|
|
.await?;
|
|
fabric::fetch_fabric(semaphore.clone(), &upload_files, &mirror_artifacts)
|
|
.await?;
|
|
fabric::fetch_quilt(semaphore.clone(), &upload_files, &mirror_artifacts)
|
|
.await?;
|
|
forge::fetch_neo(semaphore.clone(), &upload_files, &mirror_artifacts)
|
|
.await?;
|
|
forge::fetch_forge(semaphore.clone(), &upload_files, &mirror_artifacts)
|
|
.await?;
|
|
|
|
futures::future::try_join_all(upload_files.iter().map(|x| {
|
|
upload_file_to_bucket(
|
|
x.key().clone(),
|
|
x.value().file.clone(),
|
|
x.value().content_type.clone(),
|
|
&semaphore,
|
|
)
|
|
}))
|
|
.await?;
|
|
|
|
futures::future::try_join_all(mirror_artifacts.iter().map(|x| {
|
|
upload_url_to_bucket_mirrors(
|
|
format!("maven/{}", x.key()),
|
|
x.value()
|
|
.mirrors
|
|
.iter()
|
|
.map(|mirror| {
|
|
if mirror.entire_url {
|
|
mirror.path.clone()
|
|
} else {
|
|
format!("{}{}", mirror.path, x.key())
|
|
}
|
|
})
|
|
.collect(),
|
|
x.sha1.clone(),
|
|
&semaphore,
|
|
)
|
|
}))
|
|
.await?;
|
|
|
|
if dotenvy::var("CLOUDFLARE_INTEGRATION")
|
|
.ok()
|
|
.and_then(|x| x.parse::<bool>().ok())
|
|
.unwrap_or(false)
|
|
{
|
|
if let Ok(token) = dotenvy::var("CLOUDFLARE_TOKEN") {
|
|
if let Ok(zone_id) = dotenvy::var("CLOUDFLARE_ZONE_ID") {
|
|
let cache_clears = upload_files
|
|
.into_iter()
|
|
.map(|x| format_url(&x.0))
|
|
.chain(
|
|
mirror_artifacts
|
|
.into_iter()
|
|
.map(|x| format_url(&format!("maven/{}", x.0))),
|
|
)
|
|
.collect::<Vec<_>>();
|
|
|
|
// Cloudflare ratelimits cache clears to 500 files per request
|
|
for chunk in cache_clears.chunks(500) {
|
|
REQWEST_CLIENT.post(format!("https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache"))
|
|
.bearer_auth(&token)
|
|
.json(&serde_json::json!({
|
|
"files": chunk
|
|
}))
|
|
.send()
|
|
.await
|
|
.map_err(|err| {
|
|
ErrorKind::Fetch {
|
|
inner: err,
|
|
item: "cloudflare clear cache".to_string(),
|
|
}
|
|
})?
|
|
.error_for_status()
|
|
.map_err(|err| {
|
|
ErrorKind::Fetch {
|
|
inner: err,
|
|
item: "cloudflare clear cache".to_string(),
|
|
}
|
|
})?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub struct UploadFile {
|
|
file: bytes::Bytes,
|
|
content_type: Option<String>,
|
|
}
|
|
|
|
pub struct MirrorArtifact {
|
|
pub sha1: Option<String>,
|
|
pub mirrors: DashSet<Mirror>,
|
|
}
|
|
|
|
#[derive(Eq, PartialEq, Hash)]
|
|
pub struct Mirror {
|
|
path: String,
|
|
entire_url: bool,
|
|
}
|
|
|
|
#[tracing::instrument(skip(mirror_artifacts))]
|
|
pub fn insert_mirrored_artifact(
|
|
artifact: &str,
|
|
sha1: Option<String>,
|
|
mirrors: Vec<String>,
|
|
entire_url: bool,
|
|
mirror_artifacts: &DashMap<String, MirrorArtifact>,
|
|
) -> Result<()> {
|
|
let val = mirror_artifacts
|
|
.entry(get_path_from_artifact(artifact)?)
|
|
.or_insert(MirrorArtifact {
|
|
sha1,
|
|
mirrors: DashSet::new(),
|
|
});
|
|
|
|
for mirror in mirrors {
|
|
val.mirrors.insert(Mirror {
|
|
path: mirror,
|
|
entire_url,
|
|
});
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn check_env_vars() -> bool {
|
|
let mut failed = false;
|
|
|
|
fn check_var<T: std::str::FromStr>(var: &str) -> bool {
|
|
if dotenvy::var(var)
|
|
.ok()
|
|
.and_then(|s| s.parse::<T>().ok())
|
|
.is_none()
|
|
{
|
|
tracing::warn!(
|
|
"Variable `{}` missing in dotenvy or not of type `{}`",
|
|
var,
|
|
std::any::type_name::<T>()
|
|
);
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
failed |= check_var::<String>("BASE_URL");
|
|
|
|
failed |= check_var::<String>("S3_ACCESS_TOKEN");
|
|
failed |= check_var::<String>("S3_SECRET");
|
|
failed |= check_var::<String>("S3_URL");
|
|
failed |= check_var::<String>("S3_REGION");
|
|
failed |= check_var::<String>("S3_BUCKET_NAME");
|
|
|
|
if dotenvy::var("CLOUDFLARE_INTEGRATION")
|
|
.ok()
|
|
.and_then(|x| x.parse::<bool>().ok())
|
|
.unwrap_or(false)
|
|
{
|
|
failed |= check_var::<String>("CLOUDFLARE_TOKEN");
|
|
failed |= check_var::<String>("CLOUDFLARE_ZONE_ID");
|
|
}
|
|
|
|
failed
|
|
}
|