Skip to content

feat(api): add internal storage limit grant for billing - #44

Open
MehrshadFb wants to merge 1 commit into
mainfrom
feat/add-storage-limit
Open

feat(api): add internal storage limit grant for billing#44
MehrshadFb wants to merge 1 commit into
mainfrom
feat/add-storage-limit

Conversation

@MehrshadFb

Copy link
Copy Markdown
Collaborator

Summary

  • Add PhotoStorageService.addStorageLimit(userId, additionalBytes): one Prisma increment on User.storageLimitBytes, returning the new limit as a bigint
  • Reject a non-positive or fractional increment with 400 before any database work, and map Prisma's P2025 to 404 for an unknown user
  • Emit user.storage_limit.increased with audit: true and both byte counts as strings, since bigints are not JSON-serializable
  • Extract the cause-chain walk already used for serialization failures into a shared someCause() helper, now backing both isSerializationFailure and the new isRecordNotFound
  • Document the method in photos-architecture.md §9 in place of the raw Prisma snippet, and split the billing checklist into the part that now exists and the part that does not

Why

The per-user ceiling landed in #42, but raising one was still a hand-written UPDATE documented as a snippet. This turns that snippet into the single call a future payment webhook makes, so the billing layer never has to know the column exists.

Additive rather than absolute is the load-bearing choice. A purchase grants capacity instead of declaring a total, so two overlapping grants accumulate, and increment compiles to one statement, leaving no read-modify-write window for a grant to be lost in.

Notes

  • Stacked on feat/per-user-storage-limit (feat(api): store photo storage limit per user #42), which must merge first; retarget to main once it does
  • Internal only, exactly as scoped. No controller route, no Stripe or billing module, no downgrade or set-absolute path. A downgrade needs its own rules for usage already above the new ceiling, so it is deliberately not bundled here
  • Idempotency stays with the caller. A webhook redelivered twice grants twice, and deduping by payment event id is the payment layer's job
  • No OpenAPI or mobile change, since nothing new is reachable over HTTP

Test plan

  • npx jest --watchman=false (279 tests, 16 suites)
  • npx jest --config ./test/jest-e2e.json --watchman=false (98 tests)
  • npm run typecheck
  • npm run openapi:check
  • npx eslint on changed files, npm run format:check
  • CI passes on PR

@MehrshadFb
MehrshadFb force-pushed the feat/per-user-storage-limit branch from 2ab0194 to 57d886c Compare September 3, 2026 01:57
@MehrshadFb
MehrshadFb force-pushed the feat/add-storage-limit branch from 36b1ae2 to fe0c29a Compare September 3, 2026 02:02
@MehrshadFb
MehrshadFb force-pushed the feat/per-user-storage-limit branch from 57d886c to cdf85ad Compare September 4, 2026 16:24
@MehrshadFb
MehrshadFb force-pushed the feat/add-storage-limit branch from fe0c29a to cf528f7 Compare September 4, 2026 16:30
@MehrshadFb
MehrshadFb force-pushed the feat/add-storage-limit branch from cf528f7 to 8d5ce11 Compare September 4, 2026 16:40
@MehrshadFb
MehrshadFb changed the base branch from feat/per-user-storage-limit to main September 4, 2026 16:40
@MehrshadFb MehrshadFb closed this Sep 4, 2026
@MehrshadFb MehrshadFb reopened this Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

API unit-test coverage

Metric Coverage
Statements 68.14% (629/923)
Branches 50.09% (261/521)
Functions 72.67% (117/161)
Lines 66.46% (549/826)

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

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