Add plugin stats increment endpoint support
This commit is contained in:
parent
cf2786242d
commit
7f1a001f63
7 changed files with 85 additions and 10 deletions
|
@ -1,3 +1,3 @@
|
|||
mod store_plugin;
|
||||
|
||||
pub use store_plugin::{StorePlugin, StorePluginVersion, StorePluginList, StorePluginQuery, StorePluginQuerySortColumn, StorePluginQuerySortDirection};
|
||||
pub use store_plugin::{StorePlugin, StorePluginVersion, StorePluginList, StorePluginQuery, StorePluginQuerySortColumn, StorePluginQuerySortDirection, StorePluginIncrement};
|
||||
|
|
|
@ -50,3 +50,9 @@ pub enum StorePluginQuerySortDirection {
|
|||
#[serde(rename = "desc")]
|
||||
Descending,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
pub struct StorePluginIncrement {
|
||||
#[serde(rename = "isUpdate")]
|
||||
pub is_update: bool
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ async fn main() -> std::io::Result<()> {
|
|||
.service(not_decky::decky_artifact)
|
||||
.service(not_decky::decky_image)
|
||||
.service(not_decky::decky_statistics)
|
||||
.service(not_decky::decky_statistics_increment)
|
||||
})
|
||||
.bind(("0.0.0.0", args.server_port.unwrap_or(22252)))?
|
||||
.run()
|
||||
|
|
|
@ -8,4 +8,4 @@ pub use artifact::decky_artifact;
|
|||
pub use image::decky_image;
|
||||
pub use index::decky_index;
|
||||
pub use plugins::decky_plugins;
|
||||
pub use stats::decky_statistics;
|
||||
pub use stats::{decky_statistics, decky_statistics_increment};
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use actix_web::{get, web, Responder};
|
||||
use actix_web::{get, post, web, Responder};
|
||||
|
||||
use crate::storage::IStorage;
|
||||
|
||||
#[get("/stats")]
|
||||
pub async fn decky_statistics(data: actix_web::web::Data<Box<dyn IStorage>>) -> impl Responder {
|
||||
println!("stats");
|
||||
let plugins: HashMap<String, u64> = data.get_statistics();
|
||||
web::Json(plugins)
|
||||
}
|
||||
|
||||
|
||||
#[post("/plugins/{name}/versions/{version}")]
|
||||
pub async fn decky_statistics_increment(data: actix_web::web::Data<Box<dyn IStorage>>, path: actix_web::web::Path<(String, String)>, query: actix_web::web::Query<decky_api::StorePluginIncrement>) -> actix_web::Result<impl Responder> {
|
||||
let new_version_info = data.increment_statistic(&path.0, &path.1, &*query)
|
||||
.map_err(|e| actix_web::error::ErrorNotFound(e.to_string()))?;
|
||||
Ok(web::Json(new_version_info))
|
||||
}
|
||||
|
|
|
@ -98,6 +98,10 @@ impl FileStorage {
|
|||
if !lock.contains_key(&version.hash) {
|
||||
lock.insert(version.hash.clone(), AtomicU64::new(0));
|
||||
}
|
||||
let update_key = Self::version_update_name(&version.hash);
|
||||
if !lock.contains_key(&update_key) {
|
||||
lock.insert(update_key, AtomicU64::new(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +113,17 @@ impl FileStorage {
|
|||
}
|
||||
|
||||
fn version_stat_entry_name(plugin_name: &str, version_name: &str) -> String {
|
||||
format!("{} {}", plugin_name, version_name)
|
||||
format!("{} {} install", plugin_name, version_name)
|
||||
}
|
||||
|
||||
/// External stat for updates of a specific version
|
||||
fn version_stat_update_entry_name(plugin_name: &str, version_name: &str) -> String {
|
||||
format!("{} {} update", plugin_name, version_name)
|
||||
}
|
||||
|
||||
/// Internal counter key for update stat of a specific version
|
||||
fn version_update_name(hash: &str) -> String {
|
||||
format!("{}-update", hash)
|
||||
}
|
||||
|
||||
fn plugin_stat_entry_name(plugin_name: &str) -> String {
|
||||
|
@ -124,10 +138,18 @@ impl FileStorage {
|
|||
format!("{}/plugins/{}.png", self.domain_root, plugin_name)
|
||||
}
|
||||
|
||||
fn get_single_version_stats(&self, plugin_name: &str, version_name: &str) -> Option<u64> {
|
||||
fn get_single_version_stats(&self, hash: &str) -> Option<u64> {
|
||||
let lock = self.stats.as_ref()?.read().expect("Couldn't acquire stats read lock");
|
||||
let stat_entry = Self::version_stat_entry_name(plugin_name, version_name);
|
||||
if let Some(stat_counter) = lock.get(&stat_entry) {
|
||||
if let Some(stat_counter) = lock.get(hash) {
|
||||
Some(stat_counter.load(Ordering::SeqCst))
|
||||
} else {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_single_version_update_stats(&self, hash: &str) -> Option<u64> {
|
||||
let lock = self.stats.as_ref()?.read().expect("Couldn't acquire stats read lock");
|
||||
if let Some(stat_counter) = lock.get(&Self::version_update_name(hash)) {
|
||||
Some(stat_counter.load(Ordering::SeqCst))
|
||||
} else {
|
||||
Some(0)
|
||||
|
@ -156,14 +178,15 @@ impl FileStorage {
|
|||
let version_name = entry_path.file_stem().unwrap().to_string_lossy().into_owned();
|
||||
let hash_str = sha256::try_digest(&entry_path)?;
|
||||
let artifact_url = self.artifact_url(&plugin_name, &version_name, &hash_str);
|
||||
let downloads_stat = self.get_single_version_stats(&plugin_name, &version_name);
|
||||
let downloads_stat = self.get_single_version_stats(&hash_str);
|
||||
let updates_stat = self.get_single_version_update_stats(&hash_str);
|
||||
versions.push(StorePluginVersion {
|
||||
name: version_name,
|
||||
hash: hash_str,
|
||||
artifact: Some(artifact_url),
|
||||
created: Some(entry.metadata()?.created()?.into()),
|
||||
downloads: downloads_stat,
|
||||
updates: None, // TODO what is this?
|
||||
updates: updates_stat,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -228,6 +251,11 @@ impl IStorage for FileStorage {
|
|||
total += count_val;
|
||||
map.insert(Self::version_stat_entry_name(&plugin.name, &version.name), count_val);
|
||||
}
|
||||
if let Some(count) = lock.get(&Self::version_update_name(&version.hash)) {
|
||||
let count_val = count.load(Ordering::SeqCst);
|
||||
total += count_val;
|
||||
map.insert(Self::version_stat_update_entry_name(&plugin.name, &version.name), count_val);
|
||||
}
|
||||
}
|
||||
map.insert(Self::plugin_stat_entry_name(&plugin.name), total);
|
||||
}
|
||||
|
@ -239,4 +267,33 @@ impl IStorage for FileStorage {
|
|||
std::collections::HashMap::with_capacity(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_statistic(&self, name: &str, version: &str, query: &decky_api::StorePluginIncrement) -> Result<decky_api::StorePluginVersion, std::io::Error> {
|
||||
if let Some(stats) = &self.stats {
|
||||
// to find the correct stats counter, match up name and version to find hash
|
||||
if let Ok(plugins) = self.read_all_plugins(None) {
|
||||
if let Some(plugin) = plugins.into_iter().filter(|p| p.name == name).next() {
|
||||
if let Some(mut version) = plugin.versions.into_iter().filter(|v| v.name == version).next() {
|
||||
let hash = &version.hash;
|
||||
let lock = stats.read().expect("Failed to acquire stats read lock");
|
||||
if query.is_update {
|
||||
let key = Self::version_update_name(hash);
|
||||
if let Some(counter) = lock.get(&key) {
|
||||
counter.fetch_add(1, Ordering::SeqCst);
|
||||
version.updates = version.updates.map(|x| x + 1);
|
||||
return Ok(version)
|
||||
}
|
||||
} else {
|
||||
if let Some(counter) = lock.get(hash) {
|
||||
counter.fetch_add(1, Ordering::SeqCst);
|
||||
version.downloads = version.downloads.map(|x| x + 1);
|
||||
return Ok(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Plugin version not found"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@ pub trait IStorage: Send + Sync {
|
|||
fn get_statistics(&self) -> std::collections::HashMap<String, u64> {
|
||||
std::collections::HashMap::with_capacity(0)
|
||||
}
|
||||
|
||||
fn increment_statistic(&self, _name: &str, _version: &str, _query: &decky_api::StorePluginIncrement) -> Result<decky_api::StorePluginVersion, std::io::Error> {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Statistics increment not supported"))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmptyStorage;
|
||||
|
|
Loading…
Reference in a new issue