Config-Funktionen in eigenes Modul, fixes #7

This commit is contained in:
2024-06-04 14:58:52 +02:00
parent 4bcdf1071e
commit f695e874a9
5 changed files with 251 additions and 218 deletions
+1 -3
View File
@@ -1,8 +1,6 @@
{
"url": "file:///assets/data/index.html",
"url": "https://www.tooloop.de",
"whitelist": [
"localhost",
"127.0.0.1",
"tooloop.de",
"www.tooloop.de"
],
+3 -4
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Config</title>
<title>Settings</title>
<style>
html,
body {
@@ -59,7 +59,6 @@
button {
background-color: hsl(0, 0%, 50%);
/* border: 1px solid rgba(255, 255, 255, 0.12); */
border-width: 1px;
border-radius: 0.25rem;
line-height: 1.5;
@@ -72,10 +71,10 @@
<body>
<div class="config">
<label for="url">Url</label>
<input type="text" id="url" value="file:///assets/data/index.html">
<input type="text" id="url" placeholder="https://...">
<label for="whitelist">Allowed Domains</label>
<textarea name="whitelist" id="whitelist"></textarea>
<textarea name="whitelist" id="whitelist" placeholder="domain.tld;domain.tld"></textarea>
<label for="logpath">Logging Path</label>
<input type="text" id="logpath" value="/assets/logs/tooloop-kiosk-browser.log">
+53 -33
View File
@@ -22,15 +22,7 @@
}
html {
/* background-color: #000; */
background-image: url('../images/icon-512.png');
background-position: center center;
background-size: cover;
background-attachment: fixed;
}
body {
backdrop-filter: blur(10px) brightness(50%);
background: linear-gradient(to left, #de6262, #ffb88c);
}
h1,
@@ -38,18 +30,22 @@
line-height: 1;
margin: 1rem 0 1.333rem 0;
}
h2 {
margin-bottom: 2.25rem;
}
p {
line-height: 1.333;
line-height: 1.4;
margin: 1rem 0;
}
pre,
code {
background-color: hsl(0, 0%, 25%);
color: #f2f2f2;
background-color: hsl(0, 0%, 85%);
color: #333;
font-weight: 300;
font-size: 0.9rem;
padding: 0.1666em 0.333em;
padding: 0.133em 0.333em;
}
pre {
@@ -95,6 +91,7 @@
padding: 1.1666rem 2rem;
border-radius: 0.5rem;
overflow: auto;
box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.1);
}
.card h2 {
@@ -102,13 +99,33 @@
font-weight: 500;
}
table {
border-collapse: collapse;
width: 100%;
}
th {
width: min-content;
}
td {
width: max-content;
}
td, th {
margin: 0;
padding: 1rem;
border-top: 1px solid lightblue;
border-bottom: 1px solid lightblue;
}
@media screen and (max-width: 1024px) {
body,
html {
display: block;
/* height: auto; */
}
#info {
@@ -147,31 +164,34 @@
<div class="columns">
<article class="card">
<h2>Add your content</h2>
<p>You can load a local page or an online ressource.</p>
<p>The first thing you might want to do, is configure your page to load. You can load a local page or an
online ressource.</p>
<p>
Put your local content in <code>/assets/data/index.html</code>
or configure a URL in the config file (&rarr; see <i>Configuration</i>).
or configure a URL in the settings (shortcut <code>Strg + ,</code>).
</p>
</article>
<article class="card">
<h2>Configuration</h2>
<p>If you need a config file, put it in either of these locations:</p>
<ol>
<li><code>/assets/presentation/config.json</code></li>
<li>The application folder</li>
</ol>
<p>Here is an example:</p>
<p>
<pre><code>{
"url": "https://www.tooloop.de",
"whitelist": [
"tooloop.de",
"www.tooloop.de"
],
"logPath": "/assets/logs/Tooloop-Kiosk-Browser.log"
}</code></pre>
</p>
<h2>Keyboard shortcuts</h2>
<table>
<tr>
<th><code>Ctrl + ,</code></th>
<td>Settings</td>
</tr>
<tr>
<th><code>Ctrl + F</code></th>
<td>Toggle Fullscreen</td>
</tr>
<tr>
<th><code>Ctrl + Q</code></th>
<td>Quit</td>
</tr>
<tr>
<th><code>Ctrl + R</code></th>
<td>Reload</td>
</tr>
</table>
</article>
</div>
+170 -22
View File
@@ -1,29 +1,177 @@
class Config {
const { app, BrowserWindow, ipcMain } = require('electron/main');
const path = require('node:path');
const fs = require('fs');
const log = require('electron-log/main');
constructor() {
this.config = {
"url": "https://www.tooloop.de",
"whitelist": [
"tooloop.de",
"www.tooloop.de"
],
"logPath": "tooloop-kiosk-browser.log"
};
class Config {
url = "";
whitelist = [];
logPath = "tooloop-kiosk-browser.log";
}
// -----------------------------------------------------------------------------
// Private fields
// -----------------------------------------------------------------------------
const locations = [
'/assets/presentation',
path.join(app.getPath('appData'), app.name),
app.getAppPath()
];
let configPath;
let configWindow;
let saveCallback;
// -----------------------------------------------------------------------------
// Public fields
// -----------------------------------------------------------------------------
exports.data = new Config();
// -----------------------------------------------------------------------------
// Public functions
// -----------------------------------------------------------------------------
/**
* 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. appData directory
* 3. Path of the executable (Development)
*/
exports.load = () => {
// Check all locations
for (const location of locations) {
// Update the filepath
let configFile = path.join(location, 'config.json');
try {
// Try access
fs.accessSync(configFile);
// Parse the file if found
console.info('Found config file at ' + configFile);
const fileContent = fs.readFileSync(configFile, { encoding: 'utf8' });
this.data = JSON.parse(fileContent);
// store successful location
configPath = location;
// Break the loop
break;
} catch (err) {
// console.info('No config file found at ' + location);
}
}
}
/**
* Saves the current configuration to disc.
* These locations are tried in this order:
*
* 1. `/assets/presentation/config.json` (Tooloop OS)
* 2. appData directory
*
* @param {Config} newConfig
*/
exports.save = (newConfig) => {
if (!configPath) {
try {
// /assets/presentation
fs.accessSync(locations[0]);
configPath = locations[0];
} catch (error) {
// appData
configPath = locations[1];
}
}
// Store changes
if (this.data.url != newConfig.url) {
this.data.url = newConfig.url;
}
this.data.whitelist = [];
let whitelist = newConfig.whitelist.split(";");
whitelist = whitelist.filter(function (entry) { return entry.trim() != ''; });
whitelist.forEach(
token => {
this.data.whitelist.push(token.replace(/\r?\n|\r/g, ""));
}
);
this.data.logPath = newConfig.logPath;
// Write file
let configFile = path.join(configPath, 'config.json');
fs.writeFile(
configFile,
JSON.stringify(this.data, null, " "),
(error) => {
if (error) log.warn('Error writing to ' + configPath, error);
log.info("Saved config to " + configFile);
}
);
// Update UI with new values
configWindow.webContents.send('update-config', this.data);
// Notify listeners
saveCallback(this.data);
}
let config = {
"url": "https://www.tooloop.de",
"whitelist": [
"tooloop.de",
"www.tooloop.de"
],
"logPath": "tooloop-kiosk-browser.log"
};
exports.config = config;
/**
* Creates a modal config view and attaches as to the main window
*
* @param {BrowserWindow} parent Reference to the parent window
* @param {function(Config)} saveCallback
*/
exports.showWindow = (parent, callback) => {
exports.load = () => {
console.log(config);
};
// create lazily
if (configWindow == undefined) {
console.log("create windows");
saveCallback = callback;
configWindow = new BrowserWindow({
parent: parent,
width: 640,
height: 460,
minimizable: false,
maximizable: false,
fullscreenable: false,
backgroundColor: '#1f1f1f',
autoHideMenuBar: true,
excludedFromShownWindowsMenu: true,
webPreferences: {
preload: path.join(__dirname, 'configPreload.js')
}
});
configWindow.on('close', (event) => {
event.preventDefault();
configWindow.hide();
});
ipcMain.on('save-config', (event, configData) => {
configWindow.hide();
this.save(configData);
});
ipcMain.on('cancel-config', (event) => {
configWindow.hide();
configWindow.webContents.send('update-config', this.data);
});
configWindow.loadFile('./html/config.html');
}
// update text field values
configWindow.webContents.send('update-config', this.data);
// show window
configWindow.show();
}
+24 -156
View File
@@ -1,166 +1,27 @@
const { app, BrowserWindow, ipcMain, nativeTheme, Menu, MenuItem } = require('electron/main');
const { app, BrowserWindow, nativeTheme, Menu, MenuItem } = require('electron/main');
const path = require('node:path');
const fs = require('fs');
const os = require('os');
const log = require('electron-log/main');
const isReachable = require('is-reachable');
const { setInterval } = require('node:timers/promises');
const configModule = require('./js/config.js');
const config = require('./js/config.js');
const { Config } = require('./js/config.js');
const isMac = process.platform === 'darwin';
//------------------------------------------------------------------------------
// Properties
//------------------------------------------------------------------------------
let config;
let configPath;
let configWindow;
let win;
let menu;
const isMac = process.platform === 'darwin';
//------------------------------------------------------------------------------
// 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. appData directory
* 3. Path of the executable
* - Linux: `app.getPath('exe')`
* - MacOS: `path.resolve(app.getPath('exe'), "../../../../")`
* 4. `__dirname` (Development)
*/
function loadConfig() {
const locations = [
'/assets/presentation',
path.join(app.getPath('appData'), app.name),
isMac ? path.resolve(app.getPath('exe'), "../../../../") : app.getPath('exe'),
__dirname
];
// Check all locations
for (const location of locations) {
// Update the filepath
configPath = location;
let configFile = path.join(configPath, 'config.json');
try {
// Try access
fs.accessSync(configFile);
// Parse the file if found
console.info('Found config file at ' + configFile);
const data = fs.readFileSync(configFile, { encoding: 'utf8' });
config = JSON.parse(data);
// Break the loop
break;
} catch (err) {
console.warn('No config file found at ' + configPath);
}
}
}
function saveConfig(newConfig) {
// If there was no config file before create a new object
// and store a default save path
if (!config) {
config = {
url: "",
whitelist: [],
logPath: ""
};
try {
configPath = fs.accessSync('/assets/presentation');
} catch (error) {
configPath = path.join(app.getPath('appData'), app.name);
}
}
// Store changes
if (config.url != newConfig.url) {
config.url = newConfig.url;
loadUrlAsync(config.url);
}
config.whitelist = [];
let whitelist = newConfig.whitelist.split(";");
whitelist = whitelist.filter(function (entry) { return entry.trim() != ''; });
whitelist.forEach(
token => {
config.whitelist.push(token.replace(/\r?\n|\r/g, ""));
}
);
config.logPath = newConfig.logPath;
// Write file
let configFile = path.join(configPath, 'config.json');
fs.writeFile(
configFile,
JSON.stringify(config, null, " "),
(error) => {
if (error) log.warn('Error writing to ' + configPath, error);
log.info("Saved config to " + configFile);
}
);
// Update UI with new values
configWindow.webContents.send('update-config', config);
}
/**
* Creates a modal config view and attaches as to the main window
*/
function showConfigWindow() {
// create lazily
if (configWindow == undefined) {
configWindow = new BrowserWindow({
parent: win,
width: 640,
height: 460,
minimizable: false,
maximizable: false,
fullscreenable: false,
backgroundColor: '#1f1f1f',
autoHideMenuBar: true,
excludedFromShownWindowsMenu: true,
webPreferences: {
preload: path.join(__dirname, 'js/configPreload.js')
}
});
configWindow.on('close', (event) => {
event.preventDefault();
configWindow.hide();
});
ipcMain.on('save-config', (event, configData) => {
configWindow.hide();
saveConfig(configData);
});
ipcMain.on('cancel-config', (event) => {
configWindow.hide();
configWindow.webContents.send('update-config', config);
});
configWindow.loadFile('./html/config.html');
}
// update text field values
configWindow.webContents.send('update-config', config);
// show window
configWindow.show();
}
/**
* Creates the browser window
*/
@@ -197,8 +58,8 @@ function createMainWindow() {
win.on("page-title-updated", (event) => event.preventDefault());
// Load page from config file
if (config != undefined && 'url' in config) {
loadUrlAsync(config.url);
if (config.data && 'url' in config.data && config.data.url) {
loadUrlAsync(config.data.url);
} else {
try {
// Load file from data folder if available
@@ -231,7 +92,12 @@ function createMenu() {
{
label: 'Config',
accelerator: isMac ? 'Cmd+,' : 'Ctrl+,',
click: () => { showConfigWindow(); }
click: () => {
config.showWindow(
win,
(newConfig) => { loadUrlAsync(newConfig.url); }
);
}
}
]
}));
@@ -263,6 +129,7 @@ function validateDomain(event) {
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.
@@ -277,8 +144,8 @@ async function loadUrlAsync(testurl) {
reachable = await isReachable(url.href);
if (reachable) {
log.info("Successfull access to " + url.hostname);
log.info("Loading " + config.url);
win.loadURL(config.url);
log.info("Loading " + config.data.url);
win.loadURL(config.data.url);
} else {
log.warn("Could not reach " + url.hostname);
@@ -300,11 +167,13 @@ async function loadUrlAsync(testurl) {
}
// Load other protocols (i. e. offline) immedately
else {
win.loadURL(config.url);
win.loadURL(config.data.url);
}
} catch (err) {
log.error(err);
log.error(err.message);
// Load fallback page
win.loadFile('./html/onboarding.html');
}
}
@@ -319,12 +188,11 @@ app.commandLine.appendSwitch('no-sandbox');
app.whenReady().then(() => {
// configModule.load();
loadConfig();
config.load();
log.initialize();
if (config != undefined && 'logPath' in config) {
log.transports.file.resolvePathFn = () => config.logPath;
if (config.data != undefined && 'logPath' in config.data) {
log.transports.file.resolvePathFn = () => config.data.logPath;
}
log.info('----------------------------------------------');