From d88eade5a031eb1eb7dd25a9f7c6d6d8fb03f321 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Mon, 2 Feb 2026 20:00:53 +0100 Subject: [PATCH] Upload file updater.js via GUI --- updater.js | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 updater.js diff --git a/updater.js b/updater.js new file mode 100644 index 0000000..d8e7b76 --- /dev/null +++ b/updater.js @@ -0,0 +1,164 @@ +// updater.js - Moderner Auto-Updater für Git Manager GUI +const { app, shell } = require('electron'); +const https = require('https'); +const fs = require('fs'); +const path = require('path'); +const { spawn } = require('child_process'); + +const GITEA_API_URL = 'https://git.viper.ipv64.net/api/v1/repos/M_Viper/Git-Manager-Gui/releases'; + +class Updater { + constructor(mainWindow) { + this.mainWindow = mainWindow; + this.checkingForUpdates = false; + } + + /** + * Hauptfunktion zur Prüfung auf Updates + */ + async checkForUpdates(silent = false) { + if (this.checkingForUpdates) return; + this.checkingForUpdates = true; + + try { + const latestRelease = await this.getLatestRelease(); + if (!latestRelease) return; + + // Versionen säubern (nur Zahlen und Punkte) + const serverVer = latestRelease.tag_name.replace(/[^\d.]/g, ''); + const localVer = app.getVersion().replace(/[^\d.]/g, ''); + + console.log(`[Updater] Version-Check: Server(${serverVer}) vs Lokal(${localVer})`); + + if (this.compareVersions(serverVer, localVer) > 0) { + console.log("[Updater] Update verfügbar. Sende Daten an Renderer..."); + this.mainWindow.webContents.send('update-available', { + version: serverVer, + body: latestRelease.body, + url: latestRelease.html_url, + asset: this.findAsset(latestRelease.assets) + }); + } else { + console.log("[Updater] Anwendung ist auf dem neuesten Stand."); + } + } catch (error) { + console.error('[Updater] Fehler beim Update-Check:', error); + } finally { + this.checkingForUpdates = false; + } + } + + async getLatestRelease() { + return new Promise((resolve, reject) => { + const options = { headers: { 'User-Agent': 'GitManager-GUI-Updater' } }; + https.get(GITEA_API_URL, options, (res) => { + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => { + try { + const releases = JSON.parse(data); + resolve(Array.isArray(releases) ? releases[0] : null); + } catch (e) { reject(e); } + }); + }).on('error', reject); + }); + } + + compareVersions(v1, v2) { + const a = v1.split('.').map(Number); + const b = v2.split('.').map(Number); + for (let i = 0; i < 3; i++) { + if ((a[i] || 0) > (b[i] || 0)) return 1; + if ((a[i] || 0) < (b[i] || 0)) return -1; + } + return 0; + } + + findAsset(assets) { + if (!assets) return null; + const ext = process.platform === 'win32' ? '.exe' : '.AppImage'; + return assets.find(a => a.name.toLowerCase().endsWith(ext)); + } + + /** + * Startet den Download und führt die Installation aus + */ + async startDownload(asset) { + if (!asset || !asset.browser_download_url) { + console.error("[Updater] Kein gültiges Asset gefunden!"); + return; + } + + const tempPath = path.join(app.getPath('temp'), asset.name); + console.log(`[Updater] Download gestartet: ${asset.name} -> ${tempPath}`); + + const file = fs.createWriteStream(tempPath); + + const download = (url) => { + https.get(url, { headers: { 'User-Agent': 'Electron-Updater' } }, (res) => { + // Handle Redirects + if (res.statusCode === 301 || res.statusCode === 302) { + return download(res.headers.location); + } + + if (res.statusCode !== 200) { + console.error(`[Updater] Download-Fehler: Status ${res.statusCode}`); + return; + } + + res.pipe(file); + + file.on('finish', () => { + file.close(); + console.log("[Updater] Download abgeschlossen. Initialisiere entkoppelten Installer..."); + this.installAndQuit(tempPath); + }); + }).on('error', (err) => { + fs.unlink(tempPath, () => {}); + console.error("[Updater] Netzwerkfehler beim Download:", err); + }); + }; + + download(asset.browser_download_url); + } + + /** + * Führt den Installer entkoppelt aus und schließt die App mit Verzögerung + */ + installAndQuit(filePath) { + console.log(`[Updater] Bereite Installation vor: ${filePath}`); + + if (process.platform === 'win32') { + // Wir nutzen spawn mit detached: true, damit der Installer weiterläuft, + // wenn der Hauptprozess (Electron) beendet wird. + try { + const child = spawn('cmd.exe', ['/c', 'start', '""', filePath], { + detached: true, + stdio: 'ignore' + }); + + child.unref(); // Trennt die Referenz zum Installer + + console.log("[Updater] Installer-Prozess entkoppelt gestartet. App schließt in 2 Sek..."); + + // WICHTIG: Wir geben Windows Zeit, den Installer stabil zu laden, + // bevor wir die App schließen und die Dateisperren aufheben. + setTimeout(() => { + app.quit(); + }, 2000); + + } catch (err) { + console.error("[Updater] Kritischer Fehler beim Installer-Start:", err); + // Notfall-Versuch über shell + shell.openPath(filePath).then(() => app.quit()); + } + } else { + // Linux/AppImage + shell.openPath(filePath).then(() => { + setTimeout(() => app.quit(), 1000); + }); + } + } +} + +module.exports = Updater; \ No newline at end of file