feat: image upload optimization with sharp, thumbnails, and structured error codes (#304)

- Add sharp for server-side image processing (WebP conversion + thumbnails)
- New shared backend utility for image upload, optimization, and cleanup
- Return structured error codes from upload endpoints (IMAGE_TOO_LARGE, INVALID_TYPE, etc.)
- Frontend error code mapping with i18n support (EN + DE)
- MedicationAvatar tries thumbnail first, falls back to full image
- Error display in MedicationsPage, MobileEditModal, and Auth avatar upload

Closes #302
This commit is contained in:
Daniel Volz
2026-02-24 23:52:59 +01:00
committed by GitHub
parent 7a32b2045e
commit 96b2a0c96f
15 changed files with 916 additions and 93 deletions
+30
View File
@@ -0,0 +1,30 @@
import type { TFunction } from "i18next";
export const MAX_IMAGE_UPLOAD_BYTES = 10 * 1024 * 1024;
/** Error codes returned by the backend image upload endpoints. */
const IMAGE_ERROR_CODE_MAP: Record<string, string> = {
IMAGE_TOO_LARGE: "form.imageUploadErrors.tooLarge",
INVALID_TYPE: "form.imageUploadErrors.invalidType",
INVALID_IMAGE: "form.imageUploadErrors.invalidImage",
NO_FILE: "form.imageUploadErrors.noFile",
NETWORK_ERROR: "common.networkError",
};
/**
* Maps a backend image-upload error code to a translated user-facing message.
* Falls back to a generic error when the code is unknown.
*/
export function resolveImageUploadError(code: string, t: TFunction): string {
const normalized = normalizeErrorCode(code);
const key = IMAGE_ERROR_CODE_MAP[normalized];
return key ? t(key) : t("form.imageUploadErrors.generic");
}
/** Browser network errors are not error codes — normalise them. */
function normalizeErrorCode(code: string): string {
if (code === "Failed to fetch" || code.startsWith("NetworkError")) {
return "NETWORK_ERROR";
}
return code;
}