feat: auto-install silencieux des mises à jour (téléchargement + relance auto)

This commit is contained in:
s
2026-06-30 11:50:15 +02:00
parent 13a5ede027
commit 6e306a9bed
4 changed files with 154 additions and 4 deletions

70
main.js
View File

@@ -103,6 +103,70 @@ function resolveAssetUrl(release) {
return asset ? asset.browser_download_url : null;
}
function sendUpdateProgress(percent, status, error) {
if (mainWindow && !mainWindow.isDestroyed())
mainWindow.webContents.send('update-progress', { percent, status, error });
}
function downloadFileWithProgress(url, destPath, onProgress) {
return new Promise((resolve, reject) => {
const follow = (currentUrl, redirects) => {
if (redirects > 5) return reject(new Error('Trop de redirections'));
const mod = currentUrl.startsWith('https') ? require('https') : require('http');
const req = mod.get(currentUrl, { timeout: 120000, headers: { 'User-Agent': 'farm-tracker-updater' } }, (res) => {
if ([301, 302, 307, 308].includes(res.statusCode))
return follow(res.headers.location, redirects + 1);
if (res.statusCode !== 200)
return reject(new Error(`HTTP ${res.statusCode}`));
const total = parseInt(res.headers['content-length'] || '0', 10);
let downloaded = 0;
const file = fs.createWriteStream(destPath);
res.on('data', chunk => {
downloaded += chunk.length;
file.write(chunk);
if (total > 0 && onProgress) onProgress(Math.round(downloaded / total * 100));
});
res.on('end', () => file.close(() => resolve(destPath)));
res.on('error', err => { file.close(); try { fs.unlinkSync(destPath); } catch (_) {} reject(err); });
});
req.on('error', reject);
req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
};
follow(url, 0);
});
}
async function downloadAndInstall(downloadUrl, version) {
if (!app.isPackaged) {
dialog.showMessageBox(mainWindow, {
type: 'info', title: 'Mode développement',
message: 'Auto-install non disponible en mode dev.',
buttons: ['OK']
});
return;
}
const ext = process.platform === 'win32' ? '.exe' : '.AppImage';
const tmpPath = path.join(app.getPath('temp'), `farm-tracker-${version}${ext}`);
try {
sendUpdateProgress(0, 'downloading');
await downloadFileWithProgress(downloadUrl, tmpPath, pct => sendUpdateProgress(pct, 'downloading'));
sendUpdateProgress(100, 'installing');
if (process.platform === 'linux') {
fs.chmodSync(tmpPath, 0o755);
const current = process.env.APPIMAGE;
if (current) { fs.copyFileSync(tmpPath, current); fs.unlinkSync(tmpPath); app.relaunch(); }
else app.relaunch({ execPath: tmpPath });
app.exit(0);
} else if (process.platform === 'win32') {
const { spawn } = require('child_process');
spawn(tmpPath, ['/S'], { detached: true, stdio: 'ignore' }).unref();
app.quit();
}
} catch (e) {
sendUpdateProgress(0, 'error', e.message);
}
}
async function showUpdateDialog(version, downloadUrl, ignoreSnooze = false) {
const current = app.getVersion();
if (!isNewerVersion(version, current)) return false;
@@ -117,14 +181,14 @@ async function showUpdateDialog(version, downloadUrl, ignoreSnooze = false) {
type: 'info',
title: 'Mise à jour disponible',
message: `Version ${version} disponible`,
detail: `Version actuelle : ${current}`,
buttons: ['Télécharger', 'Me rappeler dans...'],
detail: `Version actuelle : ${current}\nL'application se relancera automatiquement après la mise à jour.`,
buttons: ['Installer maintenant', 'Me rappeler dans...'],
defaultId: 0,
cancelId: 1
});
if (response === 0) {
shell.openExternal(downloadUrl);
downloadAndInstall(downloadUrl, version);
return true;
}