mirror of
https://gitlab.com/vyla-entertainment/home.git
synced 2026-11-09 19:57:20 -05:00
179 lines
7.0 KiB
JavaScript
179 lines
7.0 KiB
JavaScript
const path = require('path');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const express = require('express');
|
|
const ServiceManager = require('./service-manager');
|
|
const Patcher = require('./patcher');
|
|
const UpdateClient = require('./update-client');
|
|
|
|
let serviceManager;
|
|
let expressApp;
|
|
let expressServer;
|
|
let playerExpressApp;
|
|
let playerExpressServer;
|
|
|
|
const APPDATA_DIR = process.env.APPDATA_PATH || path.join(os.homedir(), 'AppData', 'Roaming', 'vyla-home', 'vyla-home-files');
|
|
fs.mkdirSync(APPDATA_DIR, { recursive: true });
|
|
console.log(`[DEBUG] AppData directory: ${APPDATA_DIR}`);
|
|
|
|
const envConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/env.json'), 'utf8'));
|
|
const replacementsConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/replacements.json'), 'utf8'));
|
|
|
|
const updateClient = new UpdateClient(APPDATA_DIR);
|
|
|
|
process.on('unhandledRejection', (reason) => {
|
|
const message = reason && reason.message ? reason.message : String(reason);
|
|
console.error(`[ERROR] Unhandled Rejection: ${message}`);
|
|
fs.appendFileSync(path.join(APPDATA_DIR, 'crash.log'), `[${new Date().toISOString()}] UNHANDLED REJECTION: ${message}\n`, 'utf8');
|
|
});
|
|
|
|
process.on('uncaughtException', (err) => {
|
|
console.error(`[ERROR] Uncaught Exception: ${err.message}`);
|
|
fs.appendFileSync(path.join(APPDATA_DIR, 'crash.log'), `[${new Date().toISOString()}] UNCAUGHT EXCEPTION: ${err.message}\n`, 'utf8');
|
|
});
|
|
|
|
function sendToTauri(event, data) {
|
|
process.stdout.write(JSON.stringify({ event, data }) + '\n');
|
|
}
|
|
|
|
async function start() {
|
|
console.log('[DEBUG] Initializing patcher and service manager...');
|
|
const patcher = new Patcher(APPDATA_DIR);
|
|
serviceManager = new ServiceManager(APPDATA_DIR, envConfig);
|
|
|
|
const installBuild = async (release) => {
|
|
const tempZipPath = path.join(APPDATA_DIR, 'download-temp.zip');
|
|
let attempts = 0;
|
|
let downloadSuccess = false;
|
|
|
|
while (attempts < 3 && !downloadSuccess) {
|
|
attempts++;
|
|
try {
|
|
console.log(`[DEBUG] Downloading update, attempt ${attempts}...`);
|
|
await updateClient.downloadUpdate(release, tempZipPath, (downloaded, total) => {
|
|
const percent = Math.round((downloaded / total) * 100);
|
|
sendToTauri('download-progress', { downloaded, total, percent });
|
|
});
|
|
|
|
if (!patcher.verifyChecksum(tempZipPath, release.sha256)) {
|
|
throw new Error('SHA-256 checksum verification failed.');
|
|
}
|
|
|
|
console.log('[DEBUG] Extracting payload and applying patches...');
|
|
patcher.purgeOldCode();
|
|
patcher.extractZipPayload(tempZipPath);
|
|
|
|
if (!patcher.verifyExtraction()) {
|
|
throw new Error('Extraction verification failed: critical startup files missing.');
|
|
}
|
|
|
|
patcher.applyPatches(replacementsConfig);
|
|
patcher.bridgeCredentials(serviceManager.credentialManager);
|
|
patcher.saveCurrentVersion(release.version, release.sha256);
|
|
|
|
fs.unlinkSync(tempZipPath);
|
|
downloadSuccess = true;
|
|
} catch (err) {
|
|
if (fs.existsSync(tempZipPath)) fs.unlinkSync(tempZipPath);
|
|
if (attempts >= 3) {
|
|
throw new Error(`Failed to download and verify build after 3 attempts: ${err.message}`);
|
|
}
|
|
await new Promise(r => setTimeout(r, 2000));
|
|
}
|
|
}
|
|
};
|
|
|
|
try {
|
|
const isFirstRun = !patcher.verifyExtraction();
|
|
const localVersion = patcher.getCurrentVersion();
|
|
const localSha = patcher.getCurrentSha();
|
|
console.log(`[DEBUG] Local version: ${localVersion}, isFirstRun: ${isFirstRun}`);
|
|
|
|
let release = null;
|
|
try {
|
|
release = await updateClient.checkForUpdates();
|
|
} catch (e) {
|
|
console.warn('[DEBUG] Could not check for remote updates:', e.message);
|
|
}
|
|
|
|
if (release) {
|
|
const hasUpdate = release.version !== localVersion || (release.sha256 && release.sha256 !== localSha);
|
|
if (isFirstRun || hasUpdate) {
|
|
console.log('[DEBUG] Performing update install from release...');
|
|
await installBuild(release);
|
|
}
|
|
} else if (isFirstRun) {
|
|
const localZip = path.join(__dirname, 'update-payload.zip');
|
|
console.log(`[DEBUG] Checking local zip at: ${localZip} (exists: ${fs.existsSync(localZip)})`);
|
|
if (fs.existsSync(path.join(__dirname, 'update-payload.zip'))) {
|
|
const localZip = path.join(__dirname, 'update-payload.zip');
|
|
console.log('[DEBUG] Syncing local update-payload.zip to AppData...');
|
|
patcher.purgeOldCode();
|
|
patcher.extractZipPayload(localZip);
|
|
patcher.applyPatches(replacementsConfig);
|
|
patcher.bridgeCredentials(serviceManager.credentialManager);
|
|
patcher.saveCurrentVersion("1.0.0");
|
|
}
|
|
}
|
|
|
|
const verified = patcher.verifyExtraction();
|
|
console.log(`[DEBUG] Payload verification status: ${verified}`);
|
|
if (!verified) {
|
|
throw new Error('No application files found. Build the payload first using "npm run build:payload" or check your internet connection.');
|
|
}
|
|
|
|
console.log('[DEBUG] Loading credentials...');
|
|
serviceManager.credentialManager.loadCredentials();
|
|
|
|
console.log('[DEBUG] Starting auth-proxy and stream-api...');
|
|
await serviceManager.startServices([
|
|
{ name: 'auth-proxy', relativeDir: 'auth-proxy' },
|
|
{ name: 'stream-api', relativeDir: 'stream-api' },
|
|
]);
|
|
|
|
const playerDir = path.join(APPDATA_DIR, 'player');
|
|
console.log(`[DEBUG] Starting Player Express server on port 3001 from: ${playerDir}`);
|
|
playerExpressApp = express();
|
|
playerExpressApp.use(express.static(playerDir, { maxAge: '1d', etag: true }));
|
|
playerExpressApp.get('*', (req, res) => {
|
|
res.sendFile(path.join(playerDir, 'index.html'));
|
|
});
|
|
playerExpressServer = playerExpressApp.listen(3001, '0.0.0.0', () => {
|
|
console.log('[DEBUG] Player Express server listening on 3001');
|
|
});
|
|
|
|
console.log('[DEBUG] Waiting for ports [3000, 3001, 7860]...');
|
|
await serviceManager.waitForPorts([3000, 3001, 7860]);
|
|
console.log('[DEBUG] All ports ready!');
|
|
|
|
const frontendDir = path.join(APPDATA_DIR, 'frontend');
|
|
console.log(`[DEBUG] Starting Frontend Express server on port 4890 from: ${frontendDir}`);
|
|
expressApp = express();
|
|
expressApp.use(express.static(frontendDir, { maxAge: '1d', etag: true }));
|
|
expressApp.get('*', (req, res) => {
|
|
res.sendFile(path.join(frontendDir, 'index.html'));
|
|
});
|
|
|
|
const FRONTEND_PORT = 4890;
|
|
expressServer = expressApp.listen(FRONTEND_PORT, '0.0.0.0', () => {
|
|
console.log(`[DEBUG] Frontend Express server active at http://localhost:${FRONTEND_PORT}`);
|
|
sendToTauri('ready', { url: `http://localhost:${FRONTEND_PORT}` });
|
|
});
|
|
|
|
} catch (error) {
|
|
if (serviceManager) {
|
|
await serviceManager.stopAll().catch(() => { });
|
|
}
|
|
console.error('[DEBUG] Fatal error caught:', error.message);
|
|
sendToTauri('error', { message: error.message });
|
|
}
|
|
}
|
|
|
|
process.on('SIGINT', async () => {
|
|
if (expressServer) expressServer.close();
|
|
if (playerExpressServer) playerExpressServer.close();
|
|
if (serviceManager) await serviceManager.stopAll();
|
|
process.exit(0);
|
|
});
|
|
|
|
start(); |