const { getPrismaClient } = require("../config/prisma"); const { v4: uuidv4 } = require("uuid"); const prisma = getPrismaClient(); // Cached settings instance let cachedSettings = null; // Environment variable to settings field mapping const ENV_TO_SETTINGS_MAP = { SERVER_PROTOCOL: "server_protocol", SERVER_HOST: "server_host", SERVER_PORT: "server_port", }; // Helper function to construct server URL without default ports function constructServerUrl(protocol, host, port) { const isHttps = protocol.toLowerCase() === "https"; const isHttp = protocol.toLowerCase() === "http"; // Don't append port if it's the default port for the protocol if ((isHttps && port === 443) || (isHttp && port === 80)) { return `${protocol}://${host}`.toLowerCase(); } return `${protocol}://${host}:${port}`.toLowerCase(); } // Create settings from environment variables and/or defaults async function createSettingsFromEnvironment() { const protocol = process.env.SERVER_PROTOCOL || "http"; const host = process.env.SERVER_HOST || "localhost"; const port = parseInt(process.env.SERVER_PORT, 10) || 3001; const serverUrl = constructServerUrl(protocol, host, port); const settings = await prisma.settings.create({ data: { id: uuidv4(), server_url: serverUrl, server_protocol: protocol, server_host: host, server_port: port, update_interval: 60, auto_update: false, signup_enabled: false, ignore_ssl_self_signed: false, updated_at: new Date(), }, }); console.log("Created settings"); return settings; } // Sync environment variables with existing settings async function syncEnvironmentToSettings(currentSettings) { const updates = {}; let hasChanges = false; // Check each environment variable mapping for (const [envVar, settingsField] of Object.entries(ENV_TO_SETTINGS_MAP)) { if (process.env[envVar]) { const envValue = process.env[envVar]; const currentValue = currentSettings[settingsField]; // Convert environment value to appropriate type let convertedValue = envValue; if (settingsField === "server_port") { convertedValue = parseInt(envValue, 10); } // Only update if values differ if (currentValue !== convertedValue) { updates[settingsField] = convertedValue; hasChanges = true; if (process.env.ENABLE_LOGGING === "true") { console.log( `Environment variable ${envVar} (${envValue}) differs from settings ${settingsField} (${currentValue}), updating...`, ); } } } } // Construct server_url from components if any components were updated const protocol = updates.server_protocol || currentSettings.server_protocol; const host = updates.server_host || currentSettings.server_host; const port = updates.server_port || currentSettings.server_port; const constructedServerUrl = constructServerUrl(protocol, host, port); // Update server_url if it differs from the constructed value if (currentSettings.server_url !== constructedServerUrl) { updates.server_url = constructedServerUrl; hasChanges = true; if (process.env.ENABLE_LOGGING === "true") { console.log(`Updating server_url to: ${constructedServerUrl}`); } } // Update settings if there are changes if (hasChanges) { const updatedSettings = await prisma.settings.update({ where: { id: currentSettings.id }, data: { ...updates, updated_at: new Date(), }, }); if (process.env.ENABLE_LOGGING === "true") { console.log( `Synced ${Object.keys(updates).length} environment variables to settings`, ); } return updatedSettings; } return currentSettings; } // Initialise settings - create from environment or sync existing async function initSettings() { if (cachedSettings) { return cachedSettings; } try { let settings = await prisma.settings.findFirst({ orderBy: { updated_at: "desc" }, }); if (!settings) { // No settings exist, create from environment variables and defaults settings = await createSettingsFromEnvironment(); } else { // Settings exist, sync with environment variables settings = await syncEnvironmentToSettings(settings); } // Cache the initialised settings cachedSettings = settings; return settings; } catch (error) { console.error("Failed to initialise settings:", error); throw error; } } // Get current settings (returns cached if available) async function getSettings() { return cachedSettings || (await initSettings()); } // Update settings and refresh cache async function updateSettings(id, updateData) { try { const updatedSettings = await prisma.settings.update({ where: { id }, data: { ...updateData, updated_at: new Date(), }, }); // Reconstruct server_url from components const serverUrl = constructServerUrl( updatedSettings.server_protocol, updatedSettings.server_host, updatedSettings.server_port, ); if (updatedSettings.server_url !== serverUrl) { updatedSettings.server_url = serverUrl; await prisma.settings.update({ where: { id }, data: { server_url: serverUrl }, }); } // Update cache cachedSettings = updatedSettings; return updatedSettings; } catch (error) { console.error("Failed to update settings:", error); throw error; } } // Invalidate cache (useful for testing or manual refresh) function invalidateCache() { cachedSettings = null; } module.exports = { initSettings, getSettings, updateSettings, invalidateCache, syncEnvironmentToSettings, // Export for startup use };