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
+16 -6
View File
@@ -392,11 +392,21 @@ router.post('/branding/watermark-logo', adminAuth, requirePermission('settings.e
.first(); .first();
if (oldWatermarkLogoSetting && oldWatermarkLogoSetting.setting_value) { if (oldWatermarkLogoSetting && oldWatermarkLogoSetting.setting_value) {
const oldPath = JSON.parse(oldWatermarkLogoSetting.setting_value); let oldPath;
try { try {
await fs.unlink(oldPath); // Try to parse as JSON first (for JSON-stringified paths)
} catch (error) { oldPath = JSON.parse(oldWatermarkLogoSetting.setting_value);
console.error('Failed to delete old watermark logo:', error); } 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') await db('app_settings')
.insert({ .insert({
setting_key: 'branding_watermark_logo_url', setting_key: 'branding_watermark_logo_url',
setting_value: publicPath, setting_value: JSON.stringify(publicPath),
setting_type: 'branding', setting_type: 'branding',
updated_at: new Date() updated_at: new Date()
}) })
.onConflict('setting_key') .onConflict('setting_key')
.merge({ .merge({
setting_value: publicPath, setting_value: JSON.stringify(publicPath),
updated_at: new Date() updated_at: new Date()
}); });
+19 -8
View File
@@ -177,14 +177,25 @@ class WatermarkService {
settings.position settings.position
); );
// Apply watermark // Apply watermark with high quality output to preserve original image quality
const watermarkedBuffer = await image let watermarkedImage = image.composite([{
.composite([{ input: watermarkBuffer,
input: watermarkBuffer, top: position.top,
top: position.top, left: position.left
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 // Cache the result
this.cache.set(cacheKey, { this.cache.set(cacheKey, {