Files
Tooloop-Kiosk-Browser/main.js
T

222 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { app, BrowserWindow, nativeTheme, Menu, MenuItem } = require('electron/main');
const path = require('node:path');
const fs = require('fs');
const log = require('electron-log/main');
const isReachable = require('is-reachable');
const { setInterval } = require('node:timers/promises');
const config = require('./js/config.js');
const { Config } = require('./js/config.js');
//------------------------------------------------------------------------------
// Properties
//------------------------------------------------------------------------------
let win;
let menu;
const isMac = process.platform === 'darwin';
//------------------------------------------------------------------------------
// Function
//------------------------------------------------------------------------------
/**
* Creates the browser window
*/
function createMainWindow() {
win = new BrowserWindow({
width: 1920,
height: 1080,
backgroundColor: '#000000',
icon: 'images/icon-512.png',
autoHideMenuBar: true,
kiosk: true,
webPreferences: {
preload: path.join(__dirname, 'js/preload.js'),
webSecurity: false,
disableDialogs: true
},
});
nativeTheme.themeSource = 'dark';
// register event callbacks
win.on("closed", function () { win = null; });
win.webContents.on("will-frame-navigate", (event) => validateDomain(event));
win.webContents.setWindowOpenHandler(({ url }) => {
// we need to manually validate the url as `loadURL`
// doesnt trigger the `will-navigate` event
let event = new Event("DummyNavigation");
event.url = url;
if (validateDomain(event)) {
win.loadURL(url);
}
return { action: 'deny' };
});
win.on("page-title-updated", (event) => event.preventDefault());
// Load page from config file
if (config.data && 'url' in config.data && config.data.url) {
loadUrlAsync(config.data.url);
} else {
try {
// Load file from data folder if available
fs.accessSync('/assets/data/index.html');
win.loadFile('/assets/data/index.html');
} catch {
// Load fallback page
win.loadFile('./html/onboarding.html');
}
}
}
/**
* Creates all available keyboard shortcuts
*/
function createMenu() {
menu = new Menu();
menu.append(new MenuItem({
label: app.name,
submenu: [
{
label: 'Toggle Fullscreen',
accelerator: isMac ? 'Cmd+F' : 'Ctrl+F',
click: () => { win.setKiosk(!win.kiosk); }
},
{ role: 'reload' },
{ role: 'quit' },
{
label: 'Config',
accelerator: isMac ? 'Cmd+,' : 'Ctrl+,',
click: () => {
config.showWindow(
win,
(newConfig) => { loadUrlAsync(newConfig.url); }
);
}
}
]
}));
Menu.setApplicationMenu(menu);
}
/**
* Validates the url of a navigatio event against the list of allowed domains in
* the config file. See https://www.electronjs.org/docs/latest/api/web-contents#event-will-frame-navigate
* @param {Event} event
* @returns `true` if the url is allowed, `false` otherwise
*/
function validateDomain(event) {
if (config == undefined) return true;
let url = new URL(event.url);
// allow local urls
if (['file:', 'file'].includes(url.protocol)) return true;
if ('whitelist' in config && !config.whitelist.includes(url.hostname)) {
event.preventDefault();
log.info("Navigation to " + event.url + " prevented");
return false;
}
return true;
}
/**
* Tests if the host of the url is reachable.
* Access is tried in a 1 second interval and the url is loaded if successfull.
* @param {string} testurl
*/
async function loadUrlAsync(testurl) {
try {
let url = new URL(testurl);
// If its an online url, test whether its reachable
if (['https:', 'http:', 'https', 'http'].includes(url.protocol)) {
reachable = await isReachable(url.href);
if (reachable) {
log.info("Successfull access to " + url.hostname);
log.info("Loading " + config.data.url);
win.loadURL(config.data.url);
} else {
log.warn("Could not reach " + url.hostname);
for await (_ of setInterval(1000)) {
reachable = await isReachable(url.href);
if (reachable) {
log.info("Successfull access to " + url.hostname);
log.info("Loading " + config.url);
win.loadURL(config.url);
// break the interval
break;
} else {
log.warn("Could not reach " + url.hostname);
}
}
}
}
// Load other protocols (i. e. offline) immedately
else {
win.loadURL(config.data.url);
}
} catch (err) {
log.error(err.message);
// Load fallback page
win.loadFile('./html/onboarding.html');
}
}
//------------------------------------------------------------------------------
// Init electron app
//------------------------------------------------------------------------------
// https://github.com/electron/electron/issues/17972
app.commandLine.appendSwitch('--no-sandbox');
app.commandLine.appendSwitch('no-sandbox');
app.whenReady().then(() => {
config.load();
log.initialize();
if (config.data != undefined && 'logPath' in config.data) {
log.transports.file.resolvePathFn = () => config.data.logPath;
}
log.info('----------------------------------------------');
log.info('Starting Tooloop Kiosk Browser...');
createMenu();
createMainWindow();
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createMainWindow();
}
})
app.on('window-all-closed', () => {
app.quit()
});
app.on('quit', () => {
log.info('Quit');
});
// prevent error dialogs in case of exceptions
process.on('uncaughtException', function (error) {
log.error(error);
});