This commit is contained in:
JMARyA 2024-04-09 14:30:10 +02:00
commit 8dd8bc8033
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
8 changed files with 2223 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

2091
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

12
Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "mirrord"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-files = "0.6.5"
actix-web = "4.5.1"
env_logger = "0.11.3"
reqwest = "0.12.3"
serde = { version = "1.0.197", features = ["derive"] }
toml = "0.8.12"

2
README.md Normal file
View file

@ -0,0 +1,2 @@
# mirrord
mirror

10
sample.conf Normal file
View file

@ -0,0 +1,10 @@
mirrors = [
"https://mirror.leaseweb.net",
"https://geo.mirror.pkgbuild.com",
"https://mirror.leaseweb.net",
"https://archlinux.thaller.ws"
]
cache_dir = "./data"
#ttl = "3h"

15
src/config.rs Normal file
View file

@ -0,0 +1,15 @@
use serde::Deserialize;
use crate::proxy::ProxyMirror;
#[derive(Debug, Deserialize)]
pub struct Config {
pub mirrors: Vec<String>,
pub cache_dir: String,
}
impl Config {
pub fn to_proxy(&self) -> ProxyMirror {
ProxyMirror::new(self.mirrors.clone(), &self.cache_dir)
}
}

35
src/main.rs Normal file
View file

@ -0,0 +1,35 @@
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
mod config;
mod proxy;
use proxy::ProxyMirror;
async fn index(req: HttpRequest) -> impl Responder {
let path = req.path();
let p: &actix_web::web::Data<ProxyMirror> = req.app_data().unwrap();
let data = p.get(path, &req).await;
data.unwrap()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init();
let proxym = {
let config: config::Config =
toml::from_str(&std::fs::read_to_string("./sample.conf").unwrap()).unwrap();
config.to_proxy()
};
let proxym = actix_web::web::Data::new(proxym);
HttpServer::new(move || {
App::new()
.wrap(actix_web::middleware::Logger::default())
.app_data(proxym.clone())
.service(web::resource("/{path:.*}").to(index))
})
.bind("0.0.0.0:8080")?
.run()
.await
}

57
src/proxy.rs Normal file
View file

@ -0,0 +1,57 @@
use std::path::PathBuf;
use actix_web::{HttpRequest, HttpResponse};
pub struct ProxyMirror {
mirrors: Vec<String>,
data_dir: String,
}
impl ProxyMirror {
pub fn new(mirrors: Vec<String>, data_dir: &str) -> Self {
Self {
mirrors,
data_dir: data_dir.to_string(),
}
}
pub async fn get(&self, path: &str, req: &HttpRequest) -> Option<HttpResponse> {
let p = std::path::Path::new(&path[1..]);
let p = std::path::Path::new(&self.data_dir).join(p);
std::fs::create_dir_all(p.parent().unwrap()).unwrap();
if p.exists() {
// todo : refresh caches
return Some(
actix_files::NamedFile::open_async(&p)
.await
.ok()?
.into_response(req),
);
}
for mirror in &self.mirrors {
let url = format!("{mirror}{path}");
let res = self.get_url(&url, &p).await;
if let Some(res) = res {
if res.status().is_success() {
return Some(res);
}
}
}
None
}
pub async fn get_url(&self, path: &str, save: &PathBuf) -> Option<HttpResponse> {
println!("Fetching {path}");
let response = reqwest::get(path).await.unwrap();
let status_code = response.status();
let body_bytes = response.bytes().await.ok()?;
std::fs::write(save, &body_bytes).unwrap();
let mut http_response = HttpResponse::build(
actix_web::http::StatusCode::from_u16(status_code.as_u16()).unwrap(),
);
let http_response = http_response.body(body_bytes);
Some(http_response)
}
}