Fix image uploads: accept more formats, convert to JPEG, show errors
Deploy to LXC / deploy (push) Successful in 18s

- Added HEIF, GIF, AVIF, BMP, TIFF to allowed image types
- All uploads converted to JPEG via sharp (fixes HEIC/HEIF from iPhones)
- Upload action wrapped in try/catch, errors shown in UI
- Error banner displayed above image section on failure

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 17:16:02 +07:00
parent 0113803378
commit 642359fec9
3 changed files with 27 additions and 16 deletions
+9 -6
View File
@@ -6,7 +6,10 @@ import sharp from 'sharp';
const UPLOAD_BASE = 'static/uploads';
const THUMBNAIL_WIDTH = 300;
const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/heic'];
const ALLOWED_IMAGE_TYPES = [
'image/jpeg', 'image/png', 'image/webp', 'image/heic', 'image/heif',
'image/bmp', 'image/tiff', 'image/gif', 'image/avif'
];
const ALLOWED_DOC_TYPES = ['application/pdf', 'text/plain', 'application/zip'];
export async function saveImage(
@@ -14,10 +17,10 @@ export async function saveImage(
subfolder: 'devices' | 'components'
): Promise<{ filePath: string; thumbnailPath: string }> {
if (!ALLOWED_IMAGE_TYPES.includes(file.type)) {
throw new Error(`Invalid image type: ${file.type}`);
throw new Error(`Invalid image type: ${file.type}. Allowed: JPEG, PNG, WebP, HEIC, GIF, AVIF, BMP, TIFF`);
}
const ext = extname(file.name) || '.jpg';
const ext = '.jpg'; // always save as jpg for consistency
const filename = `${randomUUID()}${ext}`;
const thumbFilename = `thumb_${filename}`;
@@ -26,15 +29,15 @@ export async function saveImage(
const buffer = Buffer.from(await file.arrayBuffer());
// Save original
// Convert to JPEG and save original
const filePath = join(dir, filename);
await writeFile(filePath, buffer);
await sharp(buffer).jpeg({ quality: 90 }).toFile(filePath);
// Generate thumbnail
const thumbnailPath = join(dir, thumbFilename);
await sharp(buffer).resize(THUMBNAIL_WIDTH).jpeg({ quality: 80 }).toFile(thumbnailPath);
// Return paths relative to static/ for serving
// Return URL paths (always forward slashes)
return {
filePath: `/uploads/${subfolder}/${filename}`,
thumbnailPath: `/uploads/${subfolder}/${thumbFilename}`