feat(listings): R2.3 featured listings entitlement + admin promote + search filter (TEC-2754)
- Add Plan.featuredListingsQuota (Int?) with per-tier seed (FREE=0, AGENT_PRO=5, INVESTOR=10, ENTERPRISE unlimited) and migration 20260418000000_add_featured_listings_quota
- Wire featured_listings_promoted metric into CheckQuotaHandler METRIC_TO_PLAN_FIELD so QuotaGuard honors the new quota
- Add PromoteFeaturedListingCommand + handler (entitlement-based, no payment): verifies ownership/agent, checks quota, extends featuredUntil, meters usage
- Add POST /listings/:id/promote endpoint gated by @RequireQuota('featured_listings_promoted') + QuotaGuard
- Add AdminFeatureListingCommand + handler with LISTING_FEATURED / LISTING_UNFEATURED audit log entries (new AdminAction enum values) and transactional write
- Add POST /admin/moderation/listings/:id/feature endpoint (ADMIN-only) with reason + duration
- Expose featured?: boolean filter on SearchPropertiesDto -> isFeatured:=1|0 Typesense filter in SearchPropertiesHandler
- Unit tests: 8 for PromoteFeaturedListingHandler, 6 for AdminFeatureListingHandler, 3 for search featured filter
Keeps existing pay-per-feature FeatureListingHandler intact for backward compatibility.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CqrsModule } from '@nestjs/cqrs';
|
||||
import { MulterModule } from '@nestjs/platform-express';
|
||||
import { AdminFeatureListingHandler } from './application/commands/admin-feature-listing/admin-feature-listing.handler';
|
||||
import { CreateListingHandler } from './application/commands/create-listing/create-listing.handler';
|
||||
import { FeatureListingHandler } from './application/commands/feature-listing/feature-listing.handler';
|
||||
import { ModerateListingHandler } from './application/commands/moderate-listing/moderate-listing.handler';
|
||||
import { PromoteFeaturedListingHandler } from './application/commands/promote-featured-listing/promote-featured-listing.handler';
|
||||
import { UpdateListingHandler } from './application/commands/update-listing/update-listing.handler';
|
||||
import { UpdateListingStatusHandler } from './application/commands/update-listing-status/update-listing-status.handler';
|
||||
import { UploadMediaHandler } from './application/commands/upload-media/upload-media.handler';
|
||||
@@ -28,6 +30,8 @@ import { ListingsController } from './presentation/controllers/listings.controll
|
||||
const CommandHandlers = [
|
||||
CreateListingHandler,
|
||||
FeatureListingHandler,
|
||||
PromoteFeaturedListingHandler,
|
||||
AdminFeatureListingHandler,
|
||||
UpdateListingHandler,
|
||||
UpdateListingStatusHandler,
|
||||
UploadMediaHandler,
|
||||
|
||||
Reference in New Issue
Block a user