smart mirroring
This commit is contained in:
parent
68cb32f07b
commit
7647616242
13 changed files with 701 additions and 67 deletions
348
Cargo.lock
generated
348
Cargo.lock
generated
|
@ -153,6 +153,12 @@ dependencies = [
|
|||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
|
@ -209,8 +215,8 @@ dependencies = [
|
|||
"rand",
|
||||
"rayon",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ring",
|
||||
"reqwest 0.11.27",
|
||||
"ring 0.16.20",
|
||||
"rocket",
|
||||
"rocket_cors",
|
||||
"serde",
|
||||
|
@ -900,6 +906,25 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http 1.2.0",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
|
@ -1010,6 +1035,29 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 1.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.2.0",
|
||||
"http-body 1.0.1",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.9.5"
|
||||
|
@ -1038,9 +1086,9 @@ dependencies = [
|
|||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"http-body",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
|
@ -1052,6 +1100,43 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"h2 0.4.7",
|
||||
"http 1.2.0",
|
||||
"http-body 1.0.1",
|
||||
"httparse",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.2.0",
|
||||
"hyper 1.5.2",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
|
@ -1059,12 +1144,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"hyper 0.14.32",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper 1.5.2",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.2.0",
|
||||
"http-body 1.0.1",
|
||||
"hyper 1.5.2",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.61"
|
||||
|
@ -1643,10 +1763,13 @@ dependencies = [
|
|||
"env_logger 0.11.6",
|
||||
"log",
|
||||
"maud",
|
||||
"reqwest 0.12.12",
|
||||
"rocket",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tar",
|
||||
"toml",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
|
@ -1959,11 +2082,11 @@ dependencies = [
|
|||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.32",
|
||||
"hyper-tls 0.5.0",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
|
@ -1972,12 +2095,12 @@ dependencies = [
|
|||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls-pemfile",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"sync_wrapper 0.1.2",
|
||||
"system-configuration 0.5.1",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
|
@ -1988,6 +2111,50 @@ dependencies = [
|
|||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.4.7",
|
||||
"http 1.2.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.5.2",
|
||||
"hyper-rustls",
|
||||
"hyper-tls 0.6.0",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.2",
|
||||
"system-configuration 0.6.1",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
|
@ -1998,11 +2165,26 @@ dependencies = [
|
|||
"libc",
|
||||
"once_cell",
|
||||
"spin 0.5.2",
|
||||
"untrusted",
|
||||
"untrusted 0.7.1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted 0.9.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket"
|
||||
version = "0.5.1"
|
||||
|
@ -2085,7 +2267,7 @@ dependencies = [
|
|||
"either",
|
||||
"futures",
|
||||
"http 0.2.12",
|
||||
"hyper",
|
||||
"hyper 0.14.32",
|
||||
"indexmap",
|
||||
"log",
|
||||
"memchr",
|
||||
|
@ -2141,6 +2323,19 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
|
@ -2150,6 +2345,32 @@ dependencies = [
|
|||
"base64 0.21.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring 0.17.8",
|
||||
"rustls-pki-types",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.19"
|
||||
|
@ -2631,6 +2852,15 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
|
@ -2650,7 +2880,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
|||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
"system-configuration-sys 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags 2.7.0",
|
||||
"core-foundation",
|
||||
"system-configuration-sys 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2663,6 +2904,16 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.43"
|
||||
|
@ -2822,6 +3073,16 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.17"
|
||||
|
@ -2880,6 +3141,27 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
|
@ -3034,6 +3316,12 @@ version = "0.7.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
|
@ -3258,6 +3546,36 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
|
|
@ -10,8 +10,11 @@ chrono = "0.4.39"
|
|||
env_logger = "0.11.6"
|
||||
log = "0.4.22"
|
||||
maud = "0.26.0"
|
||||
reqwest = { version = "0.12.12" }
|
||||
rocket = "0.5.1"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.134"
|
||||
sqlx = "0.8.2"
|
||||
tar = "0.4.43"
|
||||
toml = "0.8.19"
|
||||
zstd = "0.13.2"
|
||||
|
|
15
config.toml
Normal file
15
config.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
[mirror]
|
||||
repos = [
|
||||
"core",
|
||||
"extra",
|
||||
"multilib"
|
||||
]
|
||||
|
||||
mirrorlist = [
|
||||
"http://mirrors.kernel.org/archlinux/$repo/os/$arch",
|
||||
"https://geo.mirror.pkgbuild.com/$repo/os/$arch",
|
||||
"https://ftpmirror.infania.net/mirror/archlinux/$repo/os/$arch",
|
||||
"http://mirror.rackspace.com/archlinux/$repo/os/$arch",
|
||||
"https://mirror.rackspace.com/archlinux/$repo/os/$arch"
|
||||
]
|
|
@ -5,6 +5,7 @@ services:
|
|||
- "8080:8000"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./config.toml:/config.toml
|
||||
environment:
|
||||
- "RUST_LOG=info"
|
||||
- "ROCKET_ADDRESS=0.0.0.0"
|
||||
|
|
30
src/config.rs
Normal file
30
src/config.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Default)]
|
||||
pub struct Config {
|
||||
pub mirror: Option<MirrorConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Default)]
|
||||
pub struct MirrorConfig {
|
||||
repos: Vec<String>,
|
||||
mirrorlist: Vec<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn is_mirrored_repo(&self, repo: &str) -> bool {
|
||||
if let Some(mirrorc) = &self.mirror {
|
||||
return mirrorc.repos.iter().any(|x| x == repo);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn mirrorlist(&self) -> Option<&[String]> {
|
||||
if let Some(mirror) = &self.mirror {
|
||||
return Some(mirror.mirrorlist.as_slice());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
10
src/main.rs
10
src/main.rs
|
@ -1,7 +1,9 @@
|
|||
use based::auth::User;
|
||||
use based::get_pg;
|
||||
use config::Config;
|
||||
use rocket::routes;
|
||||
|
||||
pub mod config;
|
||||
pub mod routes;
|
||||
|
||||
#[rocket::launch]
|
||||
|
@ -11,9 +13,14 @@ async fn launch() -> _ {
|
|||
let pg = get_pg!();
|
||||
sqlx::migrate!("./migrations").run(pg).await.unwrap();
|
||||
|
||||
let config: Config =
|
||||
toml::from_str(&std::fs::read_to_string("config.toml").unwrap_or_default())
|
||||
.unwrap_or_default();
|
||||
|
||||
let _ = User::create("admin".to_string(), "admin", based::auth::UserRole::Admin).await;
|
||||
|
||||
rocket::build().mount("/", routes![
|
||||
rocket::build()
|
||||
.mount("/", routes![
|
||||
based::htmx::htmx_script_route,
|
||||
routes::index_page,
|
||||
routes::pkg_route,
|
||||
|
@ -28,4 +35,5 @@ async fn launch() -> _ {
|
|||
routes::user::change_password,
|
||||
routes::user::change_password_post
|
||||
])
|
||||
.manage(config)
|
||||
}
|
||||
|
|
120
src/pkg/mirror.rs
Normal file
120
src/pkg/mirror.rs
Normal file
|
@ -0,0 +1,120 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use super::{Package, Repository, arch::Architecture};
|
||||
|
||||
pub struct MirrorRepository {
|
||||
pub inner: Repository,
|
||||
}
|
||||
|
||||
impl MirrorRepository {
|
||||
pub fn new(repo: &str) -> Self {
|
||||
std::fs::create_dir_all(format!("./data/{repo}")).unwrap();
|
||||
Self {
|
||||
inner: Repository::new(repo).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn download_file(
|
||||
&self,
|
||||
url: &str,
|
||||
file_path: PathBuf,
|
||||
mirrorlist: &[String],
|
||||
arch: Architecture,
|
||||
) {
|
||||
log::info!("Downloading {url} to {}", file_path.to_str().unwrap());
|
||||
for mirror in mirrorlist {
|
||||
let mirror = mirror
|
||||
.replace("$repo", &self.inner.name)
|
||||
.replace("$arch", &arch.to_string());
|
||||
let url = format!("{mirror}/{url}");
|
||||
log::info!("Trying mirror {url}");
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
if let Ok(resp) = client.get(url).send().await {
|
||||
if resp.status().is_success() {
|
||||
let data = resp.bytes().await.unwrap().to_vec();
|
||||
let parent = file_path.parent().unwrap();
|
||||
std::fs::create_dir_all(parent).unwrap();
|
||||
|
||||
std::fs::write(file_path, data).unwrap();
|
||||
return;
|
||||
} else {
|
||||
log::warn!("Mirror {mirror} failed [{}]", resp.status().as_u16());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `.db.tar.gz` content for the repository of `arch`
|
||||
pub async fn db_content(&self, arch: Architecture, mirrorlist: &[String]) -> Option<Vec<u8>> {
|
||||
if let Some(content) = self.inner.db_content(arch.clone()) {
|
||||
return Some(content);
|
||||
}
|
||||
|
||||
self.download_file(
|
||||
&format!("{}.db.tar.gz", self.inner.name),
|
||||
self.inner
|
||||
.base_path(arch.clone())
|
||||
.join(format!("{}.db.tar.gz", self.inner.name)),
|
||||
mirrorlist,
|
||||
arch.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
self.inner.db_content(arch)
|
||||
}
|
||||
|
||||
/// Get the `.db.tar.gz.sig` content for the repository of `arch`
|
||||
pub async fn sig_content(&self, arch: Architecture, mirrorlist: &[String]) -> Option<Vec<u8>> {
|
||||
if let Some(content) = self.inner.sig_content(arch.clone()) {
|
||||
return Some(content);
|
||||
}
|
||||
|
||||
self.download_file(
|
||||
&format!("{}.db.tar.gz.sig", self.inner.name),
|
||||
self.inner
|
||||
.base_path(arch.clone())
|
||||
.join(format!("{}.db.tar.gz.sig", self.inner.name)),
|
||||
mirrorlist,
|
||||
arch.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
self.inner.sig_content(arch)
|
||||
}
|
||||
|
||||
pub async fn get_pkg(&self, pkg_name: &str, mirrorlist: &[String]) -> Option<Package> {
|
||||
if let Some(pkg) = self.inner.get_pkg(pkg_name) {
|
||||
return Some(pkg);
|
||||
}
|
||||
|
||||
// PKG
|
||||
let (name, _, _, arch) = Package::extract_pkg_name(pkg_name).unwrap();
|
||||
|
||||
self.download_file(
|
||||
pkg_name,
|
||||
self.inner
|
||||
.base_path(arch.clone())
|
||||
.join(&name)
|
||||
.join(pkg_name),
|
||||
mirrorlist,
|
||||
arch.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// SIG
|
||||
|
||||
self.download_file(
|
||||
&format!("{pkg_name}.sig"),
|
||||
self.inner
|
||||
.base_path(arch.clone())
|
||||
.join(&name)
|
||||
.join(format!("{pkg_name}.sig")),
|
||||
mirrorlist,
|
||||
arch.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
self.inner.get_pkg(pkg_name)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,20 @@
|
|||
// TODO : Read DB Info
|
||||
|
||||
pub mod repo;
|
||||
pub use repo::Repository;
|
||||
pub mod package;
|
||||
pub use package::Package;
|
||||
pub mod arch;
|
||||
pub mod db;
|
||||
pub mod mirror;
|
||||
|
||||
pub fn find_package_by_name(pkg: &str) -> Option<Package> {
|
||||
for repo in Repository::list() {
|
||||
let repo = Repository::new(&repo).unwrap();
|
||||
let pkgs = repo.list_pkg();
|
||||
|
||||
if let Some(res) = pkgs.iter().find(|x| *x == pkg) {
|
||||
return repo.get_pkg_by_name(res);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
|
|
@ -18,18 +18,21 @@ pub struct Package {
|
|||
/// Name of the package
|
||||
pub name: String,
|
||||
/// Version of the package
|
||||
version: Option<String>,
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
impl Package {
|
||||
/// Create a new package
|
||||
pub fn new(repo: &str, arch: Architecture, pkg_name: &str, version: &str) -> Self {
|
||||
Package {
|
||||
let pkg = Package {
|
||||
repo: repo.to_string(),
|
||||
arch,
|
||||
name: pkg_name.to_string(),
|
||||
version: Some(version.to_string()),
|
||||
}
|
||||
};
|
||||
|
||||
std::fs::create_dir_all(pkg.base_path()).unwrap();
|
||||
pkg
|
||||
}
|
||||
|
||||
pub fn install_script(&self) -> Option<String> {
|
||||
|
@ -186,7 +189,7 @@ impl Package {
|
|||
|
||||
pub fn pacman_hooks(&self) -> Vec<(String, String)> {
|
||||
let pkg_file = self.base_path().join(self.file_name());
|
||||
let files = list_tar_file(&pkg_file).unwrap_or_default();
|
||||
let files = self.file_list();
|
||||
files
|
||||
.into_iter()
|
||||
.filter(|x| {
|
||||
|
@ -237,12 +240,10 @@ impl Package {
|
|||
|
||||
fn base_path(&self) -> PathBuf {
|
||||
// <repo>/<arch>/<pkg>/
|
||||
let p = Path::new("./data")
|
||||
Path::new("./data")
|
||||
.join(&self.repo)
|
||||
.join(self.arch.to_string())
|
||||
.join(&self.name);
|
||||
std::fs::create_dir_all(&p).unwrap();
|
||||
p
|
||||
.join(&self.name)
|
||||
}
|
||||
|
||||
/// Switch the `Architecture` of the package
|
||||
|
|
|
@ -18,6 +18,7 @@ impl Repository {
|
|||
repos.push(file_name);
|
||||
}
|
||||
|
||||
repos.sort();
|
||||
repos
|
||||
}
|
||||
|
||||
|
|
|
@ -2,18 +2,25 @@ use based::auth::MaybeUser;
|
|||
use based::page::{Shell, htmx_link, render_page};
|
||||
use based::request::{RawResponse, RequestContext, StringResponse, respond_with};
|
||||
use maud::{PreEscaped, html};
|
||||
use rocket::get;
|
||||
use pacco::pkg::mirror::MirrorRepository;
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::{State, get};
|
||||
|
||||
use pacco::pkg::Repository;
|
||||
use pacco::pkg::arch::Architecture;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
pub mod push;
|
||||
pub mod ui;
|
||||
pub mod user;
|
||||
|
||||
#[get("/")]
|
||||
pub async fn index_page(ctx: RequestContext, user: MaybeUser) -> StringResponse {
|
||||
pub async fn index_page(
|
||||
ctx: RequestContext,
|
||||
user: MaybeUser,
|
||||
config: &State<Config>,
|
||||
) -> StringResponse {
|
||||
let repos: Vec<String> = Repository::list();
|
||||
|
||||
let content = html!(
|
||||
|
@ -26,8 +33,12 @@ pub async fn index_page(ctx: RequestContext, user: MaybeUser) -> StringResponse
|
|||
div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6" {
|
||||
@for repo in repos {
|
||||
(htmx_link(&format!("/{repo}"), "flex items-center gap-4 p-4 bg-gray-100 dark:bg-gray-700 rounded-lg shadow-md transition hover:bg-gray-200 dark:hover:bg-gray-600", "", html! {
|
||||
p class="font-medium flex-1 text-gray-800 dark:text-gray-100" {
|
||||
p class="font-medium text-gray-800 dark:text-gray-100" {
|
||||
(repo)
|
||||
|
||||
@if config.is_mirrored_repo(&repo) {
|
||||
div class="inline-block px-3 py-1 text-sm font-medium text-white bg-blue-500 rounded-full" { "Mirrored" };
|
||||
};
|
||||
};
|
||||
}))
|
||||
}
|
||||
|
@ -38,15 +49,64 @@ pub async fn index_page(ctx: RequestContext, user: MaybeUser) -> StringResponse
|
|||
}
|
||||
|
||||
#[get("/pkg/<repo>/<arch>/<pkg_name>")]
|
||||
pub async fn pkg_route(repo: &str, arch: &str, pkg_name: &str) -> RawResponse {
|
||||
pub async fn pkg_route(
|
||||
repo: &str,
|
||||
arch: &str,
|
||||
pkg_name: &str,
|
||||
config: &State<Config>,
|
||||
) -> RawResponse {
|
||||
let arch = Architecture::parse(arch).unwrap();
|
||||
|
||||
if config.is_mirrored_repo(repo) {
|
||||
let repo = MirrorRepository::new(repo);
|
||||
if is_repo_db(pkg_name) {
|
||||
if pkg_name.ends_with("sig") {
|
||||
return respond_with(
|
||||
Status::Ok,
|
||||
ContentType::new("application", "pgp-signature"),
|
||||
repo.sig_content(arch, config.mirrorlist().unwrap())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
} else {
|
||||
return respond_with(
|
||||
Status::Ok,
|
||||
ContentType::new("application", "tar"),
|
||||
repo.db_content(arch, config.mirrorlist().unwrap())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let pkg = repo
|
||||
.get_pkg(pkg_name, config.mirrorlist().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if pkg_name.ends_with("pkg.tar.zst") {
|
||||
return respond_with(
|
||||
Status::Ok,
|
||||
ContentType::new("application", "tar"),
|
||||
pkg.pkg_content().unwrap(),
|
||||
);
|
||||
} else if pkg_name.ends_with("pkg.tar.zst.sig") {
|
||||
return respond_with(
|
||||
Status::Ok,
|
||||
ContentType::new("application", "pgp-signature"),
|
||||
pkg.sig_content().unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
return respond_with(
|
||||
Status::Ok,
|
||||
ContentType::Plain,
|
||||
"Not found".as_bytes().to_vec(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(repo) = Repository::new(repo) {
|
||||
if pkg_name.ends_with("db.tar.gz")
|
||||
|| pkg_name.ends_with("db")
|
||||
|| pkg_name.ends_with("db.tar.gz.sig")
|
||||
|| pkg_name.ends_with("db.sig")
|
||||
{
|
||||
if is_repo_db(pkg_name) {
|
||||
if pkg_name.ends_with("sig") {
|
||||
return respond_with(
|
||||
Status::Ok,
|
||||
|
@ -107,3 +167,10 @@ pub async fn render(
|
|||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn is_repo_db(pkg_name: &str) -> bool {
|
||||
pkg_name.ends_with("db.tar.gz")
|
||||
|| pkg_name.ends_with("db")
|
||||
|| pkg_name.ends_with("db.tar.gz.sig")
|
||||
|| pkg_name.ends_with("db.sig")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use based::request::api::{FallibleApiResponse, api_error};
|
||||
use rocket::tokio::io::AsyncReadExt;
|
||||
use rocket::{FromForm, post};
|
||||
use rocket::{FromForm, State, post};
|
||||
use serde_json::json;
|
||||
|
||||
use pacco::pkg::Package;
|
||||
|
@ -12,6 +12,8 @@ use pacco::pkg::arch::Architecture;
|
|||
use rocket::form::Form;
|
||||
use rocket::fs::TempFile;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct PkgUpload<'r> {
|
||||
name: String,
|
||||
|
@ -37,12 +39,17 @@ pub async fn upload_pkg(
|
|||
repo: &str,
|
||||
upload: Form<PkgUpload<'_>>,
|
||||
user: based::auth::APIUser,
|
||||
config: &State<Config>,
|
||||
) -> FallibleApiResponse {
|
||||
// TODO : Permission System
|
||||
if !user.0.is_admin() {
|
||||
return Err(api_error("Forbidden"));
|
||||
}
|
||||
|
||||
if config.is_mirrored_repo(repo) {
|
||||
return Err(api_error("This repository is a mirror."));
|
||||
}
|
||||
|
||||
let pkg = Package::new(
|
||||
repo,
|
||||
Architecture::parse(&upload.arch).ok_or_else(|| api_error("Invalid architecture"))?,
|
||||
|
|
|
@ -3,18 +3,30 @@ use based::{
|
|||
request::{RequestContext, StringResponse},
|
||||
};
|
||||
use maud::{PreEscaped, html};
|
||||
use rocket::get;
|
||||
use rocket::{State, get};
|
||||
|
||||
use pacco::pkg::{Repository, arch::Architecture};
|
||||
use pacco::pkg::{Repository, arch::Architecture, find_package_by_name};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
use super::render;
|
||||
|
||||
// TODO : API
|
||||
|
||||
#[get("/<repo>/<pkg_name>")]
|
||||
pub async fn pkg_ui(repo: &str, pkg_name: &str, ctx: RequestContext) -> Option<StringResponse> {
|
||||
#[get("/<repo>/<pkg_name>?<ver>")]
|
||||
pub async fn pkg_ui(
|
||||
repo: &str,
|
||||
pkg_name: &str,
|
||||
ctx: RequestContext,
|
||||
ver: Option<&str>,
|
||||
) -> Option<StringResponse> {
|
||||
let repo = Repository::new(repo).unwrap();
|
||||
let pkg = repo.get_pkg_by_name(pkg_name)?;
|
||||
let mut pkg = repo.get_pkg_by_name(pkg_name)?;
|
||||
|
||||
if let Some(ver) = ver {
|
||||
pkg = pkg.get_version(ver);
|
||||
}
|
||||
|
||||
let versions = pkg.versions();
|
||||
let arch = pkg.arch();
|
||||
let install_script = pkg.install_script();
|
||||
|
@ -88,9 +100,10 @@ pub async fn pkg_ui(repo: &str, pkg_name: &str, ctx: RequestContext) -> Option<S
|
|||
h2 class="text-xl font-semibold text-gray-700 dark:text-gray-300" { "Versions" }
|
||||
ul class="space-y-1" {
|
||||
@for version in versions {
|
||||
// TODO : Implement page per version ?version=
|
||||
li class="text-gray-800 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400 transition" {
|
||||
(htmx_link(&format!("/{}/{}?ver={version}", repo.name, &pkg.name), if pkg.version.as_ref().map(|x| *x == version).unwrap_or_default() { "text-blue-500" } else { "" }, "", html! {
|
||||
(version)
|
||||
}))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -166,8 +179,12 @@ pub async fn pkg_ui(repo: &str, pkg_name: &str, ctx: RequestContext) -> Option<S
|
|||
}
|
||||
|
||||
#[get("/<repo>?<arch>")]
|
||||
pub async fn repo_ui(repo: &str, ctx: RequestContext, arch: Option<&str>) -> StringResponse {
|
||||
// TODO : permissions
|
||||
pub async fn repo_ui(
|
||||
repo: &str,
|
||||
ctx: RequestContext,
|
||||
arch: Option<&str>,
|
||||
config: &State<Config>,
|
||||
) -> StringResponse {
|
||||
let arch = arch.map(|x| Architecture::parse(x).unwrap_or(Architecture::any));
|
||||
|
||||
let repo = Repository::new(repo).unwrap();
|
||||
|
@ -181,13 +198,16 @@ pub async fn repo_ui(repo: &str, ctx: RequestContext, arch: Option<&str>) -> Str
|
|||
let content = html! {
|
||||
// Repository name and architectures
|
||||
div class="flex flex-wrap items-center justify-center mb-6" {
|
||||
h1 class="text-3xl font-bold text-gray-800 dark:text-gray-100 pr-4" {
|
||||
h1 class="text-3xl font-bold text-gray-800 dark:text-gray-100" {
|
||||
(repo.name)
|
||||
};
|
||||
|
||||
div class="flex gap-2 mt-2 md:mt-0" {
|
||||
@if config.is_mirrored_repo(&repo.name) {
|
||||
div class="inline-block px-3 py-1 text-sm font-medium text-white bg-blue-500 rounded-full ml-4" { "Mirrored" };
|
||||
};
|
||||
|
||||
div class="flex gap-2 mt-2 md:mt-0 ml-4" {
|
||||
@for a in architectures {
|
||||
// TODO : Filter per arch with ?arch=
|
||||
@if let Some(arch) = arch.as_ref() {
|
||||
@if arch.to_string() == a {
|
||||
(htmx_link(&format!("/{}", repo.name), "px-3 py-1 text-sm font-medium bg-blue-400 dark:bg-blue-500 text-gray-600 dark:text-gray-300 rounded-full", "", html! {
|
||||
|
@ -258,12 +278,10 @@ pub fn build_info(key: String, value: String) -> PreEscaped<String> {
|
|||
);
|
||||
}
|
||||
"depend" => {
|
||||
// TODO : Find package link
|
||||
return key_value("Depends on".to_string(), value);
|
||||
return pkg_list_info("Depends on", &value);
|
||||
}
|
||||
"makedepend" => {
|
||||
// TODO : Find package link
|
||||
return key_value("Build Dependencies".to_string(), value);
|
||||
return pkg_list_info("Build Dependencies", &value);
|
||||
}
|
||||
"license" => {
|
||||
return key_value("License".to_string(), value);
|
||||
|
@ -307,3 +325,36 @@ pub fn take_out<T>(v: &mut Vec<T>, f: impl Fn(&T) -> bool) -> (&mut Vec<T>, Opti
|
|||
|
||||
(v, None)
|
||||
}
|
||||
|
||||
pub fn find_pkg_url(pkg: &str) -> Option<String> {
|
||||
if let Some(pkg) = find_package_by_name(pkg) {
|
||||
return Some(format!("/{}/{}", pkg.repo, pkg.name));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn pkg_list_info(key: &str, value: &str) -> PreEscaped<String> {
|
||||
let pkgs = value.split_whitespace().map(|pkg| {
|
||||
if let Some(pkg_url) = find_pkg_url(pkg) {
|
||||
html! {
|
||||
a href=(pkg_url) class="ml-2 text-blue-400" { (pkg) };
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
span class="ml-2" { (pkg) };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
div class="flex items-center" {
|
||||
span class="font-bold w-32" { (format!("{key}: ")) };
|
||||
div class="flex flex-wrap" {
|
||||
@for pkg in pkgs {
|
||||
(pkg)
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue