mirror of
https://gitlab.com/vyla-entertainment/home.git
synced 2026-11-09 19:57:20 -05:00
fixed an extreme ammount of issues
This commit is contained in:
@@ -24,14 +24,14 @@ Fixing os error 32
|
||||
Stop-Process -Name "vyla-home", "node", "cargo" -Force -ErrorAction SilentlyContinue
|
||||
```
|
||||
|
||||
Fake a test?
|
||||
|
||||
```powershell
|
||||
$env:VYLA_TEST_UI="1"; npm run dev
|
||||
```
|
||||
|
||||
Fake an old local version:
|
||||
|
||||
```powershell
|
||||
'{"version":"0.0.1","sha256":null}' | Set-Content "$env:APPDATA\vyla-home\vyla-home-files\version.json"
|
||||
```
|
||||
|
||||
Fake a test?
|
||||
|
||||
```powershell
|
||||
$env:VYLA_TEST_UI="1"; npm run dev
|
||||
```
|
||||
|
||||
@@ -9,6 +9,9 @@ const SDK_NAME = '@vyla-entertainment/sdk';
|
||||
const SDK_LATEST_URL = 'https://registry.npmjs.org/@vyla-entertainment%2Fsdk/latest';
|
||||
const ALLOWED_ROOTS = ['frontend', 'player'];
|
||||
const MAX_TARBALL = 100 * 1024 * 1024;
|
||||
const MAX_FILE = 50 * 1024 * 1024;
|
||||
const LIVE_HOSTS = ['gitlab.com'];
|
||||
const REPO_API = 'https://gitlab.com/api/v4/projects/vyla-entertainment%2F';
|
||||
|
||||
const verParts = (v) => String(v || '').replace(/^v/, '').split(/[.\-+]/).map((n) => parseInt(n, 10) || 0);
|
||||
const cmpVer = (a, b) => {
|
||||
@@ -22,6 +25,7 @@ const cmpVer = (a, b) => {
|
||||
|
||||
const sha256Buf = (buf) => crypto.createHash('sha256').update(buf).digest('hex');
|
||||
const sha256File = (p) => sha256Buf(fs.readFileSync(p));
|
||||
const gitBlobSha = (buf) => crypto.createHash('sha1').update(Buffer.concat([Buffer.from(`blob ${buf.length}\0`), buf])).digest('hex');
|
||||
|
||||
function fetchBuffer(url, { headers = {}, maxBytes, hosts, redirects = 0 }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -114,7 +118,6 @@ class FileUpdater {
|
||||
this.appDataPath = appDataPath;
|
||||
this.updateClient = updateClient;
|
||||
this.onProgress = null;
|
||||
this.pending = null;
|
||||
this.statePath = path.join(appDataPath, 'file-state.json');
|
||||
}
|
||||
|
||||
@@ -296,6 +299,99 @@ class FileUpdater {
|
||||
return changed ? Buffer.from(text, 'utf8') : buf;
|
||||
}
|
||||
|
||||
async repoTree(root) {
|
||||
const out = [];
|
||||
for (let page = 1; page <= 50; page++) {
|
||||
const url = `${REPO_API}${root}/repository/tree?recursive=true&per_page=100&ref=main&page=${page}`;
|
||||
const raw = await fetchBuffer(url, { headers: { Accept: 'application/json' }, maxBytes: 5 * 1024 * 1024, hosts: LIVE_HOSTS });
|
||||
const items = JSON.parse(raw.toString('utf8'));
|
||||
if (!Array.isArray(items) || items.length === 0) break;
|
||||
out.push(...items);
|
||||
if (items.length < 100) break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
fetchRepoFile(rel) {
|
||||
const parts = rel.split('/');
|
||||
const root = parts.shift();
|
||||
const file = encodeURIComponent(parts.join('/'));
|
||||
return fetchBuffer(`${REPO_API}${root}/repository/files/${file}/raw?ref=main`, { maxBytes: MAX_FILE, hosts: LIVE_HOSTS });
|
||||
}
|
||||
|
||||
async checkRepos() {
|
||||
const state = this.readState();
|
||||
const changes = [];
|
||||
for (const root of ALLOWED_ROOTS) {
|
||||
const tree = await this.repoTree(root);
|
||||
for (const item of tree) {
|
||||
if (item.type !== 'blob') continue;
|
||||
const rel = safeRel(`${root}/${item.path}`);
|
||||
if (!rel || !/^[0-9a-f]{40}$/.test(item.id)) continue;
|
||||
const full = this.target(rel);
|
||||
if (fs.existsSync(full) && (state.base[rel] === item.id || gitBlobSha(fs.readFileSync(full)) === item.id)) continue;
|
||||
changes.push({ path: rel, sha256: item.id, size: 0 });
|
||||
}
|
||||
}
|
||||
return { seq: state.seq === null ? 0 : state.seq, changes, removed: [] };
|
||||
}
|
||||
|
||||
async applyLive(info, replacements, onFile) {
|
||||
const staged = [];
|
||||
const total = info.changes.length;
|
||||
let next = 0;
|
||||
let finished = 0;
|
||||
let failure = null;
|
||||
const worker = async () => {
|
||||
while (next < total && !failure) {
|
||||
const c = info.changes[next++];
|
||||
try {
|
||||
const data = await this.fetchRepoFile(c.path);
|
||||
if (gitBlobSha(data) !== c.sha256) throw new Error(`Checksum mismatch: ${c.path}`);
|
||||
staged.push({ c, data: this.applyReplacements(c.path, data, replacements) });
|
||||
finished++;
|
||||
if (onFile) onFile(c.path, finished, total);
|
||||
} catch (e) {
|
||||
failure = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
await Promise.all(Array.from({ length: Math.min(8, total) }, worker));
|
||||
if (failure) throw failure;
|
||||
|
||||
const done = [];
|
||||
try {
|
||||
for (const s of staged) {
|
||||
const full = this.target(s.c.path);
|
||||
const prev = fs.existsSync(full) ? fs.readFileSync(full) : null;
|
||||
fs.mkdirSync(path.dirname(full), { recursive: true });
|
||||
fs.writeFileSync(`${full}.vyla-tmp`, s.data);
|
||||
fs.renameSync(`${full}.vyla-tmp`, full);
|
||||
done.push({ full, prev });
|
||||
}
|
||||
for (const rel of info.removed) {
|
||||
const full = this.target(rel);
|
||||
if (!fs.existsSync(full)) continue;
|
||||
const prev = fs.readFileSync(full);
|
||||
fs.rmSync(full, { force: true });
|
||||
done.push({ full, prev });
|
||||
}
|
||||
} catch (e) {
|
||||
for (const d of done.reverse()) {
|
||||
try {
|
||||
if (d.prev === null) fs.rmSync(d.full, { force: true });
|
||||
else fs.writeFileSync(d.full, d.prev);
|
||||
} catch (x) { }
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
const state = this.readState();
|
||||
for (const s of staged) state.base[s.c.path] = s.c.sha256;
|
||||
for (const rel of info.removed) delete state.base[rel];
|
||||
this.writeState({ seq: info.seq, base: state.base });
|
||||
}
|
||||
|
||||
installedSdk() {
|
||||
const roots = [this.appDataPath, path.join(this.appDataPath, 'frontend'), path.join(this.appDataPath, 'player')];
|
||||
const found = [];
|
||||
|
||||
17
main.js
17
main.js
@@ -242,7 +242,7 @@ async function start() {
|
||||
const versionNewer = isNewer(release.version, localVersion);
|
||||
const sameVerNewBuild = normalizeVer(release.version) === normalizeVer(localVersion) &&
|
||||
!!release.sha256 && !!localSha && release.sha256 !== localSha;
|
||||
const hasUpdate = versionNewer;
|
||||
const hasUpdate = versionNewer || sameVerNewBuild;
|
||||
|
||||
if (isFirstRun) {
|
||||
debugLog("First run with no local payload. Installing remote build...");
|
||||
@@ -269,12 +269,14 @@ async function start() {
|
||||
patcher.bridgeCredentials(serviceManager.credentialManager);
|
||||
|
||||
let ver = "1.0.0";
|
||||
let manSha = null;
|
||||
try {
|
||||
const man = JSON.parse(fs.readFileSync(path.join(__dirname, 'manifest.json'), 'utf8'));
|
||||
ver = man.version || ver;
|
||||
manSha = man.sha256 || null;
|
||||
} catch (e) { }
|
||||
|
||||
patcher.saveCurrentVersion(ver);
|
||||
patcher.saveCurrentVersion(ver, manSha);
|
||||
serviceManager.envConfig = getEnvConfig();
|
||||
debugLog("Local payload extracted successfully.");
|
||||
} else {
|
||||
@@ -289,20 +291,21 @@ async function start() {
|
||||
|
||||
sendToTauri('status', { message: 'Checking UI files and SDK...' });
|
||||
const [filesCheck, sdkCheck] = await Promise.all([
|
||||
withTimeout(fileUpdater.checkFiles().catch((e) => {
|
||||
debugLog(`File check failed: ${e.message}`);
|
||||
withTimeout(fileUpdater.checkRepos().catch((e) => {
|
||||
debugLog(`Live file check failed: ${e.message}`);
|
||||
return null;
|
||||
}), 180000),
|
||||
}), 60000),
|
||||
withTimeout(fileUpdater.checkSdk().catch((e) => {
|
||||
debugLog(`SDK check failed: ${e.message}`);
|
||||
return null;
|
||||
}), 12000)
|
||||
]);
|
||||
debugLog(`Repo check result: ${filesCheck ? `${filesCheck.changes.length} change(s)` : 'failed or timed out'}`);
|
||||
|
||||
if (filesCheck && filesCheck.changes.length > 0) {
|
||||
if (filesCheck && (filesCheck.changes.length > 0 || filesCheck.removed.length > 0)) {
|
||||
sendToTauri('status', { message: `Updating ${filesCheck.changes.length} file(s)...` });
|
||||
try {
|
||||
await fileUpdater.applyFiles(filesCheck.changes, filesCheck.seq, getReplacementsConfig(), (file, done, total) => {
|
||||
await fileUpdater.applyLive(filesCheck, getReplacementsConfig(), (file, done, total) => {
|
||||
sendToTauri('extract-progress', { file, done, total, percent: Math.round((done / total) * 100) });
|
||||
});
|
||||
sendToTauri('status', { message: `Updated ${filesCheck.changes.length} file(s).` });
|
||||
|
||||
1
package-lock.json
generated
1
package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"@tauri-apps/api": "2.12.1",
|
||||
"@tauri-apps/plugin-opener": "2.7.0",
|
||||
"@tauri-apps/plugin-process": "2.4.0",
|
||||
"@tauri-apps/plugin-updater": "^2",
|
||||
"@tauri-apps/plugin-shell": "2.5.0",
|
||||
"@vyla-entertainment/sdk": "^1.20.0",
|
||||
"adm-zip": "^0.6.1",
|
||||
|
||||
45
sign-ui.js
45
sign-ui.js
@@ -1,45 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const roots = { frontend: process.argv[2], player: process.argv[3] };
|
||||
const outDir = path.join(__dirname, 'ui-updates');
|
||||
const filesDir = path.join(outDir, 'files');
|
||||
const keyPath = process.env.VYLA_UI_KEY || path.join(__dirname, 'ui-private.pem');
|
||||
|
||||
fs.rmSync(outDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(filesDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(outDir, '.gitattributes'), '* -text\n');
|
||||
|
||||
const files = [];
|
||||
const walk = (src, rel) => {
|
||||
for (const name of fs.readdirSync(src)) {
|
||||
if (name === 'node_modules' || name === '.git') continue;
|
||||
const full = path.join(src, name);
|
||||
const r = `${rel}/${name}`;
|
||||
const st = fs.lstatSync(full);
|
||||
if (st.isDirectory()) {
|
||||
walk(full, r);
|
||||
} else if (st.isFile()) {
|
||||
const data = fs.readFileSync(full);
|
||||
const dest = path.join(filesDir, ...r.split('/'));
|
||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||
fs.writeFileSync(dest, data);
|
||||
files.push({ path: r, sha256: crypto.createHash('sha256').update(data).digest('hex'), size: data.length });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const [root, src] of Object.entries(roots)) {
|
||||
if (!src || !fs.existsSync(src)) {
|
||||
console.error(`Missing source dir for ${root}`);
|
||||
process.exit(1);
|
||||
}
|
||||
walk(path.resolve(src), root);
|
||||
}
|
||||
|
||||
const raw = Buffer.from(JSON.stringify({ seq: Math.floor(Date.now() / 1000), files }));
|
||||
const sig = crypto.sign(null, raw, crypto.createPrivateKey(fs.readFileSync(keyPath)));
|
||||
fs.writeFileSync(path.join(outDir, 'files-manifest.json'), raw);
|
||||
fs.writeFileSync(path.join(outDir, 'files-manifest.sig'), sig.toString('base64'));
|
||||
console.log(`${files.length} files signed`);
|
||||
BIN
source.png
BIN
source.png
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB |
Reference in New Issue
Block a user