Files
Tooloop-Kiosk-Browser/main.js
T
2024-05-29 22:50:18 +02:00

206 lines
5.8 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 } = require('electron/main');
const path = require('node:path');
const fs = require('fs');
const os = require('os');
const log = require('electron-log/main');
const { linkExists } = require('link-exists');
const { setInterval } = require('node:timers/promises');
const { type } = require('node:os');
//------------------------------------------------------------------------------
// Properties
//------------------------------------------------------------------------------
let config;
let win;
//------------------------------------------------------------------------------
// Function
//------------------------------------------------------------------------------
/**
* Loads a config file from disc.
* The file is expected to be named `config.json` and is searched for in these
* locations and in this order:
*
* 1. `/assets/presentation/config.json` (Tooloop OS)
* 2. Path of the executable
* - Linux: `app.getPath('exe')`
* - MacOS: `path.resolve(app.getPath('exe'), "../../../../")`
* 3. `__dirname` (Development)
*/
function loadConfig() {
const locations = [
'/assets/presentation',
os.platform() == 'darwin' ?
path.resolve(app.getPath('exe'), "../../../../") : app.getPath('exe'),
__dirname
];
let filePath;
// Check all locations
for (const location of locations) {
// Update the filepath
filePath = path.join(location, 'config.json');
try {
// Try access
fs.accessSync(filePath);
// Parse the file if found
console.info('Found config file at ' + filePath);
const data = fs.readFileSync(filePath, { encoding: 'utf8' });
config = JSON.parse(data);
// Break the loop
break;
} catch (err) {
console.warn('No config file found at ' + filePath);
}
}
}
/**
* Creates the browser window
*/
async function createWindow() {
win = new BrowserWindow({
width: 1920,
height: 1080,
backgroundColor: '#000000',
icon: 'images/icon-512.png',
autoHideMenuBar: true,
webPreferences: {
preload: path.join(__dirname, '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' };
});
// show black window
win.setKiosk(true);
win.show();
// Load page from config file
if (config != undefined && 'url' in config) {
testAndLoadUrl(config.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('index.html');
}
}
}
/**
* 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 ('allowedDomains' in config && !config.allowedDomains.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
*/
function testAndLoadUrl(testurl) {
try {
let url = new URL(testurl);
// If its an online url, test whether its reachable
let delay = 1;
if (['https:', 'http:', 'https', 'http'].includes(url.protocol)) {
(async function () {
for await (const startTime of setInterval(delay)) {
let reachable = linkExists(url.hostname);
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);
delay = 1000;
}
}
})();
}
// Load other protocols (i. e. offline) immedately
else {
win.loadURL(config.url);
}
} catch (err) {
log.error(err);
}
}
//------------------------------------------------------------------------------
// Init electron app
//------------------------------------------------------------------------------
app.whenReady().then(() => {
loadConfig();
log.initialize();
if (config != undefined && 'logPath' in config) {
log.transports.file.resolvePathFn = () => config.logPath;
}
createWindow();
});
Menu.setApplicationMenu(null);
app.on('window-all-closed', () => {
app.quit()
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
})