mirror of
https://gitlab.com/vyla-entertainment/home.git
synced 2026-11-14 14:17:25 -05:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d7e218ff6 | |||
| c19e1e2cf7 | |||
| 6b06bc8170 | |||
| 769c0dadcd | |||
| 414ef73e1d | |||
| 01c04111bd | |||
| a729bb81a3 | |||
| 547eb59709 | |||
| d774bed69b |
@@ -1,7 +1,6 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const JavaScriptObfuscator = require('javascript-obfuscator');
|
|
||||||
const AdmZip = require('adm-zip');
|
const AdmZip = require('adm-zip');
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
@@ -73,23 +72,6 @@ function cleanBuildDir() {
|
|||||||
fs.mkdirSync(BUILD_DIR, { recursive: true });
|
fs.mkdirSync(BUILD_DIR, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function obfuscateFile(filePath, isEsm = false) {
|
|
||||||
const content = fs.readFileSync(filePath, 'utf8');
|
|
||||||
const result = JavaScriptObfuscator.obfuscate(content, {
|
|
||||||
compact: true,
|
|
||||||
controlFlowFlattening: false,
|
|
||||||
deadCodeInjection: false,
|
|
||||||
debugProtection: false,
|
|
||||||
numbersToExpressions: false,
|
|
||||||
simplify: true,
|
|
||||||
stringArray: true,
|
|
||||||
stringArrayThreshold: 0.4,
|
|
||||||
stringArrayEncoding: [],
|
|
||||||
module: isEsm
|
|
||||||
});
|
|
||||||
fs.writeFileSync(filePath, result.getObfuscatedCode(), 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
function encryptCredentialsFile() {
|
function encryptCredentialsFile() {
|
||||||
const CryptoUtils = require('./crypto-utils');
|
const CryptoUtils = require('./crypto-utils');
|
||||||
|
|
||||||
@@ -170,13 +152,6 @@ function processFolder(src, dest) {
|
|||||||
if (pkg.type === 'module') isEsm = true;
|
if (pkg.type === 'module') isEsm = true;
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const filePath of jsFiles) {
|
|
||||||
try {
|
|
||||||
obfuscateFile(filePath, isEsm);
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFileChecksum(filePath) {
|
function getFileChecksum(filePath) {
|
||||||
|
|||||||
94
main.js
94
main.js
@@ -61,6 +61,9 @@ window.open = function(url) {
|
|||||||
</script></head>`
|
</script></head>`
|
||||||
);
|
);
|
||||||
res.set('Content-Type', 'text/html');
|
res.set('Content-Type', 'text/html');
|
||||||
|
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
||||||
|
res.set('Expires', '-1');
|
||||||
|
res.set('Pragma', 'no-cache');
|
||||||
res.send(injected);
|
res.send(injected);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -81,11 +84,20 @@ cmdApp.get('/__tauri_cmd/:cmd', (req, res) => {
|
|||||||
});
|
});
|
||||||
cmdApp.listen(4892, '127.0.0.1', () => debugLog("[CMD-SERVER] Dedicated command server listening on 127.0.0.1:4892"));
|
cmdApp.listen(4892, '127.0.0.1', () => debugLog("[CMD-SERVER] Dedicated command server listening on 127.0.0.1:4892"));
|
||||||
|
|
||||||
|
process.stdin.resume();
|
||||||
|
process.stdin.on('end', () => {
|
||||||
|
debugLog('stdin closed - parent died. Self-terminating...');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
process.stdin.on('error', () => {
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
debugLog(`CRITICAL UNCAUGHT EXCEPTION: ${err.stack || err.message}`);
|
debugLog(`CRITICAL UNCAUGHT EXCEPTION: ${err.stack || err.message}`);
|
||||||
if (err.code === 'EPIPE') {
|
if (err.code === 'EPIPE' || err.code === 'EOF') {
|
||||||
debugLog('Pipe to parent closed - parent process gone, exiting');
|
debugLog('Pipe to parent closed - parent process gone, exiting');
|
||||||
process.exit(1);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
try { sendToTauri('error', { message: err.message }); } catch (e) { }
|
try { sendToTauri('error', { message: err.message }); } catch (e) { }
|
||||||
});
|
});
|
||||||
@@ -96,8 +108,21 @@ process.on('unhandledRejection', (reason) => {
|
|||||||
sendToTauri('error', { message: reason && reason.message ? reason.message : String(reason) });
|
sendToTauri('error', { message: reason && reason.message ? reason.message : String(reason) });
|
||||||
});
|
});
|
||||||
|
|
||||||
const envConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/env.json'), 'utf8'));
|
function getEnvConfig() {
|
||||||
const replacementsConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/replacements.json'), 'utf8'));
|
try {
|
||||||
|
const appPath = path.join(APPDATA_DIR, 'config/env.json');
|
||||||
|
if (fs.existsSync(appPath)) return JSON.parse(fs.readFileSync(appPath, 'utf8'));
|
||||||
|
return JSON.parse(fs.readFileSync(path.join(__dirname, 'config/env.json'), 'utf8'));
|
||||||
|
} catch (e) { return {}; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReplacementsConfig() {
|
||||||
|
try {
|
||||||
|
const appPath = path.join(APPDATA_DIR, 'config/replacements.json');
|
||||||
|
if (fs.existsSync(appPath)) return JSON.parse(fs.readFileSync(appPath, 'utf8'));
|
||||||
|
return JSON.parse(fs.readFileSync(path.join(__dirname, 'config/replacements.json'), 'utf8'));
|
||||||
|
} catch (e) { return {}; }
|
||||||
|
}
|
||||||
|
|
||||||
const updateClient = new UpdateClient(APPDATA_DIR);
|
const updateClient = new UpdateClient(APPDATA_DIR);
|
||||||
|
|
||||||
@@ -106,10 +131,13 @@ async function start() {
|
|||||||
sendToTauri('status', { message: 'Loading core configuration files...' });
|
sendToTauri('status', { message: 'Loading core configuration files...' });
|
||||||
|
|
||||||
const patcher = new Patcher(APPDATA_DIR);
|
const patcher = new Patcher(APPDATA_DIR);
|
||||||
serviceManager = new ServiceManager(APPDATA_DIR, envConfig);
|
serviceManager = new ServiceManager(APPDATA_DIR, getEnvConfig());
|
||||||
|
|
||||||
const installBuild = async (release) => {
|
const installBuild = async (release) => {
|
||||||
const tempZipPath = path.join(APPDATA_DIR, 'download-temp.zip');
|
const tempZipPath = path.join(APPDATA_DIR, 'download-temp.zip');
|
||||||
|
if (fs.existsSync(tempZipPath)) {
|
||||||
|
try { fs.unlinkSync(tempZipPath); } catch (e) { }
|
||||||
|
}
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
let downloadSuccess = false;
|
let downloadSuccess = false;
|
||||||
|
|
||||||
@@ -119,9 +147,13 @@ async function start() {
|
|||||||
debugLog(`Attempting to download update (Attempt ${attempts})...`);
|
debugLog(`Attempting to download update (Attempt ${attempts})...`);
|
||||||
sendToTauri('status', { message: `Connecting to download servers (Attempt ${attempts}/3)...` });
|
sendToTauri('status', { message: `Connecting to download servers (Attempt ${attempts}/3)...` });
|
||||||
|
|
||||||
|
let lastPercent = -1;
|
||||||
await updateClient.downloadUpdate(release, tempZipPath, (downloaded, total) => {
|
await updateClient.downloadUpdate(release, tempZipPath, (downloaded, total) => {
|
||||||
const percent = total > 0 ? Math.round((downloaded / total) * 100) : 0;
|
const percent = total > 0 ? Math.round((downloaded / total) * 100) : 0;
|
||||||
sendToTauri('download-progress', { downloaded, total, percent });
|
if (percent !== lastPercent) {
|
||||||
|
lastPercent = percent;
|
||||||
|
sendToTauri('download-progress', { downloaded, total, percent });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
debugLog('Download complete. Verifying checksum...');
|
debugLog('Download complete. Verifying checksum...');
|
||||||
@@ -142,12 +174,14 @@ async function start() {
|
|||||||
|
|
||||||
debugLog('Extraction complete. Applying patches...');
|
debugLog('Extraction complete. Applying patches...');
|
||||||
sendToTauri('status', { message: 'Applying configuration patches...' });
|
sendToTauri('status', { message: 'Applying configuration patches...' });
|
||||||
patcher.applyPatches(replacementsConfig);
|
patcher.applyPatches(getReplacementsConfig());
|
||||||
|
|
||||||
sendToTauri('status', { message: 'Migrating security credentials...' });
|
sendToTauri('status', { message: 'Migrating security credentials...' });
|
||||||
patcher.bridgeCredentials(serviceManager.credentialManager);
|
patcher.bridgeCredentials(serviceManager.credentialManager);
|
||||||
patcher.saveCurrentVersion(release.version, release.sha256);
|
patcher.saveCurrentVersion(release.version, release.sha256);
|
||||||
|
|
||||||
|
serviceManager.envConfig = getEnvConfig();
|
||||||
|
|
||||||
if (fs.existsSync(tempZipPath)) {
|
if (fs.existsSync(tempZipPath)) {
|
||||||
fs.unlinkSync(tempZipPath);
|
fs.unlinkSync(tempZipPath);
|
||||||
}
|
}
|
||||||
@@ -241,9 +275,10 @@ async function start() {
|
|||||||
sendToTauri('status', { message: 'Extracting local baseline payload...' });
|
sendToTauri('status', { message: 'Extracting local baseline payload...' });
|
||||||
patcher.purgeOldCode();
|
patcher.purgeOldCode();
|
||||||
patcher.extractZipPayload(localZip);
|
patcher.extractZipPayload(localZip);
|
||||||
patcher.applyPatches(replacementsConfig);
|
patcher.applyPatches(getReplacementsConfig());
|
||||||
patcher.bridgeCredentials(serviceManager.credentialManager);
|
patcher.bridgeCredentials(serviceManager.credentialManager);
|
||||||
patcher.saveCurrentVersion("1.0.0");
|
patcher.saveCurrentVersion("1.0.0");
|
||||||
|
serviceManager.envConfig = getEnvConfig();
|
||||||
debugLog("Local payload extracted successfully.");
|
debugLog("Local payload extracted successfully.");
|
||||||
} else {
|
} else {
|
||||||
debugLog("No local payload found!");
|
debugLog("No local payload found!");
|
||||||
@@ -281,8 +316,16 @@ async function start() {
|
|||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
});
|
});
|
||||||
playerExpressApp.use(sameTabNavMiddleware(playerDir));
|
playerExpressApp.use(sameTabNavMiddleware(playerDir));
|
||||||
playerExpressApp.use(express.static(playerDir, { maxAge: '1d', etag: true }));
|
playerExpressApp.use(express.static(playerDir, {
|
||||||
playerExpressApp.get('*', (req, res) => { res.sendFile(path.join(playerDir, 'index.html')); });
|
etag: false,
|
||||||
|
setHeaders: (res) => {
|
||||||
|
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
playerExpressApp.get('*', (req, res) => {
|
||||||
|
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
||||||
|
res.sendFile(path.join(playerDir, 'index.html'));
|
||||||
|
});
|
||||||
playerExpressServer = playerExpressApp.listen(3001, '127.0.0.1');
|
playerExpressServer = playerExpressApp.listen(3001, '127.0.0.1');
|
||||||
debugLog("Player Express running on 3001");
|
debugLog("Player Express running on 3001");
|
||||||
|
|
||||||
@@ -305,9 +348,18 @@ async function start() {
|
|||||||
sendToTauri('tauri-cmd', { cmd: req.params.cmd, url: req.query.url, window: req.query.window });
|
sendToTauri('tauri-cmd', { cmd: req.params.cmd, url: req.query.url, window: req.query.window });
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
expressApp.use(sameTabNavMiddleware(frontendDir));
|
expressApp.use(sameTabNavMiddleware(frontendDir));
|
||||||
expressApp.use(express.static(frontendDir, { maxAge: '1d', etag: true }));
|
expressApp.use(express.static(frontendDir, {
|
||||||
expressApp.get('*', (req, res) => { res.sendFile(path.join(frontendDir, 'index.html')); });
|
etag: false,
|
||||||
|
setHeaders: (res) => {
|
||||||
|
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
expressApp.get('*', (req, res) => {
|
||||||
|
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
||||||
|
res.sendFile(path.join(frontendDir, 'index.html'));
|
||||||
|
});
|
||||||
|
|
||||||
const FRONTEND_PORT = 4890;
|
const FRONTEND_PORT = 4890;
|
||||||
expressApp.use('/build', express.static(path.join(__dirname, 'build')));
|
expressApp.use('/build', express.static(path.join(__dirname, 'build')));
|
||||||
@@ -331,17 +383,23 @@ process.on('exit', () => {
|
|||||||
if (serviceManager) {
|
if (serviceManager) {
|
||||||
for (const name of Object.keys(serviceManager.processes)) {
|
for (const name of Object.keys(serviceManager.processes)) {
|
||||||
const proc = serviceManager.processes[name];
|
const proc = serviceManager.processes[name];
|
||||||
if (proc && proc.child) {
|
if (proc && proc.child && proc.child.pid) {
|
||||||
try { proc.child.kill('SIGKILL'); } catch (e) { }
|
try {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
require('child_process').spawnSync('taskkill', ['/F', '/T', '/PID', proc.child.pid.toString()], { stdio: 'ignore', windowsHide: true });
|
||||||
|
} else {
|
||||||
|
process.kill(-proc.child.pid, 'SIGKILL');
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGINT', async () => {
|
process.on('SIGINT', () => {
|
||||||
if (expressServer) expressServer.close();
|
process.exit(0);
|
||||||
if (playerExpressServer) playerExpressServer.close();
|
});
|
||||||
if (serviceManager) await serviceManager.stopAll();
|
process.on('SIGTERM', () => {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vyla-home",
|
"name": "vyla-home",
|
||||||
"version": "1.1.7",
|
"version": "1.1.12",
|
||||||
"description": "Vyla Home Desktop App Launcher",
|
"description": "Vyla Home Desktop App Launcher",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -20,8 +20,7 @@
|
|||||||
"express": "^4.19.2"
|
"express": "^4.19.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2.0.0",
|
"@tauri-apps/cli": "^2.0.0"
|
||||||
"javascript-obfuscator": "^4.1.0"
|
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "com.vyla.home",
|
"appId": "com.vyla.home",
|
||||||
|
|||||||
57
patcher.js
57
patcher.js
@@ -2,7 +2,6 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const AdmZip = require('adm-zip');
|
const AdmZip = require('adm-zip');
|
||||||
const JavaScriptObfuscator = require('javascript-obfuscator');
|
|
||||||
const CryptoUtils = require('./crypto-utils');
|
const CryptoUtils = require('./crypto-utils');
|
||||||
|
|
||||||
class Patcher {
|
class Patcher {
|
||||||
@@ -64,17 +63,24 @@ class Patcher {
|
|||||||
const fullPath = path.join(this.appDataPath, dir);
|
const fullPath = path.join(this.appDataPath, dir);
|
||||||
if (fs.existsSync(fullPath)) {
|
if (fs.existsSync(fullPath)) {
|
||||||
try {
|
try {
|
||||||
fs.rmSync(fullPath, { recursive: true, force: true });
|
const trashPath = path.join(this.appDataPath, `.trash_${dir}_${Date.now()}`);
|
||||||
|
fs.renameSync(fullPath, trashPath);
|
||||||
|
fs.rmSync(trashPath, { recursive: true, force: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
const trashPath = fullPath + '.trash.' + Date.now();
|
fs.rmSync(fullPath, { recursive: true, force: true });
|
||||||
fs.renameSync(fullPath, trashPath);
|
} catch (err2) { }
|
||||||
} catch (renameErr) {
|
|
||||||
console.warn(`Could not remove or rename ${fullPath}. It will be overwritten.`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
const files = fs.readdirSync(this.appDataPath);
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.startsWith('.trash_')) {
|
||||||
|
fs.rmSync(path.join(this.appDataPath, file), { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
extractZipPayload(zipPath) {
|
extractZipPayload(zipPath) {
|
||||||
@@ -141,43 +147,6 @@ class Patcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ext === '.js') {
|
|
||||||
try {
|
|
||||||
content = JavaScriptObfuscator.obfuscate(content, {
|
|
||||||
compact: true,
|
|
||||||
controlFlowFlattening: true,
|
|
||||||
controlFlowFlatteningThreshold: 1,
|
|
||||||
deadCodeInjection: true,
|
|
||||||
deadCodeInjectionThreshold: 0.4,
|
|
||||||
debugProtection: false,
|
|
||||||
disableConsoleOutput: false,
|
|
||||||
identifierNamesGenerator: 'hexadecimal',
|
|
||||||
log: false,
|
|
||||||
numbersToExpressions: true,
|
|
||||||
renameGlobals: false,
|
|
||||||
selfDefending: true,
|
|
||||||
simplify: true,
|
|
||||||
splitStrings: true,
|
|
||||||
splitStringsChunkLength: 5,
|
|
||||||
stringArray: true,
|
|
||||||
stringArrayCallsTransform: true,
|
|
||||||
stringArrayCallsTransformThreshold: 1,
|
|
||||||
stringArrayEncoding: ['rc4'],
|
|
||||||
stringArrayIndexShift: true,
|
|
||||||
stringArrayRotate: true,
|
|
||||||
stringArrayShuffle: true,
|
|
||||||
stringArrayWrappersCount: 5,
|
|
||||||
stringArrayWrappersChainedCalls: true,
|
|
||||||
stringArrayWrappersParametersMaxCount: 5,
|
|
||||||
stringArrayWrappersType: 'function',
|
|
||||||
stringArrayThreshold: 1,
|
|
||||||
transformObjectKeys: true,
|
|
||||||
unicodeEscapeSequence: false
|
|
||||||
}).getObfuscatedCode();
|
|
||||||
modified = true;
|
|
||||||
} catch (e) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
fs.writeFileSync(fullPath, content, 'utf8');
|
fs.writeFileSync(fullPath, content, 'utf8');
|
||||||
}
|
}
|
||||||
|
|||||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@@ -4222,7 +4222,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vyla-home"
|
name = "vyla-home"
|
||||||
version = "1.1.7"
|
version = "1.1.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vyla-home"
|
name = "vyla-home"
|
||||||
version = "1.1.7"
|
version = "1.1.12"
|
||||||
description = "Vyla Home Desktop App Launcher"
|
description = "Vyla Home Desktop App Launcher"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,15 @@ fn clean_path(path: &std::path::Path) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn kill_child_process(child: &mut Child) {
|
fn kill_child_process(child: &mut Child) {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||||
|
let _ = Command::new("taskkill")
|
||||||
|
.args(&["/F", "/T", "/PID", &child.id().to_string()])
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.output();
|
||||||
|
}
|
||||||
let _ = child.kill();
|
let _ = child.kill();
|
||||||
let _ = child.wait();
|
let _ = child.wait();
|
||||||
}
|
}
|
||||||
@@ -196,17 +205,6 @@ const LINK_HANDLER_SCRIPT: &str = r##"
|
|||||||
|
|
||||||
const style = document.createElement('style');
|
const style = document.createElement('style');
|
||||||
style.innerHTML = `
|
style.innerHTML = `
|
||||||
#vyla-nav-filter-svg {
|
|
||||||
position: fixed !important;
|
|
||||||
top: 0 !important;
|
|
||||||
left: 0 !important;
|
|
||||||
width: 0 !important;
|
|
||||||
height: 0 !important;
|
|
||||||
pointer-events: none !important;
|
|
||||||
opacity: 0 !important;
|
|
||||||
z-index: -1 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#vyla-floating-nav {
|
#vyla-floating-nav {
|
||||||
position: fixed !important;
|
position: fixed !important;
|
||||||
top: 12px !important;
|
top: 12px !important;
|
||||||
@@ -218,7 +216,7 @@ const LINK_HANDLER_SCRIPT: &str = r##"
|
|||||||
display: flex !important;
|
display: flex !important;
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
background: rgba(255, 255, 255, 0.04) !important;
|
background: rgba(255, 255, 255, 0.04) !important;
|
||||||
backdrop-filter: url(#vyla-glass-filter) blur(16px) saturate(1.8) contrast(1.1) !important;
|
backdrop-filter: blur(16px) saturate(1.8) contrast(1.1) !important;
|
||||||
-webkit-backdrop-filter: blur(16px) saturate(1.8) contrast(1.1) !important;
|
-webkit-backdrop-filter: blur(16px) saturate(1.8) contrast(1.1) !important;
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45) !important;
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45) !important;
|
||||||
transition: width 0.3s cubic-bezier(0.16, 1, 0.3, 1), padding 0.3s cubic-bezier(0.16, 1, 0.3, 1) !important;
|
transition: width 0.3s cubic-bezier(0.16, 1, 0.3, 1), padding 0.3s cubic-bezier(0.16, 1, 0.3, 1) !important;
|
||||||
@@ -365,26 +363,6 @@ const LINK_HANDLER_SCRIPT: &str = r##"
|
|||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
|
|
||||||
const filterSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
||||||
filterSvg.id = 'vyla-nav-filter-svg';
|
|
||||||
filterSvg.innerHTML = `
|
|
||||||
<defs>
|
|
||||||
<filter id="vyla-glass-filter" color-interpolation-filters="sRGB" x="0%" y="0%" width="100%" height="100%">
|
|
||||||
<feImage id="vyla-nav-fe-img" x="0" y="0" width="100%" height="100%" preserveAspectRatio="none" result="map" />
|
|
||||||
<feDisplacementMap id="vyla-nav-red" in="SourceGraphic" in2="map" result="dispRed" />
|
|
||||||
<feColorMatrix in="dispRed" type="matrix" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" result="red" />
|
|
||||||
<feDisplacementMap id="vyla-nav-green" in="SourceGraphic" in2="map" result="dispGreen" />
|
|
||||||
<feColorMatrix in="dispGreen" type="matrix" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0" result="green" />
|
|
||||||
<feDisplacementMap id="vyla-nav-blue" in="SourceGraphic" in2="map" result="dispBlue" />
|
|
||||||
<feColorMatrix in="dispBlue" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0" result="blue" />
|
|
||||||
<feBlend in="red" in2="green" mode="screen" result="rg" />
|
|
||||||
<feBlend in="rg" in2="blue" mode="screen" result="output" />
|
|
||||||
<feGaussianBlur in="output" stdDeviation="0.8" />
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
`;
|
|
||||||
document.body.appendChild(filterSvg);
|
|
||||||
|
|
||||||
const nav = document.createElement('div');
|
const nav = document.createElement('div');
|
||||||
nav.id = 'vyla-floating-nav';
|
nav.id = 'vyla-floating-nav';
|
||||||
nav.innerHTML = `
|
nav.innerHTML = `
|
||||||
@@ -417,54 +395,6 @@ const LINK_HANDLER_SCRIPT: &str = r##"
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const feImage = document.getElementById('vyla-nav-fe-img');
|
|
||||||
const redChannel = document.getElementById('vyla-nav-red');
|
|
||||||
const greenChannel = document.getElementById('vyla-nav-green');
|
|
||||||
const blueChannel = document.getElementById('vyla-nav-blue');
|
|
||||||
|
|
||||||
function generateNavMap() {
|
|
||||||
const rect = nav.getBoundingClientRect();
|
|
||||||
const w = Math.round(rect.width) || 42;
|
|
||||||
const h = Math.round(rect.height) || 42;
|
|
||||||
const edgeSize = Math.min(w, h) * 0.08;
|
|
||||||
const svgContent = `
|
|
||||||
<svg viewBox="0 0 ${w} ${h}" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="vyla-nav-red-g" x1="100%" y1="0%" x2="0%" y2="0%">
|
|
||||||
<stop offset="0%" stop-color="#000000" stop-opacity="0"/>
|
|
||||||
<stop offset="100%" stop-color="red"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="vyla-nav-blue-g" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
||||||
<stop offset="0%" stop-color="#000000" stop-opacity="0"/>
|
|
||||||
<stop offset="100%" stop-color="blue"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<rect width="${w}" height="${h}" fill="black"/>
|
|
||||||
<rect width="${w}" height="${h}" rx="9999" fill="url(#vyla-nav-red-g)"/>
|
|
||||||
<rect width="${w}" height="${h}" rx="9999" fill="url(#vyla-nav-blue-g)" style="mix-blend-mode: screen;"/>
|
|
||||||
<rect x="${edgeSize}" y="${edgeSize}" width="${w - edgeSize * 2}" height="${h - edgeSize * 2}" rx="9999" fill="rgba(255,255,255,0.4)" style="filter: blur(4px);"/>
|
|
||||||
</svg>
|
|
||||||
`;
|
|
||||||
return "data:image/svg+xml," + encodeURIComponent(svgContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNavGlass() {
|
|
||||||
if (!feImage) return;
|
|
||||||
feImage.setAttribute("href", generateNavMap());
|
|
||||||
const distortionScale = -180;
|
|
||||||
redChannel.setAttribute("scale", distortionScale - 10);
|
|
||||||
greenChannel.setAttribute("scale", distortionScale);
|
|
||||||
blueChannel.setAttribute("scale", distortionScale + 10);
|
|
||||||
[redChannel, greenChannel, blueChannel].forEach((ch) => {
|
|
||||||
ch.setAttribute("xChannelSelector", "R");
|
|
||||||
ch.setAttribute("yChannelSelector", "G");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNavGlass();
|
|
||||||
const obs = new ResizeObserver(() => setTimeout(updateNavGlass, 0));
|
|
||||||
obs.observe(nav);
|
|
||||||
|
|
||||||
const tb = document.createElement('div');
|
const tb = document.createElement('div');
|
||||||
tb.id = 'tauri-custom-titlebar';
|
tb.id = 'tauri-custom-titlebar';
|
||||||
tb.innerHTML = `
|
tb.innerHTML = `
|
||||||
@@ -561,6 +491,30 @@ fn open_new_window(app: tauri::AppHandle, url: String) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn kill_orphans() -> Result<(), String> {
|
||||||
|
debug_log("[KILL] User requested orphan process kill");
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||||
|
let _ = std::process::Command::new("taskkill")
|
||||||
|
.args(&["/F", "/T", "/IM", "node.exe"])
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
let _ = std::process::Command::new("pkill")
|
||||||
|
.args(&["-9", "-f", "node"])
|
||||||
|
.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_logo_base64(app: tauri::AppHandle) -> Result<String, String> {
|
fn get_logo_base64(app: tauri::AppHandle) -> Result<String, String> {
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@@ -607,10 +561,11 @@ fn main() {
|
|||||||
win_close,
|
win_close,
|
||||||
win_drag,
|
win_drag,
|
||||||
open_new_window,
|
open_new_window,
|
||||||
get_logo_base64
|
get_logo_base64,
|
||||||
|
kill_orphans
|
||||||
])
|
])
|
||||||
.manage(NodeServices(Mutex::new(None)))
|
.manage(NodeServices(Mutex::new(None)))
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
debug_log("Tauri setup started");
|
debug_log("Tauri setup started");
|
||||||
let app_data = app.path().app_data_dir().unwrap_or_default();
|
let app_data = app.path().app_data_dir().unwrap_or_default();
|
||||||
let app_data_str = clean_path(&app_data);
|
let app_data_str = clean_path(&app_data);
|
||||||
@@ -619,7 +574,7 @@ fn main() {
|
|||||||
.title("Vyla Home")
|
.title("Vyla Home")
|
||||||
.inner_size(1280.0, 800.0)
|
.inner_size(1280.0, 800.0)
|
||||||
.decorations(false)
|
.decorations(false)
|
||||||
.transparent(true)
|
.transparent(false)
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.devtools(cfg!(debug_assertions))
|
.devtools(cfg!(debug_assertions))
|
||||||
.initialization_script("window.__VYLA_WINDOW_LABEL = 'main';")
|
.initialization_script("window.__VYLA_WINDOW_LABEL = 'main';")
|
||||||
@@ -645,6 +600,7 @@ fn main() {
|
|||||||
.current_dir(&root_dir)
|
.current_dir(&root_dir)
|
||||||
.env("APPDATA_PATH", &app_data_str)
|
.env("APPDATA_PATH", &app_data_str)
|
||||||
.env("NODE_EXE", &node_bin)
|
.env("NODE_EXE", &node_bin)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped());
|
.stderr(Stdio::piped());
|
||||||
|
|
||||||
|
|||||||
@@ -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.7",
|
"version": "1.1.12",
|
||||||
"identifier": "com.vyla.home",
|
"identifier": "com.vyla.home",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../ui"
|
"frontendDist": "../ui"
|
||||||
|
|||||||
@@ -256,24 +256,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="view-error" class="view-state">
|
|
||||||
<div class="container">
|
|
||||||
<svg class="icon icon-error" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<circle cx="12" cy="12" r="10" />
|
|
||||||
<line x1="12" y1="7" x2="12" y2="13" />
|
|
||||||
<circle cx="12" cy="17" r="1" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<h1>Couldn't start Vyla Home</h1>
|
|
||||||
<p id="error-msg"></p>
|
|
||||||
|
|
||||||
<div class="buttons">
|
|
||||||
<button class="primary" onclick="location.reload()">Try Again</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -560,7 +544,9 @@ void main() {
|
|||||||
if (progressTrack) progressTrack.style.display = "block";
|
if (progressTrack) progressTrack.style.display = "block";
|
||||||
const percent = data.percent || 0;
|
const percent = data.percent || 0;
|
||||||
if (progressFill) progressFill.style.width = percent + "%";
|
if (progressFill) progressFill.style.width = percent + "%";
|
||||||
setStatus(`Downloading update... ${percent}%`);
|
if (!currentStatusText.startsWith("Downloading update...")) {
|
||||||
|
setStatus("Downloading update...");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -581,15 +567,23 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
eventApi.listen("error", (e) => {
|
eventApi.listen("error", (e) => {
|
||||||
const data = e.payload || e;
|
setStatus("Cleaning up background processes...", "#fca5a5");
|
||||||
if (data && data.message) {
|
try {
|
||||||
setStatus(`Error: ${data.message}`, "#ef4444");
|
const core = window.__TAURI__?.core;
|
||||||
}
|
if (core) {
|
||||||
|
core.invoke("kill_orphans").finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
core.invoke("win_close").catch(() => {});
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) { }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
btnYes.addEventListener("click", () => {
|
btnYes.addEventListener("click", () => {
|
||||||
if (actionsEl) actionsEl.style.display = "none";
|
|
||||||
|
if (actionsEl) actionsEl.style.display = "none";
|
||||||
setStatus("Starting update installation...");
|
setStatus("Starting update installation...");
|
||||||
fetch("http://127.0.0.1:4891/update/yes", { method: "POST" }).catch(() => { });
|
fetch("http://127.0.0.1:4891/update/yes", { method: "POST" }).catch(() => { });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -228,7 +228,8 @@ class UpdateClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async checkForUpdates() {
|
async checkForUpdates() {
|
||||||
const releases = await this.fetchJson(RELEASES_API);
|
const cacheBuster = `?cb=${Date.now()}`;
|
||||||
|
const releases = await this.fetchJson(RELEASES_API + cacheBuster);
|
||||||
if (!releases || releases.length === 0) {
|
if (!releases || releases.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user