init
This commit is contained in:
commit
8dd8bc8033
8 changed files with 2223 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
2091
Cargo.lock
generated
Normal file
2091
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
12
Cargo.toml
Normal file
12
Cargo.toml
Normal 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
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# mirrord
|
||||||
|
mirror
|
10
sample.conf
Normal file
10
sample.conf
Normal 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
15
src/config.rs
Normal 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
35
src/main.rs
Normal 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
57
src/proxy.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue