Compare commits

..

2 commits

12 changed files with 91 additions and 73 deletions

2
backend/Cargo.lock generated
View file

@ -1170,7 +1170,7 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "powertools" name = "powertools"
version = "2.0.0-alpha4" version = "2.0.0-alpha5"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "powertools" name = "powertools"
version = "2.0.0-alpha4" version = "2.0.0-alpha5"
edition = "2021" edition = "2021"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"] authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
description = "Backend (superuser) functionality for PowerTools" description = "Backend (superuser) functionality for PowerTools"

View file

@ -11,7 +11,7 @@ pub async fn save_setting_handler(
content_type: web::Header<header::ContentType>, content_type: web::Header<header::ContentType>,
cli: web::Data<&'static Cli>, cli: web::Data<&'static Cli>,
) -> std::io::Result<impl Responder> { ) -> std::io::Result<impl Responder> {
println!("Content-Type: {}", content_type.to_string()); //println!("Content-Type: {}", content_type.to_string());
let bytes = match data.to_bytes_limited(PAYLOAD_LIMIT).await { let bytes = match data.to_bytes_limited(PAYLOAD_LIMIT).await {
Ok(Ok(x)) => x, Ok(Ok(x)) => x,
Ok(Err(e)) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, format!("wut: {}", e))), Ok(Err(e)) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, format!("wut: {}", e))),

View file

@ -5,10 +5,8 @@ use std::sync::{Arc, Mutex};
use usdpl_back::core::serdes::Primitive; use usdpl_back::core::serdes::Primitive;
use usdpl_back::AsyncCallable; use usdpl_back::AsyncCallable;
use chrono::{offset::Utc, DateTime};
use serde::{Deserialize, Serialize};
use super::handler::{ApiMessage, GeneralMessage}; use super::handler::{ApiMessage, GeneralMessage};
use crate::utility::CachedData;
#[cfg(feature = "online")] #[cfg(feature = "online")]
const BASE_URL_FALLBACK: &'static str = "https://powertools.ngni.us"; const BASE_URL_FALLBACK: &'static str = "https://powertools.ngni.us";
@ -18,12 +16,6 @@ static BASE_URL: RwLock<Option<String>> = RwLock::new(None);
const MAX_CACHE_DURATION: std::time::Duration = const MAX_CACHE_DURATION: std::time::Duration =
std::time::Duration::from_secs(60 * 60 * 24 * 7 /* 7 days */); std::time::Duration::from_secs(60 * 60 * 24 * 7 /* 7 days */);
#[derive(Serialize, Deserialize, Clone, Debug)]
struct CachedData<T> {
data: T,
updated: DateTime<Utc>,
}
type StoreCache = type StoreCache =
std::collections::HashMap<u32, CachedData<Vec<community_settings_core::v1::Metadata>>>; std::collections::HashMap<u32, CachedData<Vec<community_settings_core::v1::Metadata>>>;
@ -112,14 +104,14 @@ fn save_cache(_cache: &StoreCache) {}
fn get_maybe_cached(steam_app_id: u32) -> Vec<community_settings_core::v1::Metadata> { fn get_maybe_cached(steam_app_id: u32) -> Vec<community_settings_core::v1::Metadata> {
let mut cache = load_cache(); let mut cache = load_cache();
let data = if let Some(cached_result) = cache.get(&steam_app_id) { let data = if let Some(cached_result) = cache.get(&steam_app_id) {
if cached_result.updated < (Utc::now() - MAX_CACHE_DURATION) { if cached_result.needs_update(MAX_CACHE_DURATION) {
// cache needs update // cache needs update
if let Ok(result) = search_by_app_id_online(steam_app_id) { if let Ok(result) = search_by_app_id_online(steam_app_id) {
cache.insert( cache.insert(
steam_app_id, steam_app_id,
CachedData { CachedData {
data: result.clone(), data: result.clone(),
updated: Utc::now(), updated: chrono::offset::Utc::now(),
}, },
); );
save_cache(&cache); save_cache(&cache);
@ -138,7 +130,7 @@ fn get_maybe_cached(steam_app_id: u32) -> Vec<community_settings_core::v1::Metad
steam_app_id, steam_app_id,
CachedData { CachedData {
data: result.clone(), data: result.clone(),
updated: Utc::now(), updated: chrono::offset::Utc::now(),
}, },
); );
save_cache(&cache); save_cache(&cache);
@ -207,8 +199,8 @@ pub fn search_by_app_id() -> impl AsyncCallable {
}; };
super::async_utils::AsyncIsh { super::async_utils::AsyncIsh {
trans_setter: |params| { trans_setter: |params| {
if let Some(Primitive::F64(app_id)) = params.get(0) { if let Some(Primitive::String(s)) = params.get(0) {
Ok(*app_id as u32) s.parse::<u32>().map_err(|e| format!("search_by_app_id invalid parameter 0: {}", e))
} else { } else {
Err("search_by_app_id missing/invalid parameter 0".to_owned()) Err("search_by_app_id missing/invalid parameter 0".to_owned())
} }
@ -329,6 +321,8 @@ fn settings_to_web_config(
username: String, username: String,
settings: crate::persist::SettingsJson, settings: crate::persist::SettingsJson,
) -> community_settings_core::v1::Metadata { ) -> community_settings_core::v1::Metadata {
#[cfg(any(not(debug_assertions), not(feature = "dev_stuff")))]
let app_id = if app_id == 0 { 1 } else { app_id };
community_settings_core::v1::Metadata { community_settings_core::v1::Metadata {
name: settings.name, name: settings.name,
steam_app_id: app_id, steam_app_id: app_id,

View file

@ -12,6 +12,8 @@ pub const LIMITS_FILE: &str = "limits_cache.ron";
pub const LIMITS_REFRESH_PERIOD: std::time::Duration = std::time::Duration::from_secs(60 * 60 * 24); // 1 day pub const LIMITS_REFRESH_PERIOD: std::time::Duration = std::time::Duration::from_secs(60 * 60 * 24); // 1 day
#[cfg(feature = "online")] #[cfg(feature = "online")]
pub const LIMITS_STARTUP_WAIT: std::time::Duration = std::time::Duration::from_secs(60); // 1 minute pub const LIMITS_STARTUP_WAIT: std::time::Duration = std::time::Duration::from_secs(60); // 1 minute
#[cfg(feature = "online")]
pub const LIMITS_CHECK_PERIOD: std::time::Duration = std::time::Duration::from_secs(5 * 60); // 5 minutes
pub const LIMITS_OVERRIDE_FILE: &str = "limits_override.ron"; pub const LIMITS_OVERRIDE_FILE: &str = "limits_override.ron";
#[cfg(feature = "online")] #[cfg(feature = "online")]

View file

@ -8,29 +8,8 @@ use crate::persist::{DriverJson, SettingsJson};
use crate::settings::{Driver, General, ProviderBuilder, TBattery, TCpus, TGeneral, TGpu}; use crate::settings::{Driver, General, ProviderBuilder, TBattery, TCpus, TGeneral, TGpu};
fn get_limits() -> limits_core::json_v2::Base { fn get_limits() -> limits_core::json_v2::Base {
let limits_path = super::utility::limits_path();
match File::open(&limits_path) {
Ok(f) => match ron::de::from_reader(f) {
Ok(lim) => lim,
Err(e) => {
log::warn!(
"Failed to parse limits file `{}`, cannot use for auto_detect: {}",
limits_path.display(),
e
);
limits_core::json_v2::Base::default()
}
},
Err(e) => {
log::warn!(
"Failed to open limits file `{}`: {}",
limits_path.display(),
e
);
super::limits_worker::get_limits_cached() super::limits_worker::get_limits_cached()
} }
}
}
fn get_limits_overrides() -> Option<Limits> { fn get_limits_overrides() -> Option<Limits> {
let limits_override_path = super::utility::limits_override_path(); let limits_override_path = super::utility::limits_override_path();

View file

@ -2,6 +2,11 @@ use std::thread::{self, JoinHandle};
use limits_core::json_v2::Base; use limits_core::json_v2::Base;
#[inline]
fn expired_updated_time() -> chrono::DateTime<chrono::offset::Utc> {
chrono::offset::Utc::now() - (crate::consts::LIMITS_REFRESH_PERIOD * 2)
}
#[cfg(feature = "online")] #[cfg(feature = "online")]
pub fn spawn() -> JoinHandle<()> { pub fn spawn() -> JoinHandle<()> {
thread::spawn(move || { thread::spawn(move || {
@ -12,34 +17,45 @@ pub fn spawn() -> JoinHandle<()> {
loop { loop {
if (limits_path.exists() && limits_path.is_file()) || !limits_path.exists() { if (limits_path.exists() && limits_path.is_file()) || !limits_path.exists() {
// try to load limits from file, fallback to built-in default // try to load limits from file, fallback to built-in default
let base = if limits_path.exists() { let mut base = if limits_path.exists() {
match std::fs::File::open(&limits_path) { match std::fs::File::open(&limits_path) {
Ok(f) => match ron::de::from_reader(f) { Ok(f) => match ron::de::from_reader(f) {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
log::error!("Cannot parse {}: {}", limits_path.display(), e); log::error!("Cannot parse {}: {}", limits_path.display(), e);
Base::default() crate::utility::CachedData {
data: Base::default(),
updated: expired_updated_time(),
}
} }
}, },
Err(e) => { Err(e) => {
log::error!("Cannot open {}: {}", limits_path.display(), e); log::error!("Cannot open {}: {}", limits_path.display(), e);
Base::default() crate::utility::CachedData {
data: Base::default(),
updated: expired_updated_time(),
}
} }
} }
} else { } else {
let base = Base::default(); let mut base = crate::utility::CachedData {
save_base(&base, &limits_path); data: Base::default(),
updated: expired_updated_time(),
};
save_base(&mut base, &limits_path);
base base
}; };
crate::api::web::set_base_url(base.store); crate::api::web::set_base_url(base.data.store.clone());
if let Some(refresh) = &base.refresh { if let Some(refresh) = &base.data.refresh {
if base.needs_update(crate::consts::LIMITS_REFRESH_PERIOD) {
// try to retrieve newer version // try to retrieve newer version
match ureq::get(refresh).call() { match ureq::get(refresh).call() {
Ok(response) => { Ok(response) => {
let json_res: std::io::Result<Base> = response.into_json(); let json_res: std::io::Result<Base> = response.into_json();
match json_res { match json_res {
Ok(new_base) => { Ok(new_base) => {
save_base(&new_base, &limits_path); base.data = new_base;
save_base(&mut base, &limits_path);
} }
Err(e) => { Err(e) => {
log::error!("Cannot parse response from `{}`: {}", refresh, e) log::error!("Cannot parse response from `{}`: {}", refresh, e)
@ -48,6 +64,7 @@ pub fn spawn() -> JoinHandle<()> {
} }
Err(e) => log::warn!("Cannot download limits from `{}`: {}", refresh, e), Err(e) => log::warn!("Cannot download limits from `{}`: {}", refresh, e),
} }
}
} else { } else {
log::info!("limits_worker refresh is empty, terminating..."); log::info!("limits_worker refresh is empty, terminating...");
break; break;
@ -55,7 +72,7 @@ pub fn spawn() -> JoinHandle<()> {
} else if !limits_path.is_file() { } else if !limits_path.is_file() {
log::error!("Path for storing limits is not a file!"); log::error!("Path for storing limits is not a file!");
} }
thread::sleep(crate::consts::LIMITS_REFRESH_PERIOD); thread::sleep(crate::consts::LIMITS_CHECK_PERIOD);
} }
log::warn!("limits_worker completed!"); log::warn!("limits_worker completed!");
}) })
@ -70,31 +87,33 @@ pub fn spawn() -> JoinHandle<()> {
pub fn get_limits_cached() -> Base { pub fn get_limits_cached() -> Base {
let limits_path = super::utility::limits_path(); let limits_path = super::utility::limits_path();
if limits_path.is_file() { let cached: crate::utility::CachedData<Base> = if limits_path.is_file() {
match std::fs::File::open(&limits_path) { match std::fs::File::open(&limits_path) {
Ok(f) => match ron::de::from_reader(f) { Ok(f) => match ron::de::from_reader(f) {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
log::error!("Cannot parse {}: {}", limits_path.display(), e); log::error!("Cannot parse {}: {}", limits_path.display(), e);
Base::default() return Base::default();
} }
}, },
Err(e) => { Err(e) => {
log::error!("Cannot open {}: {}", limits_path.display(), e); log::error!("Cannot open {}: {}", limits_path.display(), e);
Base::default() return Base::default();
} }
} }
} else { } else {
Base::default() return Base::default();
} };
cached.data
} }
#[cfg(feature = "online")] #[cfg(feature = "online")]
fn save_base(new_base: &Base, path: impl AsRef<std::path::Path>) { fn save_base(new_base: &mut crate::utility::CachedData<Base>, path: impl AsRef<std::path::Path>) {
let limits_path = path.as_ref(); let limits_path = path.as_ref();
new_base.updated = chrono::offset::Utc::now();
match std::fs::File::create(&limits_path) { match std::fs::File::create(&limits_path) {
Ok(f) => { Ok(f) => {
match ron::ser::to_writer_pretty(f, &new_base, crate::utility::ron_pretty_config()) { match ron::ser::to_writer_pretty(f, new_base, crate::utility::ron_pretty_config()) {
Ok(_) => log::info!("Successfully saved new limits to {}", limits_path.display()), Ok(_) => log::info!("Successfully saved new limits to {}", limits_path.display()),
Err(e) => log::error!( Err(e) => log::error!(
"Failed to save limits json to file `{}`: {}", "Failed to save limits json to file `{}`: {}",

View file

@ -3,6 +3,9 @@
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use serde::{Deserialize, Serialize};
use chrono::{offset::Utc, DateTime};
/*pub fn unwrap_lock<'a, T: Sized>( /*pub fn unwrap_lock<'a, T: Sized>(
result: LockResult<MutexGuard<'a, T>>, result: LockResult<MutexGuard<'a, T>>,
lock_name: &str, lock_name: &str,
@ -16,6 +19,18 @@ use std::os::unix::fs::PermissionsExt;
} }
}*/ }*/
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CachedData<T> {
pub data: T,
pub updated: DateTime<Utc>,
}
impl <T> CachedData<T> {
pub fn needs_update(&self, max_age: std::time::Duration) -> bool {
self.updated < (Utc::now() - max_age)
}
}
pub fn ron_pretty_config() -> ron::ser::PrettyConfig { pub fn ron_pretty_config() -> ron::ser::PrettyConfig {
ron::ser::PrettyConfig::default() ron::ser::PrettyConfig::default()
.struct_names(true) .struct_names(true)

View file

@ -1,6 +1,6 @@
{ {
"name": "PowerTools", "name": "PowerTools",
"version": "2.0.0-alpha4", "version": "2.0.0-alpha5",
"description": "Power tweaks for power users", "description": "Power tweaks for power users",
"scripts": { "scripts": {
"build": "shx rm -rf dist && rollup -c", "build": "shx rm -rf dist && rollup -c",

View file

@ -376,8 +376,8 @@ export type StoreMetadata = {
//config: any, //config: any,
} }
export async function searchStoreByAppId(id: number): Promise<StoreMetadata[]> { export async function searchStoreByAppId(id: string): Promise<StoreMetadata[]> {
console.log("WEB_search_by_app"); //console.log("WEB_search_by_app");
return (await call_backend("WEB_search_by_app", [id]))[0]; return (await call_backend("WEB_search_by_app", [id]))[0];
} }
@ -395,11 +395,11 @@ export async function storeUpload(steam_id: string, steam_username: string): Pro
} }
export async function getAllSettingVariants(): Promise<VariantInfo[]> { export async function getAllSettingVariants(): Promise<VariantInfo[]> {
console.log("GENERAL_get_all_variants"); //console.log("GENERAL_get_all_variants");
return (await call_backend("GENERAL_get_all_variants", [])); return (await call_backend("GENERAL_get_all_variants", []));
} }
export async function getCurrentSettingVariant(): Promise<VariantInfo> { export async function getCurrentSettingVariant(): Promise<VariantInfo> {
console.log("GENERAL_get_current_variant"); //console.log("GENERAL_get_current_variant");
return (await call_backend("GENERAL_get_current_variant", []))[0]; return (await call_backend("GENERAL_get_current_variant", []))[0];
} }

View file

@ -44,4 +44,6 @@ export const PERIODICAL_BACKEND_PERIOD = 5000; // milliseconds
export const AUTOMATIC_REAPPLY_WAIT = 2000; // milliseconds export const AUTOMATIC_REAPPLY_WAIT = 2000; // milliseconds
export const STORE_RESULTS_URI = "/plugins/PowerTools/settings_store"; export const STORE_RESULTS_URI = "/plugins/PowerTools/settings_store";
export const STORE_MAIN_APP_ID = "1";
export const STORE_MAIN_APP_ID_DEV = "0";

View file

@ -67,6 +67,7 @@ import {
STORE_RESULTS, STORE_RESULTS,
STORE_RESULTS_URI, STORE_RESULTS_URI,
STORE_MAIN_APP_ID,
PERIODICAL_BACKEND_PERIOD, PERIODICAL_BACKEND_PERIOD,
AUTOMATIC_REAPPLY_WAIT, AUTOMATIC_REAPPLY_WAIT,
@ -130,7 +131,7 @@ const reload = function() {
}); });
if (!get_value(STORE_RESULTS)) { if (!get_value(STORE_RESULTS)) {
backend.resolve(backend.searchStoreByAppId(0), (results) => set_value(STORE_RESULTS, results)); backend.resolve(backend.searchStoreByAppId(STORE_MAIN_APP_ID), (results) => set_value(STORE_RESULTS, results));
} }
backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) }); backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) });
@ -216,13 +217,19 @@ const registerCallbacks = function(autoclear: boolean) {
}); });
} }
); );
backend.resolve(
backend.searchStoreByAppId(STORE_MAIN_APP_ID),
(results: backend.StoreMetadata[]) => {
set_value(STORE_RESULTS, results);
}
);
} }
}); });
//@ts-ignore //@ts-ignore
startHook = SteamClient.Apps.RegisterForGameActionStart((actionType, id) => { startHook = SteamClient.Apps.RegisterForGameActionStart((actionType, id) => {
//@ts-ignore //@ts-ignore
let gameInfo: any = appStore.GetAppOverviewByGameID(id); let gameInfo: any = appStore.GetAppOverviewByGameID(id);
let appId = gameInfo.appid.toString(); let appId: string = gameInfo.appid.toString();
backend.log(backend.LogLevel.Info, "RegisterForGameActionStart callback(" + actionType + ", " + id + ")"); backend.log(backend.LogLevel.Info, "RegisterForGameActionStart callback(" + actionType + ", " + id + ")");
backend.resolve( backend.resolve(