aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/apiBase.js21
-rw-r--r--src/api/server/ServerApi.js23
-rw-r--r--src/components/services/content/ServiceView.js30
-rw-r--r--src/components/services/content/ServiceWebview.js5
-rw-r--r--src/components/ui/FeatureList.js2
-rw-r--r--src/config.js1
-rw-r--r--src/lib/Menu.js3
-rw-r--r--src/stores/AppStore.js6
-rw-r--r--src/stores/FeaturesStore.js5
-rw-r--r--src/stores/GlobalErrorStore.js45
-rw-r--r--src/stores/ServicesStore.js3
-rw-r--r--src/stores/UserStore.js13
-rw-r--r--src/stores/lib/CachedRequest.js4
-rw-r--r--src/styles/tabs.scss2
14 files changed, 100 insertions, 63 deletions
diff --git a/src/api/apiBase.js b/src/api/apiBase.js
index 561b025f0..85dd0f3df 100644
--- a/src/api/apiBase.js
+++ b/src/api/apiBase.js
@@ -6,25 +6,19 @@ import {
6} from '../environment'; 6} from '../environment';
7import { 7import {
8 LOCAL_SERVER, 8 LOCAL_SERVER,
9 SERVER_NOT_LOADED,
9} from '../config'; 10} from '../config';
10 11
11const apiBase = () => { 12const apiBase = (withVersion = true) => {
12 let url; 13 let url;
13 14
14 if (!window.ferdi 15 if (!window.ferdi
15 || !window.ferdi.stores.settings 16 || !window.ferdi.stores.settings
16 || !window.ferdi.stores.settings.all 17 || !window.ferdi.stores.settings.all
17 || !window.ferdi.stores.settings.all.app.server) { 18 || !window.ferdi.stores.settings.all.app.server) {
18 // Stores have not yet been loaded - send invalid URL to force a retry when stores are loaded 19 // Stores have not yet been loaded - return SERVER_NOT_LOADED to force a retry when stores are loaded
19 // "Why 1.1.1.1 as the default, invalid URL?" 20 return SERVER_NOT_LOADED;
20 // 1.1.1.1 is the server for Cloudflare's DNS service and will be the same across most networks. 21 } if (window.ferdi.stores.settings.all.app.server === LOCAL_SERVER) {
21 // Using a random IP could result in unwanted connections, using localhost could unwantedly
22 // connect to local develoment servers.
23 // 1.1.1.1 also sends a status 400 response for invalid routes. Other servers may return status 401
24 // on some routes. This would result in Ferdi deleting its current authToken as it thinks it
25 // has gone invalid.
26 url = 'https://1.1.1.1';
27 } else if (window.ferdi.stores.settings.all.app.server === LOCAL_SERVER) {
28 // Use URL for local server 22 // Use URL for local server
29 url = `http://127.0.0.1:${window.ferdi.stores.requests.localServerPort}`; 23 url = `http://127.0.0.1:${window.ferdi.stores.requests.localServerPort}`;
30 } else { 24 } else {
@@ -32,7 +26,10 @@ const apiBase = () => {
32 url = window.ferdi.stores.settings.all.app.server; 26 url = window.ferdi.stores.settings.all.app.server;
33 } 27 }
34 28
35 return `${url}/${API_VERSION}`; 29 if (withVersion) {
30 return `${url}/${API_VERSION}`;
31 }
32 return url;
36}; 33};
37 34
38export default apiBase; 35export default apiBase;
diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js
index a5d636b4e..164bc237b 100644
--- a/src/api/server/ServerApi.js
+++ b/src/api/server/ServerApi.js
@@ -14,8 +14,7 @@ import OrderModel from '../../models/Order';
14 14
15import { sleep } from '../../helpers/async-helpers'; 15import { sleep } from '../../helpers/async-helpers';
16 16
17import { API } from '../../environment'; 17import { RECIPES_PATH, SERVER_NOT_LOADED } from '../../config';
18import { RECIPES_PATH } from '../../config';
19import apiBase from '../apiBase'; 18import apiBase from '../apiBase';
20import { prepareAuthRequest, sendAuthRequest } from '../utils/auth'; 19import { prepareAuthRequest, sendAuthRequest } from '../utils/auth';
21 20
@@ -39,8 +38,6 @@ module.paths.unshift(
39const { app } = remote; 38const { app } = remote;
40const { default: fetch } = remote.require('electron-fetch'); 39const { default: fetch } = remote.require('electron-fetch');
41 40
42const SERVER_URL = API;
43
44export default class ServerApi { 41export default class ServerApi {
45 recipePreviews = []; 42 recipePreviews = [];
46 43
@@ -121,6 +118,10 @@ export default class ServerApi {
121 } 118 }
122 119
123 async userInfo() { 120 async userInfo() {
121 if (apiBase() === SERVER_NOT_LOADED) {
122 throw new Error('Server not loaded');
123 }
124
124 const request = await sendAuthRequest(`${apiBase()}/me`); 125 const request = await sendAuthRequest(`${apiBase()}/me`);
125 if (!request.ok) { 126 if (!request.ok) {
126 throw request; 127 throw request;
@@ -163,6 +164,10 @@ export default class ServerApi {
163 164
164 // Services 165 // Services
165 async getServices() { 166 async getServices() {
167 if (apiBase() === SERVER_NOT_LOADED) {
168 throw new Error('Server not loaded');
169 }
170
166 const request = await sendAuthRequest(`${apiBase()}/me/services`); 171 const request = await sendAuthRequest(`${apiBase()}/me/services`);
167 if (!request.ok) { 172 if (!request.ok) {
168 throw request; 173 throw request;
@@ -287,6 +292,10 @@ export default class ServerApi {
287 } 292 }
288 293
289 async getFeatures() { 294 async getFeatures() {
295 if (apiBase() === SERVER_NOT_LOADED) {
296 throw new Error('Server not loaded');
297 }
298
290 const request = await sendAuthRequest(`${apiBase()}/features`); 299 const request = await sendAuthRequest(`${apiBase()}/features`);
291 if (!request.ok) { 300 if (!request.ok) {
292 throw request; 301 throw request;
@@ -466,7 +475,11 @@ export default class ServerApi {
466 475
467 // Health Check 476 // Health Check
468 async healthCheck() { 477 async healthCheck() {
469 const request = await sendAuthRequest(`${SERVER_URL}/health`, { 478 if (apiBase() === SERVER_NOT_LOADED) {
479 throw new Error('Server not loaded');
480 }
481
482 const request = await sendAuthRequest(`${apiBase(false)}/health`, {
470 method: 'GET', 483 method: 'GET',
471 }, false); 484 }, false);
472 if (!request.ok) { 485 if (!request.ok) {
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js
index 49ee24361..1fff5ef7a 100644
--- a/src/components/services/content/ServiceView.js
+++ b/src/components/services/content/ServiceView.js
@@ -42,10 +42,10 @@ export default @inject('stores', 'actions') @observer class ServiceView extends
42 forceRepaint: false, 42 forceRepaint: false,
43 targetUrl: '', 43 targetUrl: '',
44 statusBarVisible: false, 44 statusBarVisible: false,
45 hibernate: false,
46 hibernationTimer: null,
47 }; 45 };
48 46
47 hibernationTimer = null;
48
49 autorunDisposer = null; 49 autorunDisposer = null;
50 50
51 forceRepaintTimeout = null; 51 forceRepaintTimeout = null;
@@ -73,15 +73,12 @@ export default @inject('stores', 'actions') @observer class ServiceView extends
73 // Service is inactive - start hibernation countdown 73 // Service is inactive - start hibernation countdown
74 this.startHibernationTimer(); 74 this.startHibernationTimer();
75 } else { 75 } else {
76 if (this.state.hibernationTimer) { 76 if (this.hibernationTimer) {
77 // Service is active but we have an active hibernation timer: Clear timeout 77 // Service is active but we have an active hibernation timer: Clear timeout
78 clearTimeout(this.state.hibernationTimer); 78 clearTimeout(this.hibernationTimer);
79 } 79 }
80 80
81 // Service is active, wake up service from hibernation 81 // Service is active, wake up service from hibernation
82 this.setState({
83 hibernate: false,
84 });
85 this.props.actions.service.setHibernation({ 82 this.props.actions.service.setHibernation({
86 serviceId: this.props.service.id, 83 serviceId: this.props.service.id,
87 hibernating: false, 84 hibernating: false,
@@ -90,16 +87,6 @@ export default @inject('stores', 'actions') @observer class ServiceView extends
90 }, 87 },
91 ); 88 );
92 89
93 // Store hibernation status to state, otherwise the webview won't get unloaded correctly
94 reaction(
95 () => this.props.service.isHibernating,
96 () => {
97 this.setState({
98 hibernate: this.props.service.isHibernating,
99 });
100 },
101 );
102
103 // Start hibernation counter if we are in background 90 // Start hibernation counter if we are in background
104 if (!this.props.service.isActive && this.props.stores.settings.all.app.hibernate) { 91 if (!this.props.service.isActive && this.props.stores.settings.all.app.hibernate) {
105 this.startHibernationTimer(); 92 this.startHibernationTimer();
@@ -126,18 +113,13 @@ export default @inject('stores', 'actions') @observer class ServiceView extends
126 const timerDuration = (Number(this.props.stores.settings.all.app.hibernationStrategy) || 300) * 1000; 113 const timerDuration = (Number(this.props.stores.settings.all.app.hibernationStrategy) || 300) * 1000;
127 114
128 const hibernationTimer = setTimeout(() => { 115 const hibernationTimer = setTimeout(() => {
129 this.setState({
130 hibernate: true,
131 });
132 this.props.actions.service.setHibernation({ 116 this.props.actions.service.setHibernation({
133 serviceId: this.props.service.id, 117 serviceId: this.props.service.id,
134 hibernating: true, 118 hibernating: true,
135 }); 119 });
136 }, timerDuration); 120 }, timerDuration);
137 121
138 this.setState({ 122 this.hibernationTimer = hibernationTimer;
139 hibernationTimer,
140 });
141 } 123 }
142 124
143 render() { 125 render() {
@@ -208,7 +190,7 @@ export default @inject('stores', 'actions') @observer class ServiceView extends
208 </Fragment> 190 </Fragment>
209 ) : ( 191 ) : (
210 <> 192 <>
211 {!this.state.hibernate ? ( 193 {!service.isHibernating ? (
212 <> 194 <>
213 {(service.recipe.id === CUSTOM_WEBSITE_ID || showServiceNavigationBar) && ( 195 {(service.recipe.id === CUSTOM_WEBSITE_ID || showServiceNavigationBar) && (
214 <WebControlsScreen service={service} /> 196 <WebControlsScreen service={service} />
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js
index e6ebb6afb..652647470 100644
--- a/src/components/services/content/ServiceWebview.js
+++ b/src/components/services/content/ServiceWebview.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { observable, reaction } from 'mobx'; 4import { observable, reaction } from 'mobx';
5import ElectronWebView from 'react-electron-web-view'; 5import ElectronWebView from 'react-electron-web-view';
6import path from 'path';
6 7
7import ServiceModel from '../../../models/Service'; 8import ServiceModel from '../../../models/Service';
8 9
@@ -51,6 +52,8 @@ class ServiceWebview extends Component {
51 setWebviewReference, 52 setWebviewReference,
52 } = this.props; 53 } = this.props;
53 54
55 const preloadScript = path.join(__dirname, '../../../', 'webview', 'recipe.js');
56
54 return ( 57 return (
55 <ElectronWebView 58 <ElectronWebView
56 ref={(webview) => { 59 ref={(webview) => {
@@ -61,7 +64,7 @@ class ServiceWebview extends Component {
61 }} 64 }}
62 autosize 65 autosize
63 src={service.url} 66 src={service.url}
64 preload="./webview/recipe.js" 67 preload={preloadScript}
65 partition={`persist:service-${service.id}`} 68 partition={`persist:service-${service.id}`}
66 onDidAttach={() => { 69 onDidAttach={() => {
67 setWebviewReference({ 70 setWebviewReference({
diff --git a/src/components/ui/FeatureList.js b/src/components/ui/FeatureList.js
index 7ba8b54d7..f1039709c 100644
--- a/src/components/ui/FeatureList.js
+++ b/src/components/ui/FeatureList.js
@@ -72,7 +72,7 @@ export class FeatureList extends Component {
72 static propTypes = { 72 static propTypes = {
73 className: PropTypes.string, 73 className: PropTypes.string,
74 featureClassName: PropTypes.string, 74 featureClassName: PropTypes.string,
75 plan: PropTypes.oneOf(PLANS), 75 plan: PropTypes.oneOf(Object.values(PLANS)),
76 }; 76 };
77 77
78 static defaultProps = { 78 static defaultProps = {
diff --git a/src/config.js b/src/config.js
index a7389b8bc..bd67aee6b 100644
--- a/src/config.js
+++ b/src/config.js
@@ -112,6 +112,7 @@ export const FILE_SYSTEM_SETTINGS_TYPES = [
112]; 112];
113 113
114export const LOCAL_SERVER = 'You are using Ferdi without a server'; 114export const LOCAL_SERVER = 'You are using Ferdi without a server';
115export const SERVER_NOT_LOADED = 'Ferdi::SERVER_NOT_LOADED';
115 116
116// Set app directory before loading user modules 117// Set app directory before loading user modules
117if (process.env.FERDI_APPDATA_DIR != null) { 118if (process.env.FERDI_APPDATA_DIR != null) {
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index f3fbb0c73..826fe843f 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -678,7 +678,8 @@ const _titleBarTemplateFactory = intl => [
678 ], 678 ],
679 }, 679 },
680 { 680 {
681 label: '&?', 681 label: '?',
682 accelerator: 'Alt+?',
682 submenu: [ 683 submenu: [
683 { 684 {
684 label: intl.formatMessage(menuItems.learnMore), 685 label: intl.formatMessage(menuItems.learnMore),
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index 894c19347..0756a05eb 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -181,7 +181,9 @@ export default class AppStore extends Store {
181 181
182 this.locale = this._getDefaultLocale(); 182 this.locale = this._getDefaultLocale();
183 183
184 this._healthCheck(); 184 setTimeout(() => {
185 this._healthCheck();
186 }, 1000);
185 187
186 this.isSystemDarkModeEnabled = systemPreferences.isDarkMode(); 188 this.isSystemDarkModeEnabled = systemPreferences.isDarkMode();
187 189
@@ -251,7 +253,7 @@ export default class AppStore extends Store {
251 hasCrashed: service.hasCrashed, 253 hasCrashed: service.hasCrashed,
252 isDarkModeEnabled: service.isDarkModeEnabled, 254 isDarkModeEnabled: service.isDarkModeEnabled,
253 })), 255 })),
254 errors: this.stores.globalError.errors, 256 messages: this.stores.globalError.messages,
255 workspaces: this.stores.workspaces.workspaces.map(workspace => ({ id: workspace.id, services: workspace.services })), 257 workspaces: this.stores.workspaces.workspaces.map(workspace => ({ id: workspace.id, services: workspace.services })),
256 windowSettings: readJsonSync(path.join(app.getPath('userData'), 'window-state.json')), 258 windowSettings: readJsonSync(path.join(app.getPath('userData'), 'window-state.json')),
257 settings, 259 settings,
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index ab5d762c7..780cde3a7 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -57,7 +57,10 @@ export default class FeaturesStore extends Store {
57 _updateFeatures = () => { 57 _updateFeatures = () => {
58 const features = Object.assign({}, DEFAULT_FEATURES_CONFIG); 58 const features = Object.assign({}, DEFAULT_FEATURES_CONFIG);
59 if (this.stores.user.isLoggedIn) { 59 if (this.stores.user.isLoggedIn) {
60 const requestResult = this.featuresRequest.execute().result; 60 let requestResult = {};
61 try {
62 requestResult = this.featuresRequest.execute().result;
63 } catch (e) {} // eslint-disable-line no-empty
61 Object.assign(features, requestResult); 64 Object.assign(features, requestResult);
62 } 65 }
63 runInAction('FeaturesStore::_updateFeatures', () => { 66 runInAction('FeaturesStore::_updateFeatures', () => {
diff --git a/src/stores/GlobalErrorStore.js b/src/stores/GlobalErrorStore.js
index 09c215c3e..aacaa247f 100644
--- a/src/stores/GlobalErrorStore.js
+++ b/src/stores/GlobalErrorStore.js
@@ -5,30 +5,52 @@ import Request from './lib/Request';
5export default class GlobalErrorStore extends Store { 5export default class GlobalErrorStore extends Store {
6 @observable error = null; 6 @observable error = null;
7 7
8 @observable errors = []; 8 @observable messages = [];
9 9
10 @observable response = {}; 10 @observable response = {};
11 11
12 constructor(...args) { 12 constructor(...args) {
13 super(...args); 13 super(...args);
14 14
15 window.onerror = this._handleConsoleError.bind(this); 15 window.onerror = (...errorArgs) => {
16 this._handleConsoleError.call(this, ['error', ...errorArgs]);
17 };
16 18
17 const origConsoleError = console.error; 19 const origConsoleError = console.error;
18 console.error = (...errorArgs) => { 20 window.console.error = (...errorArgs) => {
19 this._handleConsoleError.call(this, errorArgs); 21 this._handleConsoleError.call(this, ['error', ...errorArgs]);
20 origConsoleError.apply(this, errorArgs); 22 origConsoleError.apply(this, errorArgs);
21 }; 23 };
22 24
25 const origConsoleLog = console.log;
26 window.console.log = (...logArgs) => {
27 this._handleConsoleError.call(this, ['log', ...logArgs]);
28 origConsoleLog.apply(this, logArgs);
29 };
30
31 const origConsoleInfo = console.info;
32 window.console.info = (...infoArgs) => {
33 this._handleConsoleError.call(this, ['info', ...infoArgs]);
34 origConsoleInfo.apply(this, infoArgs);
35 };
36
23 Request.registerHook(this._handleRequests); 37 Request.registerHook(this._handleRequests);
24 } 38 }
25 39
26 _handleConsoleError(error, url, line) { 40 _handleConsoleError(type, error, url, line) {
27 this.errors.push({ 41 if (typeof type === 'object' && type.length && type.length >= 1) {
28 error, 42 this.messages.push({
29 url, 43 type: type[0],
30 line, 44 info: type,
31 }); 45 });
46 } else {
47 this.messages.push({
48 type,
49 error,
50 url,
51 line,
52 });
53 }
32 } 54 }
33 55
34 _handleRequests = action(async (request) => { 56 _handleRequests = action(async (request) => {
@@ -47,7 +69,8 @@ export default class GlobalErrorStore extends Store {
47 } 69 }
48 } 70 }
49 71
50 this.errors.push({ 72 this.messages.push({
73 type: 'error',
51 request: { 74 request: {
52 result: request.result, 75 result: request.result,
53 wasExecuted: request.wasExecuted, 76 wasExecuted: request.wasExecuted,
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 934a8a6e0..f65faa5a5 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -585,7 +585,8 @@ export default class ServicesStore extends Store {
585 585
586 service.resetMessageCount(); 586 service.resetMessageCount();
587 587
588 service.webview.loadURL(service.url); 588 // service.webview.loadURL(service.url);
589 service.webview.reload();
589 } 590 }
590 591
591 @action _reloadActive() { 592 @action _reloadActive() {
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js
index d6a2e5fde..ec0b0cf8d 100644
--- a/src/stores/UserStore.js
+++ b/src/stores/UserStore.js
@@ -348,7 +348,12 @@ export default class UserStore extends Store {
348 // Reactions 348 // Reactions
349 async _getUserData() { 349 async _getUserData() {
350 if (this.isLoggedIn) { 350 if (this.isLoggedIn) {
351 const data = await this.getUserInfoRequest.execute()._promise; 351 let data;
352 try {
353 data = await this.getUserInfoRequest.execute()._promise;
354 } catch (e) {
355 return false;
356 }
352 357
353 // We need to set the beta flag for the SettingsStore 358 // We need to set the beta flag for the SettingsStore
354 this.actions.settings.update({ 359 this.actions.settings.update({
@@ -408,7 +413,11 @@ export default class UserStore extends Store {
408 } 413 }
409 414
410 async _migrateUserLocale() { 415 async _migrateUserLocale() {
411 await this.getUserInfoRequest._promise; 416 try {
417 await this.getUserInfoRequest._promise;
418 } catch (e) {
419 return false;
420 }
412 421
413 if (!this.data.locale) { 422 if (!this.data.locale) {
414 debug('Migrate "locale" to user data'); 423 debug('Migrate "locale" to user data');
diff --git a/src/stores/lib/CachedRequest.js b/src/stores/lib/CachedRequest.js
index ac8b2bd81..31c7ce241 100644
--- a/src/stores/lib/CachedRequest.js
+++ b/src/stores/lib/CachedRequest.js
@@ -39,7 +39,7 @@ export default class CachedRequest extends Request {
39 }), 0); 39 }), 0);
40 40
41 // Issue api call & save it as promise that is handled to update the results of the operation 41 // Issue api call & save it as promise that is handled to update the results of the operation
42 this._promise = new Promise((resolve, reject) => { 42 this._promise = new Promise((resolve) => {
43 this._api[this._method](...callArgs) 43 this._api[this._method](...callArgs)
44 .then((result) => { 44 .then((result) => {
45 setTimeout(action(() => { 45 setTimeout(action(() => {
@@ -63,7 +63,7 @@ export default class CachedRequest extends Request {
63 this.wasExecuted = true; 63 this.wasExecuted = true;
64 this._isWaitingForResponse = false; 64 this._isWaitingForResponse = false;
65 this._triggerHooks(); 65 this._triggerHooks();
66 reject(error); 66 // reject(error);
67 }), 1); 67 }), 1);
68 })); 68 }));
69 }); 69 });
diff --git a/src/styles/tabs.scss b/src/styles/tabs.scss
index e500830ed..5bd0555d2 100644
--- a/src/styles/tabs.scss
+++ b/src/styles/tabs.scss
@@ -72,6 +72,8 @@
72 font-size: 0px; 72 font-size: 0px;
73 min-height: 10px; 73 min-height: 10px;
74 min-width: 10px; 74 min-width: 10px;
75 right: auto;
76 left: 8px;
75 } 77 }
76 } 78 }
77 79