@@ -4,8 +4,10 @@ const { app, nativeTheme, nativeImage, BrowserWindow, Menu, Tray, screen, shell,
44const path = require ( 'path' )
55const fs = require ( 'fs' )
66const request = require ( 'request' )
7+ const https = require ( 'https' )
78const extract = require ( 'extract-zip' )
89const defaultMenu = require ( 'electron-default-menu' ) ;
10+ const ProgressBar = require ( 'electron-progressbar' )
911const { spawn, exec } = require ( 'child_process' ) ;
1012
1113const helpers = require ( './helpers' )
@@ -54,14 +56,6 @@ if (isDev) {
5456 logger . info ( 'Running the Electron app in dev mode.' )
5557}
5658
57- logger . info ( process . env )
58- let appSettings = getAppSettings ( )
59-
60- let dimensions = { widIth : 1500 , height : 1000 } ;
61-
62- const contextMenu = require ( 'electron-context-menu' ) ;
63- const { options } = require ( 'request' )
64-
6559// Set the dock icon (MacOS and for development only)
6660if ( isMac && isDev ) {
6761 const dockIcon = nativeImage . createFromPath (
@@ -70,6 +64,11 @@ if (isMac && isDev) {
7064 app . dock . setIcon ( dockIcon )
7165}
7266
67+ let appSettings = getAppSettings ( )
68+ let dimensions = { width : 1500 , height : 1000 } ;
69+
70+ // Modify the context menu
71+ const contextMenu = require ( 'electron-context-menu' ) ;
7372contextMenu ( {
7473 menu : ( actions ) => [
7574 {
@@ -91,16 +90,102 @@ contextMenu({
9190 ]
9291} ) ;
9392
93+ // The standard quit item cannot be replaced / modified and it is not triggering the
94+ // before-quit event on MacOS if a child window is open
95+ const dockMenuWithforceQuit = Menu . buildFromTemplate ( [
96+ {
97+ label : 'Force Quit during download' ,
98+ click : ( ) => {
99+ // If the progress bar exists, close it
100+ if ( progressBar ) {
101+ progressBar . close ( ) ;
102+ }
103+ // Quit the app
104+ app . quit ( ) ;
105+ }
106+ }
107+ ] ) ;
108+
109+ // Download function with progress bar
110+ let progressBar
94111const download = ( uri , filename , callback ) => {
95- request . head ( uri , ( err , res , body ) => {
96- logger . info ( 'content-type:' , res . headers [ 'content-type' ] )
97- logger . info ( 'content-length:' , res . headers [ 'content-length' ] )
98- if ( res . statusCode != 404 ) {
99- request ( uri ) . pipe ( fs . createWriteStream ( filename ) ) . on ( 'close' , callback )
100- } else {
101- callback ( true )
112+ // HEAD request first
113+ request . head ( uri , ( err , res , body ) => {
114+ if ( res . statusCode != 404 ) {
115+ let receivedBytes = 0
116+ const totalBytes = res . headers [ 'content-length' ]
117+ logger . info ( `Total size to download: ${ totalBytes } ` )
118+ progressBar = new ProgressBar ( {
119+ indeterminate : false ,
120+ abortOnError : true ,
121+ text : 'Downloading the Specter binary from GitHub' ,
122+ detail : 'This can take several minutes depending on your Internet connection. Specter will start once the download is finished.' ,
123+ maxValue : totalBytes ,
124+ browserWindow : {
125+ parent : mainWindow
126+ } ,
127+ style : {
128+ detail : {
129+ 'margin-bottom' : '12px'
130+ } ,
131+ bar : {
132+ 'background-color' : '#fff'
133+ } ,
134+ value : {
135+ 'background-color' : '#000'
136+ }
137+ }
138+ } )
139+
140+ // Add Force Quit item during download for MacOS dock
141+ if ( isMac ) {
142+ app . dock . setMenu ( dockMenuWithforceQuit ) ;
102143 }
103- } )
144+
145+ progressBar . on ( 'completed' , ( ) => {
146+ progressBar . close ( )
147+ // Remove the Force Quit dock item again for Mac
148+ if ( isMac ) {
149+ const updatedDockMenu = Menu . buildFromTemplate (
150+ dockMenuWithforceQuit . items . filter ( item => item . label !== 'Force Quit during download' )
151+ ) ;
152+ app . dock . setMenu ( updatedDockMenu ) ;
153+ }
154+ } ) ;
155+
156+ progressBar . on ( 'aborted' , ( ) => {
157+ logger . info ( 'Download was aborted before it could finish.' )
158+ progressBar = null
159+ } ) ;
160+
161+ // Loggin the download progress
162+ let lastLogTime = 0 ;
163+ const logInterval = 5000 ; // log every 5 seconds
164+ progressBar . on ( 'progress' , ( ) => {
165+ const currentTime = Date . now ( ) ;
166+ if ( currentTime - lastLogTime >= logInterval ) {
167+ lastLogTime = currentTime ;
168+ logger . info ( `Download status: ${ ( receivedBytes / totalBytes * 100 ) . toFixed ( 0 ) } %` ) ;
169+ }
170+ } ) ;
171+
172+ // GET request
173+ request ( uri )
174+ . on ( 'data' , ( chunk ) => {
175+ receivedBytes += chunk . length
176+ if ( progressBar ) {
177+ progressBar . value = receivedBytes
178+ }
179+ } )
180+ . pipe ( fs . createWriteStream ( filename ) )
181+ . on ( 'close' , callback )
182+ }
183+ // If the download link was not found, call callback (updatingLoaderMsg with error feedback)
184+ else {
185+ logger . info ( `Error while trying to download specterd: ${ err } ` )
186+ callback ( true )
187+ }
188+ } )
104189}
105190
106191let specterdProcess
@@ -253,11 +338,9 @@ app.whenReady().then(() => {
253338 if ( fs . existsSync ( specterdPath + ( platformName == 'win64' ? '.exe' : '' ) ) ) {
254339 getFileHash ( specterdPath + ( platformName == 'win64' ? '.exe' : '' ) , function ( specterdHash ) {
255340 if ( appSettings . specterdHash . toLowerCase ( ) == specterdHash || appSettings . specterdHash == "" ) {
256-
257341 startSpecterd ( specterdPath )
258342 } else if ( appSettings . specterdVersion != "" ) {
259- updatingLoaderMsg ( 'Specterd version could not be validated. Retrying fetching specterd...' )
260- updateSpecterdStatus ( 'Fetching Specter binary...' )
343+ updatingLoaderMsg ( 'Specterd version could not be validated. Trying again to download the Specter binary ...' )
261344 downloadSpecterd ( specterdPath )
262345 } else {
263346 updatingLoaderMsg ( 'Specterd file could not be validated and no version is configured in the settings<br>Please go to Preferences and set version to fetch or add an executable manually...' )
@@ -308,21 +391,20 @@ function initMainWindow() {
308391}
309392
310393function downloadSpecterd ( specterdPath ) {
311- updatingLoaderMsg ( `Fetching the ${ appName } binary.<br>This might take a minute...` )
312- updateSpecterdStatus ( `Fetching ${ appName } binary...` )
394+ updatingLoaderMsg ( `Starting download` )
395+ updateSpecterdStatus ( `Downloading the ${ appName } binary...` )
396+ // Some logging
313397 logger . info ( "Using version " + appSettings . specterdVersion ) ;
314398 logger . info ( "Using platformName " + platformName ) ;
315-
316399 download_location = getDownloadLocation ( appSettings . specterdVersion , platformName )
317400 logger . info ( "Downloading from " + download_location ) ;
318401 download ( download_location , specterdPath + '.zip' , function ( errored ) {
319402 if ( errored == true ) {
320- updatingLoaderMsg ( `Fetching ${ appNameLower } binary from the server failed, could not reach the server or the file could not have been found.` )
321- updateSpecterdStatus ( `Fetching ${ appNameLower } d failed...` )
403+ updatingLoaderMsg ( `Downloading the ${ appNameLower } binary from GitHub failed, could not reach the server or the file wasn't found.` )
404+ updateSpecterdStatus ( `Downloading ${ appNameLower } d failed...` )
322405 return
323406 }
324-
325- updatingLoaderMsg ( 'Unpacking files...' )
407+ updatingLoaderMsg ( 'Download completed. Unpacking files...' )
326408 logger . info ( "Extracting " + specterdPath ) ;
327409
328410 extract ( specterdPath + '.zip' , { dir : specterdPath + '-dir' } ) . then ( function ( ) {
@@ -341,7 +423,6 @@ function downloadSpecterd(specterdPath) {
341423 var newPath = specterdPath + ( platformName == 'win64' ? '.exe' : '' )
342424
343425 fs . renameSync ( oldPath , newPath )
344- updatingLoaderMsg ( 'Cleaning up...' )
345426 fs . unlinkSync ( specterdPath + '.zip' )
346427 fs . rmdirSync ( specterdPath + '-dir' , { recursive : true } ) ;
347428 getFileHash ( specterdPath + ( platformName == 'win64' ? '.exe' : '' ) , function ( specterdHash ) {
@@ -352,10 +433,6 @@ function downloadSpecterd(specterdPath) {
352433 logger . error ( `hash of downloaded file: ${ specterdHash } ` )
353434 logger . error ( `Expected hash: ${ appSettings . specterdHash } ` )
354435 updateSpecterdStatus ( 'Failed to launch specterd...' )
355-
356- // app.quit()
357- // TODO: This should never happen unless the specterd file was swapped on GitHub.
358- // Think of what would be the appropriate way to handle this...
359436 }
360437 } )
361438 } )
@@ -520,17 +597,21 @@ app.on('window-all-closed', function(){
520597 }
521598} ) ;
522599
523- app . on ( 'before-quit' , ( ) => {
600+ app . on ( 'before-quit' , ( event ) => {
524601 if ( ! quitted ) {
602+ logger . info ( 'Quitting ...' )
525603 quitted = true
526604 quitSpecterd ( )
527-
528605 if ( mainWindow && ! mainWindow . isDestroyed ( ) ) {
606+ if ( progressBar ) {
607+ progressBar . destroy ( )
608+ progressBar = null
609+ }
529610 mainWindow . destroy ( )
530611 mainWindow = null
531612 prefWindow = null
532613 tray = null
533- }
614+ }
534615 }
535616} )
536617
0 commit comments