'use strict'; const { dialog } = require('electron'); const { autoUpdater } = require('electron-updater'); const { useGiteaReleases, getGiteaUpdaterFeedBase } = require('./gitea-release'); /** * @param {import('electron').App} app * @param {() => import('electron').BrowserWindow | null} getMainWindow * @param {{ * updateFeedUrl?: string, * githubOwner?: string, * githubRepo?: string, * githubToken?: string, * giteaToken?: string, * allowGithubLauncherUpdates?: boolean, * config?: object, * }} opts */ async function setupAutoUpdater(app, getMainWindow, opts = {}) { if (!app.isPackaged) { return { checkNow: async () => ({ skipped: true, reason: 'development build' }), }; } const ghToken = String(opts.githubToken || '').trim(); const giteaTok = String(opts.giteaToken || '').trim(); const envGeneric = String(process.env.LAUNCHER_UPDATE_URL || '').trim(); const configGeneric = String(opts.updateFeedUrl || '').trim(); let genericUrl = envGeneric || configGeneric; let genericAuthHeader = ''; if (!genericUrl && opts.config && useGiteaReleases(opts.config)) { const gfb = await getGiteaUpdaterFeedBase(opts.config); if (gfb && gfb.url) { genericUrl = gfb.url; const t = String(gfb.token || giteaTok || '').trim(); if (t) genericAuthHeader = `token ${t}`; } } else if (genericUrl) { if (giteaTok) genericAuthHeader = `token ${giteaTok}`; else if (ghToken) genericAuthHeader = `Bearer ${ghToken}`; } const owner = String(opts.githubOwner || '').trim(); const repo = String(opts.githubRepo || '').trim(); let feedConfigured = false; if (genericUrl) { const base = genericUrl.replace(/\/?$/, '/'); autoUpdater.setFeedURL({ provider: 'generic', url: base, }); if (genericAuthHeader) { autoUpdater.requestHeaders = { ...autoUpdater.requestHeaders, Authorization: genericAuthHeader, }; } feedConfigured = true; } else if (opts.allowGithubLauncherUpdates && ghToken && owner && repo) { autoUpdater.setFeedURL({ provider: 'github', owner, repo, private: true, token: ghToken, }); feedConfigured = true; } if (!feedConfigured) { const reason = 'No update channel configured. Set launcher.json → update_feed_url (HTTPS folder with latest.yml), ' + 'or fill gitea.base_url/owner/repo (+ GITEA_TOKEN for private), ' + 'or set launcher_updates_from_github to true with GITHUB_TOKEN for private GitHub release feeds.'; return { checkNow: async () => ({ skipped: true, reason }), }; } autoUpdater.autoDownload = true; autoUpdater.autoInstallOnAppQuit = true; const send = (msg) => { const w = getMainWindow(); if (w && !w.isDestroyed()) { w.webContents.send('launcher:progress', msg); } }; autoUpdater.on('checking-for-update', () => send('Checking for launcher updates…')); autoUpdater.on('update-available', (info) => { send(`Launcher update available: ${info.version}`); }); autoUpdater.on('update-not-available', () => {}); autoUpdater.on('error', (err) => { const m = (err && (err.message || String(err))) || ''; if (/404|releases\.atom|HttpError:\s*404/i.test(m)) { send( 'Launcher update: 404 (no latest.yml or wrong URL). For Gitea use gitea.* + token, or set update_feed_url. ' + 'For private GitHub set GITHUB_TOKEN.' ); return; } if (m && !/net::ERR|ENOTFOUND|ETIMEDOUT/i.test(m)) { send(`Launcher update: ${m.slice(0, 400)}`); } }); autoUpdater.on('download-progress', (p) => { const pct = Math.round(p.percent || 0); send(`Launcher update download: ${pct}%`); }); autoUpdater.on('update-downloaded', async (info) => { const win = getMainWindow(); const r = await dialog.showMessageBox(win || undefined, { type: 'info', title: 'Launcher update', message: `Version ${info.version} is ready to install.`, detail: 'Restart the launcher now to finish. You can finish patching WoW after restart.', buttons: ['Restart now', 'Later'], defaultId: 0, cancelId: 1, noLink: true, }); if (r.response === 0) { autoUpdater.quitAndInstall(false, true); } }); const checkNow = async () => { const r = await autoUpdater.checkForUpdates(); return { ok: true, updateInfo: r && r.updateInfo }; }; const tick = () => { checkNow().catch(() => {}); }; setTimeout(tick, 5000); setInterval(tick, 6 * 60 * 60 * 1000); return { checkNow }; } module.exports = { setupAutoUpdater };