diff --git a/backend/src/routes/adminSettings.js b/backend/src/routes/adminSettings.js index e655e6e3..b92c5ec7 100644 --- a/backend/src/routes/adminSettings.js +++ b/backend/src/routes/adminSettings.js @@ -392,11 +392,21 @@ router.post('/branding/watermark-logo', adminAuth, requirePermission('settings.e .first(); if (oldWatermarkLogoSetting && oldWatermarkLogoSetting.setting_value) { - const oldPath = JSON.parse(oldWatermarkLogoSetting.setting_value); + let oldPath; try { - await fs.unlink(oldPath); - } catch (error) { - console.error('Failed to delete old watermark logo:', error); + // Try to parse as JSON first (for JSON-stringified paths) + oldPath = JSON.parse(oldWatermarkLogoSetting.setting_value); + } catch (e) { + // If it's not valid JSON, use the raw value + oldPath = oldWatermarkLogoSetting.setting_value; + } + + if (oldPath && typeof oldPath === 'string') { + try { + await fs.unlink(oldPath); + } catch (error) { + console.error('Failed to delete old watermark logo:', error); + } } } @@ -421,13 +431,13 @@ router.post('/branding/watermark-logo', adminAuth, requirePermission('settings.e await db('app_settings') .insert({ setting_key: 'branding_watermark_logo_url', - setting_value: publicPath, + setting_value: JSON.stringify(publicPath), setting_type: 'branding', updated_at: new Date() }) .onConflict('setting_key') .merge({ - setting_value: publicPath, + setting_value: JSON.stringify(publicPath), updated_at: new Date() }); diff --git a/backend/src/services/watermarkService.js b/backend/src/services/watermarkService.js index 8c5de36c..2ef44a6c 100644 --- a/backend/src/services/watermarkService.js +++ b/backend/src/services/watermarkService.js @@ -177,14 +177,25 @@ class WatermarkService { settings.position ); - // Apply watermark - const watermarkedBuffer = await image - .composite([{ - input: watermarkBuffer, - top: position.top, - left: position.left - }]) - .toBuffer(); + // Apply watermark with high quality output to preserve original image quality + let watermarkedImage = image.composite([{ + input: watermarkBuffer, + top: position.top, + left: position.left + }]); + + // Preserve original format with high quality settings + const format = metadata.format || 'jpeg'; + let watermarkedBuffer; + + if (format === 'png') { + watermarkedBuffer = await watermarkedImage.png({ quality: 100, compressionLevel: 6 }).toBuffer(); + } else if (format === 'webp') { + watermarkedBuffer = await watermarkedImage.webp({ quality: 95, lossless: false }).toBuffer(); + } else { + // Default to JPEG with maximum quality (100) to prevent recompression + watermarkedBuffer = await watermarkedImage.jpeg({ quality: 100, mozjpeg: true }).toBuffer(); + } // Cache the result this.cache.set(cacheKey, {