UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

322 lines (261 loc) 9.64 kB
import log from 'electron-log'; import { autoUpdater } from 'electron-updater'; import { isDev } from '@/const/env'; import { UPDATE_CHANNEL as channel, updaterConfig } from '@/modules/updater/configs'; import { createLogger } from '@/utils/logger'; import type { App as AppCore } from './App'; // Create logger const logger = createLogger('core:UpdaterManager'); export class UpdaterManager { private app: AppCore; private checking: boolean = false; private downloading: boolean = false; private updateAvailable: boolean = false; private isManualCheck: boolean = false; constructor(app: AppCore) { this.app = app; // 设置日志 log.transports.file.level = 'info'; autoUpdater.logger = log; logger.debug(`[Updater] Log file should be at: ${log.transports.file.getFile().path}`); // 打印路径 } get mainWindow() { return this.app.browserManager.getMainWindow(); } public initialize = async () => { logger.debug('Initializing UpdaterManager'); // If updates are disabled and in production environment, don't initialize updates if (!updaterConfig.enableAppUpdate && !isDev) { logger.info('App updates are disabled, skipping updater initialization'); return; } // Configure autoUpdater autoUpdater.autoDownload = false; // Set to false, we'll control downloads manually autoUpdater.autoInstallOnAppQuit = false; autoUpdater.channel = channel; autoUpdater.allowPrerelease = channel !== 'stable'; autoUpdater.allowDowngrade = false; // Enable test mode in development environment if (isDev) { logger.info(`Running in dev mode, forcing update check, channel: ${autoUpdater.channel}`); // Allow testing updates in development environment autoUpdater.forceDevUpdateConfig = true; } // Register events this.registerEvents(); // If auto-check for updates is configured, set up periodic checks if (updaterConfig.app.autoCheckUpdate) { // Delay update check by 1 minute after startup to avoid network instability setTimeout(() => this.checkForUpdates(), 60 * 1000); // Set up periodic checks setInterval(() => this.checkForUpdates(), updaterConfig.app.checkUpdateInterval); } // Log the channel and allowPrerelease values logger.debug( `Initialized with channel: ${autoUpdater.channel}, allowPrerelease: ${autoUpdater.allowPrerelease}`, ); logger.info('UpdaterManager initialization completed'); }; /** * Check for updates * @param manual whether this is a manual check for updates */ public checkForUpdates = async ({ manual = false }: { manual?: boolean } = {}) => { if (this.checking || this.downloading) return; this.checking = true; this.isManualCheck = manual; logger.info(`${manual ? 'Manually checking' : 'Auto checking'} for updates...`); // If manual check, notify renderer process about check start if (manual) { this.mainWindow.broadcast('manualUpdateCheckStart'); } try { await autoUpdater.checkForUpdates(); } catch (error) { logger.error('Error checking for updates:', error.message); // If manual check, notify renderer process about check error if (manual) { this.mainWindow.broadcast('updateError', (error as Error).message); } } finally { this.checking = false; } }; /** * Download update * @param manual whether this is a manual download */ public downloadUpdate = async (manual: boolean = false) => { if (this.downloading || !this.updateAvailable) return; this.downloading = true; logger.info(`${manual ? 'Manually downloading' : 'Auto downloading'} update...`); // If manual download or manual check, notify renderer process about download start if (manual || this.isManualCheck) { this.mainWindow.broadcast('updateDownloadStart'); } try { await autoUpdater.downloadUpdate(); } catch (error) { this.downloading = false; logger.error('Error downloading update:', error); // If manual download or manual check, notify renderer process about download error if (manual || this.isManualCheck) { this.mainWindow.broadcast('updateError', (error as Error).message); } } }; /** * Install update immediately */ public installNow = () => { logger.info('Installing update now...'); // Mark application for exit this.app.isQuiting = true; // Delay installation by 1 second to ensure window is closed autoUpdater.quitAndInstall(); }; /** * Install update on next launch */ public installLater = () => { logger.info('Update will be installed on next restart'); // Mark for installation on next launch, but don't exit application autoUpdater.autoInstallOnAppQuit = true; // Notify renderer process that update will be installed on next launch this.mainWindow.broadcast('updateWillInstallLater'); }; /** * Test mode: Simulate update available * Only for use in development environment */ public simulateUpdateAvailable = () => { if (!isDev) return; logger.info('Simulating update available...'); const mainWindow = this.mainWindow; // Simulate a new version update const mockUpdateInfo = { releaseDate: new Date().toISOString(), releaseNotes: ` #### Version 1.0.0 Release Notes - Added some great new features - Fixed bugs affecting usability - Optimized overall application performance - Updated dependency libraries `, version: '1.0.0', }; // Set update available state this.updateAvailable = true; // Notify renderer process if (this.isManualCheck) { mainWindow.broadcast('manualUpdateAvailable', mockUpdateInfo); } else { // In auto-check mode, directly simulate download this.simulateDownloadProgress(); } }; /** * Test mode: Simulate update downloaded * Only for use in development environment */ public simulateUpdateDownloaded = () => { if (!isDev) return; logger.info('Simulating update downloaded...'); const mainWindow = this.app.browserManager.getMainWindow(); if (mainWindow) { // Simulate a new version update const mockUpdateInfo = { releaseDate: new Date().toISOString(), releaseNotes: ` #### Version 1.0.0 Release Notes - Added some great new features - Fixed bugs affecting usability - Optimized overall application performance - Updated dependency libraries `, version: '1.0.0', }; // Set download state this.downloading = false; // Notify renderer process mainWindow.broadcast('updateDownloaded', mockUpdateInfo); } }; /** * Test mode: Simulate update download progress * Only for use in development environment */ public simulateDownloadProgress = () => { if (!isDev) return; logger.info('Simulating download progress...'); const mainWindow = this.app.browserManager.getMainWindow(); // Set download state this.downloading = true; // Only broadcast download start event if manual check if (this.isManualCheck) { mainWindow.broadcast('updateDownloadStart'); } // Simulate progress updates let progress = 0; const interval = setInterval(() => { progress += 10; if ( progress <= 100 && // Only broadcast download progress if manual check this.isManualCheck ) { mainWindow.broadcast('updateDownloadProgress', { bytesPerSecond: 1024 * 1024, percent: progress, // 1MB/s total: 1024 * 1024 * 100, // 100MB transferred: 1024 * 1024 * progress, // Progress * 1MB }); } if (progress >= 100) { clearInterval(interval); this.simulateUpdateDownloaded(); } }, 300); }; private registerEvents() { logger.debug('Registering updater events'); autoUpdater.on('checking-for-update', () => { logger.info('[Updater] Checking for update...'); }); autoUpdater.on('update-available', (info) => { logger.info(`Update available: ${info.version}`); this.updateAvailable = true; if (this.isManualCheck) { this.mainWindow.broadcast('manualUpdateAvailable', info); } else { // If it's an automatic check, start downloading automatically logger.info('Auto check found update, starting download automatically...'); this.downloadUpdate(); } }); autoUpdater.on('update-not-available', (info) => { logger.info(`Update not available. Current: ${info.version}`); if (this.isManualCheck) { this.mainWindow.broadcast('manualUpdateNotAvailable', info); } }); autoUpdater.on('error', (err) => { logger.error('Error in auto-updater:', err); if (this.isManualCheck) { this.mainWindow.broadcast('updateError', err.message); } }); autoUpdater.on('download-progress', (progressObj) => { logger.debug( `Download speed: ${progressObj.bytesPerSecond} - Downloaded ${progressObj.percent}% (${progressObj.transferred}/${progressObj.total})`, ); if (this.isManualCheck) { this.mainWindow.broadcast('updateDownloadProgress', progressObj); } }); autoUpdater.on('update-downloaded', (info) => { logger.info(`Update downloaded: ${info.version}`); this.downloading = false; // Always notify about downloaded update this.mainWindow.broadcast('updateDownloaded', info); }); logger.debug('Updater events registered'); } }