Files
modeling-app/src/main.ts
Jess Frazelle 27883e7800 Electron machine api tests (#3534)
* start

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Look at this (photo)Graph *in the voice of Nickelback*

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* hide on webapp

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix machine-api

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-19 15:57:31 -07:00

161 lines
4.8 KiB
TypeScript

// Some of the following was taken from bits and pieces of the vite-typescript
// template that ElectronJS provides.
import dotenv from 'dotenv'
import { app, BrowserWindow, ipcMain, dialog, shell } from 'electron'
import path from 'path'
import { Issuer } from 'openid-client'
import { Bonjour, Service } from 'bonjour-service'
// @ts-ignore: TS1343
import * as kittycad from '@kittycad/lib/import'
// If it's not set, scream.
const NODE_ENV = process.env.NODE_ENV
if (!NODE_ENV) {
console.error('*FOX SCREAM* process.env.NODE_ENV is not explicitly set!')
process.exit(1)
}
dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
app.quit()
}
const createWindow = () => {
const mainWindow = new BrowserWindow({
autoHideMenuBar: true,
show: false,
width: 1800,
height: 1200,
webPreferences: {
nodeIntegration: false, // do not give the application implicit system access
contextIsolation: true, // expose system functions in preload
sandbox: false, // expose nodejs in preload
preload: path.join(__dirname, './preload.js'),
},
})
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL)
} else {
mainWindow.loadFile(
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`)
)
}
// Open the DevTools.
// mainWindow.webContents.openDevTools()
mainWindow.show()
}
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// 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)
// For now there is no good reason to separate these out to another file(s)
// There is just not enough code to warrant it and further abstracts everything
// which is already quite abstracted
ipcMain.handle('app.getPath', (event, data) => {
return app.getPath(data)
})
ipcMain.handle('dialog.showOpenDialog', (event, data) => {
return dialog.showOpenDialog(data)
})
ipcMain.handle('dialog.showSaveDialog', (event, data) => {
return dialog.showSaveDialog(data)
})
ipcMain.handle('shell.showItemInFolder', (event, data) => {
return shell.showItemInFolder(data)
})
ipcMain.handle('shell.openExternal', (event, data) => {
return shell.openExternal(data)
})
ipcMain.handle('login', async (event, host) => {
// Do an OAuth 2.0 Device Authorization Grant dance to get a token.
// We quiet ts because we are not using this in the standard way.
// @ts-ignore
const issuer = new Issuer({
device_authorization_endpoint: `${host}/oauth2/device/auth`,
token_endpoint: `${host}/oauth2/device/token`,
})
const client = new issuer.Client({
// We can hardcode the client ID.
// This value is safe to be embedded in version control.
// This is the client ID of the KittyCAD app.
client_id: '2af127fb-e14e-400a-9c57-a9ed08d1a5b7',
token_endpoint_auth_method: 'none',
})
const handle = await client.deviceAuthorization()
shell.openExternal(handle.verification_uri_complete)
// Wait for the user to login.
try {
console.log('Polling for token')
const tokenSet = await handle.poll()
console.log('Received token set')
console.log(tokenSet)
return tokenSet.access_token
} catch (e) {
console.log(e)
}
return Promise.reject(new Error('No access token received'))
})
ipcMain.handle('kittycad', (event, data) => {
return data.access
.split('.')
.reduce(
(obj: any, prop: any) => obj[prop],
kittycad
)(data.args)
})
ipcMain.handle('find_machine_api', () => {
const timeoutAfterMs = 5000
return new Promise((resolve, reject) => {
// if it takes too long reject this promise
setTimeout(() => resolve(null), timeoutAfterMs)
const bonjourEt = new Bonjour({}, (error: Error) => {
console.log('An issue with Bonjour services was encountered!')
console.error(error)
resolve(null)
})
console.log('Looking for machine API...')
bonjourEt.find(
{ protocol: 'tcp', type: 'machine-api' },
(service: Service) => {
console.log('Found machine API!', JSON.stringify(service))
if (!service.addresses || service.addresses?.length === 0) {
console.log('No addresses found for machine API!')
return resolve(null)
}
const ip = service.addresses[0]
const port = service.port
// We want to return the ip address of the machine API.
resolve(`${ip}:${port}`)
}
)
})
})