From 13b0b6095ea6f1164c184ce64bedd7288b7aed31 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Sun, 26 Jan 2025 11:08:03 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Download=20Count=20for=20Packages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- migrations/0001_pkg_meta.sql | 9 +++++++ src/pkg/package.rs | 47 +++++++++++++++++++++++++++++++++--- src/routes/mod.rs | 2 ++ src/routes/ui.rs | 9 +++++++ 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 migrations/0001_pkg_meta.sql diff --git a/migrations/0001_pkg_meta.sql b/migrations/0001_pkg_meta.sql new file mode 100644 index 0000000..6dd71c2 --- /dev/null +++ b/migrations/0001_pkg_meta.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS package_meta ( + repo TEXT NOT NULL, + name TEXT NOT NULL, + arch TEXT NOT NULL, + version TEXT NOT NULL, + rel INTEGER NOT NULL, + download_count INTEGER NOT NULL DEFAULT 1, + PRIMARY KEY (repo, name, arch, version, rel) +); diff --git a/src/pkg/package.rs b/src/pkg/package.rs index d275835..ea2a4b4 100644 --- a/src/pkg/package.rs +++ b/src/pkg/package.rs @@ -4,6 +4,8 @@ use std::{ path::{Path, PathBuf}, }; +use based::get_pg; +use sqlx::FromRow; use tar::Archive; use super::{Repository, arch::Architecture}; @@ -17,14 +19,14 @@ pub struct Package { pub arch: Architecture, /// Name of the package pub name: String, - pub rel: u64, + pub rel: i32, /// Version of the package pub version: Option, } impl Package { /// Create a new package - pub fn new(repo: &str, arch: Architecture, pkg_name: &str, version: &str, rel: u64) -> Self { + pub fn new(repo: &str, arch: Architecture, pkg_name: &str, version: &str, rel: i32) -> Self { let pkg = Package { repo: repo.to_string(), arch, @@ -37,7 +39,7 @@ impl Package { pkg } - pub fn version(ver: &str) -> (String, u64) { + pub fn version(ver: &str) -> (String, i32) { let mut splitted = ver.split('-').collect::>(); let rel = splitted.pop().unwrap(); let ver = splitted.join("-"); @@ -402,3 +404,42 @@ pub fn list_tar_file(tar: &PathBuf) -> Option> { Some(paths) } + +#[derive(Debug, Clone, FromRow)] +pub struct PackageMeta { + pub repo: String, + pub name: String, + pub arch: String, + pub version: String, + pub rel: String, + pub download_count: i32, +} + +pub trait PackageMetaInfo { + fn download_amount(&self) -> impl std::future::Future; + fn increase_download_count(&self) -> impl std::future::Future; +} + +impl PackageMetaInfo for Package { + async fn download_amount(&self) -> i32 { + let res: Option<(i32,)> = sqlx::query_as("SELECT download_count FROM package_meta WHERE repo = $1 AND name = $2 AND version = $3 AND arch = $4 AND rel = $5") + .bind(&self.repo) + .bind(&self.name) + .bind(self.version.as_ref().unwrap()) + .bind(&self.arch.to_string()) + .bind(&self.rel) + .fetch_optional(get_pg!()).await.unwrap(); + + res.map(|x| x.0).unwrap_or(0) + } + + async fn increase_download_count(&self) { + sqlx::query("INSERT INTO package_meta (repo, name, arch, version, rel) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (repo, name, arch, version, rel) DO UPDATE SET download_count = package_meta.download_count + 1") + .bind(&self.repo) + .bind(&self.name) + .bind(&self.arch.to_string()) + .bind(&self.version.as_ref().map(|x| x.to_string()).unwrap_or_default()) + .bind(&self.rel) + .execute(get_pg!()).await.unwrap(); + } +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 825be5b..c47e381 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -4,6 +4,7 @@ use based::ui::components::Shell; use based::ui::{prelude::*, render_page}; use maud::{PreEscaped, Render, html}; use pacco::pkg::mirror::MirrorRepository; +use pacco::pkg::package::PackageMetaInfo; use rocket::http::{ContentType, Status}; use rocket::{State, get}; @@ -98,6 +99,7 @@ pub async fn pkg_route( .unwrap(); if pkg_name.ends_with("pkg.tar.zst") { + pkg.increase_download_count().await; return respond_with( Status::Ok, ContentType::new("application", "tar"), diff --git a/src/routes/ui.rs b/src/routes/ui.rs index 6659fab..6599aba 100644 --- a/src/routes/ui.rs +++ b/src/routes/ui.rs @@ -4,6 +4,7 @@ use based::ui::primitives::space::SpaceBetweenWidget; use based::ui::primitives::text::{Code, TextWidget}; use based::ui::{UIWidget, prelude::*}; use maud::{PreEscaped, Render, html}; +use pacco::pkg::package::PackageMetaInfo; use rocket::{State, get}; use pacco::pkg::{Package, Repository, arch::Architecture, find_package_by_name}; @@ -30,6 +31,8 @@ pub async fn pkg_ui( pkg.rel = rel; } + let dl_count = pkg.download_amount().await; + let versions = pkg.versions(); let arch = pkg.arch(); let install_script = pkg.install_script(); @@ -118,6 +121,9 @@ pub async fn pkg_ui( take_out(&mut pkginfo, |x| { x.0 == "size" }).1, |x: (String, String)| build_info(x.0, x.1) ) + .push( + build_info("download_amount".to_string(), dl_count.to_string()) + ) .push_for_each(&pkginfo, |(key, val)| build_info(key.clone(), val.clone())) ).y(ScreenValue::_2)).all(ScreenValue::_4)).all(ScreenValue::_2)).size(Size::Large))))) )).push( @@ -413,6 +419,9 @@ pub async fn repo_ui( pub fn build_info(key: String, value: String) -> PreEscaped { match key.as_str() { + "download_amount" => { + return key_value("Downloads".to_string(), value); + } "pkgname" => {} "xdata" => {} "arch" => {}