/* Libraries / Imports */ const fs = require('fs'); const path = require('path'); const { logDebug, logError, logInfo, logWarn } = require('./console'); let configName = 'config'; const index = process.argv.indexOf('--config'); if (index !== -1 && index + 1 < process.argv.length) { configName = process.argv[index + 1]; logInfo('Loading with a custom config file:', configName + '.json'); } const configPath = path.join(__dirname, '../' + configName + '.json'); let serverConfig = { webserver: { webserverIp: "0.0.0.0", webserverPort: 8080, banlist: [], chatEnabled: true, tuningLimit: false, tuningLowerLimit: "100", tuningUpperLimit: "108", presets: [ "87.5", "87.5", "87.5", "87.5" ], defaultTheme: "theme1", bgImage: "", rdsMode: false }, xdrd: { wirelessConnection: true, comPort: "", xdrdIp: "127.0.0.1", xdrdPort: 7373, xdrdPassword: "" }, audio: { audioDevice: "Microphone (High Definition Audio Device)", audioChannels: 2, audioBitrate: "128k", softwareMode: false, startupVolume: "0.95" }, identification: { token: null, tunerName: "", tunerDesc: "", lat: "", lon: "", broadcastTuner: false, proxyIp: "", contact: null, }, password: { tunePass: "", adminPass: "" }, antennas: { enabled: false, ant1: { enabled: true, name: "Ant A" }, ant2: { enabled: true, name: "Ant B" }, ant3: { enabled: false, name: "Ant C" }, ant4: { enabled: false, name: "Ant D" } }, extras: { fmlistIntegration: true, fmlistOmid: "", }, tunnel: { enabled: false, username: "", token: "", lowLatencyMode: false, subdomain: "", httpName: "", httpPassword: "", }, plugins: [], device: 'tef', defaultFreq: 87.5, publicTuner: true, lockToAdmin: false, autoShutdown: false, enableDefaultFreq: false, defaultFreq: "87.5", bwSwitch: false }; // Function to add missing fields without overwriting existing values function addMissingFields(target, source) { Object.keys(source).forEach(function(key) { if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) { if (!target[key]) { target[key] = {}; // Create missing object } addMissingFields(target[key], source[key]); // Recursively add missing fields } else { if (target[key] === undefined) { target[key] = source[key]; // Add missing fields only } } }); } // Function to merge new configuration, overwriting existing values function deepMerge(target, source) { Object.keys(source).forEach(function(key) { if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) { if (!target[key] || typeof target[key] !== 'object') { target[key] = {}; // Ensure target[key] is an object before merging } deepMerge(target[key], source[key]); // Recursively merge objects } else { target[key] = source[key]; // Overwrite or add the value } }); } // Function to update the configuration at runtime function configUpdate(newConfig) { if (newConfig.webserver && (newConfig.webserver.banlist !== undefined || newConfig.plugins !== undefined)) { serverConfig.webserver.banlist = newConfig.webserver.banlist; serverConfig.plugins = newConfig.plugins; delete newConfig.webserver.banlist; } deepMerge(serverConfig, newConfig); // Overwrite with newConfig values configSave(); } // Function to save the configuration to the file system function configSave() { try { fs.writeFileSync(configPath, JSON.stringify(serverConfig, null, 2)); logInfo('Server config saved successfully.'); } catch (err) { logError(err); } } // Function to check if the configuration file exists function configExists() { return fs.existsSync(configPath); } // On startup, check for missing fields and add them if necessary if (configExists()) { const configFileContents = fs.readFileSync(configPath, 'utf8'); try { const configFile = JSON.parse(configFileContents); addMissingFields(configFile, serverConfig); // Add only missing fields from serverConfig serverConfig = configFile; // Use the updated configFile as the new serverConfig configSave(); // Save the merged config back to the file } catch (err) { logError('Error parsing config file:', err); } } module.exports = { configName, serverConfig, configUpdate, configSave, configExists, configPath };