Skip to content

Commit 5e49f07

Browse files
committed
Upgraded dependencies and reorganized project structure
1 parent 0810da0 commit 5e49f07

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1299
-153
lines changed

.babelrc

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
{
2-
"presets": [
2+
"plugins": [
33
[
4-
"env",
4+
"module-resolver",
55
{
6-
"modules": false
6+
"cwd": "babelrc",
7+
"alias": {
8+
"Components": "./app/src/components",
9+
"Containers": "./app/src/containers"
10+
}
711
}
8-
],
9-
"react",
10-
"stage-0"
12+
]
1113
],
12-
"plugins": ["transform-es2015-modules-commonjs"],
13-
"env": {
14-
"test": {
15-
"plugins": ["transform-es2015-modules-commonjs"]
16-
}
17-
}
14+
// presets are a set of of plug-ins
15+
"presets": [
16+
"@babel/preset-typescript",
17+
"@babel/preset-env",
18+
"@babel/preset-react"
19+
]
1820
}

app/electron/main.js

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
const {
2+
app,
3+
protocol,
4+
BrowserWindow,
5+
session,
6+
ipcMain,
7+
Menu
8+
} = require('electron');
9+
// The splash screen is what appears while the app is loading
10+
const { initSplashScreen, OfficeTemplate } = require('electron-splashscreen');
11+
const { resolve } = require('app-root-path');
12+
const {
13+
default: installExtension,
14+
REACT_DEVELOPER_TOOLS
15+
} = require('electron-devtools-installer');
16+
const Protocol = require('./protocol');
17+
// menu from another file to modularize the code
18+
const MenuBuilder = require('./menu');
19+
20+
const path = require('path');
21+
// const fs = require('fs');
22+
23+
console.log('NODE ENV is ', process.env.NODE_ENV);
24+
const isDev = process.env.NODE_ENV === 'development';
25+
const port = 8080;
26+
const selfHost = `http://localhost:${port}`;
27+
28+
// Keep a global reference of the window object, if you don't, the window will
29+
// be closed automatically when the JavaScript object is garbage collected.
30+
let win;
31+
let menuBuilder;
32+
33+
async function createWindow() {
34+
if (isDev) {
35+
await installExtension([REACT_DEVELOPER_TOOLS])
36+
.then(name => console.log(`Added Extension: ${name}`))
37+
.catch(err => console.log('An error occurred: ', err));
38+
} else {
39+
// Needs to happen before creating/loading the browser window;
40+
// not necessarily instead of extensions, just using this code block
41+
// so I don't have to write another 'if' statement
42+
protocol.registerBufferProtocol(Protocol.scheme, Protocol.requestHandler);
43+
}
44+
45+
// Create the browser window.
46+
win = new BrowserWindow({
47+
// full screen
48+
width: 1920,
49+
height: 1080,
50+
// window title
51+
title: `ReacType`,
52+
show: false,
53+
icon: path.join(__dirname, '../src/public/icons/png/256x256.png'),
54+
win: {
55+
icon: path.join(__dirname, '../src/public/icons/win/icon.ico'),
56+
target: ['portable']
57+
},
58+
webPreferences: {
59+
zoomFactor: 0.7,
60+
// enable devtools
61+
devTools: isDev,
62+
// crucial security feature - blocks rendering process from having access to node moduels
63+
nodeIntegration: false,
64+
// web workers will not have access to node
65+
nodeIntegrationInWorker: false,
66+
// disallow experimental feature to allow node.js suppport in subframes (iframes/child windows)
67+
nodeIntegrationInSubFrames: false,
68+
// runs electron apis and preload script in a seperate JS context
69+
// sepearate context has access to document/window but has it's own built-ins and is isolate from chagnes to gloval environment by locaded page
70+
// Electron API only available from preload, not loaded page
71+
contextIsolation: true,
72+
// disables remote module. critical for ensuring that rendering process doesn't have access to node functionality
73+
enableRemoteModule: false
74+
// path of preload script. preload is how the renderer page will have access to electron functionality
75+
// preload: path.join(__dirname, "preload.js"),
76+
}
77+
});
78+
79+
console.log('PATH is ', resolve('/'));
80+
81+
//splash screen deets
82+
// TODO: splash screen logo/icon aren't loading in dev mode
83+
const hideSplashscreen = initSplashScreen({
84+
mainWindow: win,
85+
icon: resolve('app/src/public/icons/png/64x64.png'),
86+
url: OfficeTemplate,
87+
width: 500,
88+
height: 300,
89+
brand: 'OS Labs',
90+
productName: 'ReacType',
91+
logo: resolve('app/src/public/icons/png/64x64.png'),
92+
color: '#3BBCAF',
93+
website: 'www.reactype.io',
94+
text: 'Initializing ...'
95+
});
96+
97+
// Load app
98+
if (isDev) {
99+
// load app from webdev server
100+
win.loadURL(selfHost);
101+
} else {
102+
// load local file if in production
103+
win.loadURL(`${Protocol.scheme}://rse/index-prod.html`);
104+
}
105+
106+
// load page once window is loaded
107+
win.once('ready-to-show', () => {
108+
win.show();
109+
hideSplashscreen();
110+
});
111+
112+
// Only do these things when in development
113+
if (isDev) {
114+
// automatically open DevTools when opening the app
115+
win.webContents.openDevTools();
116+
require('electron-debug')(); // https://github.com/sindresorhus/electron-debug
117+
}
118+
119+
// Emitted when the window is closed.
120+
win.on('closed', () => {
121+
// Dereference the window object, usually you would store windows
122+
// in an array if your app supports multi windows, this is the time
123+
// when you should delete the corresponding element.
124+
win = null;
125+
});
126+
127+
// https://electronjs.org/docs/tutorial/security#4-handle-session-permission-requests-from-remote-content
128+
// TODO: is this the same type of sessions that have in react type
129+
// Could potentially remove this session capability - it appears to be more focused on approving requests from 3rd party notifications
130+
const ses = session;
131+
const partition = 'default';
132+
ses
133+
.fromPartition(partition)
134+
.setPermissionRequestHandler((webContents, permission, callback) => {
135+
let allowedPermissions = []; // Full list here: https://developer.chrome.com/extensions/declare_permissions#manifest
136+
137+
if (allowedPermissions.includes(permission)) {
138+
callback(true); // Approve permission request
139+
} else {
140+
console.error(
141+
`The application tried to request permission for '${permission}'. This permission was not whitelisted and has been blocked.`
142+
);
143+
144+
callback(false); // Deny
145+
}
146+
});
147+
148+
// https://electronjs.org/docs/tutorial/security#1-only-load-secure-content;
149+
// The below code can only run when a scheme and host are defined, I thought
150+
// we could use this over _all_ urls
151+
ses
152+
.fromPartition(partition)
153+
.webRequest.onBeforeRequest({ urls: ['http://localhost./*'] }, listener => {
154+
if (listener.url.indexOf('http://') >= 0) {
155+
listener.callback({
156+
cancel: true
157+
});
158+
}
159+
});
160+
161+
menuBuilder = MenuBuilder(win, 'ReacType');
162+
menuBuilder.buildMenu();
163+
}
164+
165+
// TODO: unclear of whether this is necsssary or not. Looks like a security best practice but will likely introduce complications
166+
// Needs to be called before app is ready;
167+
// gives our scheme access to load relative files,
168+
// as well as local storage, cookies, etc.
169+
// https://electronjs.org/docs/api/protocol#protocolregisterschemesasprivilegedcustomschemes
170+
protocol.registerSchemesAsPrivileged([
171+
{
172+
scheme: Protocol.scheme,
173+
privileges: {
174+
standard: true,
175+
secure: true
176+
}
177+
}
178+
]);
179+
180+
// This method will be called when Electron has finished
181+
// initialization and is ready to create browser windows.
182+
// Some APIs can only be used after this event occurs.
183+
app.on('ready', createWindow);
184+
185+
// Quit when all windows are closed.
186+
app.on('window-all-closed', () => {
187+
// On macOS it is common for applications and their menu bar
188+
// to stay active until the user quits explicitly with Cmd + Q
189+
if (process.platform !== 'darwin') {
190+
app.quit();
191+
} else {
192+
// TODO: remove i18nextbackend
193+
// i18nextBackend.clearMainBindings(ipcMain);
194+
ContextMenu.clearMainBindings(ipcMain);
195+
}
196+
});
197+
198+
app.on('activate', () => {
199+
// On macOS it's common to re-create a window in the app when the
200+
// dock icon is clicked and there are no other windows open.
201+
if (win === null) {
202+
createWindow();
203+
}
204+
});
205+
206+
// https://electronjs.org/docs/tutorial/security#12-disable-or-limit-navigation
207+
// limits navigation within the app to a whitelisted array
208+
// redirects are a common attack vector
209+
// TODO: add github to the validOrigins whitelist array
210+
211+
// after the contents of the webpage are rendered, set up event listeners on the webContents
212+
app.on('web-contents-created', (event, contents) => {
213+
contents.on('will-navigate', (event, navigationUrl) => {
214+
const parsedUrl = new URL(navigationUrl);
215+
const validOrigins = [selfHost];
216+
217+
// Log and prevent the app from navigating to a new page if that page's origin is not whitelisted
218+
if (!validOrigins.includes(parsedUrl.origin)) {
219+
console.error(
220+
`The application tried to redirect to the following address: '${parsedUrl}'. This origin is not whitelisted and the attempt to navigate was blocked.`
221+
);
222+
// if the requested URL is not in the whitelisted array, then don't navigate there
223+
event.preventDefault();
224+
return;
225+
}
226+
});
227+
228+
contents.on('will-redirect', (event, navigationUrl) => {
229+
const parsedUrl = new URL(navigationUrl);
230+
const validOrigins = [];
231+
232+
// Log and prevent the app from redirecting to a new page
233+
if (!validOrigins.includes(parsedUrl.origin)) {
234+
console.error(
235+
`The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.`
236+
);
237+
238+
event.preventDefault();
239+
return;
240+
}
241+
});
242+
243+
// https://electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation
244+
// The web-view is used to embed guest content in a page
245+
// This event listener deletes webviews if they happen to occur in the app
246+
// https://www.electronjs.org/docs/api/web-contents#event-will-attach-webview
247+
contents.on('will-attach-webview', (event, webPreferences, params) => {
248+
// Strip away preload scripts if unused or verify their location is legitimate
249+
delete webPreferences.preload;
250+
delete webPreferences.preloadURL;
251+
252+
// Disable Node.js integration
253+
webPreferences.nodeIntegration = false;
254+
});
255+
256+
// https://electronjs.org/docs/tutorial/security#13-disable-or-limit-creation-of-new-windows
257+
contents.on('new-window', async (event, navigationUrl) => {
258+
// Log and prevent opening up a new window
259+
console.error(
260+
`The application tried to open a new window at the following address: '${navigationUrl}'. This attempt was blocked.`
261+
);
262+
263+
event.preventDefault();
264+
return;
265+
});
266+
});
267+
268+
// Filter loading any module via remote;
269+
// you shouldn't be using remote at all, though
270+
// https://electronjs.org/docs/tutorial/security#16-filter-the-remote-module
271+
app.on('remote-require', (event, webContents, moduleName) => {
272+
event.preventDefault();
273+
});
274+
275+
// built-ins are modules such as "app"
276+
app.on('remote-get-builtin', (event, webContents, moduleName) => {
277+
event.preventDefault();
278+
});
279+
280+
app.on('remote-get-global', (event, webContents, globalName) => {
281+
event.preventDefault();
282+
});
283+
284+
app.on('remote-get-current-window', (event, webContents) => {
285+
event.preventDefault();
286+
});
287+
288+
app.on('remote-get-current-web-contents', (event, webContents) => {
289+
event.preventDefault();
290+
});

0 commit comments

Comments
 (0)