Skip to content

fix(api): restore the stale pending upload sweeper lost from main - #47

Merged
MehrshadFb merged 4 commits into
mainfrom
restore/photo-pending-cleanup
Sep 3, 2026
Merged

fix(api): restore the stale pending upload sweeper lost from main#47
MehrshadFb merged 4 commits into
mainfrom
restore/photo-pending-cleanup

Conversation

@MehrshadFb

@MehrshadFb MehrshadFb commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Restore the stale PENDING upload sweeper from feat(api): sweep stale pending photo uploads hourly #39, which was merged on 1 September and then lost when main was force-updated
  • Re-applies the original three commits unchanged, rebased onto current main and reconciled with the storage quota work that landed after them
  • Adds PhotoPendingCleanupService and its hourly scheduler, the PHOTO_PENDING_CLEANUP_* config, and the (status, createdAt) index that makes the sweep query cheap

Why

#39's merge commit is not an ancestor of main. The history went from the type-check commit on 25 August, to #38 on the 28th, to #39 on 1 September, and was then force-updated to the storage quota commit, whose parent is that 25 August commit. That rewind discarded two merge commits. #38 survived by accident, because the quota branch already contained its changes and carried them into the squash. #39 did not, so the sweeper is absent from main while GitHub still reports the pull request as merged.

The gap matters beyond the missing job. PENDING rows count toward the storage quota, so an upload slot that is never confirmed holds its bytes against the uploader's cap. Reads filter to READY, so the row is invisible to both the list endpoint and single-photo reads, and the owner has nothing to delete. Without this sweeper a user can reach their limit with no visible photos and no way to recover.

Conflicts resolved

Five files needed both sides kept, since the quota work and the sweeper touch the same places:

File Resolution
src/config/photos.config.ts Both config blocks, both parsers
src/photos/photos.constants.ts Quota constants and cleanup constants
src/photos/photos.module.ts PhotoStorageService plus the two cleanup providers
.env.example Both variable blocks
docs/photos-architecture.md Quota stays §9, cleanup becomes §10, cross-referenced

The buildPhotoS3Key part of #39's diff applied as a no-op, because #38's uploader-namespaced keys are already on main.

Notes

Test plan

  • npx jest --watchman=false (254 tests, 17 suites)
  • npx jest --config ./test/jest-e2e.json --watchman=false (94 tests)
  • npm run typecheck
  • npm run openapi:check
  • npx eslint, npm run format:check
  • CI passes on PR

MehrshadFb and others added 3 commits September 2, 2026 21:17
Add a scheduled cleanup job that deletes PENDING photos older than 24h from
S3 and Postgres, with batching, env-based config, and a status index for
efficient sweeps.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

API unit-test coverage

Metric Coverage
Statements 67.47% (610/904)
Branches 49.22% (253/514)
Functions 71.97% (113/157)
Lines 65.84% (534/811)

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

…-cleanup

# Conflicts:
#	api/docs/photos-architecture.md
#	api/src/photos/photos.constants.ts
@MehrshadFb
MehrshadFb merged commit 1f935e6 into main Sep 3, 2026
4 checks passed
MehrshadFb added a commit that referenced this pull request Sep 4, 2026
Usage was already per uploader, but the cap was one env var for
everyone, so a paid or beta account could not be given a bigger ceiling
without a redeploy. The limit belongs on the account, not in config.

- Add User.storageLimitBytes (BigInt, default 5 GiB) and its migration;
  NOT NULL DEFAULT means existing rows and JIT-provisioned users get
  the free tier with no backfill and no code setting it
- Read the limit per userId in PhotoStorageService, inside the same
  Serializable transaction as the usage SUM so the reservation compares
  against a consistent snapshot; a missing user row surfaces as 404
- Remove the global override: PHOTO_STORAGE_LIMIT_BYTES, the
  storageLimitBytes config key, and the ConfigService dependency of
  PhotoStorageService. Raising a limit is now a row update
- Keep src/config/photos.config.ts, which still holds the pending
  cleanup keys added by #47, and keep photosConfig registered in
  AppModule. Deleting the file was correct when it held only the
  storage limit, and is not correct after #47
- Add an e2e assertion that the photos config namespace resolves from a
  booted AppModule; the cleanup scheduler reads those keys only when
  its cron fires, so an unregistered namespace would otherwise surface
  in production rather than in any test
- Add storageLimitBytes to the user fixtures and inline UserWithDetails
  literals; cover the per-user limit, the free-tier default and the 404
  in unit and e2e tests
- GET /users/me/storage and POST .../photos/upload-urls keep their
  contracts, so no OpenAPI or mobile change
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