Skip to content

feat(api): purge S3 objects when an event is deleted - #52

Merged
MehrshadFb merged 1 commit into
mainfrom
feat/event-delete-s3-purge
Sep 9, 2026
Merged

MehrshadFb merged 1 commit into
mainfrom
feat/event-delete-s3-purge

Conversation

@MehrshadFb

Copy link
Copy Markdown
Collaborator

Stacked on #51; the diff here is only the purge change once that merges.

Summary

  • DELETE /events/:eventId now removes the S3 objects of every photo in the event. EventsService.delete reads the s3Key of each Photo row and deletes the event in one transaction (the cascade drops the rows), then hands the keys to PhotoPurgeService.purgeObjects() after the commit.
  • New S3Service.deleteObjects(keys): S3 DeleteObjects in quiet mode, 1000 keys per request, returning per-key failures; a request that fails as a whole throws like the other S3Service methods.
  • New PhotoPurgeService in the photos module (exported; EventsModule now imports PhotosModule). It is best effort by contract: it never throws, logs event.photos.purged at info with counts on success and at error when any key remains, and the request still returns 204.
  • event.deleted audit log gains photoCount.
  • Tests: deleteObjects batching and error mapping, PhotoPurgeService outcomes, EventsService.delete ordering (keys read before the cascade, purge after the transaction, no purge when the transaction fails), and events e2e with S3Service stubbed.
  • Docs: §5 "Event delete", the §6 row, and §11 updated.

Why

Deleting an event cascaded the rows but never touched S3, so every photo in the event kept costing storage until the daily orphan reconciler ran. That reconciler is opt-in and off outside a deployed environment, so in practice the objects stayed for good. The rows go first on purpose: once they are gone no member can see a photo whose object is missing and the uploaders' quota is released immediately, and what is left at stake is only billing, which is exactly the failure the reconciler exists for.

Notes

  • No new IAM permission: DeleteObjects is authorised by the s3:DeleteObject action the API user already has on the bucket objects.
  • A slot minted between the key read and the commit has its row cascaded and, if the upload still lands, leaves an orphan for the reconciler. Documented in §5.
  • Single-photo delete keeps its S3-first order; the doc explains why the two differ.
  • No API or OpenAPI change; nothing for mobile.

Test plan

  • npx jest --watchman=false (400 tests)
  • npx jest --config ./test/jest-e2e.json --watchman=false (108 tests)
  • npm run typecheck
  • npm run format:check and eslint on the changed files
  • npm run openapi:generate (no diff)
  • CI passes on PR

@MehrshadFb
MehrshadFb force-pushed the fix/photo-list-cursor branch from deeac20 to 0d96d39 Compare September 6, 2026 13:49
@MehrshadFb
MehrshadFb force-pushed the feat/event-delete-s3-purge branch from a190d3a to cfa73ed Compare September 6, 2026 13:49
@MehrshadFb
MehrshadFb force-pushed the fix/photo-list-cursor branch from 0d96d39 to 2248583 Compare September 6, 2026 13:50
@MehrshadFb
MehrshadFb force-pushed the feat/event-delete-s3-purge branch from cfa73ed to 721fe28 Compare September 6, 2026 13:50
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

API unit-test coverage

Metric Coverage
Statements 73.49% (818/1113)
Branches 55.64% (340/611)
Functions 77.55% (152/196)
Lines 71.91% (712/990)

Unit suite only; controllers are exercised by the e2e suite.

@MehrshadFb
MehrshadFb changed the base branch from fix/photo-list-cursor to main September 9, 2026 02:52
@MehrshadFb
MehrshadFb force-pushed the feat/event-delete-s3-purge branch from e7f4296 to 4ac0e76 Compare September 9, 2026 02:52
@MehrshadFb
MehrshadFb merged commit 99420f0 into main Sep 9, 2026
4 checks passed
@MehrshadFb
MehrshadFb deleted the feat/event-delete-s3-purge branch September 9, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant