forked from NG-SD-Plugins/PowerTools
Add all back-end functionality for interacting with variants on the front-end
This commit is contained in:
parent
437f5beb71
commit
4eaf6fae2b
20 changed files with 590 additions and 27 deletions
16
backend/Cargo.lock
generated
16
backend/Cargo.lock
generated
|
@ -289,6 +289,13 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "community_settings_core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.6"
|
version = "0.8.6"
|
||||||
|
@ -778,7 +785,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libryzenadj"
|
name = "libryzenadj"
|
||||||
version = "0.14.0"
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c5bccdf07c3234c06c435648a53d8cb369f76d20e03bb8d2f8c24fb2330efc32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libryzenadj-sys",
|
"libryzenadj-sys",
|
||||||
|
@ -788,7 +797,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libryzenadj-sys"
|
name = "libryzenadj-sys"
|
||||||
version = "0.14.0"
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1de3621be974e892e12d4a07a6a2e32b6a05950759b062d94f5b54f78fabc3a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cmake",
|
"cmake",
|
||||||
|
@ -1055,6 +1066,7 @@ name = "powertools"
|
||||||
version = "1.5.0-ng1"
|
version = "1.5.0-ng1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"community_settings_core",
|
||||||
"libryzenadj",
|
"libryzenadj",
|
||||||
"limits_core",
|
"limits_core",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
@ -28,9 +28,11 @@ simplelog = "0.12"
|
||||||
|
|
||||||
# limits & driver functionality
|
# limits & driver functionality
|
||||||
limits_core = { version = "3", path = "./limits_core" }
|
limits_core = { version = "3", path = "./limits_core" }
|
||||||
|
community_settings_core = { version = "0.1", path = "./community_settings_core" }
|
||||||
regex = "1"
|
regex = "1"
|
||||||
smokepatio = { version = "*", path = "../../smokepatio" }
|
smokepatio = { version = "*", path = "../../smokepatio" }
|
||||||
libryzenadj = { version = "0.14", path = "../../libryzenadj-rs-14" }
|
#libryzenadj = { version = "0.14", path = "../../libryzenadj-rs-14" }
|
||||||
|
libryzenadj = { version = "0.13" }
|
||||||
# ureq's tls feature does not like musl targets
|
# ureq's tls feature does not like musl targets
|
||||||
ureq = { version = "2", features = ["json", "gzip", "brotli", "charset"], default-features = false, optional = true }
|
ureq = { version = "2", features = ["json", "gzip", "brotli", "charset"], default-features = false, optional = true }
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ pub struct Metadata {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub steam_app_id: u32,
|
pub steam_app_id: u32,
|
||||||
pub steam_user_id: u64,
|
pub steam_user_id: u64,
|
||||||
pub stream_username: String,
|
pub steam_username: String,
|
||||||
|
pub tags: Vec<String>,
|
||||||
/// Should always be a valid u128, but some parsers do not support that
|
/// Should always be a valid u128, but some parsers do not support that
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub config: super::Config,
|
pub config: super::Config,
|
||||||
|
|
121
backend/community_settings_srv/src/api/get_game.rs
Normal file
121
backend/community_settings_srv/src/api/get_game.rs
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
use actix_web::{get, web, Responder, http::header};
|
||||||
|
|
||||||
|
use crate::cli::Cli;
|
||||||
|
use crate::file_util;
|
||||||
|
|
||||||
|
const MAX_RESULTS: usize = 50;
|
||||||
|
|
||||||
|
fn special_settings() -> Vec<community_settings_core::v1::Metadata> {
|
||||||
|
vec![
|
||||||
|
community_settings_core::v1::Metadata {
|
||||||
|
name: "Zeroth the Least".to_owned(),
|
||||||
|
steam_app_id: 0,
|
||||||
|
steam_user_id: 76561198116690523,
|
||||||
|
steam_username: "NGnius".to_owned(),
|
||||||
|
tags: vec!["0".to_owned(), "gr8".to_owned()],
|
||||||
|
id: 0.to_string(),
|
||||||
|
config: community_settings_core::v1::Config {
|
||||||
|
cpus: vec![
|
||||||
|
community_settings_core::v1::Cpu {
|
||||||
|
online: true,
|
||||||
|
clock_limits: Some(community_settings_core::v1::MinMax { max: Some(1), min: Some(0) }),
|
||||||
|
governor: "Michaëlle Jean".to_owned(),
|
||||||
|
},
|
||||||
|
community_settings_core::v1::Cpu {
|
||||||
|
online: false,
|
||||||
|
clock_limits: Some(community_settings_core::v1::MinMax { max: Some(1), min: Some(0) }),
|
||||||
|
governor: "Adrienne Clarkson".to_owned(),
|
||||||
|
},
|
||||||
|
community_settings_core::v1::Cpu {
|
||||||
|
online: true,
|
||||||
|
clock_limits: Some(community_settings_core::v1::MinMax { max: Some(1), min: Some(0) }),
|
||||||
|
governor: "Michael Collins".to_owned(),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
gpu: community_settings_core::v1::Gpu {
|
||||||
|
fast_ppt: Some(1),
|
||||||
|
slow_ppt: Some(1),
|
||||||
|
tdp: None,
|
||||||
|
tdp_boost: None,
|
||||||
|
clock_limits: Some(community_settings_core::v1::MinMax { max: Some(1), min: Some(0) }),
|
||||||
|
slow_memory: false,
|
||||||
|
},
|
||||||
|
battery: community_settings_core::v1::Battery {
|
||||||
|
charge_rate: Some(42),
|
||||||
|
charge_mode: Some("nuclear fusion".to_owned()),
|
||||||
|
events: vec![
|
||||||
|
community_settings_core::v1::BatteryEvent {
|
||||||
|
trigger: "anything but one on a gun".to_owned(),
|
||||||
|
charge_rate: Some(42),
|
||||||
|
charge_mode: Some("neutral".to_owned()),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_some_settings_by_app_id(steam_app_id: u32, cli: &'static Cli) -> std::io::Result<Vec<community_settings_core::v1::Metadata>> {
|
||||||
|
let app_id_folder = file_util::setting_folder_by_app_id(&cli.folder, steam_app_id);
|
||||||
|
let mut files: Vec<_> = app_id_folder.read_dir()?
|
||||||
|
.filter_map(|res| res.ok())
|
||||||
|
.filter(|f| f.path().extension().map(|ext| ext == file_util::RON_EXTENSION).unwrap_or(false))
|
||||||
|
.filter_map(|f| f.metadata().ok().map(|meta| (f, meta)))
|
||||||
|
.filter_map(|(f, meta)| meta.created().ok().map(|time| (f, meta, time)))
|
||||||
|
.collect();
|
||||||
|
files.sort_by(|(_, _, a_created), (_, _, b_created)| a_created.cmp(b_created));
|
||||||
|
|
||||||
|
let mut results = Vec::with_capacity(MAX_RESULTS);
|
||||||
|
for (_, (f, _, _)) in files.into_iter().enumerate().take_while(|(i, _)| *i < MAX_RESULTS) {
|
||||||
|
let reader = std::io::BufReader::new(std::fs::File::open(f.path())?);
|
||||||
|
let setting = match ron::de::from_reader(reader) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
let e_msg = format!("{}", e);
|
||||||
|
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e_msg));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
results.push(setting);
|
||||||
|
}
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/setting/by_app_id/{id}")]
|
||||||
|
pub async fn get_setting_by_app_id_handler(
|
||||||
|
id: web::Path<u32>,
|
||||||
|
accept: web::Header<header::Accept>,
|
||||||
|
cli: web::Data<&'static Cli>,
|
||||||
|
) -> std::io::Result<impl Responder> {
|
||||||
|
let id: u32 = *id;
|
||||||
|
println!("Accept: {}", accept.to_string());
|
||||||
|
let preferred = accept.preference();
|
||||||
|
if super::is_mime_type_ron_capable(&preferred) {
|
||||||
|
// Send RON
|
||||||
|
let ron = if id != 0 {
|
||||||
|
get_some_settings_by_app_id(id, &*cli)?
|
||||||
|
} else {
|
||||||
|
special_settings()
|
||||||
|
};
|
||||||
|
// TODO don't dump to string
|
||||||
|
let result_body = ron::ser::to_string(&ron).unwrap();
|
||||||
|
Ok(actix_web::HttpResponse::Ok()
|
||||||
|
//.insert_header(header::ContentType("application/ron".parse().unwrap()))
|
||||||
|
.insert_header(header::ContentType(mime::STAR_STAR))
|
||||||
|
.body(actix_web::body::BoxBody::new(result_body))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Send JSON (fallback)
|
||||||
|
let json = if id != 0 {
|
||||||
|
get_some_settings_by_app_id(id, &*cli)?
|
||||||
|
} else {
|
||||||
|
special_settings()
|
||||||
|
};
|
||||||
|
// TODO don't dump to string
|
||||||
|
let result_body = serde_json::to_string(&json).unwrap();
|
||||||
|
Ok(actix_web::HttpResponse::Ok()
|
||||||
|
.insert_header(header::ContentType::json())
|
||||||
|
.body(actix_web::body::BoxBody::new(result_body))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,8 @@ fn special_settings() -> community_settings_core::v1::Metadata {
|
||||||
name: "Zeroth the Least".to_owned(),
|
name: "Zeroth the Least".to_owned(),
|
||||||
steam_app_id: 1675200,
|
steam_app_id: 1675200,
|
||||||
steam_user_id: 76561198116690523,
|
steam_user_id: 76561198116690523,
|
||||||
stream_username: "NGnius".to_owned(),
|
steam_username: "NGnius".to_owned(),
|
||||||
|
tags: vec!["0".to_owned(), "gr8".to_owned()],
|
||||||
id: 0.to_string(),
|
id: 0.to_string(),
|
||||||
config: community_settings_core::v1::Config {
|
config: community_settings_core::v1::Config {
|
||||||
cpus: vec![
|
cpus: vec![
|
||||||
|
@ -51,7 +52,7 @@ fn special_settings() -> community_settings_core::v1::Metadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/api/setting/{id}")]
|
#[get("/api/setting/by_id/{id}")]
|
||||||
pub async fn get_setting_handler(
|
pub async fn get_setting_handler(
|
||||||
id: web::Path<String>,
|
id: web::Path<String>,
|
||||||
accept: web::Header<header::Accept>,
|
accept: web::Header<header::Accept>,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
mod get_game;
|
||||||
mod get_setting;
|
mod get_setting;
|
||||||
mod save_setting;
|
mod save_setting;
|
||||||
|
|
||||||
|
pub use get_game::get_setting_by_app_id_handler as get_setting_by_steam_app_id;
|
||||||
pub use get_setting::get_setting_handler as get_setting_by_id;
|
pub use get_setting::get_setting_handler as get_setting_by_id;
|
||||||
pub use save_setting::save_setting_handler as save_setting_with_new_id;
|
pub use save_setting::save_setting_handler as save_setting_with_new_id;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub async fn save_setting_handler(
|
||||||
Err(_e) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "too many bytes in payload")),
|
Err(_e) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "too many bytes in payload")),
|
||||||
};
|
};
|
||||||
let next_id = file_util::next_setting_id(&cli.folder);
|
let next_id = file_util::next_setting_id(&cli.folder);
|
||||||
let parsed_data = if super::is_mime_type_ron_capable(&content_type) {
|
let parsed_data: community_settings_core::v1::Metadata = if super::is_mime_type_ron_capable(&content_type) {
|
||||||
// Parse as RON
|
// Parse as RON
|
||||||
match ron::de::from_reader(bytes.as_ref()) {
|
match ron::de::from_reader(bytes.as_ref()) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
|
@ -44,15 +44,15 @@ pub async fn save_setting_handler(
|
||||||
};
|
};
|
||||||
// TODO validate user and app id
|
// TODO validate user and app id
|
||||||
// Reject blocked users and apps
|
// Reject blocked users and apps
|
||||||
let path = file_util::setting_path_by_id(&cli.folder, next_id, file_util::RON_EXTENSION);
|
let path_ron = file_util::setting_path_by_id(&cli.folder, next_id, file_util::RON_EXTENSION);
|
||||||
let writer = std::io::BufWriter::new(std::fs::File::create(path)?);
|
let writer = std::io::BufWriter::new(std::fs::File::create(&path_ron)?);
|
||||||
if let Err(e) = ron::ser::to_writer(writer, &parsed_data) {
|
if let Err(e) = ron::ser::to_writer(writer, &parsed_data) {
|
||||||
let e_msg = format!("{}", e);
|
let e_msg = format!("{}", e);
|
||||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e_msg));
|
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e_msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = file_util::setting_path_by_id(&cli.folder, next_id, file_util::JSON_EXTENSION);
|
let path_json = file_util::setting_path_by_id(&cli.folder, next_id, file_util::JSON_EXTENSION);
|
||||||
let writer = std::io::BufWriter::new(std::fs::File::create(path)?);
|
let writer = std::io::BufWriter::new(std::fs::File::create(&path_json)?);
|
||||||
if let Err(e) = serde_json::to_writer(writer, &parsed_data) {
|
if let Err(e) = serde_json::to_writer(writer, &parsed_data) {
|
||||||
let e_msg = format!("{}", e);
|
let e_msg = format!("{}", e);
|
||||||
if let Some(io_e) = e.io_error_kind() {
|
if let Some(io_e) = e.io_error_kind() {
|
||||||
|
@ -62,7 +62,59 @@ pub async fn save_setting_handler(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO create symlinks for other ways of looking up these settings files
|
// create symlinks for other ways of looking up these settings files
|
||||||
|
let filename_ron = file_util::filename(next_id, file_util::RON_EXTENSION);
|
||||||
|
let filename_json = file_util::filename(next_id, file_util::JSON_EXTENSION);
|
||||||
|
|
||||||
|
// create symlinks to app id folder
|
||||||
|
let app_id_folder = file_util::setting_folder_by_app_id(&cli.folder, parsed_data.steam_app_id);
|
||||||
|
if !app_id_folder.exists() {
|
||||||
|
std::fs::create_dir(&app_id_folder)?;
|
||||||
|
}
|
||||||
|
#[cfg(target_family = "windows")] // NOTE: windows support is untested and unmaintained
|
||||||
|
{
|
||||||
|
std::os::windows::fs::symlink_file(&path_ron, app_id_folder.join(&filename_ron))?;
|
||||||
|
std::os::windows::fs::symlink_file(&path_json, app_id_folder.join(&filename_json))?;
|
||||||
|
}
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
{
|
||||||
|
std::os::unix::fs::symlink(&path_ron, app_id_folder.join(&filename_ron))?;
|
||||||
|
std::os::unix::fs::symlink(&path_json, app_id_folder.join(&filename_json))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create symlinks for user id folder
|
||||||
|
let user_id_folder = file_util::setting_folder_by_user_id(&cli.folder, parsed_data.steam_user_id);
|
||||||
|
if !user_id_folder.exists() {
|
||||||
|
std::fs::create_dir(&user_id_folder)?;
|
||||||
|
}
|
||||||
|
#[cfg(target_family = "windows")] // NOTE: windows support is untested and unmaintained
|
||||||
|
{
|
||||||
|
std::os::windows::fs::symlink_file(&path_ron, user_id_folder.join(&filename_ron))?;
|
||||||
|
std::os::windows::fs::symlink_file(&path_json, user_id_folder.join(&filename_json))?;
|
||||||
|
}
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
{
|
||||||
|
std::os::unix::fs::symlink(&path_ron, user_id_folder.join(&filename_ron))?;
|
||||||
|
std::os::unix::fs::symlink(&path_json, user_id_folder.join(&filename_json))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create symlinks for each tag
|
||||||
|
for tag in parsed_data.tags.iter() {
|
||||||
|
let tag_folder = file_util::setting_folder_by_tag(&cli.folder, tag);
|
||||||
|
if !tag_folder.exists() {
|
||||||
|
std::fs::create_dir(&tag_folder)?;
|
||||||
|
}
|
||||||
|
#[cfg(target_family = "windows")] // NOTE: windows support is untested and unmaintained
|
||||||
|
{
|
||||||
|
std::os::windows::fs::symlink_file(&path_ron, tag_folder.join(&filename_ron))?;
|
||||||
|
std::os::windows::fs::symlink_file(&path_json, tag_folder.join(&filename_json))?;
|
||||||
|
}
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
{
|
||||||
|
std::os::unix::fs::symlink(&path_ron, tag_folder.join(&filename_ron))?;
|
||||||
|
std::os::unix::fs::symlink(&path_json, tag_folder.join(&filename_json))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(actix_web::HttpResponse::NoContent())
|
Ok(actix_web::HttpResponse::NoContent())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,66 @@ pub const JSON_EXTENSION: &'static str = "json";
|
||||||
|
|
||||||
const SETTING_FOLDER: &'static str = "settings";
|
const SETTING_FOLDER: &'static str = "settings";
|
||||||
const ID_FOLDER: &'static str = "by_id";
|
const ID_FOLDER: &'static str = "by_id";
|
||||||
|
const APP_ID_FOLDER: &'static str = "by_app_id";
|
||||||
|
const USER_ID_FOLDER: &'static str = "by_user_id";
|
||||||
|
const TAG_FOLDER: &'static str = "by_tag";
|
||||||
|
|
||||||
static LAST_SETTING_ID: Mutex<u128> = Mutex::new(0);
|
static LAST_SETTING_ID: Mutex<u128> = Mutex::new(0);
|
||||||
|
|
||||||
|
pub fn build_folder_layout(root: impl AsRef<Path>) -> std::io::Result<()> {
|
||||||
|
std::fs::create_dir_all(
|
||||||
|
root.as_ref()
|
||||||
|
.join(SETTING_FOLDER)
|
||||||
|
.join(ID_FOLDER)
|
||||||
|
)?;
|
||||||
|
std::fs::create_dir_all(
|
||||||
|
root.as_ref()
|
||||||
|
.join(SETTING_FOLDER)
|
||||||
|
.join(APP_ID_FOLDER)
|
||||||
|
)?;
|
||||||
|
std::fs::create_dir_all(
|
||||||
|
root.as_ref()
|
||||||
|
.join(SETTING_FOLDER)
|
||||||
|
.join(USER_ID_FOLDER)
|
||||||
|
)?;
|
||||||
|
std::fs::create_dir_all(
|
||||||
|
root.as_ref()
|
||||||
|
.join(SETTING_FOLDER)
|
||||||
|
.join(TAG_FOLDER)
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filename(id: u128, ext: &str) -> String {
|
||||||
|
format!("{}.{}", id, ext)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setting_path_by_id(root: impl AsRef<Path>, id: u128, ext: &str) -> PathBuf {
|
pub fn setting_path_by_id(root: impl AsRef<Path>, id: u128, ext: &str) -> PathBuf {
|
||||||
root.as_ref()
|
root.as_ref()
|
||||||
.join(SETTING_FOLDER)
|
.join(SETTING_FOLDER)
|
||||||
.join(ID_FOLDER)
|
.join(ID_FOLDER)
|
||||||
.join(format!("{}.{}", id, ext))
|
.join(filename(id, ext))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setting_folder_by_app_id(root: impl AsRef<Path>, steam_app_id: u32) -> PathBuf {
|
||||||
|
root.as_ref()
|
||||||
|
.join(SETTING_FOLDER)
|
||||||
|
.join(APP_ID_FOLDER)
|
||||||
|
.join(steam_app_id.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setting_folder_by_user_id(root: impl AsRef<Path>, steam_user_id: u64) -> PathBuf {
|
||||||
|
root.as_ref()
|
||||||
|
.join(SETTING_FOLDER)
|
||||||
|
.join(USER_ID_FOLDER)
|
||||||
|
.join(steam_user_id.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setting_folder_by_tag(root: impl AsRef<Path>, tag: &str) -> PathBuf {
|
||||||
|
root.as_ref()
|
||||||
|
.join(SETTING_FOLDER)
|
||||||
|
.join(TAG_FOLDER)
|
||||||
|
.join(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_setting_id(root: impl AsRef<Path>) -> u128 {
|
pub fn next_setting_id(root: impl AsRef<Path>) -> u128 {
|
||||||
|
|
|
@ -25,6 +25,10 @@ async fn main() -> std::io::Result<()> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
log::debug!("Logging to: {}", args.log.display());
|
log::debug!("Logging to: {}", args.log.display());
|
||||||
|
|
||||||
|
// setup
|
||||||
|
log::debug!("Building folder layout (if not exists) at: {}", &args.folder.display());
|
||||||
|
file_util::build_folder_layout(&args.folder)?;
|
||||||
|
|
||||||
let leaked_args: &'static cli::Cli = Box::leak::<'static>(Box::new(args));
|
let leaked_args: &'static cli::Cli = Box::leak::<'static>(Box::new(args));
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
|
@ -32,6 +36,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
//.app_data(web::Data::new(IndexPage::load("dist/index.html").unwrap()))
|
//.app_data(web::Data::new(IndexPage::load("dist/index.html").unwrap()))
|
||||||
//.app_data(basic::Config::default().realm("Restricted area"))
|
//.app_data(basic::Config::default().realm("Restricted area"))
|
||||||
.service(api::get_setting_by_id)
|
.service(api::get_setting_by_id)
|
||||||
|
.service(api::get_setting_by_steam_app_id)
|
||||||
.service(api::save_setting_with_new_id)
|
.service(api::save_setting_with_new_id)
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", leaked_args.port))?
|
.bind(("0.0.0.0", leaked_args.port))?
|
||||||
|
|
|
@ -61,3 +61,9 @@ pub struct GpuLimits {
|
||||||
pub clock_step: u64,
|
pub clock_step: u64,
|
||||||
pub memory_control_capable: bool,
|
pub memory_control_capable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct VariantInfo {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
|
@ -391,3 +391,61 @@ fn wait_for_response<T>(sender: &Sender<ApiMessage>, rx: mpsc::Receiver<T>, api_
|
||||||
sender.send(api_msg).expect(&format!("{} send failed", op));
|
sender.send(api_msg).expect(&format!("{} send failed", op));
|
||||||
rx.recv().expect(&format!("{} callback recv failed", op))
|
rx.recv().expect(&format!("{} callback recv failed", op))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate get variants
|
||||||
|
pub fn get_all_variants(sender: Sender<ApiMessage>) -> impl AsyncCallable {
|
||||||
|
let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety
|
||||||
|
let getter = move || {
|
||||||
|
let sender2 = sender.clone();
|
||||||
|
move || {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let callback =
|
||||||
|
move |variants: Vec<super::VariantInfo>| tx.send(variants).expect("get_all_variants callback send failed");
|
||||||
|
sender2
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.send(ApiMessage::General(GeneralMessage::GetAllVariants(
|
||||||
|
Box::new(callback),
|
||||||
|
)))
|
||||||
|
.expect("get_all_variants send failed");
|
||||||
|
rx.recv().expect("get_all_variants callback recv failed")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
super::async_utils::AsyncIshGetter {
|
||||||
|
set_get: getter,
|
||||||
|
trans_getter: |result| {
|
||||||
|
let mut output = Vec::with_capacity(result.len());
|
||||||
|
for status in result.iter() {
|
||||||
|
output.push(Primitive::Json(serde_json::to_string(status).expect("Failed to serialize variant info to JSON")));
|
||||||
|
}
|
||||||
|
output
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate get current variant
|
||||||
|
pub fn get_current_variant(sender: Sender<ApiMessage>) -> impl AsyncCallable {
|
||||||
|
let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety
|
||||||
|
let getter = move || {
|
||||||
|
let sender2 = sender.clone();
|
||||||
|
move || {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let callback =
|
||||||
|
move |variant: super::VariantInfo| tx.send(variant).expect("get_all_variants callback send failed");
|
||||||
|
sender2
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.send(ApiMessage::General(GeneralMessage::GetCurrentVariant(
|
||||||
|
Box::new(callback),
|
||||||
|
)))
|
||||||
|
.expect("get_current_variant send failed");
|
||||||
|
rx.recv().expect("get_current_variant callback recv failed")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
super::async_utils::AsyncIshGetter {
|
||||||
|
set_get: getter,
|
||||||
|
trans_getter: |result| {
|
||||||
|
vec![Primitive::Json(serde_json::to_string(&result).expect("Failed to serialize variant info to JSON"))]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -227,6 +227,9 @@ pub enum GeneralMessage {
|
||||||
GetPersistent(Callback<bool>),
|
GetPersistent(Callback<bool>),
|
||||||
GetCurrentProfileName(Callback<String>),
|
GetCurrentProfileName(Callback<String>),
|
||||||
GetPath(Callback<std::path::PathBuf>),
|
GetPath(Callback<std::path::PathBuf>),
|
||||||
|
GetCurrentVariant(Callback<super::VariantInfo>),
|
||||||
|
GetAllVariants(Callback<Vec<super::VariantInfo>>),
|
||||||
|
AddVariant(crate::persist::SettingsJson, Callback<Vec<super::VariantInfo>>),
|
||||||
ApplyNow,
|
ApplyNow,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,6 +241,15 @@ impl GeneralMessage {
|
||||||
Self::GetPersistent(cb) => cb(*settings.persistent()),
|
Self::GetPersistent(cb) => cb(*settings.persistent()),
|
||||||
Self::GetCurrentProfileName(cb) => cb(settings.get_name().to_owned()),
|
Self::GetCurrentProfileName(cb) => cb(settings.get_name().to_owned()),
|
||||||
Self::GetPath(cb) => cb(settings.get_path().to_owned()),
|
Self::GetPath(cb) => cb(settings.get_path().to_owned()),
|
||||||
|
Self::GetCurrentVariant(cb) => cb(settings.get_variant_info()),
|
||||||
|
Self::GetAllVariants(cb) => cb(settings.get_variants()),
|
||||||
|
Self::AddVariant(variant, cb) => match settings.add_variant(variant) {
|
||||||
|
Ok(variants) => cb(variants),
|
||||||
|
Err(e) => {
|
||||||
|
print_errors("GeneralMessage::AddVariant => TGeneral::add_variant", vec![e]);
|
||||||
|
cb(Vec::with_capacity(0))
|
||||||
|
},
|
||||||
|
},
|
||||||
Self::ApplyNow => {}
|
Self::ApplyNow => {}
|
||||||
}
|
}
|
||||||
dirty
|
dirty
|
||||||
|
@ -389,7 +401,7 @@ impl ApiMessageHandler {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
ApiMessage::LoadSystemSettings => {
|
ApiMessage::LoadSystemSettings => {
|
||||||
settings.load_system_default(settings.general.get_name().to_owned(), settings.general.get_variant_id(), settings.general.get_variant_name().to_owned());
|
settings.load_system_default(settings.general.get_name().to_owned(), settings.general.get_variant_id(), settings.general.get_variant_info().name);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
ApiMessage::GetLimits(cb) => {
|
ApiMessage::GetLimits(cb) => {
|
||||||
|
|
|
@ -7,6 +7,7 @@ pub mod gpu;
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
mod utility;
|
mod utility;
|
||||||
|
pub mod web;
|
||||||
|
|
||||||
pub(super) type ApiParameterType = Vec<usdpl_back::core::serdes::Primitive>;
|
pub(super) type ApiParameterType = Vec<usdpl_back::core::serdes::Primitive>;
|
||||||
|
|
||||||
|
|
142
backend/src/api/web.rs
Normal file
142
backend/src/api/web.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use std::sync::mpsc::{self, Sender};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
use usdpl_back::AsyncCallable;
|
||||||
|
|
||||||
|
use super::handler::{ApiMessage, GeneralMessage};
|
||||||
|
|
||||||
|
const BASE_URL: &'static str = "http://powertools.ngni.us";
|
||||||
|
|
||||||
|
/// Get search results web method
|
||||||
|
pub fn search_by_app_id() -> impl AsyncCallable {
|
||||||
|
let getter = move || {
|
||||||
|
move |steam_app_id: u32| {
|
||||||
|
let req_url = format!("{}/api/setting/by_app_id/{}", BASE_URL, steam_app_id);
|
||||||
|
match ureq::get(&req_url).call() {
|
||||||
|
Ok(response) => {
|
||||||
|
let json_res: std::io::Result<Vec<community_settings_core::v1::Metadata>> = response.into_json();
|
||||||
|
match json_res {
|
||||||
|
Ok(search_results) => {
|
||||||
|
// search results may be quite large, so let's do the JSON string conversion in the background (blocking) thread
|
||||||
|
match serde_json::to_string(&search_results) {
|
||||||
|
Err(e) => log::error!("Cannot convert search results from `{}` to JSON: {}", req_url, e),
|
||||||
|
Ok(s) => return s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Cannot parse response from `{}`: {}", req_url, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => log::warn!("Cannot get search results from `{}`: {}", req_url, e),
|
||||||
|
}
|
||||||
|
"[]".to_owned()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
super::async_utils::AsyncIsh {
|
||||||
|
trans_setter: |params| {
|
||||||
|
if let Some(Primitive::F64(app_id)) = params.get(0) {
|
||||||
|
Ok(*app_id as u32)
|
||||||
|
} else {
|
||||||
|
Err("search_by_app_id missing/invalid parameter 0".to_owned())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set_get: getter,
|
||||||
|
trans_getter: |result| vec![Primitive::Json(result)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn web_config_to_settings_json(meta: community_settings_core::v1::Metadata) -> crate::persist::SettingsJson {
|
||||||
|
crate::persist::SettingsJson {
|
||||||
|
version: crate::persist::LATEST_VERSION,
|
||||||
|
name: meta.name,
|
||||||
|
variant: u64::MAX, // TODO maybe change this to use the 64 low bits of id (u64::MAX will cause it to generate a new id when added to file variant map
|
||||||
|
persistent: true,
|
||||||
|
cpus: meta.config.cpus.into_iter().map(|cpu| crate::persist::CpuJson {
|
||||||
|
online: cpu.online,
|
||||||
|
clock_limits: cpu.clock_limits.map(|lim| crate::persist::MinMaxJson {
|
||||||
|
min: lim.min,
|
||||||
|
max: lim.max,
|
||||||
|
}),
|
||||||
|
governor: cpu.governor,
|
||||||
|
root: None,
|
||||||
|
}).collect(),
|
||||||
|
gpu: crate::persist::GpuJson {
|
||||||
|
fast_ppt: meta.config.gpu.fast_ppt,
|
||||||
|
slow_ppt: meta.config.gpu.slow_ppt,
|
||||||
|
tdp: meta.config.gpu.tdp,
|
||||||
|
tdp_boost: meta.config.gpu.tdp_boost,
|
||||||
|
clock_limits: meta.config.gpu.clock_limits.map(|lim| crate::persist::MinMaxJson {
|
||||||
|
min: lim.min,
|
||||||
|
max: lim.max,
|
||||||
|
}),
|
||||||
|
slow_memory: meta.config.gpu.slow_memory,
|
||||||
|
root: None,
|
||||||
|
},
|
||||||
|
battery: crate::persist::BatteryJson {
|
||||||
|
charge_rate: meta.config.battery.charge_rate,
|
||||||
|
charge_mode: meta.config.battery.charge_mode,
|
||||||
|
events: meta.config.battery.events.into_iter().map(|be| crate::persist::BatteryEventJson {
|
||||||
|
charge_rate: be.charge_rate,
|
||||||
|
charge_mode: be.charge_mode,
|
||||||
|
trigger: be.trigger,
|
||||||
|
}).collect(),
|
||||||
|
root: None,
|
||||||
|
},
|
||||||
|
provider: Some(crate::persist::DriverJson::AutoDetect),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Download config web method
|
||||||
|
pub fn download_new_config(sender: Sender<ApiMessage>) -> impl AsyncCallable {
|
||||||
|
let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety
|
||||||
|
let getter = move || {
|
||||||
|
let sender2 = sender.clone();
|
||||||
|
move |id: u128| {
|
||||||
|
let req_url = format!("{}/api/setting/by_id/{}", BASE_URL, id);
|
||||||
|
match ureq::get(&req_url).call() {
|
||||||
|
Ok(response) => {
|
||||||
|
let json_res: std::io::Result<community_settings_core::v1::Metadata> = response.into_json();
|
||||||
|
match json_res {
|
||||||
|
Ok(meta) => {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let callback =
|
||||||
|
move |values: Vec<super::VariantInfo>| tx.send(values).expect("download_new_config callback send failed");
|
||||||
|
sender2
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.send(ApiMessage::General(GeneralMessage::AddVariant(web_config_to_settings_json(meta), Box::new(callback))))
|
||||||
|
.expect("download_new_config send failed");
|
||||||
|
return rx.recv().expect("download_new_config callback recv failed");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Cannot parse response from `{}`: {}", req_url, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => log::warn!("Cannot get setting result from `{}`: {}", req_url, e),
|
||||||
|
}
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
super::async_utils::AsyncIsh {
|
||||||
|
trans_setter: |params| {
|
||||||
|
if let Some(Primitive::String(id)) = params.get(0) {
|
||||||
|
match id.parse::<u128>() {
|
||||||
|
Ok(id) => Ok(id),
|
||||||
|
Err(e) => Err(format!("download_new_config non-u128 string parameter 0: {} (got `{}`)", e, id))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("download_new_config missing/invalid parameter 0".to_owned())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set_get: getter,
|
||||||
|
trans_getter: |result| {
|
||||||
|
let mut output = Vec::with_capacity(result.len());
|
||||||
|
for status in result.iter() {
|
||||||
|
output.push(Primitive::Json(serde_json::to_string(status).expect("Failed to serialize variant info to JSON")));
|
||||||
|
}
|
||||||
|
output
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -299,8 +299,24 @@ fn main() -> Result<(), ()> {
|
||||||
"GENERAL_get_periodicals",
|
"GENERAL_get_periodicals",
|
||||||
api::general::get_periodicals(api_sender.clone())
|
api::general::get_periodicals(api_sender.clone())
|
||||||
)
|
)
|
||||||
|
.register_async(
|
||||||
|
"GENERAL_get_all_variants",
|
||||||
|
api::general::get_all_variants(api_sender.clone())
|
||||||
|
)
|
||||||
|
.register_async(
|
||||||
|
"GENERAL_get_current_variant",
|
||||||
|
api::general::get_current_variant(api_sender.clone())
|
||||||
|
)
|
||||||
.register_async("MESSAGE_get", message_getter)
|
.register_async("MESSAGE_get", message_getter)
|
||||||
.register_async("MESSAGE_dismiss", message_dismisser);
|
.register_async("MESSAGE_dismiss", message_dismisser)
|
||||||
|
.register_async(
|
||||||
|
"WEB_search_by_app",
|
||||||
|
api::web::search_by_app_id()
|
||||||
|
)
|
||||||
|
.register_async(
|
||||||
|
"WEB_download_new",
|
||||||
|
api::web::download_new_config(api_sender.clone())
|
||||||
|
);
|
||||||
|
|
||||||
if let Err(e) = loaded_settings.on_set() {
|
if let Err(e) = loaded_settings.on_set() {
|
||||||
e.iter()
|
e.iter()
|
||||||
|
|
|
@ -37,14 +37,24 @@ impl FileJson {
|
||||||
ron::de::from_reader(&mut file).map_err(|e| SerdeError::Serde(e.into()))
|
ron::de::from_reader(&mut file).map_err(|e| SerdeError::Serde(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_variant_or_create<P: AsRef<std::path::Path>>(path: P, setting: SettingsJson, given_name: String) -> Result<(), SerdeError> {
|
fn next_available_id(&self) -> u64 {
|
||||||
|
self.variants.keys()
|
||||||
|
.max()
|
||||||
|
.map(|k| k+1)
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_variant_or_create<P: AsRef<std::path::Path>>(path: P, mut setting: SettingsJson, given_name: String) -> Result<Self, SerdeError> {
|
||||||
if !setting.persistent {
|
if !setting.persistent {
|
||||||
return Ok(())
|
return Self::open(path)
|
||||||
}
|
}
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
||||||
let file = if path.exists() {
|
let file = if path.exists() {
|
||||||
let mut file = Self::open(path)?;
|
let mut file = Self::open(path)?;
|
||||||
|
if setting.variant == u64::MAX {
|
||||||
|
setting.variant = file.next_available_id();
|
||||||
|
}
|
||||||
file.variants.insert(setting.variant, setting);
|
file.variants.insert(setting.variant, setting);
|
||||||
file
|
file
|
||||||
} else {
|
} else {
|
||||||
|
@ -57,6 +67,7 @@ impl FileJson {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
file.save(path)
|
file.save(path)?;
|
||||||
|
Ok(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,3 +14,5 @@ pub use general::{MinMaxJson, SettingsJson};
|
||||||
pub use gpu::GpuJson;
|
pub use gpu::GpuJson;
|
||||||
|
|
||||||
pub use error::{SerdeError, RonError};
|
pub use error::{SerdeError, RonError};
|
||||||
|
|
||||||
|
pub const LATEST_VERSION: u64 = 0;
|
||||||
|
|
|
@ -89,14 +89,45 @@ impl TGeneral for General {
|
||||||
self.variant_id = id;
|
self.variant_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_variant_name(&self) -> &'_ str {
|
|
||||||
&self.variant_name
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variant_name(&mut self, name: String) {
|
fn variant_name(&mut self, name: String) {
|
||||||
self.variant_name = name;
|
self.variant_name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_variants(&self) -> Vec<crate::api::VariantInfo> {
|
||||||
|
if let Ok(file) = crate::persist::FileJson::open(self.get_path()) {
|
||||||
|
file.variants.into_iter()
|
||||||
|
.map(|(id, conf)| crate::api::VariantInfo {
|
||||||
|
id: id.to_string(),
|
||||||
|
name: conf.name,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![self.get_variant_info()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_variant(&self, variant: crate::persist::SettingsJson) -> Result<Vec<crate::api::VariantInfo>, SettingError> {
|
||||||
|
let variant_name = variant.name.clone();
|
||||||
|
crate::persist::FileJson::update_variant_or_create(self.get_path(), variant, variant_name)
|
||||||
|
.map_err(|e| SettingError {
|
||||||
|
msg: format!("failed to add variant: {}", e),
|
||||||
|
setting: SettingVariant::General,
|
||||||
|
})
|
||||||
|
.map(|file| file.variants.into_iter()
|
||||||
|
.map(|(id, conf)| crate::api::VariantInfo {
|
||||||
|
id: id.to_string(),
|
||||||
|
name: conf.name,
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_variant_info(&self) -> crate::api::VariantInfo {
|
||||||
|
crate::api::VariantInfo {
|
||||||
|
id: self.variant_id.to_string(),
|
||||||
|
name: self.variant_name.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn provider(&self) -> crate::persist::DriverJson {
|
fn provider(&self) -> crate::persist::DriverJson {
|
||||||
self.driver.clone()
|
self.driver.clone()
|
||||||
}
|
}
|
||||||
|
@ -265,9 +296,10 @@ impl Settings {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
pub fn json(&self) -> SettingsJson {
|
pub fn json(&self) -> SettingsJson {
|
||||||
|
let variant_info = self.general.get_variant_info();
|
||||||
SettingsJson {
|
SettingsJson {
|
||||||
version: LATEST_VERSION,
|
version: LATEST_VERSION,
|
||||||
name: self.general.get_variant_name().to_owned(),
|
name: variant_info.name,
|
||||||
variant: self.general.get_variant_id(),
|
variant: self.general.get_variant_id(),
|
||||||
persistent: self.general.get_persistent(),
|
persistent: self.general.get_persistent(),
|
||||||
cpus: self.cpus.json(),
|
cpus: self.cpus.json(),
|
||||||
|
|
|
@ -109,14 +109,18 @@ pub trait TGeneral: OnSet + OnResume + OnPowerEvent + Debug + Send {
|
||||||
|
|
||||||
fn name(&mut self, name: String);
|
fn name(&mut self, name: String);
|
||||||
|
|
||||||
fn get_variant_id(&self) -> u64;
|
|
||||||
|
|
||||||
fn variant_id(&mut self, id: u64);
|
fn variant_id(&mut self, id: u64);
|
||||||
|
|
||||||
fn get_variant_name(&self) -> &'_ str;
|
|
||||||
|
|
||||||
fn variant_name(&mut self, name: String);
|
fn variant_name(&mut self, name: String);
|
||||||
|
|
||||||
|
fn get_variant_id(&self) -> u64;
|
||||||
|
|
||||||
|
fn get_variants(&self) -> Vec<crate::api::VariantInfo>;
|
||||||
|
|
||||||
|
fn get_variant_info(&self) -> crate::api::VariantInfo;
|
||||||
|
|
||||||
|
fn add_variant(&self, variant: crate::persist::SettingsJson) -> Result<Vec<crate::api::VariantInfo>, SettingError>;
|
||||||
|
|
||||||
fn provider(&self) -> crate::persist::DriverJson;
|
fn provider(&self) -> crate::persist::DriverJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -349,3 +349,34 @@ export async function getPeriodicals(): Promise<Periodicals> {
|
||||||
settings_path: result[4],
|
settings_path: result[4],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StoreMetadata = {
|
||||||
|
name: string,
|
||||||
|
steam_app_id: number,
|
||||||
|
steam_user_id: number,
|
||||||
|
steam_username: string,
|
||||||
|
tags: string[],
|
||||||
|
id: string,
|
||||||
|
//config: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function searchStoreByAppId(id: number): Promise<StoreMetadata[]> {
|
||||||
|
return (await call_backend("WEB_search_by_app", [id]))[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VariantInfo = {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function storeDownloadById(id: string): Promise<VariantInfo[]> {
|
||||||
|
return (await call_backend("WEB_download_new", [id]));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllSettingVariants(): Promise<VariantInfo[]> {
|
||||||
|
return (await call_backend("GENERAL_get_all_variants", []));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCurrentSettingVariant(): Promise<VariantInfo> {
|
||||||
|
return (await call_backend("GENERAL_get_current_variant", []))[0];
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue