Compare commits

...

2 Commits

Author SHA1 Message Date
ba8266bd86 fixed files not downloading properly 2026-09-07 11:21:39 -07:00
bb39db0272 v1.1.34 2026-09-06 19:37:27 -07:00
10 changed files with 89 additions and 29 deletions

4
.gitignore vendored
View File

@@ -35,4 +35,6 @@ yarn-error.log*
*.tmp *.tmp
*.temp *.temp
download-temp.zip download-temp.zip
.env

View File

@@ -172,7 +172,6 @@ function validatePayload(zipPath) {
zip.extractAllTo(TEMP_EXTRACT_DIR, true); zip.extractAllTo(TEMP_EXTRACT_DIR, true);
const requiredFiles = [ const requiredFiles = [
'player/server.js',
'frontend/index.html' 'frontend/index.html'
]; ];

48
main.js
View File

@@ -216,18 +216,20 @@ async function start() {
debugLog(`Is First Run: ${isFirstRun}, Local Version: ${localVersion}, Local SHA: ${localSha}`); debugLog(`Is First Run: ${isFirstRun}, Local Version: ${localVersion}, Local SHA: ${localSha}`);
function findLocalZip() { function findLocalZip() {
const searchRoots = [__dirname, process.cwd()]; const searchRoots = [__dirname, process.cwd(), path.join(__dirname, '../../..')];
for (const root of searchRoots) { for (const root of searchRoots) {
let cur = root; let cur = root;
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
const zp = path.join(cur, 'update-payload.zip'); try {
if (fs.existsSync(zp)) { const zp = path.join(cur, 'update-payload.zip');
const mp = path.join(cur, 'manifest.json'); if (fs.existsSync(zp)) {
return { zipPath: zp, manifestPath: fs.existsSync(mp) ? mp : null }; const mp = path.join(cur, 'manifest.json');
} return { zipPath: zp, manifestPath: fs.existsSync(mp) ? mp : null };
const parent = path.dirname(cur); }
if (parent === cur) break; const parent = path.dirname(cur);
cur = parent; if (parent === cur) break;
cur = parent;
} catch (e) { }
} }
} }
return null; return null;
@@ -309,12 +311,24 @@ async function start() {
debugLog("Update available. Prompting user..."); debugLog("Update available. Prompting user...");
sendToTauri('status', { message: `New update available (${release.version}). Awaiting confirmation...` }); sendToTauri('status', { message: `New update available (${release.version}). Awaiting confirmation...` });
try {
if (process.platform === 'win32') {
require('child_process').spawnSync('powershell.exe', [
'-NoProfile', '-NonInteractive', '-Command',
`Get-NetTCPConnection -LocalPort 4891 -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }`
], { stdio: 'ignore', windowsHide: true });
} else {
require('child_process').spawnSync('fuser', ['-k', '-9', `4891/tcp`], { stdio: 'ignore' });
}
} catch (e) { }
const tempApp = express(); const tempApp = express();
let userDecision = null; let userDecision = null;
tempApp.use((req, res, next) => { tempApp.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'); res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
res.header('Access-Control-Allow-Headers', '*'); res.header('Access-Control-Allow-Headers', '*');
res.header('Connection', 'close');
if (req.method === 'OPTIONS') return res.sendStatus(200); if (req.method === 'OPTIONS') return res.sendStatus(200);
next(); next();
}); });
@@ -326,15 +340,27 @@ async function start() {
const tempServer = await new Promise((resolve) => { const tempServer = await new Promise((resolve) => {
const s = tempApp.listen(4891, '127.0.0.1', () => resolve(s)); const s = tempApp.listen(4891, '127.0.0.1', () => resolve(s));
s.on('error', (err) => {
debugLog(`Update server bind error: ${err.message}`);
resolve(null);
});
}); });
sendToTauri('update-available', { current: localVersion, latest: release.version }); sendToTauri('update-available', { current: localVersion, latest: release.version });
let waitLoops = 0;
while (userDecision === null) { while (userDecision === null) {
if (!tempServer && waitLoops > 400) {
userDecision = false;
break;
}
waitLoops++;
await new Promise(r => setTimeout(r, 25)); await new Promise(r => setTimeout(r, 25));
} }
await new Promise((resolve) => tempServer.close(resolve)); if (tempServer) {
tempServer.close();
}
if (userDecision) { if (userDecision) {
debugLog("User accepted update."); debugLog("User accepted update.");
@@ -374,7 +400,7 @@ async function start() {
sendToTauri('status', { message: 'Validating critical components...' }); sendToTauri('status', { message: 'Validating critical components...' });
if (!patcher.verifyExtraction()) { if (!patcher.verifyExtraction()) {
throw new Error('No application files found. Ensure you run "npm run build:payload" to generate the local development payload.'); throw new Error('No application files found. Ensure you ran "npm run build:payload" and the extraction succeeded.');
} }
sendToTauri('status', { message: 'Loading encrypted credentials...' }); sendToTauri('status', { message: 'Loading encrypted credentials...' });

View File

@@ -1,6 +1,6 @@
{ {
"name": "vyla-home", "name": "vyla-home",
"version": "1.1.33", "version": "1.1.34",
"description": "Vyla Home Desktop App Launcher", "description": "Vyla Home Desktop App Launcher",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@@ -84,8 +84,25 @@ class Patcher {
} }
extractZipPayload(zipPath) { extractZipPayload(zipPath) {
const AdmZip = require('adm-zip');
const zip = new AdmZip(zipPath); const zip = new AdmZip(zipPath);
const entries = zip.getEntries();
if (!entries || entries.length === 0) {
throw new Error('Downloaded archive is empty, invalid, or truncated.');
}
zip.extractAllTo(this.appDataPath, true); zip.extractAllTo(this.appDataPath, true);
const testPath = path.join(this.appDataPath, 'frontend', 'index.html');
if (!fs.existsSync(testPath)) {
const { execSync } = require('child_process');
if (process.platform === 'win32') {
execSync(`powershell.exe -NoProfile -NonInteractive -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${this.appDataPath}' -Force"`, { windowsHide: true });
} else {
execSync(`unzip -o "${zipPath}" -d "${this.appDataPath}"`);
}
}
} }
verifyChecksum(zipPath, expectedSha256) { verifyChecksum(zipPath, expectedSha256) {
@@ -99,8 +116,8 @@ class Patcher {
verifyExtraction() { verifyExtraction() {
const requiredFiles = [ const requiredFiles = [
'player/server.js', 'frontend/index.html',
'frontend/index.html' 'player/server.js'
]; ];
for (const file of requiredFiles) { for (const file of requiredFiles) {
@@ -112,12 +129,16 @@ class Patcher {
if (stat.size === 0) { if (stat.size === 0) {
return false; return false;
} }
const sampleSize = Math.min(stat.size, 4096); try {
const fd = fs.openSync(fullPath, 'r'); const sampleSize = Math.min(stat.size, 4096);
const buffer = Buffer.alloc(sampleSize); const fd = fs.openSync(fullPath, 'r');
fs.readSync(fd, buffer, 0, sampleSize, 0); const buffer = Buffer.alloc(sampleSize);
fs.closeSync(fd); fs.readSync(fd, buffer, 0, sampleSize, 0);
if (buffer.every(byte => byte === 0)) { fs.closeSync(fd);
if (buffer.every(byte => byte === 0)) {
return false;
}
} catch (e) {
return false; return false;
} }
} }

2
src-tauri/Cargo.lock generated
View File

@@ -4238,7 +4238,7 @@ dependencies = [
[[package]] [[package]]
name = "vyla-home" name = "vyla-home"
version = "1.1.33" version = "1.1.34"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"objc2 0.5.2", "objc2 0.5.2",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "vyla-home" name = "vyla-home"
version = "1.1.33" version = "1.1.34"
description = "Vyla Home Desktop App Launcher" description = "Vyla Home Desktop App Launcher"
edition = "2021" edition = "2021"

View File

@@ -831,7 +831,12 @@ fn main() {
let msg = match serde_json::from_str::<BackendMessage>(&line) { let msg = match serde_json::from_str::<BackendMessage>(&line) {
Ok(m) => m, Ok(m) => m,
Err(_) => continue, Err(_) => {
if !line.trim().is_empty() {
debug_log(&format!("[NODE RAW] {}", line));
}
continue;
}
}; };
debug_log(&format!("[MAIN-DEBUG] Received event: {}", msg.event)); debug_log(&format!("[MAIN-DEBUG] Received event: {}", msg.event));

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Vyla Home", "productName": "Vyla Home",
"version": "1.1.33", "version": "1.1.34",
"identifier": "com.vyla.home", "identifier": "com.vyla.home",
"build": { "build": {
"frontendDist": "../ui" "frontendDist": "../ui"

View File

@@ -136,7 +136,14 @@ class UpdateClient {
}); });
file.on('finish', () => { file.on('finish', () => {
file.close(resolve); file.close(() => {
if (totalSize && downloadedSize < totalSize) {
fs.unlink(destPath, () => { });
reject(new Error(`Download truncated: Received ${downloadedSize} of ${totalSize} bytes. Connection dropped prematurely.`));
} else {
resolve();
}
});
}); });
file.on('error', (err) => { file.on('error', (err) => {
@@ -150,10 +157,10 @@ class UpdateClient {
reject(err); reject(err);
}); });
req.setTimeout(120000, () => { req.setTimeout(900000, () => {
req.destroy(); req.destroy();
fs.unlink(destPath, () => { }); fs.unlink(destPath, () => { });
reject(new Error('Download timeout')); reject(new Error('Download timeout (15 minutes reached)'));
}); });
}; };