Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Upgraded dependencies and reorganized project structure
- Loading branch information
Showing
82 changed files
with
1,299 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,20 @@ | ||
{ | ||
"presets": [ | ||
"plugins": [ | ||
[ | ||
"env", | ||
"module-resolver", | ||
{ | ||
"modules": false | ||
"cwd": "babelrc", | ||
"alias": { | ||
"Components": "./app/src/components", | ||
"Containers": "./app/src/containers" | ||
} | ||
} | ||
], | ||
"react", | ||
"stage-0" | ||
] | ||
], | ||
"plugins": ["transform-es2015-modules-commonjs"], | ||
"env": { | ||
"test": { | ||
"plugins": ["transform-es2015-modules-commonjs"] | ||
} | ||
} | ||
// presets are a set of of plug-ins | ||
"presets": [ | ||
"@babel/preset-typescript", | ||
"@babel/preset-env", | ||
"@babel/preset-react" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
const { | ||
app, | ||
protocol, | ||
BrowserWindow, | ||
session, | ||
ipcMain, | ||
Menu | ||
} = require('electron'); | ||
// The splash screen is what appears while the app is loading | ||
const { initSplashScreen, OfficeTemplate } = require('electron-splashscreen'); | ||
const { resolve } = require('app-root-path'); | ||
const { | ||
default: installExtension, | ||
REACT_DEVELOPER_TOOLS | ||
} = require('electron-devtools-installer'); | ||
const Protocol = require('./protocol'); | ||
// menu from another file to modularize the code | ||
const MenuBuilder = require('./menu'); | ||
|
||
const path = require('path'); | ||
// const fs = require('fs'); | ||
|
||
console.log('NODE ENV is ', process.env.NODE_ENV); | ||
const isDev = process.env.NODE_ENV === 'development'; | ||
const port = 8080; | ||
const selfHost = `http://localhost:${port}`; | ||
|
||
// Keep a global reference of the window object, if you don't, the window will | ||
// be closed automatically when the JavaScript object is garbage collected. | ||
let win; | ||
let menuBuilder; | ||
|
||
async function createWindow() { | ||
if (isDev) { | ||
await installExtension([REACT_DEVELOPER_TOOLS]) | ||
.then(name => console.log(`Added Extension: ${name}`)) | ||
.catch(err => console.log('An error occurred: ', err)); | ||
} else { | ||
// Needs to happen before creating/loading the browser window; | ||
// not necessarily instead of extensions, just using this code block | ||
// so I don't have to write another 'if' statement | ||
protocol.registerBufferProtocol(Protocol.scheme, Protocol.requestHandler); | ||
} | ||
|
||
// Create the browser window. | ||
win = new BrowserWindow({ | ||
// full screen | ||
width: 1920, | ||
height: 1080, | ||
// window title | ||
title: `ReacType`, | ||
show: false, | ||
icon: path.join(__dirname, '../src/public/icons/png/256x256.png'), | ||
win: { | ||
icon: path.join(__dirname, '../src/public/icons/win/icon.ico'), | ||
target: ['portable'] | ||
}, | ||
webPreferences: { | ||
zoomFactor: 0.7, | ||
// enable devtools | ||
devTools: isDev, | ||
// crucial security feature - blocks rendering process from having access to node moduels | ||
nodeIntegration: false, | ||
// web workers will not have access to node | ||
nodeIntegrationInWorker: false, | ||
// disallow experimental feature to allow node.js suppport in subframes (iframes/child windows) | ||
nodeIntegrationInSubFrames: false, | ||
// runs electron apis and preload script in a seperate JS context | ||
// 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 | ||
// Electron API only available from preload, not loaded page | ||
contextIsolation: true, | ||
// disables remote module. critical for ensuring that rendering process doesn't have access to node functionality | ||
enableRemoteModule: false | ||
// path of preload script. preload is how the renderer page will have access to electron functionality | ||
// preload: path.join(__dirname, "preload.js"), | ||
} | ||
}); | ||
|
||
console.log('PATH is ', resolve('/')); | ||
|
||
//splash screen deets | ||
// TODO: splash screen logo/icon aren't loading in dev mode | ||
const hideSplashscreen = initSplashScreen({ | ||
mainWindow: win, | ||
icon: resolve('app/src/public/icons/png/64x64.png'), | ||
url: OfficeTemplate, | ||
width: 500, | ||
height: 300, | ||
brand: 'OS Labs', | ||
productName: 'ReacType', | ||
logo: resolve('app/src/public/icons/png/64x64.png'), | ||
color: '#3BBCAF', | ||
website: 'www.reactype.io', | ||
text: 'Initializing ...' | ||
}); | ||
|
||
// Load app | ||
if (isDev) { | ||
// load app from webdev server | ||
win.loadURL(selfHost); | ||
} else { | ||
// load local file if in production | ||
win.loadURL(`${Protocol.scheme}://rse/index-prod.html`); | ||
} | ||
|
||
// load page once window is loaded | ||
win.once('ready-to-show', () => { | ||
win.show(); | ||
hideSplashscreen(); | ||
}); | ||
|
||
// Only do these things when in development | ||
if (isDev) { | ||
// automatically open DevTools when opening the app | ||
win.webContents.openDevTools(); | ||
require('electron-debug')(); // https://github.com/sindresorhus/electron-debug | ||
} | ||
|
||
// Emitted when the window is closed. | ||
win.on('closed', () => { | ||
// Dereference the window object, usually you would store windows | ||
// in an array if your app supports multi windows, this is the time | ||
// when you should delete the corresponding element. | ||
win = null; | ||
}); | ||
|
||
// https://electronjs.org/docs/tutorial/security#4-handle-session-permission-requests-from-remote-content | ||
// TODO: is this the same type of sessions that have in react type | ||
// Could potentially remove this session capability - it appears to be more focused on approving requests from 3rd party notifications | ||
const ses = session; | ||
const partition = 'default'; | ||
ses | ||
.fromPartition(partition) | ||
.setPermissionRequestHandler((webContents, permission, callback) => { | ||
let allowedPermissions = []; // Full list here: https://developer.chrome.com/extensions/declare_permissions#manifest | ||
|
||
if (allowedPermissions.includes(permission)) { | ||
callback(true); // Approve permission request | ||
} else { | ||
console.error( | ||
`The application tried to request permission for '${permission}'. This permission was not whitelisted and has been blocked.` | ||
); | ||
|
||
callback(false); // Deny | ||
} | ||
}); | ||
|
||
// https://electronjs.org/docs/tutorial/security#1-only-load-secure-content; | ||
// The below code can only run when a scheme and host are defined, I thought | ||
// we could use this over _all_ urls | ||
ses | ||
.fromPartition(partition) | ||
.webRequest.onBeforeRequest({ urls: ['http://localhost./*'] }, listener => { | ||
if (listener.url.indexOf('http://') >= 0) { | ||
listener.callback({ | ||
cancel: true | ||
}); | ||
} | ||
}); | ||
|
||
menuBuilder = MenuBuilder(win, 'ReacType'); | ||
menuBuilder.buildMenu(); | ||
} | ||
|
||
// TODO: unclear of whether this is necsssary or not. Looks like a security best practice but will likely introduce complications | ||
// Needs to be called before app is ready; | ||
// gives our scheme access to load relative files, | ||
// as well as local storage, cookies, etc. | ||
// https://electronjs.org/docs/api/protocol#protocolregisterschemesasprivilegedcustomschemes | ||
protocol.registerSchemesAsPrivileged([ | ||
{ | ||
scheme: Protocol.scheme, | ||
privileges: { | ||
standard: true, | ||
secure: true | ||
} | ||
} | ||
]); | ||
|
||
// This method will be called when Electron has finished | ||
// initialization and is ready to create browser windows. | ||
// Some APIs can only be used after this event occurs. | ||
app.on('ready', createWindow); | ||
|
||
// Quit when all windows are closed. | ||
app.on('window-all-closed', () => { | ||
// On macOS it is common for applications and their menu bar | ||
// to stay active until the user quits explicitly with Cmd + Q | ||
if (process.platform !== 'darwin') { | ||
app.quit(); | ||
} else { | ||
// TODO: remove i18nextbackend | ||
// i18nextBackend.clearMainBindings(ipcMain); | ||
ContextMenu.clearMainBindings(ipcMain); | ||
} | ||
}); | ||
|
||
app.on('activate', () => { | ||
// On macOS it's common to re-create a window in the app when the | ||
// dock icon is clicked and there are no other windows open. | ||
if (win === null) { | ||
createWindow(); | ||
} | ||
}); | ||
|
||
// https://electronjs.org/docs/tutorial/security#12-disable-or-limit-navigation | ||
// limits navigation within the app to a whitelisted array | ||
// redirects are a common attack vector | ||
// TODO: add github to the validOrigins whitelist array | ||
|
||
// after the contents of the webpage are rendered, set up event listeners on the webContents | ||
app.on('web-contents-created', (event, contents) => { | ||
contents.on('will-navigate', (event, navigationUrl) => { | ||
const parsedUrl = new URL(navigationUrl); | ||
const validOrigins = [selfHost]; | ||
|
||
// Log and prevent the app from navigating to a new page if that page's origin is not whitelisted | ||
if (!validOrigins.includes(parsedUrl.origin)) { | ||
console.error( | ||
`The application tried to redirect to the following address: '${parsedUrl}'. This origin is not whitelisted and the attempt to navigate was blocked.` | ||
); | ||
// if the requested URL is not in the whitelisted array, then don't navigate there | ||
event.preventDefault(); | ||
return; | ||
} | ||
}); | ||
|
||
contents.on('will-redirect', (event, navigationUrl) => { | ||
const parsedUrl = new URL(navigationUrl); | ||
const validOrigins = []; | ||
|
||
// Log and prevent the app from redirecting to a new page | ||
if (!validOrigins.includes(parsedUrl.origin)) { | ||
console.error( | ||
`The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.` | ||
); | ||
|
||
event.preventDefault(); | ||
return; | ||
} | ||
}); | ||
|
||
// https://electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation | ||
// The web-view is used to embed guest content in a page | ||
// This event listener deletes webviews if they happen to occur in the app | ||
// https://www.electronjs.org/docs/api/web-contents#event-will-attach-webview | ||
contents.on('will-attach-webview', (event, webPreferences, params) => { | ||
// Strip away preload scripts if unused or verify their location is legitimate | ||
delete webPreferences.preload; | ||
delete webPreferences.preloadURL; | ||
|
||
// Disable Node.js integration | ||
webPreferences.nodeIntegration = false; | ||
}); | ||
|
||
// https://electronjs.org/docs/tutorial/security#13-disable-or-limit-creation-of-new-windows | ||
contents.on('new-window', async (event, navigationUrl) => { | ||
// Log and prevent opening up a new window | ||
console.error( | ||
`The application tried to open a new window at the following address: '${navigationUrl}'. This attempt was blocked.` | ||
); | ||
|
||
event.preventDefault(); | ||
return; | ||
}); | ||
}); | ||
|
||
// Filter loading any module via remote; | ||
// you shouldn't be using remote at all, though | ||
// https://electronjs.org/docs/tutorial/security#16-filter-the-remote-module | ||
app.on('remote-require', (event, webContents, moduleName) => { | ||
event.preventDefault(); | ||
}); | ||
|
||
// built-ins are modules such as "app" | ||
app.on('remote-get-builtin', (event, webContents, moduleName) => { | ||
event.preventDefault(); | ||
}); | ||
|
||
app.on('remote-get-global', (event, webContents, globalName) => { | ||
event.preventDefault(); | ||
}); | ||
|
||
app.on('remote-get-current-window', (event, webContents) => { | ||
event.preventDefault(); | ||
}); | ||
|
||
app.on('remote-get-current-web-contents', (event, webContents) => { | ||
event.preventDefault(); | ||
}); |
Oops, something went wrong.