diff --git a/README.md b/README.md index d1b42c4..a98b53e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This desktop app allow you to configure 3 different types of backup job (One tim The app is designed with Electron and Angular so you can use it on Windows, Mac and Linux. -## Requirement: +## Requirement This app rely on AWS CLI to use the efficient "sync" command, this mean that you need to install the AWS CLI on your own in order to use this app. @@ -26,9 +26,15 @@ You can find the AWS CLI installer here: [Download AWS CLI](https://aws.amazon.c ## Windows executable latest version -[Portable](https://github.com/ulver2812/aws-s3-backup/raw/master/app-builds/AWS%20S3%20Backup%201.1.0.exe) +[Portable](https://github.com/ulver2812/aws-s3-backup/raw/master/app-builds/AWS%20S3%20Backup%201.2.0.exe) -[Setup](https://github.com/ulver2812/aws-s3-backup/raw/master/app-builds/AWS%20S3%20Backup%20Setup%201.1.0.exe) +[Setup](https://github.com/ulver2812/aws-s3-backup/raw/master/app-builds/AWS%20S3%20Backup%20Setup%201.2.0.exe) + +## Changelog + +- 1.2.0 Added email notifications +- 1.1.0 UI Enhancements +- 1.0.0 First release ## Getting Started diff --git a/app-builds/AWS S3 Backup 1.1.0.exe b/app-builds/AWS S3 Backup 1.2.0.exe similarity index 86% rename from app-builds/AWS S3 Backup 1.1.0.exe rename to app-builds/AWS S3 Backup 1.2.0.exe index b530c41..75056b3 100644 Binary files a/app-builds/AWS S3 Backup 1.1.0.exe and b/app-builds/AWS S3 Backup 1.2.0.exe differ diff --git a/app-builds/AWS S3 Backup Setup 1.1.0.exe b/app-builds/AWS S3 Backup Setup 1.2.0.exe similarity index 86% rename from app-builds/AWS S3 Backup Setup 1.1.0.exe rename to app-builds/AWS S3 Backup Setup 1.2.0.exe index 2e6bc0c..c9b7311 100644 Binary files a/app-builds/AWS S3 Backup Setup 1.1.0.exe and b/app-builds/AWS S3 Backup Setup 1.2.0.exe differ diff --git a/main.ts b/main.ts index 889ab83..e795a69 100644 --- a/main.ts +++ b/main.ts @@ -2,6 +2,7 @@ import {app, BrowserWindow, BrowserWindowConstructorOptions, screen, Tray, Menu, import * as path from 'path'; import * as url from 'url'; import * as Splashscreen from '@trodi/electron-splashscreen'; +import * as contextMenuInternal from 'electron-context-menu'; let win, serve, tray; const args = process.argv.slice(1); @@ -88,6 +89,12 @@ function createTray() { }); } +function createContextMenuInternal() { + contextMenuInternal.default({ + showInspectElement: false + }); +} + try { // This method will be called when Electron has finished @@ -97,6 +104,8 @@ try { app.on('ready', createTray); + app.on('ready', createContextMenuInternal); + // Quit when all windows are closed. app.on('window-all-closed', () => { // On OS X it is common for applications and their menu bar diff --git a/package-lock.json b/package-lock.json index 9e44c33..fedf4a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "aws-s3-backup", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3753,6 +3753,25 @@ } } }, + "electron-context-menu": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-0.11.0.tgz", + "integrity": "sha512-sgDIGqjgazUQ5fbfz0ObRkmODAsw00eylQprp5q4jyuL6dskd27yslhoJTrjLbFGErfVVYzRXPW2rQPJxARKmg==", + "requires": { + "electron-dl": "^1.2.0", + "electron-is-dev": "^1.0.1" + } + }, + "electron-dl": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-1.13.0.tgz", + "integrity": "sha512-0sg/g7zOuH5TABWSgsqFB275jbRCkIxYq6Nz1lFqMQP/Z93EF2mOo+RPNL1CyFH0dEZLFOvPgwmSMqiPICVOyQ==", + "requires": { + "ext-name": "^5.0.0", + "pupa": "^1.0.0", + "unused-filename": "^1.0.0" + } + }, "electron-download": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz", @@ -3809,6 +3828,11 @@ } } }, + "electron-is-dev": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.0.1.tgz", + "integrity": "sha512-iwM3EotA9HTXqMGpQRkR/kT8OZqBbdfHTnlwcxsjSLYqY8svvsq0MuujsWCn3/vtgRmDv/PC/gKUUpoZvi5C1w==" + }, "electron-osx-sign": { "version": "0.4.10", "resolved": "http://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.10.tgz", @@ -4578,6 +4602,23 @@ } } }, + "ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "requires": { + "mime-db": "^1.28.0" + } + }, + "ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "requires": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -7856,6 +7897,40 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true + }, + "nodemailer": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.7.2.tgz", + "integrity": "sha1-8kLmSa7q45tsftdA73sGHEBNMPk=", + "dev": true, + "optional": true, + "requires": { + "libmime": "3.0.0", + "mailcomposer": "4.0.1", + "nodemailer-direct-transport": "3.3.2", + "nodemailer-shared": "1.1.0", + "nodemailer-smtp-pool": "2.8.2", + "nodemailer-smtp-transport": "2.7.2", + "socks": "1.1.9" + } + }, + "smart-buffer": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", + "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", + "dev": true, + "optional": true + }, + "socks": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz", + "integrity": "sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE=", + "dev": true, + "optional": true, + "requires": { + "ip": "^1.1.2", + "smart-buffer": "^1.0.4" + } } } }, @@ -8317,8 +8392,7 @@ "mime-db": { "version": "1.36.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==", - "dev": true + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" }, "mime-types": { "version": "2.1.20", @@ -8455,6 +8529,11 @@ "minimist": "0.0.8" } }, + "modify-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modify-filename/-/modify-filename-1.1.0.tgz", + "integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=" + }, "moment": { "version": "2.22.2", "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", @@ -8798,40 +8877,9 @@ } }, "nodemailer": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.7.2.tgz", - "integrity": "sha1-8kLmSa7q45tsftdA73sGHEBNMPk=", - "dev": true, - "optional": true, - "requires": { - "libmime": "3.0.0", - "mailcomposer": "4.0.1", - "nodemailer-direct-transport": "3.3.2", - "nodemailer-shared": "1.1.0", - "nodemailer-smtp-pool": "2.8.2", - "nodemailer-smtp-transport": "2.7.2", - "socks": "1.1.9" - }, - "dependencies": { - "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", - "dev": true, - "optional": true - }, - "socks": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz", - "integrity": "sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE=", - "dev": true, - "optional": true, - "requires": { - "ip": "^1.1.2", - "smart-buffer": "^1.0.4" - } - } - } + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-5.1.1.tgz", + "integrity": "sha512-hKGCoeNdFL2W7S76J/Oucbw0/qRlfG815tENdhzcqTpSjKgAN91mFOqU2lQUflRRxFM7iZvCyaFcAR9noc/CqQ==" }, "nodemailer-direct-transport": { "version": "3.3.2", @@ -13476,8 +13524,7 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, "path-is-absolute": { "version": "1.0.1", @@ -14095,6 +14142,11 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "pupa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-1.0.0.tgz", + "integrity": "sha1-mpVopa9+ZXuEYqbp1TKHQ1YM7/Y=" + }, "q": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", @@ -15417,6 +15469,24 @@ "is-plain-obj": "^1.0.0" } }, + "sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", + "requires": { + "sort-keys": "^1.0.0" + }, + "dependencies": { + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "requires": { + "is-plain-obj": "^1.0.0" + } + } + } + }, "sorted-array-functions": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.2.0.tgz", @@ -16508,6 +16578,15 @@ } } }, + "unused-filename": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-1.0.0.tgz", + "integrity": "sha1-00CID3GuIRXrqhMlvvBcxmhEacY=", + "requires": { + "modify-filename": "^1.1.0", + "path-exists": "^3.0.0" + } + }, "unzip-response": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", diff --git a/package.json b/package.json index 329f547..2ed038e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aws-s3-backup", - "version": "1.1.0", + "version": "1.2.0", "description": "AWS S3 backup system", "homepage": "https://github.com/ulver2812/aws-s3-backup", "author": { @@ -47,12 +47,14 @@ "@types/node-schedule": "^1.2.2", "aws-sdk": "^2.335.0", "chokidar": "^2.0.4", + "electron-context-menu": "^0.11.0", "fs-extra": "^7.0.0", "hammerjs": "^2.0.8", "is-online": "^7.0.0", "moment": "^2.22.2", "ngx-electron": "^1.0.4", "node-schedule": "^1.3.0", + "nodemailer": "^5.1.1", "sugar": "^2.0.4" }, "devDependencies": { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4b85e50..dda4b74 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -36,6 +36,7 @@ import { S3ExplorerComponent } from './components/s3-explorer/s3-explorer.compon import { JobAlertDialogComponent } from './components/dialogs/job-alert-dialog/job-alert-dialog.component'; import { JobBackupManuallyComponent } from './components/dialogs/job-backup-manually/job-backup-manually.component'; import { NoInternetConnectionComponent } from './components/dialogs/no-internet-connection/no-internet-connection.component'; +import {NotificationsService} from './providers/notifications.service'; // AoT requires an exported function for factories @@ -76,7 +77,7 @@ export function HttpLoaderFactory(http: HttpClient) { }), BrowserAnimationsModule ], - providers: [ElectronService, JobsService, JobSchedulerService], + providers: [ElectronService, JobsService, JobSchedulerService, NotificationsService], bootstrap: [AppComponent], entryComponents: [JobAlertDialogComponent, JobBackupManuallyComponent, NoInternetConnectionComponent] }) diff --git a/src/app/components/settings/settings.component.html b/src/app/components/settings/settings.component.html index 358746a..1394b84 100644 --- a/src/app/components/settings/settings.component.html +++ b/src/app/components/settings/settings.component.html @@ -39,4 +39,52 @@

{{'PAGES.SETTINGS.TITLE' | translate}}

+
+
+ +
+

{{'PAGES.NOTIFICATIONS-SETTINGS.TITLE' | translate}}

+
+

+ {{ 'PAGES.NOTIFICATIONS-SETTINGS.ALLOW' | translate }} +

+ +
+ + + + + + +

+ {{ 'PAGES.NOTIFICATIONS-SETTINGS.EMAIL.SECURE' | translate }} +

+ + + + + + + + + + + + + {{email}} + cancel + + + + + +
+
+
+ diff --git a/src/app/components/settings/settings.component.ts b/src/app/components/settings/settings.component.ts index 2fce4ca..c00258c 100644 --- a/src/app/components/settings/settings.component.ts +++ b/src/app/components/settings/settings.component.ts @@ -5,19 +5,37 @@ import {AppMenuService} from '../../providers/appmenu.service'; import {AwsService} from '../../providers/aws.service'; import {UtilsService} from '../../providers/utils.service'; import {TranslateService} from '@ngx-translate/core'; +import {MatChipInputEvent} from '@angular/material'; +import {COMMA, ENTER} from '@angular/cdk/keycodes'; +import {isUndefined} from 'util'; @Component({ selector: 'app-settings', templateUrl: './settings.component.html', styleUrls: ['./settings.component.scss'] }) + export class SettingsComponent implements OnInit { + visible = true; + selectable = true; + removable = true; + addOnBlur = true; + readonly separatorKeysCodes: number[] = [ENTER, COMMA]; + settings: { awsAccessKeyID: string, awsSecretAccessKey: string, awsRegion: string, - language: string + language: string, + allowNotifications: boolean, + emailHost: string, + emailPort: number, + emailSecure: boolean, + emailUser: string, + emailPassword: string, + emailSender: string, + emailReceivers: string[] }; awsCliStatus: any; @@ -56,12 +74,16 @@ export class SettingsComponent implements OnInit { private utilsService: UtilsService, private translate: TranslateService ) { - } ngOnInit() { this.appMenuService.changeMenuPage('Settings'); this.settings = this.settingsService.getSettings(); + + if (isUndefined(this.settings.emailReceivers)) { + this.settings.emailReceivers = []; + } + this.checkSettings(); this.utilsService.checkInternetConnection(); } @@ -93,4 +115,27 @@ export class SettingsComponent implements OnInit { this.spinner = false; }); } + + add(event: MatChipInputEvent): void { + const input = event.input; + const value = event.value; + + // Add our email + if ((value || '').trim()) { + this.settings.emailReceivers.push(value.trim()); + } + + // Reset the input value + if (input) { + input.value = ''; + } + } + + remove(email: string): void { + const index = this.settings.emailReceivers.indexOf(email); + + if (index >= 0) { + this.settings.emailReceivers.splice(index, 1); + } + } } diff --git a/src/app/providers/aws.service.ts b/src/app/providers/aws.service.ts index e52bee2..6cf177a 100644 --- a/src/app/providers/aws.service.ts +++ b/src/app/providers/aws.service.ts @@ -12,6 +12,8 @@ import * as moment from 'moment'; import {Job} from '../models/job.model'; import {JobsService} from './jobs.service'; import {JobType} from '../enum/job.type.enum'; +import {NotificationsService} from './notifications.service'; +import {isUndefined} from 'util'; @Injectable({ providedIn: 'root' @@ -24,7 +26,8 @@ export class AwsService { private settings: SettingsService, private logService: LogService, private electron: ElectronService, - private jobService: JobsService) { + private jobService: JobsService, + private notification: NotificationsService) { } @@ -46,8 +49,17 @@ export class AwsService { }); } - checkCredentials() { - this.configureAwsCli(); + async checkCredentials() { + const settings = this.settings.getSettings(); + + if (isUndefined(settings.awsAccessKeyID) || isUndefined(settings.awsSecretAccessKey) || isUndefined(settings.awsRegion)) { + return new Promise(resolve => { + resolve(false); + }); + } + + await this.configureAwsCli(); + return new Promise(resolve => { const proc = child.spawn('aws', ['s3', 'ls'], {shell: true}); @@ -63,14 +75,16 @@ export class AwsService { proc.on('error', (err) => { resolve(false); }); + + // TODO: quando non ci sono bucket s3 associati all'account il loader non scompare perchè la aws cli non restituisce nulla }); } - configureAwsCli() { + async configureAwsCli() { const settings = this.settings.getSettings(); - child.spawn('aws', ['configure', 'set', 'aws_access_key_id', settings.awsAccessKeyID], {shell: true}); - child.spawn('aws', ['configure', 'set', 'aws_secret_access_key', settings.awsSecretAccessKey], {shell: true}); - child.spawn('aws', ['configure', 'set', 'default.region', settings.awsRegion], {shell: true}); + await child.spawn('aws', ['configure', 'set', 'aws_access_key_id', settings.awsAccessKeyID], {shell: true}); + await child.spawn('aws', ['configure', 'set', 'aws_secret_access_key', settings.awsSecretAccessKey], {shell: true}); + await child.spawn('aws', ['configure', 'set', 'default.region', settings.awsRegion], {shell: true}); } s3Sync(job: Job) { @@ -84,6 +98,8 @@ export class AwsService { job.setIsRunning(true); if (job.type !== JobType.Live) { this.logService.printLog(LogType.INFO, 'Start job: ' + job.name); + this.notification.sendNotification('Start job: ' + job.name, 'The job ' + job.name + + ' has just begun you will receive another email notification on job end.
- AWS S3 Backup', 'email'); } let bucket = 's3://' + job.bucket; let s3Args = ['s3', 'sync']; @@ -115,11 +131,13 @@ export class AwsService { job.setAlert(true); this.jobService.save(job); this.logService.printLog(LogType.ERROR, 'Can\'t run job ' + job.name + ' because of: \r\n' + err); + this.notification.sendNotification('Problem with job: ' + job.name, 'The job ' + job.name + + ' has just stopped because of ' + err + '.
- AWS S3 Backup', 'email'); }); proc.stdout.on('data', data => { if (job.type !== JobType.Live) { - this.logService.printLog(LogType.INFO, data); + // this.logService.printLog(LogType.INFO, data); } }); @@ -128,11 +146,15 @@ export class AwsService { job.setAlert(true); this.jobService.save(job); this.logService.printLog(LogType.ERROR, 'Can\'t run job ' + job.name + ' because of: \r\n' + err); + this.notification.sendNotification('Problem with job: ' + job.name, 'The job ' + job.name + + ' has just stopped because of ' + err + '.
- AWS S3 Backup', 'email'); }); proc.on('close', (code) => { if (code === 0 && job.type !== JobType.Live) { this.logService.printLog(LogType.INFO, 'End job: ' + job.name); + this.notification.sendNotification('End job: ' + job.name, 'The job ' + job.name + + ' has just ended.
- AWS S3 Backup', 'email'); } job.setIsRunning(false); if (job.type !== JobType.Live) { diff --git a/src/app/providers/notifications.service.ts b/src/app/providers/notifications.service.ts new file mode 100644 index 0000000..71d736b --- /dev/null +++ b/src/app/providers/notifications.service.ts @@ -0,0 +1,60 @@ +import {Injectable} from '@angular/core'; +import * as nodemailer from 'nodemailer'; +import {SettingsService} from './settings.service'; +import {LogService} from './log.service'; +import {LogType} from '../enum/log.type.enum'; +import {isUndefined} from 'util'; + +@Injectable({ + providedIn: 'root' +}) +export class NotificationsService { + + constructor(private settingsService: SettingsService, private log: LogService) { + } + + getEmailTransporter() { + const settings = this.settingsService.getSettings(); + // email channel + // tslint:disable-next-line:prefer-const + let options = { + host: settings.emailHost, + port: settings.emailPort, + secure: settings.emailSecure // true for 465, false for other ports + }; + + if (!isUndefined(settings.emailUser) && !isUndefined(settings.emailPassword)) { + options['auth'] = { + user: settings.emailUser, // generated ethereal user + pass: settings.emailPassword // generated ethereal password + }; + } + + return nodemailer.createTransport(options); + } + + // subject = await this.translate.get('NOTIFICATIONS.SUBJECT-START').toPromise(); + sendNotification(subject: string, message: string, channel: string) { + const settings = this.settingsService.getSettings(); + if (settings.allowNotifications === false) { + return; + } + + if (channel === 'email') { + const transporter = this.getEmailTransporter(); + transporter.sendMail(this.getMailOptions(subject, message), (err: Error, res: Response) => { + this.log.printLog(LogType.ERROR, 'Email - ' + err.message); + }); + } + } + + getMailOptions(subject: string, message: string) { + const settings = this.settingsService.getSettings(); + return { + from: settings.emailSender, // sender address + to: settings.emailReceivers, // list of receivers + subject: subject, // Subject line + html: message // html body + }; + } +} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index bef36ed..4d71dab 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -86,6 +86,19 @@ "SAVE": "Save", "LANGUAGE": "Language" }, + "NOTIFICATIONS-SETTINGS": { + "TITLE": "Notifications settings", + "ALLOW": "Allow notifications", + "EMAIL": { + "HOST": "Email host", + "PORT": "Email port", + "SECURE": "Email secure layer", + "USER": "Email user", + "PASSWORD": "Email password", + "SENDER": "Email sender", + "RECEIVERS": "Email receivers (press enter to insert emails)" + } + }, "HELP": { "MENU": "Help" } @@ -130,5 +143,9 @@ "SAT": "Saturday", "SUN": "Sunday" } + }, + "NOTIFICATIONS": { + "SUBJECT-START": "Start backup notification", + "SUBJECT-END": "End backup notification" } } diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index 8c81846..5995af1 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -86,6 +86,19 @@ "SAVE": "Salva", "LANGUAGE": "Lingua" }, + "NOTIFICATIONS-SETTINGS": { + "TITLE": "Opzioni notifiche", + "ALLOW": "Abilita notifiche", + "EMAIL": { + "HOST": "Email server", + "PORT": "Email porta", + "SECURE": "Email sicura", + "USER": "Email user", + "PASSWORD": "Email password", + "SENDER": "Email mittente", + "RECEIVERS": "Email destinatari (premi invio per inserire le email)" + } + }, "HELP": { "MENU": "Aiuto" } @@ -129,6 +142,10 @@ "FRI": "Venerdi", "SAT": "Sabato", "SUN": "Domenica" + }, + "NOTIFICATIONS": { + "SUBJECT-START": "Notifica di inizio backup", + "SUBJECT-END": "Notifica di fine backup" } } }