diff options
-rw-r--r-- | src/api/apiBase.js | 6 | ||||
-rw-r--r-- | src/config.js | 2 | ||||
-rw-r--r-- | src/electron/ipc-api/index.js | 2 | ||||
-rw-r--r-- | src/electron/ipc-api/localServer.js | 52 | ||||
-rw-r--r-- | src/helpers/serverless-helpers.js | 4 | ||||
-rw-r--r-- | src/i18n/locales/zh-HANT.json | 6 | ||||
-rw-r--r-- | src/index.js | 3 | ||||
-rw-r--r-- | src/server/config/database.js | 6 | ||||
-rw-r--r-- | src/server/start.js | 31 | ||||
-rw-r--r-- | src/stores/RequestStore.js | 9 | ||||
-rw-r--r-- | src/stores/SettingsStore.js | 14 |
11 files changed, 110 insertions, 25 deletions
diff --git a/src/api/apiBase.js b/src/api/apiBase.js index e8d571171..561b025f0 100644 --- a/src/api/apiBase.js +++ b/src/api/apiBase.js | |||
@@ -4,6 +4,9 @@ | |||
4 | import { | 4 | import { |
5 | API_VERSION, | 5 | API_VERSION, |
6 | } from '../environment'; | 6 | } from '../environment'; |
7 | import { | ||
8 | LOCAL_SERVER, | ||
9 | } from '../config'; | ||
7 | 10 | ||
8 | const apiBase = () => { | 11 | const apiBase = () => { |
9 | let url; | 12 | let url; |
@@ -21,6 +24,9 @@ const apiBase = () => { | |||
21 | // on some routes. This would result in Ferdi deleting its current authToken as it thinks it | 24 | // on some routes. This would result in Ferdi deleting its current authToken as it thinks it |
22 | // has gone invalid. | 25 | // has gone invalid. |
23 | url = 'https://1.1.1.1'; | 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 | ||
29 | url = `http://127.0.0.1:${window.ferdi.stores.requests.localServerPort}`; | ||
24 | } else { | 30 | } else { |
25 | // Load URL from store | 31 | // Load URL from store |
26 | url = window.ferdi.stores.settings.all.app.server; | 32 | url = window.ferdi.stores.settings.all.app.server; |
diff --git a/src/config.js b/src/config.js index 0673e994a..e30a6d4b2 100644 --- a/src/config.js +++ b/src/config.js | |||
@@ -111,6 +111,8 @@ export const FILE_SYSTEM_SETTINGS_TYPES = [ | |||
111 | 'proxy', | 111 | 'proxy', |
112 | ]; | 112 | ]; |
113 | 113 | ||
114 | export const LOCAL_SERVER = 'You are using Ferdi without a server'; | ||
115 | |||
114 | export const SETTINGS_PATH = path.join(app.getPath('userData'), 'config'); | 116 | export const SETTINGS_PATH = path.join(app.getPath('userData'), 'config'); |
115 | 117 | ||
116 | // Replacing app.asar is not beautiful but unforunately necessary | 118 | // Replacing app.asar is not beautiful but unforunately necessary |
diff --git a/src/electron/ipc-api/index.js b/src/electron/ipc-api/index.js index 3b7f31e4b..dcdef6b32 100644 --- a/src/electron/ipc-api/index.js +++ b/src/electron/ipc-api/index.js | |||
@@ -3,6 +3,7 @@ import settings from './settings'; | |||
3 | import appIndicator from './appIndicator'; | 3 | import appIndicator from './appIndicator'; |
4 | import download from './download'; | 4 | import download from './download'; |
5 | import processManager from './processManager'; | 5 | import processManager from './processManager'; |
6 | import localServer from './localServer'; | ||
6 | 7 | ||
7 | export default (params) => { | 8 | export default (params) => { |
8 | settings(params); | 9 | settings(params); |
@@ -10,4 +11,5 @@ export default (params) => { | |||
10 | appIndicator(params); | 11 | appIndicator(params); |
11 | download(params); | 12 | download(params); |
12 | processManager(params); | 13 | processManager(params); |
14 | localServer(params); | ||
13 | }; | 15 | }; |
diff --git a/src/electron/ipc-api/localServer.js b/src/electron/ipc-api/localServer.js new file mode 100644 index 000000000..2f8f1020a --- /dev/null +++ b/src/electron/ipc-api/localServer.js | |||
@@ -0,0 +1,52 @@ | |||
1 | import { ipcMain, app } from 'electron'; | ||
2 | import path from 'path'; | ||
3 | import net from 'net'; | ||
4 | import startServer from '../../server/start'; | ||
5 | |||
6 | const DEFAULT_PORT = 45569; | ||
7 | |||
8 | const portInUse = function (port) { | ||
9 | return new Promise((resolve) => { | ||
10 | const server = net.createServer((socket) => { | ||
11 | socket.write('Echo server\r\n'); | ||
12 | socket.pipe(socket); | ||
13 | }); | ||
14 | |||
15 | server.listen(port, '127.0.0.1'); | ||
16 | server.on('error', () => { | ||
17 | resolve(true); | ||
18 | }); | ||
19 | server.on('listening', () => { | ||
20 | server.close(); | ||
21 | resolve(false); | ||
22 | }); | ||
23 | }); | ||
24 | }; | ||
25 | |||
26 | let localServerStarted = false; | ||
27 | |||
28 | export default (params) => { | ||
29 | ipcMain.on('startLocalServer', () => { | ||
30 | if (!localServerStarted) { | ||
31 | // Find next unused port for server | ||
32 | let port = DEFAULT_PORT; | ||
33 | (async () => { | ||
34 | // eslint-disable-next-line no-await-in-loop | ||
35 | while (await portInUse(port) && port < DEFAULT_PORT + 10) { | ||
36 | port += 1; | ||
37 | } | ||
38 | console.log('Starting local server on port', port); | ||
39 | |||
40 | startServer( | ||
41 | path.join(app.getPath('userData'), 'server.sqlite'), | ||
42 | port, | ||
43 | ); | ||
44 | |||
45 | params.mainWindow.webContents.send('localServerPort', { | ||
46 | port, | ||
47 | }); | ||
48 | })(); | ||
49 | localServerStarted = true; | ||
50 | } | ||
51 | }); | ||
52 | }; | ||
diff --git a/src/helpers/serverless-helpers.js b/src/helpers/serverless-helpers.js index 741bce7f9..01549e038 100644 --- a/src/helpers/serverless-helpers.js +++ b/src/helpers/serverless-helpers.js | |||
@@ -1,9 +1,11 @@ | |||
1 | import { LOCAL_SERVER } from '../config'; | ||
2 | |||
1 | export default function useLocalServer(actions) { | 3 | export default function useLocalServer(actions) { |
2 | // Use local server for user | 4 | // Use local server for user |
3 | actions.settings.update({ | 5 | actions.settings.update({ |
4 | type: 'app', | 6 | type: 'app', |
5 | data: { | 7 | data: { |
6 | server: 'http://localhost:45569', | 8 | server: LOCAL_SERVER, |
7 | }, | 9 | }, |
8 | }); | 10 | }); |
9 | 11 | ||
diff --git a/src/i18n/locales/zh-HANT.json b/src/i18n/locales/zh-HANT.json index 5784c1ab6..678554c05 100644 --- a/src/i18n/locales/zh-HANT.json +++ b/src/i18n/locales/zh-HANT.json | |||
@@ -11,6 +11,7 @@ | |||
11 | "feature.delayApp.upgrade.actionShort": "Upgrade account", | 11 | "feature.delayApp.upgrade.actionShort": "Upgrade account", |
12 | "feature.quickSwitch.info": "Select a service with TAB, ↑ and ↓. Open a service with ENTER.", | 12 | "feature.quickSwitch.info": "Select a service with TAB, ↑ and ↓. Open a service with ENTER.", |
13 | "feature.quickSwitch.search": "Search...", | 13 | "feature.quickSwitch.search": "Search...", |
14 | "feature.quickSwitch.title": "QuickSwitch", | ||
14 | "feature.serviceLimit.limitReached": "You have added {amount} out of {limit} services that are included in your plan. Please upgrade your account to add more services.", | 15 | "feature.serviceLimit.limitReached": "You have added {amount} out of {limit} services that are included in your plan. Please upgrade your account to add more services.", |
15 | "feature.shareFranz.action.email": "Send as email", | 16 | "feature.shareFranz.action.email": "Send as email", |
16 | "feature.shareFranz.action.facebook": "Share on Facebook", | 17 | "feature.shareFranz.action.facebook": "Share on Facebook", |
@@ -213,11 +214,13 @@ | |||
213 | "settings.account.upgradeToPro.label": "Upgrade to Ferdi Professional", | 214 | "settings.account.upgradeToPro.label": "Upgrade to Ferdi Professional", |
214 | "settings.account.userInfoRequestFailed": "無法載入帳戶資訊", | 215 | "settings.account.userInfoRequestFailed": "無法載入帳戶資訊", |
215 | "settings.account.yourLicense": "Your Ferdi License", | 216 | "settings.account.yourLicense": "Your Ferdi License", |
217 | "settings.app.accentColorInfo": "Write your accent color in a CSS-compatible format. (Default: #7367f0)", | ||
216 | "settings.app.buttonClearAllCache": "Clear cache", | 218 | "settings.app.buttonClearAllCache": "Clear cache", |
217 | "settings.app.buttonInstallUpdate": "重新啟動並且更新", | 219 | "settings.app.buttonInstallUpdate": "重新啟動並且更新", |
218 | "settings.app.buttonSearchForUpdate": "Check for updates", | 220 | "settings.app.buttonSearchForUpdate": "Check for updates", |
219 | "settings.app.cacheInfo": "Ferdi cache is currently using {size} of disk space.", | 221 | "settings.app.cacheInfo": "Ferdi cache is currently using {size} of disk space.", |
220 | "settings.app.currentVersion": "當前版本:", | 222 | "settings.app.currentVersion": "當前版本:", |
223 | "settings.app.form.accentColor": "Accent color", | ||
221 | "settings.app.form.autoLaunchInBackground": "背景啟動", | 224 | "settings.app.form.autoLaunchInBackground": "背景啟動", |
222 | "settings.app.form.autoLaunchOnStart": "開機時啟動", | 225 | "settings.app.form.autoLaunchOnStart": "開機時啟動", |
223 | "settings.app.form.beta": "包含開發中版本", | 226 | "settings.app.form.beta": "包含開發中版本", |
@@ -244,6 +247,7 @@ | |||
244 | "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", | 247 | "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", |
245 | "settings.app.form.showServiceNavigationBar": "Always show service navigation bar", | 248 | "settings.app.form.showServiceNavigationBar": "Always show service navigation bar", |
246 | "settings.app.form.todoServer": "Todo Server", | 249 | "settings.app.form.todoServer": "Todo Server", |
250 | "settings.app.form.universalDarkMode": "Enable universal Dark Mode", | ||
247 | "settings.app.headline": "Settings", | 251 | "settings.app.headline": "Settings", |
248 | "settings.app.headlineAdvanced": "Advanced", | 252 | "settings.app.headlineAdvanced": "Advanced", |
249 | "settings.app.headlineAppearance": "Appearance", | 253 | "settings.app.headlineAppearance": "Appearance", |
@@ -263,6 +267,7 @@ | |||
263 | "settings.app.subheadlineCache": "Cache", | 267 | "settings.app.subheadlineCache": "Cache", |
264 | "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature. (default: https://app.franztodos.com)", | 268 | "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature. (default: https://app.franztodos.com)", |
265 | "settings.app.translationHelp": "Help us to translate Ferdi into your language.", | 269 | "settings.app.translationHelp": "Help us to translate Ferdi into your language.", |
270 | "settings.app.universalDarkModeInfo": "Universal Dark Mode tries to dynamically generate dark mode styles for services that are otherwise not currently supported.", | ||
266 | "settings.app.updateStatusAvailable": "有可用更新,下載中...", | 271 | "settings.app.updateStatusAvailable": "有可用更新,下載中...", |
267 | "settings.app.updateStatusSearching": "檢查更新中...", | 272 | "settings.app.updateStatusSearching": "檢查更新中...", |
268 | "settings.app.updateStatusUpToDate": "已經是最新版本了", | 273 | "settings.app.updateStatusUpToDate": "已經是最新版本了", |
@@ -315,6 +320,7 @@ | |||
315 | "settings.service.form.indirectMessages": "針對全部訊息顯示通知", | 320 | "settings.service.form.indirectMessages": "針對全部訊息顯示通知", |
316 | "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", | 321 | "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", |
317 | "settings.service.form.name": "名子", | 322 | "settings.service.form.name": "名子", |
323 | "settings.service.form.openDarkmodeCss": "Open darkmode.css", | ||
318 | "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings", | 324 | "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings", |
319 | "settings.service.form.proxy.host": "Proxy Host/IP", | 325 | "settings.service.form.proxy.host": "Proxy Host/IP", |
320 | "settings.service.form.proxy.info": "Proxy settings will not synced with the Ferdi servers.", | 326 | "settings.service.form.proxy.info": "Proxy settings will not synced with the Ferdi servers.", |
diff --git a/src/index.js b/src/index.js index 7a0e89285..4d7215d5e 100644 --- a/src/index.js +++ b/src/index.js | |||
@@ -34,9 +34,6 @@ import { isPositionValid } from './electron/windowUtils'; | |||
34 | import { appId } from './package.json'; // eslint-disable-line import/no-unresolved | 34 | import { appId } from './package.json'; // eslint-disable-line import/no-unresolved |
35 | import './electron/exception'; | 35 | import './electron/exception'; |
36 | 36 | ||
37 | // Start internal server | ||
38 | import './server/start'; | ||
39 | |||
40 | import { | 37 | import { |
41 | DEFAULT_APP_SETTINGS, | 38 | DEFAULT_APP_SETTINGS, |
42 | DEFAULT_WINDOW_OPTIONS, | 39 | DEFAULT_WINDOW_OPTIONS, |
diff --git a/src/server/config/database.js b/src/server/config/database.js index 86f18dac5..a413f7050 100644 --- a/src/server/config/database.js +++ b/src/server/config/database.js | |||
@@ -2,11 +2,7 @@ | |||
2 | /** @type {import('@adonisjs/framework/src/Env')} */ | 2 | /** @type {import('@adonisjs/framework/src/Env')} */ |
3 | const Env = use('Env'); | 3 | const Env = use('Env'); |
4 | 4 | ||
5 | // eslint-disable-next-line import/no-extraneous-dependencies | 5 | const dbPath = process.env.DB_PATH; |
6 | const { app } = require('electron'); | ||
7 | const path = require('path'); | ||
8 | |||
9 | const dbPath = path.join(app.getPath('userData'), 'server.sqlite'); | ||
10 | 6 | ||
11 | module.exports = { | 7 | module.exports = { |
12 | /* | 8 | /* |
diff --git a/src/server/start.js b/src/server/start.js index 8a8711a78..34b2cb5fa 100644 --- a/src/server/start.js +++ b/src/server/start.js | |||
@@ -17,24 +17,25 @@ | |||
17 | */ | 17 | */ |
18 | const path = require('path'); | 18 | const path = require('path'); |
19 | const fs = require('fs-extra'); | 19 | const fs = require('fs-extra'); |
20 | // eslint-disable-next-line import/no-extraneous-dependencies | ||
21 | const { app } = require('electron'); | ||
22 | 20 | ||
23 | process.env.ENV_PATH = path.join(__dirname, 'env.ini'); | 21 | process.env.ENV_PATH = path.join(__dirname, 'env.ini'); |
24 | 22 | ||
25 | // Make sure local database exists | ||
26 | const dbPath = path.join(app.getPath('userData'), 'server.sqlite'); | ||
27 | if (!fs.existsSync(dbPath)) { | ||
28 | fs.copySync( | ||
29 | path.join(__dirname, 'database', 'template.sqlite'), | ||
30 | dbPath, | ||
31 | ); | ||
32 | } | ||
33 | |||
34 | const { Ignitor } = require('@adonisjs/ignitor'); | 23 | const { Ignitor } = require('@adonisjs/ignitor'); |
35 | const fold = require('@adonisjs/fold'); | 24 | const fold = require('@adonisjs/fold'); |
36 | 25 | ||
37 | new Ignitor(fold) | 26 | module.exports = (dbPath, port) => { |
38 | .appRoot(__dirname) | 27 | if (!fs.existsSync(dbPath)) { |
39 | .fireHttpServer() | 28 | fs.copySync( |
40 | .catch(console.error); // eslint-disable-line no-console | 29 | path.join(__dirname, 'database', 'template.sqlite'), |
30 | dbPath, | ||
31 | ); | ||
32 | } | ||
33 | |||
34 | process.env.DB_PATH = dbPath; | ||
35 | process.env.PORT = port; | ||
36 | |||
37 | new Ignitor(fold) | ||
38 | .appRoot(__dirname) | ||
39 | .fireHttpServer() | ||
40 | .catch(console.error); // eslint-disable-line no-console | ||
41 | }; | ||
diff --git a/src/stores/RequestStore.js b/src/stores/RequestStore.js index 2587d4eef..a92f4c685 100644 --- a/src/stores/RequestStore.js +++ b/src/stores/RequestStore.js | |||
@@ -1,3 +1,4 @@ | |||
1 | import { ipcRenderer } from 'electron'; | ||
1 | import { action, computed, observable } from 'mobx'; | 2 | import { action, computed, observable } from 'mobx'; |
2 | import ms from 'ms'; | 3 | import ms from 'ms'; |
3 | 4 | ||
@@ -12,6 +13,8 @@ export default class RequestStore extends Store { | |||
12 | 13 | ||
13 | @observable showRequiredRequestsError = false; | 14 | @observable showRequiredRequestsError = false; |
14 | 15 | ||
16 | @observable localServerPort = 45569; | ||
17 | |||
15 | retries = 0; | 18 | retries = 0; |
16 | 19 | ||
17 | retryDelay = ms('2s'); | 20 | retryDelay = ms('2s'); |
@@ -29,6 +32,12 @@ export default class RequestStore extends Store { | |||
29 | setup() { | 32 | setup() { |
30 | this.userInfoRequest = this.stores.user.getUserInfoRequest; | 33 | this.userInfoRequest = this.stores.user.getUserInfoRequest; |
31 | this.servicesRequest = this.stores.services.allServicesRequest; | 34 | this.servicesRequest = this.stores.services.allServicesRequest; |
35 | |||
36 | ipcRenderer.on('localServerPort', (event, data) => { | ||
37 | if (data.port) { | ||
38 | this.localServerPort = data.port; | ||
39 | } | ||
40 | }); | ||
32 | } | 41 | } |
33 | 42 | ||
34 | @computed get areRequiredRequestsSuccessful() { | 43 | @computed get areRequiredRequestsSuccessful() { |
diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index 8c4cd47eb..df0fc77e9 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js | |||
@@ -9,7 +9,7 @@ import Request from './lib/Request'; | |||
9 | import { getLocale } from '../helpers/i18n-helpers'; | 9 | import { getLocale } from '../helpers/i18n-helpers'; |
10 | import { API } from '../environment'; | 10 | import { API } from '../environment'; |
11 | 11 | ||
12 | import { DEFAULT_APP_SETTINGS, FILE_SYSTEM_SETTINGS_TYPES } from '../config'; | 12 | import { DEFAULT_APP_SETTINGS, FILE_SYSTEM_SETTINGS_TYPES, LOCAL_SERVER } from '../config'; |
13 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; | 13 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; |
14 | 14 | ||
15 | const debug = require('debug')('Ferdi:SettingsStore'); | 15 | const debug = require('debug')('Ferdi:SettingsStore'); |
@@ -53,6 +53,18 @@ export default class SettingsStore extends Store { | |||
53 | ); | 53 | ); |
54 | 54 | ||
55 | reaction( | 55 | reaction( |
56 | () => this.all.app.server, | ||
57 | (server) => { | ||
58 | if (server === LOCAL_SERVER) { | ||
59 | ipcRenderer.send('startLocalServer'); | ||
60 | } | ||
61 | }, | ||
62 | { | ||
63 | fireImmediately: true, | ||
64 | }, | ||
65 | ); | ||
66 | |||
67 | reaction( | ||
56 | () => this.all.app.locked, | 68 | () => this.all.app.locked, |
57 | () => { | 69 | () => { |
58 | const { router } = window.ferdi.stores; | 70 | const { router } = window.ferdi.stores; |