From 4966bc6a58a2ffaf5403ca0855d3c9e2deee6e68 Mon Sep 17 00:00:00 2001 From: paul Date: Fri, 18 Jul 2025 22:46:26 +0200 Subject: [PATCH] fix: resolve database connection error for analytics settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update publicSettings.js to handle missing analytics setting_type gracefully - Add dedicated PUT /analytics endpoint for saving analytics settings - Update frontend settings service to route to correct endpoints based on setting type - Fix query to use WHERE clause that won't fail if analytics type doesn't exist This fixes the "Connection terminated unexpectedly" error when fetching public settings with analytics configuration. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- backend/src/routes/adminSettings.js | 37 +++++++++++++++++++++++ backend/src/routes/publicSettings.js | 14 ++++++--- frontend/src/services/settings.service.ts | 14 ++++++++- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/backend/src/routes/adminSettings.js b/backend/src/routes/adminSettings.js index 746283e..287896a 100644 --- a/backend/src/routes/adminSettings.js +++ b/backend/src/routes/adminSettings.js @@ -496,6 +496,43 @@ router.put('/security', adminAuth, async (req, res) => { } }); +// Update analytics settings +router.put('/analytics', adminAuth, async (req, res) => { + try { + const settings = req.body; + + // Update or insert each setting + for (const [key, value] of Object.entries(settings)) { + await db('app_settings') + .insert({ + setting_key: key, + setting_value: JSON.stringify(value), + setting_type: 'analytics', + updated_at: new Date() + }) + .onConflict('setting_key') + .merge({ + setting_value: JSON.stringify(value), + updated_at: new Date() + }); + } + + // Log activity + await db('activity_logs').insert({ + activity_type: 'analytics_settings_updated', + actor_type: 'admin', + actor_id: req.admin.id, + actor_name: req.admin.username, + metadata: JSON.stringify({ settings_count: Object.keys(settings).length }) + }); + + res.json({ message: 'Analytics settings updated successfully' }); + } catch (error) { + console.error('Analytics settings update error:', error); + res.status(500).json({ error: 'Failed to update analytics settings' }); + } +}); + // Get storage info router.get('/storage/info', adminAuth, async (req, res) => { try { diff --git a/backend/src/routes/publicSettings.js b/backend/src/routes/publicSettings.js index 8f13f2d..a002b2d 100644 --- a/backend/src/routes/publicSettings.js +++ b/backend/src/routes/publicSettings.js @@ -5,9 +5,13 @@ const router = express.Router(); // Get public settings (branding and theme) router.get('/', async (req, res) => { try { - // Fetch branding, theme, general, security, and analytics settings + // Fetch branding, theme, general, and security settings + // Note: We include analytics in the query but it might not exist yet const settings = await db('app_settings') - .whereIn('setting_type', ['branding', 'theme', 'general', 'security', 'analytics']) + .where(function() { + this.whereIn('setting_type', ['branding', 'theme', 'general', 'security', 'analytics']) + .orWhere('setting_key', 'like', 'analytics_%'); + }) .select('setting_key', 'setting_value'); // Convert to object format @@ -44,9 +48,9 @@ router.get('/', async (req, res) => { maintenance_mode: settingsObject.general_maintenance_mode === true || settingsObject.general_maintenance_mode === 'true', // Umami analytics configuration (only if enabled) umami_enabled: settingsObject.analytics_umami_enabled === true || settingsObject.analytics_umami_enabled === 'true', - umami_url: settingsObject.analytics_umami_enabled ? (settingsObject.analytics_umami_url || null) : null, - umami_website_id: settingsObject.analytics_umami_enabled ? (settingsObject.analytics_umami_website_id || null) : null, - umami_share_url: settingsObject.analytics_umami_enabled ? (settingsObject.analytics_umami_share_url || null) : null + umami_url: (settingsObject.analytics_umami_enabled === true || settingsObject.analytics_umami_enabled === 'true') ? (settingsObject.analytics_umami_url || null) : null, + umami_website_id: (settingsObject.analytics_umami_enabled === true || settingsObject.analytics_umami_enabled === 'true') ? (settingsObject.analytics_umami_website_id || null) : null, + umami_share_url: (settingsObject.analytics_umami_enabled === true || settingsObject.analytics_umami_enabled === 'true') ? (settingsObject.analytics_umami_share_url || null) : null }; res.json(publicSettings); diff --git a/frontend/src/services/settings.service.ts b/frontend/src/services/settings.service.ts index 5852fea..ecdd6bc 100644 --- a/frontend/src/services/settings.service.ts +++ b/frontend/src/services/settings.service.ts @@ -162,7 +162,19 @@ export const settingsService = { // Update multiple settings at once async updateSettings(settings: Record): Promise { - await api.put('/admin/settings/general', settings); + // Determine the endpoint based on setting keys + const firstKey = Object.keys(settings)[0]; + let endpoint = '/admin/settings/general'; + + if (firstKey?.startsWith('security_')) { + endpoint = '/admin/settings/security'; + } else if (firstKey?.startsWith('analytics_')) { + endpoint = '/admin/settings/analytics'; + } else if (firstKey?.startsWith('branding_')) { + endpoint = '/admin/settings/branding'; + } + + await api.put(endpoint, settings); }, // Get storage information