@@ -1,92 +1,118 @@ | |||
module.exports = { | |||
root: true, | |||
env: { | |||
browser: true, | |||
node: true, | |||
es2020: true, | |||
'shared-node-browser': true, | |||
}, | |||
parserOptions: { | |||
parser: 'babel-eslint', | |||
node: true, | |||
"shared-node-browser": true | |||
}, | |||
extends: [ | |||
'@nuxtjs', | |||
'airbnb-base', | |||
'plugin:import/errors', | |||
'plugin:import/warnings', | |||
'plugin:promise/recommended', | |||
'plugin:lodash/recommended', | |||
'plugin:vue/recommended', | |||
'plugin:nuxt/recommended' | |||
"@nuxtjs", | |||
"airbnb-base", | |||
"plugin:import/errors", | |||
"plugin:import/warnings", | |||
"plugin:promise/recommended", | |||
"plugin:lodash/recommended", | |||
"plugin:vue/recommended", | |||
"plugin:nuxt/recommended" | |||
], | |||
plugins: ['import', 'promise', 'lodash', 'vue'], | |||
settings: { | |||
'import/resolver': { | |||
nuxt: {} | |||
} | |||
globals: { | |||
$dream: false, | |||
$nucleus: false, | |||
$rollbar: false, | |||
$settings: false, | |||
$tools: false, | |||
AppError: false | |||
}, | |||
parserOptions: { | |||
parser: "babel-eslint" | |||
}, | |||
plugins: [ | |||
"import", | |||
"promise", | |||
"lodash", | |||
"vue" | |||
], | |||
root: true, | |||
rules: { | |||
quotes: ['error', 'single', { allowTemplateLiterals: true }], | |||
semi: ['error', 'never'], | |||
'linebreak-style': 'off', | |||
'max-len': 'off', | |||
'comma-dangle': 'warn', | |||
'class-methods-use-this': 'off', | |||
'padded-blocks': ['error', 'never'], | |||
'no-await-in-loop': 'warn', | |||
'no-console': 'off', | |||
'no-continue': 'off', | |||
'no-restricted-syntax': 'off', | |||
'no-useless-constructor': 'warn', | |||
'no-underscore-dangle': ['error', { allowAfterThis: true }], | |||
'no-shadow': 'off', | |||
'no-unused-vars': 'warn', | |||
'no-debugger': 'warn', | |||
'no-restricted-globals': 'warn', | |||
'no-unreachable': 'warn', | |||
'no-lone-blocks': 'off', | |||
'no-param-reassign': 'off', | |||
'object-shorthand': ['error', 'always'], | |||
'quote-props': ['error', 'as-needed'], | |||
'spaced-comment': 'warn', | |||
'import/no-webpack-loader-syntax': 'off', | |||
'vue/no-v-html': 'off', | |||
'vue/html-indent': ['warn', 2], | |||
'vue/html-self-closing': 'error', | |||
'vue/singleline-html-element-content-newline': 'warn', | |||
'vue/html-closing-bracket-newline': [ | |||
'warn', | |||
"class-methods-use-this": "off", | |||
"comma-dangle": "warn", | |||
"import/default": "off", | |||
"import/no-webpack-loader-syntax": "off", | |||
"import/order": "error", | |||
"import/prefer-default-export": "off", | |||
"linebreak-style": "warn", | |||
"lodash/import-scope": [ | |||
"warn", | |||
"member" | |||
], | |||
"lodash/prefer-constant": "off", | |||
"lodash/prefer-immutable-method": "warn", | |||
"lodash/prefer-includes": "warn", | |||
"lodash/prefer-lodash-method": "off", | |||
"lodash/prefer-lodash-typecheck": "warn", | |||
"lodash/prefer-noop": "off", | |||
"max-len": "off", | |||
"no-await-in-loop": "warn", | |||
"no-console": "off", | |||
"no-continue": "off", | |||
"no-debugger": "error", | |||
"no-lone-blocks": "error", | |||
"no-param-reassign": "error", | |||
"no-restricted-globals": "warn", | |||
"no-restricted-syntax": "off", | |||
"no-shadow": "off", | |||
"no-underscore-dangle": [ | |||
"error", | |||
{ | |||
singleline: 'never', | |||
multiline: 'never' | |||
allowAfterThis: true | |||
} | |||
], | |||
'lodash/import-scope': ['warn', 'full'], | |||
'lodash/prefer-lodash-method': 'off', | |||
'lodash/prefer-immutable-method': 'warn', | |||
'lodash/prefer-noop': 'off', | |||
'lodash/prefer-includes': 'warn', | |||
'lodash/prefer-lodash-typecheck': 'warn', | |||
'lodash/prefer-constant': 'off', | |||
'import/order': 'error', | |||
'import/prefer-default-export': 'off', | |||
'import/default': 'off', | |||
'import/no-extraneous-dependencies': [ | |||
'off', | |||
"no-unreachable": "warn", | |||
"no-unused-vars": "warn", | |||
"no-useless-constructor": "warn", | |||
"nuxt/no-globals-in-created": "off", | |||
"object-shorthand": [ | |||
"error", | |||
"always" | |||
], | |||
"padded-blocks": [ | |||
"error", | |||
"never" | |||
], | |||
"quote-props": [ | |||
"error", | |||
"as-needed" | |||
], | |||
quotes: [ | |||
"error", | |||
"single", | |||
{ | |||
devDependencies: false, | |||
optionalDependencies: false, | |||
peerDependencies: false | |||
allowTemplateLiterals: true | |||
} | |||
], | |||
'nuxt/no-globals-in-created': 'off' | |||
}, | |||
globals: { | |||
$dream: false, | |||
$settings: false, | |||
$rollbar: false, | |||
$nucleus: false, | |||
$tools: false, | |||
AppError: false, | |||
semi: [ | |||
"error", | |||
"never" | |||
], | |||
"spaced-comment": "warn", | |||
"vue/html-closing-bracket-newline": [ | |||
"warn", | |||
{ | |||
multiline: "never", | |||
singleline: "never" | |||
} | |||
], | |||
"vue/html-indent": [ | |||
"warn", | |||
2 | |||
], | |||
"vue/html-self-closing": "error", | |||
"vue/no-v-html": "off", | |||
"vue/singleline-html-element-content-newline": "warn" | |||
}, | |||
settings: { | |||
"import/resolver": { | |||
nuxt: {} | |||
} | |||
} | |||
} |
@@ -7,23 +7,23 @@ | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
const { app, BrowserWindow } = require('electron') | |||
const http = require('http') | |||
const path = require('path') | |||
const fs = require('fs') | |||
const contextMenu = require('electron-context-menu') | |||
const utils = require('electron-utils') | |||
const logger = require('logplease').create('electron') | |||
const AppError = require('./modules/error') | |||
const { settings, nucleus, rollbar } = require('./modules') | |||
const { AppError } = require('./scripts') | |||
const { settings, nucleus, rollbar } = require('./scripts/services') | |||
const { system } = require('./scripts/tools') | |||
const config = require('../nuxt.config') | |||
// Indicate to NuxtJS the root directory of the project | |||
// NuxtJS root directory | |||
config.rootDir = path.dirname(__dirname) | |||
// Copyright. | |||
// DO NOT DELETE OR ALTER THIS SECTION! | |||
// copyright | |||
console.log(` | |||
DreamTime. | |||
Copyright (C) DreamNet. All rights reserved. | |||
@@ -33,10 +33,9 @@ console.log(` | |||
the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html> | |||
`) | |||
// Debug | |||
console.log('Starting...') | |||
logger.info('Starting...') | |||
console.log({ | |||
logger.debug({ | |||
env: process.env.NODE_ENV, | |||
paths: { | |||
appPath: app.getAppPath(), | |||
@@ -47,7 +46,7 @@ console.log({ | |||
class DreamApp { | |||
/** | |||
* Start the magic! | |||
* Start the app! | |||
*/ | |||
static async start() { | |||
await this.setup() | |||
@@ -56,7 +55,7 @@ class DreamApp { | |||
} | |||
/** | |||
* Prepare the application for use | |||
* Prepare the application. | |||
*/ | |||
static async setup() { | |||
// https://electronjs.org/docs/tutorial/notifications#windows | |||
@@ -65,16 +64,17 @@ class DreamApp { | |||
// https://github.com/sindresorhus/electron-util#enforcemacosapplocation-macos | |||
utils.enforceMacOSAppLocation() | |||
// User settings | |||
await settings.init() | |||
// system stats. | |||
await system.setup() | |||
// user settings. | |||
await settings.setup() | |||
// Analytics & App settings | |||
// https://nucleus.sh/docs/gettingstarted | |||
await nucleus.init() | |||
// analytics. | |||
await nucleus.setup() | |||
// Error reporting | |||
// https://docs.rollbar.com/docs/nodejs | |||
rollbar.init() | |||
// bug tracking. | |||
rollbar.setup() | |||
// | |||
this.createModelsDir() |
@@ -1,31 +0,0 @@ | |||
class AppError extends Error { | |||
/** | |||
* | |||
* @param {*} message | |||
*/ | |||
constructor(message, error, level = 'error') { | |||
if (level !== 'debug') { | |||
if ($rollbar.isEnabled) { | |||
const response = $rollbar[level](error || new Error(message)) | |||
if (response.uuid) { | |||
message += ` | |||
\nFor more information please report the following URL on Github or to the developers:\n | |||
https://rollbar.com/occurrence/uuid/?uuid=${response.uuid}` | |||
} else { | |||
message += ` | |||
\nFor more information please take a screenshot and report the following on Github or to the developers:\n | |||
${error}` | |||
} | |||
} else if (error) { | |||
message += ` | |||
\nFor more information please take a screenshot and report the following on Github or to the developers:\n | |||
${error}` | |||
} | |||
} | |||
super(message) | |||
} | |||
} | |||
module.exports = AppError |
@@ -1,7 +0,0 @@ | |||
/* eslint-disable global-require */ | |||
module.exports = { | |||
AppError: require('./error'), | |||
settings: require('./settings'), | |||
nucleus: require('./nucleus'), | |||
rollbar: require('./rollbar'), | |||
} |
@@ -1,90 +0,0 @@ | |||
/* eslint-disable no-underscore-dangle */ | |||
const Nucleus = require('electron-nucleus') | |||
const axios = require('axios') | |||
const debug = require('debug').default('app:electron:modules:nucleus') | |||
const settings = require('./settings') | |||
/** | |||
* https://nucleus.sh | |||
* Service that provides us with analytical information and global application settings | |||
*/ | |||
const nucleus = { | |||
isEnabled: false, | |||
_nucleus: undefined, | |||
_settings: {}, | |||
/** | |||
* Initialize the service, this is called automatically | |||
*/ | |||
async init() { | |||
if (!this.can()) { | |||
// Can't start the service | |||
return | |||
} | |||
// Nucleus Configuration | |||
const config = { | |||
disableTracking: settings.telemetry.enabled === false, | |||
disableErrorReports: true, | |||
userId: settings.user, | |||
version: process.env.APP_VERSION, | |||
persist: false, | |||
} | |||
try { | |||
// Create the Nucleus instance | |||
this._nucleus = Nucleus(this.getAppId(), config) | |||
// Get global application settings | |||
this._settings = (await axios.get( | |||
`https://nucleus.sh/app/${this.getAppId()}/customdata`, | |||
)).data | |||
this.isEnabled = true | |||
debug('Nucleus initialized!', { | |||
config, | |||
settings: this._settings, | |||
}) | |||
} catch (err) { | |||
console.warn('Error at connecting to Nucleus', err) | |||
} | |||
}, | |||
/** | |||
* Returns the APP ID to use the service | |||
*/ | |||
getAppId() { | |||
return process.env.NUCLEUS_APPID | |||
}, | |||
/** | |||
* Returns if the service can be used | |||
*/ | |||
can() { | |||
return this.getAppId() | |||
}, | |||
} | |||
module.exports = new Proxy(nucleus, { | |||
get(obj, prop) { | |||
if (prop in obj) { | |||
return obj[prop] | |||
} | |||
if (prop in obj._settings) { | |||
return obj._settings[prop] | |||
} | |||
if (obj._nucleus && prop in obj._nucleus) { | |||
if (obj.isEnabled) { | |||
return obj._nucleus[prop] | |||
} | |||
} | |||
return () => { } | |||
}, | |||
}) |
@@ -1,95 +0,0 @@ | |||
/* eslint-disable no-underscore-dangle */ | |||
const _ = require('lodash') | |||
const { Rollbar } = require('rollbar') | |||
const debug = require('debug').default('app:electron:modules:rollbar') | |||
const settings = require('./settings') | |||
const nucleus = require('./nucleus') | |||
const instance = { | |||
isEnabled: false, | |||
_rollbar: undefined, | |||
/** | |||
* | |||
*/ | |||
init() { | |||
if (!this.can()) { | |||
return | |||
} | |||
const config = this.getConfig() | |||
try { | |||
this._rollbar = new Rollbar(config) | |||
this.isEnabled = true | |||
debug('Rollbar initialized!', config) | |||
} catch (err) { | |||
console.warn('Error at connecting to Rollbar', err) | |||
} | |||
}, | |||
/** | |||
* | |||
*/ | |||
getConfig() { | |||
return { | |||
accessToken: this.getAccessToken(), | |||
captureUncaught: true, | |||
captureUnhandledRejections: true, | |||
captureIp: 'anonymize', | |||
verbose: process.env.NODE_ENV === 'development', | |||
nodeSourceMaps: true, | |||
reportLevel: 'warning', | |||
payload: { | |||
environment: | |||
process.env.NODE_ENV !== 'development' ? 'production' : 'development', | |||
person: { | |||
id: settings.user, | |||
}, | |||
client: { | |||
javascript: { | |||
source_map_enabled: true, | |||
code_version: process.env.APP_VERSION, | |||
}, | |||
}, | |||
settings: settings._settings, | |||
}, | |||
} | |||
}, | |||
/** | |||
* | |||
*/ | |||
getAccessToken() { | |||
return ( | |||
process.env.ROLLBAR_ACCESS_TOKEN | |||
|| _.get(nucleus, 'keys.rollbar_access_token') | |||
) | |||
}, | |||
/** | |||
* | |||
*/ | |||
can() { | |||
return settings.telemetry.enabled && this.getAccessToken() | |||
}, | |||
} | |||
module.exports = new Proxy(instance, { | |||
get(obj, prop) { | |||
if (prop in obj) { | |||
return obj[prop] | |||
} | |||
if (obj._rollbar && prop in obj._rollbar) { | |||
if (obj.isEnabled) { | |||
return obj._rollbar[prop] | |||
} | |||
} | |||
return () => { } | |||
}, | |||
}) |
@@ -0,0 +1,95 @@ | |||
// DreamTime. | |||
// Copyright (C) DreamNet. All rights reserved. | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License 3.0 as published by | |||
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html> | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
const _ = require('lodash') | |||
const { api } = require('electron-utils') | |||
const logger = require('logplease').create('electron:scripts:error') | |||
const { rollbar } = require('./services/rollbar') | |||
/** | |||
* @typedef {Object} ErrorOptions | |||
* @property {string} title | |||
* @property {Error} error | |||
* @property {string} level | |||
* @property {Object} extra | |||
*/ | |||
class AppError extends Error { | |||
/** | |||
* | |||
* @param {string} message | |||
* @param {ErrorOptions} options | |||
*/ | |||
constructor(message, options = {}) { | |||
super(message) | |||
this.options = { | |||
title: 'A problem has occurred.', | |||
error: undefined, | |||
level: 'error', | |||
extra: {}, | |||
...options, | |||
} | |||
} | |||
report() { | |||
const { title, level, extra } = this.options | |||
// logger | |||
logger[level](`💔 ${this.message}`) | |||
if (rollbar.enabled) { | |||
const error = this.options.error || Error(this.message) | |||
try { | |||
const response = rollbar[level](error, { | |||
title, | |||
message: this.message, | |||
...extra, | |||
}) | |||
if (response.uuid) { | |||
this.message += `\nFor more information please report the following:\nhttps://rollbar.com/occurrence/uuid/?uuid=${response.uuid}` | |||
} | |||
} catch (err) { | |||
logger.warn('💔 Error trying to report the error!', err) | |||
} | |||
} | |||
this.show() | |||
api.app.exit() | |||
} | |||
show() { | |||
api.dialog.showErrorBox( | |||
this.options.title, | |||
this.message, | |||
) | |||
} | |||
static handle(error) { | |||
let appError = error | |||
if (!(appError instanceof AppError)) { | |||
appError = new AppError( | |||
_.isError(appError) ? error.message : 'The program has detected an unknown error. Sorry!', | |||
{ | |||
error: _.isError(appError) ? appError : new Error(appError), | |||
}, | |||
) | |||
} | |||
appError.report() | |||
} | |||
} | |||
module.exports = { | |||
AppError, | |||
} |
@@ -0,0 +1,6 @@ | |||
const { AppError } = require('./error') | |||
/* eslint-disable global-require */ | |||
module.exports = { | |||
AppError, | |||
} |
@@ -0,0 +1,159 @@ | |||
// DreamTime. | |||
// Copyright (C) DreamNet. All rights reserved. | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License 3.0 as published by | |||
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html> | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
const { | |||
set, get, isNil, isPlainObject, | |||
} = require('lodash') | |||
const fs = require('fs') | |||
const logger = require('logplease').create('electron:scripts:services') | |||
class BaseService { | |||
/** | |||
* the payload. | |||
* a proxy will be used to get or set this information. | |||
* | |||
* @type {Object} | |||
*/ | |||
payload = {} | |||
/** | |||
* the service. | |||
* | |||
* @type {Object} | |||
*/ | |||
_service | |||
/** | |||
* service enabled? | |||
* | |||
* @type {Boolean} | |||
*/ | |||
enabled: false | |||
/** | |||
* file where to save the payload. | |||
* | |||
* @type {string} | |||
*/ | |||
get path() { | |||
return null | |||
} | |||
/** | |||
* Create a new instance with a Proxy. | |||
* | |||
* @return {BaseService} | |||
*/ | |||
static make() { | |||
return new Proxy(new this(), { | |||
get: (obj, prop) => { | |||
if (prop in obj) { | |||
return obj[prop] | |||
} | |||
/* eslint-disable no-underscore-dangle */ | |||
if (obj._service && prop in obj._service) { | |||
return obj._service[prop] | |||
} | |||
/* eslint-enable no-underscore-dangle */ | |||
if (prop in obj.payload) { | |||
return obj.payload[prop] | |||
} | |||
return undefined | |||
}, | |||
/* eslint-disable no-param-reassign */ | |||
set: (obj, prop, value) => { | |||
if (!isNil(obj.payload)) { | |||
if (prop in obj.payload) { | |||
obj.payload[prop] = value | |||
obj.save() | |||
return true | |||
} | |||
} | |||
obj[prop] = value | |||
return true | |||
}, | |||
/* eslint-enable no-param-reassign */ | |||
}) | |||
} | |||
/** | |||
* Setup service | |||
*/ | |||
async setup() { | |||
await this.load() | |||
} | |||
/** | |||
* Load the service file. | |||
*/ | |||
async load() { | |||
if (isNil(this.path)) { | |||
return | |||
} | |||
this.payload = JSON.parse(fs.readFileSync(this.path)) | |||
logger.info('💖 Service loaded.') | |||
logger.debug(this.payload) | |||
} | |||
/** | |||
* Save the service file. | |||
* This function is called automatically if you set a first level variable. | |||
*/ | |||
async save() { | |||
if (isNil(this.path)) { | |||
return | |||
} | |||
const payload = JSON.stringify(this.payload, null, 2) | |||
fs.writeFileSync(this.getPath(), payload) | |||
logger.debug('💖 Service saved.') | |||
} | |||
/** | |||
* Returns the value | |||
* | |||
* @param {string} path | |||
*/ | |||
get(path = '') { | |||
if (path.length === 0) { | |||
return this.payload | |||
} | |||
return get(this.payload, path) | |||
} | |||
/** | |||
* Set a new value in the settings | |||
* | |||
* @param {any} path | |||
* @param {any} payload | |||
*/ | |||
set(path, payload) { | |||
if (isPlainObject(path)) { | |||
this.payload = path | |||
this.save() | |||
} | |||
this.payload = set(this.payload, path, payload) | |||
this.save() | |||
} | |||
} | |||
module.exports = { | |||
BaseService, | |||
} |
@@ -0,0 +1,18 @@ | |||
// DreamTime. | |||
// Copyright (C) DreamNet. All rights reserved. | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License 3.0 as published by | |||
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html> | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
const { settings } = require('./settings') | |||
const { nucleus } = require('./nucleus') | |||
const { rollbar } = require('./rollbar') | |||
module.exports = { | |||
settings, | |||
nucleus, | |||
rollbar, | |||
} |
@@ -0,0 +1,85 @@ | |||
// DreamTime. | |||
// Copyright (C) DreamNet. All rights reserved. | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License 3.0 as published by | |||
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html> | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
const { isNil } = require('lodash') | |||
const Nucleus = require('nucleus-nodejs') | |||
const axios = require('axios') | |||
const logger = require('logplease').create('electron:scripts:services:nucleus') | |||
const { BaseService } = require('./base') | |||
const { system } = require('../tools') | |||
const { settings } = require('./settings') | |||
/** | |||
* https://nucleus.sh | |||
* Analytics and bug tracking for Javascript desktop apps. | |||
*/ | |||
class NucleusService extends BaseService { | |||
/** | |||
* @type {string} | |||
*/ | |||
get appId() { | |||
return process.env.NUCLEUS_APPID | |||
} | |||
/** | |||
* @type {boolean} | |||
*/ | |||
get can() { | |||
return !isNil(this.appId) | |||
} | |||
/** | |||
* Setup service | |||
*/ | |||
async setup() { | |||
if (!this.can) { | |||
return | |||
} | |||
try { | |||
// nucleus configuration | |||
const config = { | |||
disableTracking: settings.telemetry.enabled === false, | |||
disableErrorReports: true, | |||
userId: settings.user, | |||
version: process.env.APP_VERSION, | |||
persist: true, | |||
} | |||
Nucleus.init(this.appId, config) | |||
Nucleus.appStarted() | |||
this._service = Nucleus | |||
await this.fetchData() | |||
setInterval(this.fetchData.bind(this), 15 * 60 * 1000) | |||
this.enabled = true | |||
logger.info('Nucleus enabled!') | |||
} catch (err) { | |||
logger.warn('💔 Error trying to start Nucleus!', err) | |||
} | |||
} | |||
async fetchData() { | |||
if (!system.online) { | |||
return | |||
} | |||
this.payload = (await axios.get( | |||
`https://nucleus.sh/app/${this.appId}/customdata`, | |||
)).data | |||
} | |||
} | |||
module.exports = { | |||
nucleus: NucleusService.make(), | |||
} |
@@ -0,0 +1,87 @@ | |||
// DreamTime. | |||
// Copyright (C) DreamNet. All rights reserved. | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License 3.0 as published by | |||
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html> | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
const { isNil, isString, get } = require('lodash') | |||
const { Rollbar } = require('rollbar') | |||
const logger = require('logplease').create('electron:scripts:services:rollbar') | |||
const { BaseService } = require('./base') | |||
const { settings } = require('./settings') | |||
const { nucleus } = require('./nucleus') | |||
/** | |||
* https://rollbar.com | |||
* Bug tracking service. | |||
*/ | |||
class RollbarService extends BaseService { | |||
/** | |||
* @type {string} | |||
*/ | |||
get accessToken() { | |||
return process.env.ROLLBAR_ACCESS_TOKEN || get(nucleus, 'keys.rollbar_access_token') | |||
} | |||
/** | |||
* @type {boolean} | |||
*/ | |||
get can() { | |||
return !isNil(settings.telemetry.enabled) && isString(this.accessToken) | |||
} | |||
/** | |||
* @type {Object} | |||
*/ | |||
get config() { | |||
return { | |||
accessToken: this.accessToken, | |||
captureUncaught: true, | |||
captureUnhandledRejections: true, | |||
captureIp: 'anonymize', | |||
verbose: process.env.NODE_ENV === 'development', | |||
nodeSourceMaps: true, | |||
reportLevel: 'warning', | |||
payload: { | |||
environment: | |||
process.env.NODE_ENV !== 'development' ? 'production' : 'development', | |||
person: { | |||
id: settings.user, | |||
}, | |||
client: { | |||
javascript: { | |||
source_map_enabled: true, | |||
code_version: process.env.APP_VERSION, | |||
}, | |||
}, | |||
settings: settings.payload, | |||
}, | |||
} | |||
} | |||
/** | |||
* Setup service | |||
*/ | |||
async setup() { | |||
if (!this.can) { | |||
return | |||
} | |||
try { | |||
this._service = new Rollbar(this.config) | |||
this.enabled = true | |||
logger.info('Rollbar enabled!') | |||
} catch (err) { | |||
logger.warn('💔 Error trying to start Rollbar!', err) | |||
} | |||
} | |||
} | |||
module.exports = { | |||
rollbar: RollbarService.make(), | |||
} |
@@ -1,48 +1,59 @@ | |||
// DreamTime. | |||
// Copyright (C) DreamNet. All rights reserved. | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License 3.0 as published by | |||
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html> | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
const fs = require('fs') | |||
const _ = require('lodash') | |||
const { | |||
memoize, isNil, round, cloneDeep, | |||
} = require('lodash') | |||
const uuid = require('uuid') | |||
const { api } = require('electron-utils') | |||
const debug = require('debug').default('app:electron:modules:settings') | |||
const tools = require('../tools') | |||
const logger = require('logplease').create('electron:scripts:services:settings') | |||
const { BaseService } = require('./base') | |||
const { AppError } = require('../error') | |||
const { paths, system } = require('../tools') | |||
/** | |||
* User settings. | |||
* This class is responsible for loading, saving and offering easy access to user settings. | |||
* | |||
* Examples: | |||
* settings.processing.device -> 'CPU' | |||
* | |||
* settings.telemetry.enabled = false | |||
* settings.save() | |||
*/ | |||
const settings = { | |||
class SettingsService extends BaseService { | |||
/** | |||
* Initialize the settings | |||
* file where to save the payload. | |||
* | |||
* @type {string} | |||
*/ | |||
async init() { | |||
await this._initDefault() | |||
get path() { | |||
return memoize(() => paths.get('userData', 'settings.json'))('settings.path') | |||
} | |||
this._path = tools.paths.get('userData', 'settings.json') | |||
this._settings = {} | |||
/** | |||
* settings defaults. | |||
* | |||
* @type {Object} | |||
*/ | |||
_defaults = {} | |||
await this._ensure() | |||
/** | |||
* Setup service | |||
*/ | |||
async setup() { | |||
this._setupDefaults() | |||
this.load() | |||
await this._create() | |||
await this._upgrade() | |||
}, | |||
await this.load() | |||
/** | |||
* | |||
*/ | |||
async _initDefault() { | |||
let hasGPU = false | |||
await this._upgrade() | |||
} | |||
try { | |||
hasGPU = (await tools.getGpusList()).length > 0 | |||
// eslint-disable-next-line | |||
} catch (err) { } | |||
_setupDefaults() { | |||
const hasGPU = system.graphics.length > 0 | |||
const cores = round(system.cores / 2) | |||
this._default = { | |||
version: 3, | |||
@@ -52,7 +63,7 @@ const settings = { | |||
processing: { | |||
device: hasGPU ? 'GPU' : 'CPU', | |||
gpus: [0], | |||
cores: 4, | |||
cores, | |||
disablePersistentGan: false, | |||
usePython: process.env.NODE_ENV === 'development', | |||
}, | |||
@@ -108,42 +119,36 @@ const settings = { | |||
}, | |||
folders: { | |||
cropped: tools.paths.get('temp'), | |||
models: tools.paths.get('userData', 'models'), | |||
masks: tools.paths.get('userData', 'masks'), | |||
cli: tools.paths.get('userData', 'dreampower'), | |||
cropped: paths.get('temp'), | |||
models: paths.get('userData', 'models'), | |||
masks: paths.get('userData', 'masks'), | |||
cli: paths.get('userData', 'dreampower'), | |||
}, | |||
telemetry: { | |||
enabled: true, | |||
}, | |||
} | |||
}, | |||
} | |||
/** | |||
* Make sure the settings file exists | |||
* Create the settings file if it does not exist | |||
*/ | |||
async _ensure() { | |||
if (fs.existsSync(this._path)) { | |||
// Exists | |||
async _create() { | |||
if (fs.existsSync(this.path)) { | |||
return | |||
} | |||
try { | |||
fs.writeFileSync(this._path, JSON.stringify(this._default, null, 2)) | |||
} catch (err) { | |||
api.dialog.showErrorBox( | |||
'The program could not be started', | |||
`An error occurred while trying to save the settings, please make sure the program has the necessary permissions to write to:\n${this._path}`, | |||
) | |||
api.app.exit() | |||
fs.outputFileSync(this.path, JSON.stringify(this._defaults, null, 2)) | |||
} catch (error) { | |||
throw new AppError(`Could not create settings file. Please make sure the program has the necessary permissions to write to:\n${this.path}`, { error }) | |||
} | |||
}, | |||
} | |||
/** | |||
* Check if it is necessary to update the settings file. | |||
* - Ugly code, here we go! | |||
* legacy code :WutFaceW: | |||
*/ | |||
async _upgrade() { | |||
const currentVersion = this._settings.version || 1 | |||
@@ -154,7 +159,7 @@ const settings = { | |||
} | |||
const currentSettings = this._settings | |||
const newSettings = _.cloneDeep(currentSettings) | |||
const newSettings = cloneDeep(currentSettings) | |||
// Upgrade 1 -> 2 | |||
if (currentVersion === 1 && newVersion === 2) { | |||
@@ -215,91 +220,9 @@ const settings = { | |||
} | |||
this.set(newSettings) | |||
}, | |||
/** | |||
* Returns the value of the settings in the path | |||
* | |||
* @param {string} path | |||
*/ | |||
get(path = '') { | |||
if (path.length === 0) { | |||
return this._settings | |||
} | |||
return _.get(this._settings, path) | |||
}, | |||
/** | |||
* Set a new value in the settings | |||
* | |||
* @param {any} path | |||
* @param {any} payload | |||
*/ | |||
set(path, payload) { | |||
if (_.isPlainObject(path)) { | |||
this._settings = path | |||
this.save() | |||
} | |||
this._settings = _.set(this._settings, path, payload) | |||
this.save() | |||
}, | |||
/** | |||
* Load the settings file. If it is already loaded then it refreshes. | |||
*/ | |||
async load() { | |||
this._settings = JSON.parse(fs.readFileSync(this._path)) | |||
debug('User Settings loaded!', { path: this._path, settings: this._settings }) | |||
}, | |||
/** | |||
* Save the settings. | |||
* This function is called automatically if you set a first level variable. | |||
*/ | |||
async save() { | |||
const payload = JSON.stringify(this._settings, null, 2) | |||
fs.writeFileSync(this._path, payload) | |||
if (window && window.$rollbar) { | |||
$rollbar.configure({ | |||
payload: { | |||
settings: this._settings, | |||
}, | |||
}) | |||
} | |||
}, | |||
} | |||
} | |||
module.exports = new Proxy(settings, { | |||
get: (obj, prop) => { | |||
if (prop in obj) { | |||
return obj[prop] | |||
} | |||
/* eslint-disable no-underscore-dangle */ | |||
if (prop in obj._settings) { | |||
return obj._settings[prop] | |||
} | |||
/* eslint-enable no-underscore-dangle */ | |||
return undefined | |||
}, | |||
set: (obj, prop, value) => { | |||
/* eslint-disable no-underscore-dangle */ | |||
if (!_.isNil(obj._settings)) { | |||
if (prop in obj._settings) { | |||
obj._settings[prop] = value | |||
obj.save() | |||
return true | |||
} | |||
} | |||
/* eslint-enable no-underscore-dangle */ | |||
obj[prop] = value | |||
return true | |||
}, | |||
}) | |||
module.exports = { | |||
settings: SettingsService.make(), | |||
} |
@@ -179,6 +179,7 @@ module.exports = { | |||
download(url, options = {}) { | |||
const bus = new EventBus() | |||
// eslint-disable-next-line no-param-reassign | |||
options = { | |||
// showSaveAs: false, | |||
directory: api.app.getPath('downloads'), |
@@ -7,8 +7,9 @@ | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
const { filter } = require('lodash') | |||
const si = require('systeminformation') | |||
const _ = require('lodash') | |||
const isOnline = require('is-online') | |||
class System { | |||
/** | |||
@@ -26,24 +27,38 @@ class System { | |||
*/ | |||
_memory | |||
constructor() { | |||
this._setup() | |||
} | |||
/** | |||
* @type {boolean} | |||
*/ | |||
online | |||
/** | |||
* | |||
*/ | |||
async _setup() { | |||
this._graphics = await si.graphics() | |||
this._cpu = await si.cpu() | |||
this._memory = await si.mem() | |||
const { | |||
graphics, | |||
cpu, | |||
mem, | |||
online, | |||
} = await Promise.all([ | |||
si.graphics(), | |||
si.cpu(), | |||
si.mem(), | |||
isOnline(), | |||
]) | |||
this._graphics = graphics | |||
this._cpu = cpu | |||
this._memory = mem | |||
this.online = online | |||
} | |||
/** | |||
* @return {Array} | |||
*/ | |||
get graphics() { | |||
return _.filter(this._graphics.controllers, { vendor: 'NVIDIA' }) | |||
return filter(this._graphics.controllers, { vendor: 'NVIDIA' }) | |||
} | |||
/** |
@@ -46,21 +46,24 @@ | |||
"cropperjs": "^1.5.6", | |||
"debug": "^4.1.1", | |||
"deferred": "^0.7.11", | |||
"electron": "^7.1.1", | |||
"electron-context-menu": "^0.15.1", | |||
"electron-nucleus": "^2.4.0", | |||
"electron-utils": "^3.0.11", | |||
"filesize": "^6.0.1", | |||
"fs-extra": "^8.1.0", | |||
"gpu-info": "^0.0.1", | |||
"gsap": "^3.0.1", | |||
"image-js": "^0.21.8", | |||
"is-online": "^8.2.0", | |||
"js-event-bus": "^1.0.0", | |||
"lodash": "^4.17.15", | |||
"logplease": "^1.2.15", | |||
"markdown": "^0.5.0", | |||
"md5": "^2.2.1", | |||
"mime-types": "^2.1.25", | |||
"moment": "^2.24.0", | |||
"node-7z": "^2.0.3", | |||
"nucleus-nodejs": "^3.0.1", | |||
"nuxt": "^2.10.2", | |||
"patch-package": "^6.2.0", | |||
"postinstall-postinstall": "^2.0.0", | |||
@@ -89,7 +92,6 @@ | |||
"@octokit/rest": "^16.35.0", | |||
"babel-eslint": "^10.0.3", | |||
"cross-env": "^6.0.3", | |||
"electron": "^7.1.1", | |||
"electron-builder": "^22.1.0", | |||
"electron-devtools-installer": "^2.2.4", | |||
"electron-rebuild": "^1.8.6", | |||
@@ -113,6 +115,7 @@ | |||
"rollbar-sourcemap-webpack-plugin": "^2.5.1", | |||
"sass-loader": "^8.0.0", | |||
"tailwindcss": "^1.1.3", | |||
"tailwindcss-alpha": "hacknug/tailwindcss-alpha#feature/tests" | |||
"tailwindcss-alpha": "hacknug/tailwindcss-alpha#feature/tests", | |||
"webpack-cli": "^3.3.10" | |||
} | |||
} |