Fix deploy data fetching (#1494)

Co-authored-by: Uriel <imurx@proton.me>
This commit is contained in:
lucas lelievre
2025-07-16 19:29:32 +02:00
committed by GitHub
parent d45527f72e
commit 2bce1af454
10 changed files with 1103 additions and 494 deletions

1482
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,7 @@
"@tauri-apps/api": "^2.0.2",
"@tauri-apps/plugin-dialog": "^2.0.0",
"@tauri-apps/plugin-fs": "^2.0.0",
"@tauri-apps/plugin-http": "^2.5.0",
"@tauri-apps/plugin-os": "^2.0.0",
"@tauri-apps/plugin-shell": "^2.0.0",
"@tauri-apps/plugin-store": "^2.0.0",

View File

@@ -54,6 +54,7 @@ dirs-next = "2.0.0"
discord-sdk = "0.3.6"
tokio = { version = "1.37.0", features = ["time"] }
itertools = "0.13.0"
tauri-plugin-http = "2.5.0"
[target.'cfg(windows)'.dependencies]
win32job = "1"

View File

@@ -31,6 +31,14 @@
{
"identifier": "fs:scope",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
},
{
"identifier": "http:default",
"allow": [
{
"url": "https://github.com/SlimeVR/SlimeVR-Tracker-ESP/releases/download/*"
}
]
}
]
}

View File

@@ -254,6 +254,7 @@ fn setup_tauri(
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_http::init())
.invoke_handler(tauri::generate_handler![
update_window_state,
logging,

View File

@@ -105,7 +105,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
.title("SlimeVR")
.tooltip("SlimeVR")
.icon_as_template(true)
.menu_on_left_click(false)
.show_menu_on_left_click(false)
.icon(if cfg!(target_os = "macos") {
include_image!("icons/appleTrayIcon.png")
} else {
@@ -146,7 +146,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
_ => {}
})
// We don't want this as we open the window on left click
.menu_on_left_click(false)
.show_menu_on_left_click(false)
.build(app)?;
app.manage(TrayAvailable(true));

View File

@@ -1,11 +1,9 @@
import { BoardType, DeviceDataT } from 'solarxr-protocol';
import { fetch as tauriFetch } from '@tauri-apps/plugin-http';
import { cacheWrap } from './cache';
import semver from 'semver';
import { hostname, locale, platform, version } from '@tauri-apps/plugin-os';
type DeployDataJson = Map<string, string>;
type DeployData = Map<number, Date>;
export interface FirmwareRelease {
name: string;
version: string;
@@ -26,49 +24,49 @@ const hash = (str: string) => {
return (hash >>> 0) / 2 ** 32;
};
const uniqueUserKey = `${await hostname()}-${await locale()}-${platform()}-${version()}`;
const firstAsset = (assets: any[], name: string) =>
assets.find((asset: any) => asset.name === name && asset.browser_download_url);
const processDeployData = (sortedMap: [string, string][]) => {
const deployData: DeployData = new Map();
let minTime = 0;
for (const [percent, date] of sortedMap) {
const d = new Date(date);
if (d.getTime() <= minTime) return null; // Dates in the wrong order / cancel
minTime = d.getTime();
deployData.set(parseFloat(percent), new Date(date));
}
return deployData;
};
const checkUserCanUpdate = async (deployAssetUrl: string) => {
if (!deployAssetUrl) return false;
const deployDataJson: DeployData | null = await fetch(deployAssetUrl)
.then((res) => res.json())
.catch(() => null);
if (!deployDataJson) return false;
const deployDataMap = new Map(
Object.entries(deployDataJson)
) as unknown as DeployDataJson;
const sortedMap = [...deployDataMap].sort(
([a], [b]) => parseFloat(b) - parseFloat(a)
const checkUserCanUpdate = async (url: string, fwVersion: string) => {
if (!url) return true;
const deployDataJson = JSON.parse(
(await cacheWrap(
`firmware-${fwVersion}-deploy`,
() =>
tauriFetch(url)
.then((res) => res.text())
.catch(() => null),
60 * 60 * 1000
)) || 'null'
);
if (!deployDataJson) return true;
if (sortedMap.keys().find((key) => key > 1 || key <= 0)) return false; // values outside boundaries / cancel
const deployData = (
Object.entries(deployDataJson).map(([key, val]) => {
return [parseFloat(key), new Date(val as string)];
}) as [number, Date][]
).sort(([a], [b]) => a - b);
const deployData = processDeployData(sortedMap);
if (!deployData) return false;
if (deployData.find(([key]) => key > 1 || key <= 0)) return false; // values outside boundaries / cancel
if (
deployData.find(
([, date], index) =>
index > 0 && date.getTime() < deployData[index - 1][1].getTime()
)
)
return false; // Dates in the wrong order / cancel
const todayUpdateRange = deployData.find(([, date], index) => {
if (index === 0 && Date.now() < date.getTime()) return true;
return Date.now() >= date.getTime();
})?.[0];
const todayUpdateRange = deployData
.entries()
.find(([, date]) => Date.now() >= date.getTime())?.[0];
if (!todayUpdateRange) return false;
const uniqueUserKey = `${await hostname()}-${await locale()}-${platform()}-${version()}`;
// Make it so the hash change every version. Prevent the same user from getting the same delay
return hash(`${uniqueUserKey}-${version}`) <= todayUpdateRange;
return hash(`${uniqueUserKey}-${fwVersion}`) <= todayUpdateRange;
};
export async function fetchCurrentFirmwareRelease(): Promise<FirmwareRelease | null> {
@@ -87,7 +85,6 @@ export async function fetchCurrentFirmwareRelease(): Promise<FirmwareRelease | n
const processedReleses = [];
for (const release of releases) {
const fwAsset = firstAsset(release.assets, 'BOARD_SLIMEVR-firmware.bin');
const deployAsset = firstAsset(release.assets, 'deploy.json');
if (!release.assets || !fwAsset /* || release.prerelease */) continue;
let version = release.tag_name;
@@ -95,10 +92,11 @@ export async function fetchCurrentFirmwareRelease(): Promise<FirmwareRelease | n
version = version.substring(1);
}
const userCanUpdate = !deployAsset
? true
: await checkUserCanUpdate(deployAsset?.browser_download_url);
const deployAsset = firstAsset(release.assets, 'deploy.json');
const userCanUpdate = await checkUserCanUpdate(
deployAsset?.browser_download_url,
version
);
processedReleses.push({
name: release.name,
version,

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "es2022",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": ["dom", "dom.iterable", "ES2023"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,

16
pnpm-lock.yaml generated
View File

@@ -62,6 +62,9 @@ importers:
'@tauri-apps/plugin-fs':
specifier: ^2.0.0
version: 2.0.0
'@tauri-apps/plugin-http':
specifier: ^2.5.0
version: 2.5.0
'@tauri-apps/plugin-os':
specifier: ^2.0.0
version: 2.0.0
@@ -485,6 +488,7 @@ packages:
'@dword-design/functions@5.0.27':
resolution: {integrity: sha512-16A97RKtn21xLWI7Y7VhFz4WCWui+TLit/J5/l77BDpWmPi2LzgjQzDbafDDw7OUeY6GEAi/M4jdbuQhsIbSEg==}
engines: {node: '>=14'}
deprecated: Use lodash and endent
'@esbuild/aix-ppc64@0.21.5':
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
@@ -1179,6 +1183,9 @@ packages:
'@tauri-apps/api@2.0.2':
resolution: {integrity: sha512-3wSwmG+1kr6WrgAFKK5ijkNFPp8TT3FLj3YHUb5EwMO+3FxX4uWlfSWkeeBy+Kc1RsKzugtYLuuya+98Flj+3w==}
'@tauri-apps/api@2.6.0':
resolution: {integrity: sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==}
'@tauri-apps/cli-darwin-arm64@2.0.3':
resolution: {integrity: sha512-jIsbxGWS+As1ZN7umo90nkql/ZAbrDK0GBT6UsgHSz5zSwwArICsZFFwE1pLZip5yoiV5mn3TGG2c1+v+0puzQ==}
engines: {node: '>= 10'}
@@ -1250,6 +1257,9 @@ packages:
'@tauri-apps/plugin-fs@2.0.0':
resolution: {integrity: sha512-BNEeQQ5aH8J5SwYuWgRszVyItsmquRuzK2QRkVj8Z0sCsLnSvJFYI3JHRzzr3ltZGq1nMPtblrlZzuKqVzRawA==}
'@tauri-apps/plugin-http@2.5.0':
resolution: {integrity: sha512-l4M2DUIsOBIMrbj4dJZwrB4mJiB7OA/2Tj3gEbX2fjq5MOpETklJPKfDvzUTDwuq4lIKCKKykz8E8tpOgvi0EQ==}
'@tauri-apps/plugin-os@2.0.0':
resolution: {integrity: sha512-M7hG/nNyQYTJxVG/UhTKhp9mpXriwWzrs9mqDreB8mIgqA3ek5nHLdwRZJWhkKjZrnDT4v9CpA9BhYeplTlAiA==}
@@ -5405,6 +5415,8 @@ snapshots:
'@tauri-apps/api@2.0.2': {}
'@tauri-apps/api@2.6.0': {}
'@tauri-apps/cli-darwin-arm64@2.0.3':
optional: true
@@ -5456,6 +5468,10 @@ snapshots:
dependencies:
'@tauri-apps/api': 2.0.2
'@tauri-apps/plugin-http@2.5.0':
dependencies:
'@tauri-apps/api': 2.6.0
'@tauri-apps/plugin-os@2.0.0':
dependencies:
'@tauri-apps/api': 2.0.2

View File

@@ -1,4 +1,4 @@
[toolchain]
channel = "1.81"
channel = "1.82"
profile = "default"
components = ["rustc", "cargo", "clippy", "rustfmt", "rust-analyzer", "rust-src"]