Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaslopezj committed Mar 30, 2023
2 parents 3302a25 + fd20e11 commit d46bd79
Show file tree
Hide file tree
Showing 14 changed files with 1,995 additions and 43 deletions.
51 changes: 51 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'eslint-plugin-import', 'unused-imports'],
extends: ['plugin:@typescript-eslint/recommended'],
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
modules: true
}
},
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-types': 'off',
'func-names': 0,
'@typescript-eslint/no-namespace': 0,
'@typescript-eslint/explicit-module-boundary-types': 'off',
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never'
}
],
'no-restricted-imports': ['error', 'lodash'],
'unused-imports/no-unused-imports': 'error'
},
env: {
browser: true,
node: true,
commonjs: true,
jest: true
},
settings: {
indent: ['error', 2],
'import/extensions': ['.js', '.jsx', '.ts', '.tsx'],
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx']
},
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx']
}
}
}
}
23 changes: 18 additions & 5 deletions app/App/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import {BrowserWindow} from 'electron'
import {BrowserWindow, powerSaveBlocker} from 'electron'
import ElectronStore from 'electron-store'
import {autoUpdater} from 'electron-updater'

import ElectronStore = require('electron-store')

import {baseURL, browserWindowConfig, isMac} from '../env'
import {registerIntraSync} from './offline/ipc'

import './offline'

const store = new ElectronStore()

export default class Main {
static mainWindow: Electron.BrowserWindow
static application: Electron.App
static powerSaveBlockerId: number
static BrowserWindow: typeof BrowserWindow

private static onWindowAllClosed() {
Expand All @@ -21,6 +24,7 @@ export default class Main {
private static onClosed() {
// Dereference the window object.
Main.mainWindow = null
powerSaveBlocker.stop(Main.powerSaveBlockerId)
}

private static onActivate() {
Expand All @@ -41,11 +45,17 @@ export default class Main {

private static onReadyToShow() {
Main.mainWindow.show()

if (!isMac) {
Main.mainWindow.maximize()
}
}

private static onReady() {
Main.createWindow()
registerIntraSync()
}

private static createWindow() {
Main.mainWindow = new Main.BrowserWindow(browserWindowConfig)
Main.mainWindow.on('close', Main.onClose)
Expand All @@ -55,10 +65,13 @@ export default class Main {
const latestURL = (store.get('latestURL') || '') as string
const initialURL = latestURL.startsWith(baseURL) ? latestURL : baseURL

// and load the index.html of the app.
console.log('opening...', initialURL)

Main.mainWindow.loadURL(initialURL)

autoUpdater.checkForUpdatesAndNotify()

Main.powerSaveBlockerId = powerSaveBlocker.start('prevent-app-suspension')
}

static main(app: Electron.App, browserWindow: typeof BrowserWindow) {
Expand All @@ -69,7 +82,7 @@ export default class Main {
Main.BrowserWindow = browserWindow
Main.application = app
Main.application.on('window-all-closed', Main.onWindowAllClosed)
Main.application.on('ready', Main.createWindow)
Main.application.on('ready', Main.onReady)
Main.application.on('activate', Main.onActivate)
}
}
3 changes: 3 additions & 0 deletions app/App/offline/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './ipc'
import './initConnection'
import './ws'
68 changes: 68 additions & 0 deletions app/App/offline/initConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import express from 'express'
import cors from 'cors'
import {v4 as uuidv4} from 'uuid'
import bodyParser from 'body-parser'
import Main from '..'

const app = express()
const port = 2163

const jsonParser = bodyParser.json()

app.use(cors())
app.use(jsonParser)

export interface AuthorizationRequest {
token: string
deviceName: string
status: 'pending' | 'accepted' | 'rejected'
}

export const pendingRequests: AuthorizationRequest[] = []

app.post('/init', (req, res) => {
const params = req.body
const token = uuidv4()

const pendingRequest: AuthorizationRequest = {
token,
deviceName: params.deviceName,
status: 'pending'
}

pendingRequests.push(pendingRequest)
sendAuthorizationRequestToMaster(pendingRequest)

res.end(JSON.stringify(pendingRequest))
})

app.post('/get-status', (req, res) => {
const {token} = req.body
const pendingRequest = pendingRequests.find(request => request.token === token)
res.end(JSON.stringify(pendingRequest))
})

app.post('/test-connection', (req, res) => {
res.end(JSON.stringify({success: true}))
})

app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})

export function respondToRequest(token: string, status: 'accepted' | 'rejected') {
console.log({
token,
status
})
const pendingRequest = pendingRequests.find(request => request.token === token)
if (pendingRequest) {
pendingRequest.status = status
}
}

function sendAuthorizationRequestToMaster(request: AuthorizationRequest) {
console.log('Sending request to master')
console.log(request)
Main.mainWindow.webContents.send(`intraSync:onNewAuthorizationRequest`, request)
}
19 changes: 19 additions & 0 deletions app/App/offline/ip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os from 'os'

export function getDeviceIP(): string {
console.log(`Will get ip`)
// Get the network interfaces of the device
const networkInterfaces = os.networkInterfaces()

// Find the interface with a non-internal IPv4 address
const interfaceKeys = Object.keys(networkInterfaces)
for (const interfaceKey of interfaceKeys) {
const interfaceInfo = networkInterfaces[interfaceKey]
for (const iface of interfaceInfo) {
if (!iface.internal && iface.family === 'IPv4') {
console.log(`Local IP address: ${iface.address}`)
return iface.address
}
}
}
}
54 changes: 54 additions & 0 deletions app/App/offline/ipc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {ipcMain} from 'electron'
import {validateSender} from './validateSender'
import {getDeviceIP} from './ip'
import {AuthorizationRequest, respondToRequest} from './initConnection'
import {
acceptConnection,
getConnectedClientTokens,
pong,
rejectConnection,
resetAllConnections,
sendIntraSyncMessage,
setIsCentral
} from './ws'

export interface IntraSyncAPI {
getDeviceIP: () => Promise<string>
respondToRequest: (token: string, status: 'accepted' | 'rejected') => void
onNewAuthorizationRequest: (callback: (request: AuthorizationRequest) => void) => void

acceptConnection: (token: string) => void
rejectConnection: (token: string) => void
getConnectedClientTokens: () => Promise<string[]>
pong: (token: string) => void
sendIntraSyncMessage: (token: string, data: any) => void
resetAllConnections: () => void // when app starts, all connections are reset
setIsCentral: (isCentral: boolean) => void // when app starts, all connections are reset

onPing: (callback: (token: string) => void) => void
onNewConnection: (callback: (token: string) => void) => void
onConnectionClosed: (callback: (token: string) => void) => void
onIntraSyncMessage: (callback: (params: {token: string; data: any}) => void) => void
}

export function registerIntraSync() {
handleEvent('getDeviceIP', getDeviceIP)
handleEvent('respondToRequest', respondToRequest)
handleEvent('acceptConnection', acceptConnection)
handleEvent('rejectConnection', rejectConnection)
handleEvent('getConnectedClientTokens', getConnectedClientTokens)
handleEvent('sendIntraSyncMessage', sendIntraSyncMessage)
handleEvent('resetAllConnections', resetAllConnections)
handleEvent('pong', pong)
handleEvent('setIsCentral', setIsCentral)
}

function handleEvent(eventName: string, handler: (...args: any[]) => any) {
console.log(`Registering handler for ${eventName}`)
ipcMain.handle(`intraSync:${eventName}`, async (event, ...args) => {
console.log(`Received ${eventName} event`, ...args)
if (!validateSender(event.senderFrame)) return null
const result = await handler(...args)
return result
})
}
34 changes: 34 additions & 0 deletions app/App/offline/preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {contextBridge, ipcRenderer} from 'electron'

export const methods = [
'respondToRequest',
'getDeviceIP',
'acceptConnection',
'rejectConnection',
'getConnectedClientTokens',
'sendIntraSyncMessage',
'resetAllConnections',
'pong',
'setIsCentral'
]
export const events = [
'onNewAuthorizationRequest',
'onNewConnection',
'onConnectionClosed',
'onPing',
'onIntraSyncMessage'
]

const exposeObj = {}

for (const methodName of methods) {
console.log(`Exposing method ${methodName}`)
exposeObj[methodName] = (...args: any[]) => ipcRenderer.invoke(`intraSync:${methodName}`, ...args)
}

for (const eventName of events) {
console.log(`Exposing event ${eventName}`)
exposeObj[eventName] = (callback: any) => ipcRenderer.on(`intraSync:${eventName}`, callback)
}

contextBridge.exposeInMainWorld('intraSyncAPI', exposeObj)
7 changes: 7 additions & 0 deletions app/App/offline/validateSender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {host} from '../../env'

export function validateSender(frame: Electron.WebFrameMain) {
// Value the host of the URL using an actual URL parser and an allowlist
if (new URL(frame.url).host === host) return true
return false
}
Loading

0 comments on commit d46bd79

Please sign in to comment.