fix: watermark upload JSON parsing and image quality preservation

- Fix JSON parsing error when uploading watermark logo by handling both
  JSON-stringified and raw string paths
- Ensure publicPath is JSON.stringify'd consistently when saving
- Preserve original image format (PNG/WebP/JPEG) when applying watermarks
- Use maximum quality (100) to prevent unnecessary recompression
This commit is contained in:
Paul Nothaft
2026-01-12 13:24:21 +01:00
parent bd8b885f7f
commit 0e3b50d1b6
2 changed files with 35 additions and 14 deletions
+13 -3
View File
@@ -392,13 +392,23 @@ 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 {
// 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);
}
}
}
// Save new watermark logo path
const logoPath = req.file.path;
@@ -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()
});
+16 -5
View File
@@ -177,14 +177,25 @@ class WatermarkService {
settings.position
);
// Apply watermark
const watermarkedBuffer = await image
.composite([{
// Apply watermark with high quality output to preserve original image quality
let watermarkedImage = image.composite([{
input: watermarkBuffer,
top: position.top,
left: position.left
}])
.toBuffer();
}]);
// 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, {