fix: address PR #102 review feedback (GRO-145)
- factories.ts: add photoKey/photoUploadedAt null defaults to buildPet (TS regression fix)
- s3.ts: lazy singleton S3Client to avoid re-instantiation per call
- routes/pets.ts: server-side 5MB file size limit, explicit content-type allowlist (drops image/svg+xml etc), validate confirm key ownership against pets/${petId}/ prefix, delete old S3 object on re-upload, fix RBAC comment on DELETE photo
- PetPhotoUpload.tsx: bypass canvas resize for GIFs (preserves animation), pass fileSizeBytes in upload-url request
- Add PetPhotoDisplay.test.tsx: 7 tests covering fetch states, placeholder, refetch on petId change, custom size
- Add PetPhotoUpload.test.tsx: 8 tests covering idle state, type validation, upload flow, progress, GIF bypass
- Update petPhotos.test.ts: add SVG rejection, 5MB limit, key ownership, and old-photo deletion tests (18 total)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -31,6 +31,11 @@ export function PetPhotoUpload({ petId, onUploaded }: Props) {
|
||||
>({ status: "idle" });
|
||||
|
||||
async function resizeImage(file: File): Promise<{ blob: Blob; contentType: string }> {
|
||||
// GIFs must bypass canvas resize — canvas destroys animation frames
|
||||
if (file.type === "image/gif") {
|
||||
return { blob: file, contentType: "image/gif" };
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
const url = URL.createObjectURL(file);
|
||||
@@ -90,7 +95,7 @@ export function PetPhotoUpload({ petId, onUploaded }: Props) {
|
||||
const res = await fetch(`/api/pets/${petId}/photo/upload-url`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ contentType }),
|
||||
body: JSON.stringify({ contentType, fileSizeBytes: blob.size }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = (await res.json()) as { error?: string };
|
||||
|
||||
Reference in New Issue
Block a user