Skip to content

feat(donations): donation history, CSV export with Bull queue, and fund release#283

Open
dDevAhmed wants to merge 3 commits into
Dfunder:mainfrom
dDevAhmed:main
Open

feat(donations): donation history, CSV export with Bull queue, and fund release#283
dDevAhmed wants to merge 3 commits into
Dfunder:mainfrom
dDevAhmed:main

Conversation

@dDevAhmed
Copy link
Copy Markdown

Summary

Implements four related features covering donation history visibility, personal donation tracking, CSV export with async queue support, and milestone fund release requests.


What Changed

Issue 261 — Campaign Donation Leaderboard

  • Route: GET /campaigns/:campaignId/donations
  • Paginated (20 per page default, configurable), sorted by �mount descending by default
  • Added isAnonymous boolean field to the Donation model; anonymous donations display wallet as Anonymous in the leaderboard
  • Response shape: { rank, walletAddress, amount, assetCode, createdAt, txHash, pagination }
  • New DTOs: GetCampaignDonationsQueryDto, GetCampaignDonationsResponseDto, DonorLeaderboardDto

Issue 262 — Personal Donation History

  • Route: GET /users/me/donations (JWT-protected)
  • Filterable by campaignId, startDate, endDate (ISO 8601)
  • Sortable by �mount or createdAt, ascending or descending
  • Response includes campaignTitle, campaignStatus, xHash, donatedAt, pagination, and summary stats (totalDonated, totalDonations, averageDonation)
  • New DTOs: GetUserDonationsQueryDto, GetUserDonationsResponseDto, UserDonationHistoryDto

Issue 263 — CSV Export with Bull Queue

  • Route: GET /users/me/donations/export (JWT-protected)
  • CSV columns: Campaign, Amount, Asset, Date, Tx Hash, USD Equivalent
  • Small exports (<= 500 rows): returned inline as ext/csv with Content-Disposition: attachment
  • Large exports (> 500 rows): enqueued via Bull export queue, returns 202 Accepted with jobId and statusUrl
  • Polling route: GET /users/me/donations/export/:jobId/status — returns job state or streams CSV when complete
  • New ExportProcessor (@processor('export')) handles async job processing
  • Added QUEUE_EXPORT = 'export' constant, registered queue in QueueModule and Bull Board

Issue 272 — Request Fund Release

  • Route: POST /campaigns/:campaignId/milestones/:milestoneId/release (JWT-protected, creator only)
  • Legacy path /fund-releases preserved for backward compatibility
  • Validates: campaign exists, caller is creator, milestone is UNLOCKED, amount <= milestone target, no existing PENDING release
  • Records signaturePayload for on-chain verification
  • Returns pending FundRelease record with full metadata
  • Removed overly-restrictive @@unique([milestoneId, campaignId, creatorId]) constraint — application-level check on PENDING status is sufficient and allows re-requests after cancellation

Schema Changes

  • Donation: added isAnonymous Boolean @default(false)
  • MilestoneStatus enum: added UNLOCKED
  • New FundReleaseStatus enum: PENDING | APPROVED | RELEASED | REJECTED | CANCELLED
  • New FundRelease model with full relation set to Milestone, Campaign, User
  • MilestonesModule: wired JwtModule so JwtAuthGuard can verify tokens

Why

These four issues form a cohesive donation-transparency and fund-management surface. They were implemented together to share schema migrations and avoid multiple PRs touching the same models.


Testing Performed

  • TypeScript compilation verified: zero errors in all changed files (users/, donations/, milestones/, campaigns/, queue/)
  • Pre-existing build errors (Prisma client not generated in CI, stellar-sdk types) are unrelated to this PR
  • Manually traced all acceptance criteria against implementation:
    • Pagination defaults (20/page), sort defaults (amount desc)
    • Anonymous masking via isAnonymous field
    • Date range + campaign filters on personal history
    • CSV column order matches spec
    • Bull queue threshold (500 rows), 202 response with jobId
    • Creator-only guard, UNLOCKED status check, duplicate PENDING prevention

Risks Considered

  • USD Equivalent in CSV: currently

closes: #261
closes: #262
closes: #263
closes: #272

dDevAhmed added 2 commits June 1, 2026 23:29
…ease

- Issue 261: GET /campaigns/:campaignId/donations - paginated donor leaderboard
  sorted by amount desc, 20 per page, anonymous donations masked via isAnonymous
  field, returns rank/walletAddress/amount/assetCode/createdAt/txHash

- Issue 262: GET /users/me/donations - personal donation history for authenticated
  user, filterable by campaignId/startDate/endDate, sortable by amount or
  createdAt, includes campaignTitle/campaignStatus/txHash per donation

- Issue 263: GET /users/me/donations/export - CSV export with columns Campaign,
  Amount, Asset, Date, Tx Hash, USD Equivalent; exports over 500 rows are
  processed asynchronously via Bull export queue with job-status polling at
  GET /users/me/donations/export/:jobId/status

- Issue 272: POST /campaigns/:campaignId/milestones/:milestoneId/release -
  fund release request for UNLOCKED milestones, creator-only, records
  signaturePayload, validates amount <= milestone target, prevents duplicate
  PENDING releases; legacy /fund-releases path preserved for compat

Schema changes:
  - Add isAnonymous Boolean field to Donation model
  - Add UNLOCKED to MilestoneStatus enum
  - Add FundReleaseStatus enum and FundRelease model
  - Remove overly-restrictive unique([milestoneId, campaignId, creatorId])
    constraint on FundRelease to allow re-requests after cancellation

Queue changes:
  - Add QUEUE_EXPORT constant and register export queue in QueueModule
  - Add ExportProcessor (@processor) for async donation CSV generation
  - Register export queue in Bull Board (dev admin UI)
…nation-history-fund-release

feat(donations): donation history, CSV export with Bull queue, and fund release
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Jun 1, 2026

@dDevAhmed Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant