feat: Fix/env validation jwt network platform port - #641
Open
Lanhubs wants to merge 4 commits into
Open
Conversation
…FORM_SECRET_KEY, PORT config/env.js validated that JWT_SECRET is present but did not enforce a minimum length or warn about weak secrets. A short or weak secret could allow token forgery. - Validate JWT_SECRET minimum length of 32 characters ( emits a warning for short secrets rather than blocking startup, to preserve dev/test ergonomics) - Validate STELLAR_NETWORK is one of testnet or mainnet - Validate PLATFORM_SECRET_KEY starts with S and is 56 characters - Validate PORT is a number if set - Add env.test.js covering each rule
…nv.example Update Playwright config and unit-test JWT_SECRET stubs to use a value of at least 32 characters so they pass the new env validation. Document the JWT_SECRET, STELLAR_NETWORK, PLATFORM_SECRET_KEY, and PORT requirements in the example env files. No production code or behavior changes.
Pin husky to its current declared version so the npm ci install in CI can resolve its lifecycle script (which invokes the husky binary). Without this, npm ci fails with `sh: 1: husky: not found` because the binary referenced by an existing lifecycle script is not present in the lockfile.
…tform-port Fix/env validation jwt network platform port
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #397 — Reward tier
claimed_countis now atomically decremented when a tier-backed contribution is made, preventing overselling of limited-edition reward tiers.The Problem
The
claimed_count(and hence computedremaining) onreward_tierswas only incremented asynchronously in the ledger monitor when Horizon detected the on-chain payment. The contribution route itself never decremented the count, so:The Fix
A new
reserveTierSlot()function inrewardTierService.jsatomically incrementsclaimed_countinside the contribution route's database transaction — before the Stellar transaction is even submitted. If the tier is already at capacity, the contribution is rejected with HTTP 409 Sold out and the entire transaction is rolled back.Changes
backend/src/services/rewardTierService.jsreserveTierSlot(client, { tierId, campaignId })— Atomically incrementsclaimed_countviaUPDATE ... WHERE (tier_limit IS NULL OR claimed_count < tier_limit) RETURNING. Returns the tier row on success, ornullif sold out.assignTierToContribution(client, { ..., tierId })— Extended to accept an optionaltierId. When provided, only creates thecontribution_rewardsjoin row (idempotent viaON CONFLICT DO NOTHING) without double-incrementingclaimed_count, since the route already reserved the slot.backend/src/routes/contributions.jsPOST /route now extracts optionaltier_idfromreq.bodySELECT)reserveTierSlot()inside the existing transaction, before submitting to Stellar{ error: "This reward tier is sold out", tier_id }if the tier is at capacitytierIdthrough tosubmitCustodialContributionso it flows into metadatabackend/src/services/contributionService.jstierIdparameter and includes it instellar_transactions.metadataastier_idbackend/src/services/ledgerMonitor.jstier_idfromstellar_transactions.metadataand passes it toassignTierToContribution, ensuring pre-reserved tiers are honoured without double-countingclaimed_countbackend/src/middleware/validation.jstier_idvalidation (UUID format) tocontributionValidationHow It Works End-to-End
Acceptance Criteria
claimed_countdecrements atomically with each contributionUPDATE ... SET claimed_count = claimed_count + 1inside route transactionreserveTierSlot()returns null →res.status(409)UPDATE ... WHERE claimed_count < tier_limitwith PostgreSQL row-level locking inside transactionclaimed_countis updated atomically in the route, so the nextlistTiersWithAvailability()query reflects the correctremainingTesting
reserveTierSlot()— success pathreserveTierSlot()— sold-out path returns nullassignTierToContribution()with explicittierId— no double-incrementtier_id, verifyclaimed_countincrementedtier_id, verify 409 response