smart mirroring
This commit is contained in:
parent
68cb32f07b
commit
7647616242
13 changed files with 701 additions and 67 deletions
|
@ -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,16 +100,17 @@ 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" {
|
||||
(version)
|
||||
(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)
|
||||
}))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
div class="flex flex-wrap pt-6" {
|
||||
div class="flex flex-wrap pt-6" {
|
||||
div class="space-y-2" {
|
||||
h2 class="text-xl font-bold text-gray-700 dark:text-gray-300" { "Content" }
|
||||
|
||||
|
@ -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
Add a link
Reference in a new issue