Skip to content

feat: implement feature flags module (closes #433)#1

Open
dreamgenies wants to merge 197 commits intomainfrom
feat/feature-flags-433
Open

feat: implement feature flags module (closes #433)#1
dreamgenies wants to merge 197 commits intomainfrom
feat/feature-flags-433

Conversation

@dreamgenies
Copy link
Copy Markdown
Owner

Implements Feature Flags module with runtime toggling and rollout control.

  • Adds FeatureFlag entity and repository
  • Implements service and controller endpoints
  • Adds Redis caching with TTL
  • Introduces decorator for flag checks
  • Includes unit and e2e tests

closes Rub-a-Dab-Dub#433

Explanation (max 3 lines):
Enables dynamic feature control without redeployment.
Uses Redis caching for fast evaluation.
Supports per-user, tier, and percentage rollout.

dreamgenies and others added 30 commits March 27, 2026 10:05
## Summary
Full implementation of the Badge and Achievement system with 7 badge
definitions, event-driven award triggers, display selection, real-time
in-app notifications, and comprehensive tests.

Entities:
- Badge: key (enum), name, description, iconUrl, tier (BRONZE/SILVER/
  GOLD/PLATINUM), criteria (JSONB with documented fields), timestamps.
- UserBadge: immutable award record per user per badge, isDisplayed flag
  (max 3 per user), awardedAt. Unique constraint prevents duplicates.

Badge definitions (seeded on startup via OnApplicationBootstrap):
- FIRST_TRANSFER  (BRONZE)  - complete 1 outgoing transfer
- TOP_REFERRER    (GOLD)    - refer >= 5 users
- CHAT_CHAMPION   (SILVER)  - send >= 100 messages
- DAO_VOTER       (SILVER)  - cast >= 1 DAO governance vote
- EARLY_ADOPTER   (GOLD)    - register before 2025-01-01
- CRYPTO_WHALE    (PLATINUM)- single transfer >= 10,000 tokens
- GROUP_FOUNDER   (BRONZE)  - create >= 1 group chat

Database:
- Migration 1736000000000-BadgesSchema creates badges and user_badges
  tables, badge_tier_enum, badge_key_enum, FK with CASCADE delete, and
  indexes on key and userId.

Repositories:
- BadgeRepository: findAll, findByKey, findById, upsert (for seeding).
- UserBadgeRepository: award (idempotent - returns null if already
  awarded), findByUser, findByUserAndBadge, countDisplayed,
  updateDisplayed (clears all then sets selected).

Service (BadgesService):
- seed(): upserts all 7 definitions on bootstrap.
- findAll(): returns all badge DTOs.
- findForUser(userId): returns user's earned badges.
- awardBadge(userId, key): idempotent award + in-app notification.
- updateDisplayedBadges(userId, badgeIds): validates ownership, enforces
  max-3 limit, updates display flags.
- Event triggers: onTransferCompleted, onReferralCompleted, onMessageSent,
  onDaoVoteCast, onGroupCreated, onUserRegistered - call awardBadge with
  the appropriate threshold checks.

Controller (GET|PUT /api/badges/*):
- GET  /badges              - all badges (public)
- GET  /badges/users/:userId - user badges (public)
- PUT  /badges/display       - update displayed badges (authenticated)

Notifications:
- In-app notification fired on every new badge award via
  NotificationsService.createNotification (fire-and-forget).

Tests:
- badges.service.spec.ts: 20 unit tests covering findAll, findForUser,
  awardBadge (award/already-awarded/key-not-found), updateDisplayedBadges
  (success/too-many/not-owned), and all 7 event trigger methods.
- user-badge.repository.spec.ts: 5 unit tests covering idempotent award,
  new award, findByUser query shape, updateDisplayed with and without IDs.
- test/badges.e2e-spec.ts: e2e suite covering GET /badges (public, fields,
  all 7 keys present, criteria shape), GET /badges/users/:userId (empty,
  invalid UUID), PUT /badges/display (401 without token).

AppModule:
- BadgesModule registered in AppModule imports list.
Closes Rub-a-Dab-Dub#500

## Summary
Complete Voice Message module supporting secure pre-signed upload,
confirmation, storage, playback metadata, waveform generation queue,
and strict validation with tier-based duration limits.

## Entity
VoiceMessage fields:
- id (uuid PK)
- messageId (uuid, indexed)
- uploaderId (uuid, indexed)
- fileKey (varchar 512, unique, indexed)
- fileUrl (text)
- duration (integer, nullable - set on confirm)
- waveformData (jsonb, nullable - set async by queue)
- mimeType (varchar 100)
- fileSize (integer)
- confirmed (boolean, default false)
- createdAt (timestamp)

## Database
Migration 1737000000000-VoiceMessagesSchema creates the voice_messages
table with indexes on messageId, uploaderId, and fileKey.

## Repository (VoiceMessageRepository)
- create, save, findById, findByFileKey
- findByMessageId: returns confirmed-only records in ASC order
- remove

## Service (VoiceMessagesService)
- presign(user, dto): validates MIME type and tier-based duration limit,
  generates a pre-signed PUT URL expiring in 5 minutes (300s), returns
  uploadUrl, fileKey, fileUrl, expiresIn, expiresAt.
- confirm(user, messageId, dto): idempotent - returns existing confirmed
  record unchanged; confirms pending record or creates new one; validates
  ownership; enqueues waveform generation fire-and-forget.
- findById(id): returns DTO or 404.
- findByMessageId(messageId): returns all confirmed voice messages.
- delete(id, requesterId): ownership check, removes DB record and S3 file.

## Validation rules
- Allowed MIME types: audio/ogg, audio/mp4, audio/webm only.
- SILVER tier: max 300s (5 min).
- GOLD / BLACK tier: max 600s (10 min).
- Pre-signed URL TTL: 300s (5 min).
- Invalid MIME type or exceeded duration throws BadRequestException.
- Non-owner delete/confirm throws ForbiddenException.

## WaveformQueueService
Async background queue (setTimeout-based, mirrors VirusScanQueueService
pattern). Logs the job; replace processJob() with real ffprobe/
fluent-ffmpeg invocation. Swap for BullMQ when Redis queue is needed.

## Controller (POST|GET|DELETE /api/voice-messages/*)
- POST /voice-messages/presign                      - generate upload URL
- POST /voice-messages/messages/:messageId/confirm  - confirm upload
- GET  /voice-messages/:id                          - fetch by ID
- GET  /voice-messages/messages/:messageId          - fetch by message
- DELETE /voice-messages/:id                        - delete (owner only)

## Module
VoiceMessagesModule imports TypeOrmModule and AttachmentsModule (reuses
S3StorageService). Exports VoiceMessagesService.

## Tests
- voice-messages.service.spec.ts: 18 unit tests covering presign (valid,
  invalid MIME, SILVER over limit, GOLD at limit, GOLD over limit, all 3
  MIME types), confirm (new, idempotent, wrong owner, invalid MIME in key),
  findById (found, 404), findByMessageId, delete (success, 404, forbidden).
- voice-message.repository.spec.ts: 4 unit tests for findByMessageId query
  shape, findByFileKey, remove, save delegation.
- test/voice-messages.e2e-spec.ts: e2e suite verifying 401 enforcement on
  all endpoints and 400 for invalid UUID params.

## AppModule
VoiceMessagesModule registered in AppModule imports list.
Add comprehensive implementations of four backend modules with clean architecture,
proper validation, indexing, and full unit + e2e test coverage:

1. Blockchain Transaction Tracking Module (8 files)
   - Records on-chain Stellar transactions independently from business transactions
   - Entity with fields: id, userId, type (6 enums), txHash (unique, nullable),
     status (4 states), fromAddress, toAddress, amountUsdc, feeStroops, ledger,
     errorMessage, referenceId, timestamps
   - DB indexes: (userId, createdAt DESC), unique txHash, referenceId, status
   - Service methods: create(), updateStatus(), findByReference(), findByTxHash()
   - Idempotent operations with proper state transitions
   - Controllers: GET /blockchain/transactions (paginated, filterable), GET /blockchain/transactions/:id
   - Full unit tests for lifecycle, idempotency, and filtering
   - Full e2e tests with authorization checks

2. Message Forwarding Module (7 files)
   - ForwardedMessage entity with proper chain tracking
   - Service methods: forwardMessage(), forwardToMultiple() (max 5 targets),
     getForwardedFrom(), getForwardChain() (depth limit: 3)
   - Validation: forwarder is participant in both conversations,
     prevents deleted message forwarding
   - Content preservation without re-uploading
   - POST /messages/:id/forward endpoint with validation
   - WebSocket event hooks for message:new
   - Full forward chain tracking with depth limits
   - Complete unit and e2e tests

3. Poll Module (7 files)
   - Poll entity with JSONB options array
   - PollVote entity with option indexes array
   - Service methods: createPoll() (2-10 options enforced),
     castVote() (respects allowMultiple),
     retractVote() (before close only),
     closePoll() (creator-only),
     getPoll(), getPollResults() (respects anonymity),
     getPollsInConversation()
   - @Cron scheduled job for auto-closing expired polls
   - 6 REST endpoints for full poll lifecycle
   - WebSocket event hooks for poll:updated
   - Anonymous polls hide voter identity but expose totals
   - Vote aggregation and accurate counting
   - Comprehensive unit and e2e tests

4. Mention Module (7 files)
   - Mention entity with read tracking
   - Service methods: parseMentions() (regex @username pattern),
     createMentions() (during message creation, no extra call),
     markMentionRead(), getUnreadMentions(),
     getMentionsInConversation(), getUnreadCount(),
     markAllRead(), deleteByMessageId()
   - Mention parsing: @([a-zA-Z0-9._-]+) with position tracking
   - Validation: mentioned users are conversation participants
   - 4 REST endpoints for mention management
   - WebSocket event hooks for mention:new
   - In-app and push notification hooks
   - Batch mark-as-read operations
   - Unread count accuracy tracking
   - Full user ownership validation
   - Complete unit and e2e tests

All modules include:
- Clean architecture with separation of concerns
- Strategic database indexing
- Production-safe idempotent operations
- Comprehensive input validation
- Complete error handling
- Unit test coverage (85%+)
- E2E test coverage (85%+)
- Swagger/OpenAPI documentation ready
- Proper authorization checks

Total: 34 files (29 source files + 4 e2e tests + 1 updated app.module)
- 25+ service methods
- 14 REST endpoints
- 4 scheduled jobs
- Full WebSocket event integration points
…ntegration-test-suite

feat: add comprehensive e2e integration test suite (Rub-a-Dab-Dub#434)
…management

feat: add legal consent management module
…ent-system

feat(badges): implement Badge and Achievement system
…module

feat(voice-messages): implement Voice Message module
Xaxxoo and others added 30 commits March 30, 2026 00:18
…irdrop-distribution

[Soroban] Airdrop & Reward Distribution Contract
…scrow-payment

[Soroban] Escrow & Conditional Payment Contract
…taking-rewards

[Soroban] Token Staking & Rewards Contract
…sername-registry

[Soroban] Decentralized Username Registry Contract
- YieldStrategy storage: strategyId, protocolAddress, token, apy_bps,
  tvl, total_shares, isActive, lastUpdated
- UserYieldPosition storage: user, strategyId, deposited, shares,
  lastHarvested
- add_strategy(protocol, token) -> StrategyId — admin only; derives
  deterministic id via sha256(protocol_xdr ++ token_xdr)
- deposit(user, strategy_id, amount) -> shares — transfers tokens into
  contract, mints shares proportional to current pool value
- withdraw(user, strategy_id, shares) -> amount — burns shares, returns
  principal + accrued yield via token transfer back to user
- harvest(strategy_id) — compounds 1% simulated yield into TVL;
  recalculates APY in basis points from realised yield / elapsed seconds
- rebalance(from, to, amount) — moves TVL between strategies, admin only
- get_apy, get_position, get_strategy view functions
- Event emission for deposit, withdraw, harvest, rebalance
- Full unit and integration test suite covering all functions,
  error paths, multi-user proportional yield, and APY updates
…b-Dub#642)

- HistoryImportJob entity with PENDING/RUNNING/COMPLETED/FAILED status
  and resumable cursor field (stellar_history_import_jobs table)
- StellarHistoryImporterService: importHistory, getImportStatus,
  syncNewTransactions, mapHorizonTxToInternal, deduplicateByTxHash,
  resumeFromCursor
- Cursor-based Horizon pagination (200 tx/page); progress saved after
  each page so jobs are resumable on failure
- Deduplication by txHash via ON CONFLICT DO NOTHING; graceful fallback
  when table does not exist on first run
- HistoryImportProcessor (BullMQ WorkerHost) on stellar-history-import
  queue with 3 retries and exponential backoff
- StellarHistoryImporterController: POST /wallets/:id/import-history
  (202 Accepted), GET /wallets/:id/import-status
- WalletVerifiedListener auto-triggers import on wallet.verified event
- Uses @stellar/stellar-sdk consistent with repo conventions
- 22 unit tests covering all public methods, error paths, dedup,
  cursor resume, multi-page import, and sync
…Dub#645)

- Add GeoRestriction and UserGeoRecord TypeORM entities
- Implement GeoRestrictionService with OFAC fast-path (15 sanctioned countries),
  in-memory cache with 5-min TTL, and full CRUD for restriction rules
- Add GeoRestrictionMiddleware resolving country from CDN headers
  (CF-IPCountry, X-Country-Code, CloudFront-Viewer-Country) with OFAC 403 fast-path
- Add GeoFeatureGuard with @geofeature decorator for endpoint-level feature restrictions
- Add GeoRestrictionController with admin and user-facing endpoints
- Add GeoRestrictionModule wiring middleware to all routes
- Add unit test suite covering all service methods

Closes Rub-a-Dab-Dub#645
- Add OfflineMessageQueue TypeORM entity (QUEUED/DELIVERED/FAILED status,
  recipientId, messageId, conversationId, payload, attempts, deliveredAt)
- Implement OfflineQueueService with Redis sorted-set queue (score = timestamp)
  and full CRUD: enqueue, dequeueForUser, markDelivered, retryFailed,
  getQueueDepth, flushOnConnect, pruneOld, getStats, persistStaleRedisEntries
- Messages queued > 1 hour in Redis are persisted to PostgreSQL for durability
  (scheduled every 10 min via @Cron)
- Delivered records pruned after 30 days (daily cron at 02:00)
- flushOnConnect delivers all queued messages in strict chronological order
  bracketed by sync:start and sync:complete WebSocket events
- Admin alert logged when any user's queue depth exceeds 1000 messages
- OfflineQueueGateway hooks into /chat namespace OnGatewayConnection to
  auto-flush on user reconnect
- OfflineQueueController: GET /admin/offline-queue/stats,
  POST /admin/offline-queue/flush/:userId
- Unit test suite covering all service methods with mocked Redis + TypeORM

Closes Rub-a-Dab-Dub#641
…and-OAuth2-Module

feat: social login and OAuth2 Module.
…y-importer

feat: NestJS Stellar Transaction History Importer module (Rub-a-Dab-Dub#642)
…-Module

feat: User Blocking & Privacy Enforcement Module
…odule

feat: Group Expense Splitting Module
…-Module

feat: Bot & Webhook Integration Module
…Module

feat: Terms & Consent Management Module
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.

Feature Flags Module