diff --git a/app/main.ts b/app/main.ts index 6f56428..5042e5a 100644 --- a/app/main.ts +++ b/app/main.ts @@ -889,13 +889,37 @@ try { }); } catch (error) { - event.sender.send('download-progress', { progress: 0, status: `Error: ${error}` }); + event.sender.send('download-progress', { progress: 0, status: `${error}` }); win?.setProgressBar(0, { mode: 'error' }); } }); + ipcMain.handle('download-file', async (event: IpcMainInvokeEvent, url: string, destination: string) => { + try { + event.sender.send('download-file-progress', { progress: 0, status: 'Starting download' }); + + const fileName = await downloadFile(url, destination, (progress) => { + win?.setProgressBar(progress, { + mode: 'normal' + }); + + event.sender.send('download-file-progress', { progress, status: 'Downloading' }); + }); + + win?.setProgressBar(0, { + mode: 'none' + }); + + event.sender.send('download-file-complete', `${destination}${separator}${fileName}`); + } + catch(error: any) { + console.error(error); + event.sender.send('download-file-error', `${error}`); + } + }); + ipcMain.handle('read-file', (event: IpcMainInvokeEvent, filePath: string) => { fs.readFile(filePath, 'utf-8', (err, data) => { if (err != null) { @@ -1080,6 +1104,10 @@ try { // #endregion + ipcMain.handle('show-error-box', (event: IpcMainInvokeEvent, title: string, content: string) => { + dialog.showErrorBox(title, content); + }); + ipcMain.handle('set-tray-item-enabled', (event: IpcMainInvokeEvent, id: string, enabled: boolean) => { setTrayItemEnabled(id, enabled); }); @@ -1096,10 +1124,13 @@ try { clipboard.writeText(content, "selection"); }); - -} catch (e) { +} catch (e: any) { // Catch Error console.error(e); + + dialog.showErrorBox('', `${e}`); + + app.quit(); // throw e; } diff --git a/app/preload.js b/app/preload.js index 52fd7a0..756c026 100644 --- a/app/preload.js +++ b/app/preload.js @@ -244,5 +244,25 @@ contextBridge.exposeInMainWorld('electronAPI', { }, unregisterOnIsAutoLaunched: () => { ipcRenderer.removeAllListeners('on-is-auto-launched'); + }, + downloadFile: (url, destination) => { + ipcRenderer.invoke('download-file', url, destination); + }, + onDownloadFileProgress: (callback) => { + ipcRenderer.on('download-file-progress', callback); + }, + onDownloadFileError: (callback) => { + ipcRenderer.on('download-file-error', callback); + }, + onDownloadFileComplete: (callback) => { + ipcRenderer.on('download-file-complete', callback); + }, + unregisterOnDownloadFile: () => { + ipcRenderer.removeAllListeners('download-file-progress'); + ipcRenderer.removeAllListeners('download-file-error'); + ipcRenderer.removeAllListeners('download-file-complete'); + }, + showErrorBox: (title, content) => { + ipcRenderer.invoke('show-error-box', title, content); } }); diff --git a/src/app/core/services/electron/electron.service.ts b/src/app/core/services/electron/electron.service.ts index 0aff963..e2ccd8a 100644 --- a/src/app/core/services/electron/electron.service.ts +++ b/src/app/core/services/electron/electron.service.ts @@ -311,4 +311,25 @@ export class ElectronService { return await promise; } + public async downloadFile(url: string, destination: string, progressFunction?: (info: { progress: number, status: string }) => void): Promise { + const promise = new Promise((resolve, reject) => { + if (progressFunction) { + window.electronAPI.onDownloadProgress((event: any, prog: { progress: number, status: string }) => progressFunction(prog)); + } + + window.electronAPI.onDownloadFileError((event: any, error: string) => { + window.electronAPI.unregisterOnDownloadFile(); + reject(new Error(error)); + }); + + window.electronAPI.onDownloadFileComplete((event: any, fileName: string) => { + window.electronAPI.unregisterOnDownloadFile(); + resolve(fileName); + }); + }); + + window.electronAPI.downloadFile(url, destination); + return await promise; + } + } diff --git a/src/app/pages/settings/settings.component.html b/src/app/pages/settings/settings.component.html index 56a0733..d85dea0 100644 --- a/src/app/pages/settings/settings.component.html +++ b/src/app/pages/settings/settings.component.html @@ -65,6 +65,40 @@ +
+

Ban

+ +
+
+ + +
+ Specify a remote URL for download ban list +
+
+ +
+ +
+ +
+ + + +
+ Specify ban list file, one IP address per line. It is not recommended to statically ban any IP addresses unless you absolutely need to. Banning IPs often excludes the most vulnerable users who are forced to operate entirely behind Tor or other anonymity networks +
+ +
+ +
+ + +
+ + Specify ban list file, one IP address per line. It is not recommended to statically ban any IP addresses unless you absolutely need to. Banning IPs often excludes the most vulnerable users who are forced to operate entirely behind Tor or other anonymity networks +
+ @@ -545,7 +579,6 @@
-
diff --git a/src/app/pages/settings/settings.component.ts b/src/app/pages/settings/settings.component.ts index 05a5bf9..96475ba 100644 --- a/src/app/pages/settings/settings.component.ts +++ b/src/app/pages/settings/settings.component.ts @@ -16,6 +16,22 @@ export class SettingsComponent { private originalSettings: DaemonSettings; public currentSettings: DaemonSettings; + public banListUrl: string = ''; + + public get validBanListUrl(): boolean { + if (this.banListUrl == '') { + return false; + } + + try { + new URL(this.banListUrl); + return true; + } + catch { + return false; + } + } + public savingChanges: boolean = false; public savingChangesError = ``; public savingChangesSuccess: boolean = false; @@ -339,6 +355,50 @@ export class SettingsComponent { }); } + public async chooseBanListFile(): Promise { + const file = await this.electronService.selectFile(['txt']); + + if (file == '') { + return; + } + + this.ngZone.run(() => { + this.currentSettings.banList = file; + }); + } + + public removeBanListFile(): void { + this.currentSettings.banList = ''; + } + + public downloadingBanListFile: boolean = false; + + public async downloadBanListFile(): Promise { + if (!this.currentSettings.remoteBanList) { + return; + } + + this.downloadingBanListFile = true; + + try { + const destination = await this.electronService.selectFolder(); + const filePath = await this.electronService.downloadFile(this.banListUrl, destination); + + if (!filePath.endsWith('.txt')) { + throw new Error("Downloaded file doesn't seem to be a valid ban list txt file"); + } + + this.currentSettings.banList = filePath; + this.currentSettings.remoteBanList = false; + } + catch (error: any) { + console.error(error); + window.electronAPI.showErrorBox('', `${error}`.replace('Error:', '')); + } + + this.downloadingBanListFile = false; + } + private async choosePemFile(): Promise { return await this.electronService.selectFile(['pem', 'PEM']); } diff --git a/src/common/DaemonSettings.ts b/src/common/DaemonSettings.ts index 369ee0e..871ba6e 100644 --- a/src/common/DaemonSettings.ts +++ b/src/common/DaemonSettings.ts @@ -98,6 +98,7 @@ export class DaemonSettings { public anonymousInbound: string = ''; public banList: string = ''; + public remoteBanList: boolean = false; public hideMyPort: boolean = false; public noSync: boolean = false; @@ -146,6 +147,10 @@ export class DaemonSettings { public rpcPaymentAllowFreeLoopback: boolean = false; public disableRpcBan: boolean = false; + public get banListArray(): string[] { + return this.banList.split('\n'); + } + public equals(settings: DaemonSettings): boolean { //return this.toCommandOptions().join('') == settings.toCommandOptions().join(''); return this.deepEqual(this, settings); diff --git a/src/polyfills.ts b/src/polyfills.ts index c3ddc57..aff43af 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -161,6 +161,14 @@ declare global { unregisterOnIsOnBatteryPower: () => void; onBattery: (callback: (event: any) => void) => void; onAc: (callback: (event: any) => void) => void; + + downloadFile: (url: string, destination: string) => void; + onDownloadFileProgress: (callback: (event: any, info: { progress: number, status: string }) => void) => void; + onDownloadFileError: (callback: (event: any, error: string) => void) => void; + onDownloadFileComplete: (callback: (event: any, fileName: string) => void) => void; + unregisterOnDownloadFile: () => void; + + showErrorBox: (title: string, content: string) => void; }; } } \ No newline at end of file