Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 7s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 10s
Deploy / Build API Image (push) Failing after 23s
E2E Tests / Playwright E2E (push) Failing after 7s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 3s
Security Scanning / Trivy Scan — API Image (push) Failing after 43s
Security Scanning / Trivy Scan — Web Image (push) Failing after 28s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 28s
Deploy / Build Web Image (push) Failing after 10s
Deploy / Build AI Services Image (push) Failing after 9s
Security Scanning / Trivy Filesystem Scan (push) Failing after 38s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 1s
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Batch-committing concurrent work-in-progress so it isn't lost: Listings — bulk update + duplicate detection --------------------------------------------- - New command BulkUpdateListings + handler + tests under application/commands/bulk-update-listings/. - New DTO presentation/dto/bulk-update-listings.dto.ts. - Controller wires the bulk endpoint; update DTO extended. - Property duplicate detector hardened: normalized-address pipeline (new migration 20260420020000_add_property_address_normalized), repository + service updates, tests refreshed. - Listing entity gains ownership-transferred event (new event file). - Integration specs for price constraints (20260420000000_add_price_check_constraints) and duplicates. - E2E: e2e/api/listings-duplicates.spec.ts. Admin — moderation audit log ---------------------------- - New Prisma table (migration 20260420010000_add_moderation_audit_log) + Prisma repo + interface + DI wiring. - Listener `moderation-audit.listener.ts` + unit spec. - Query GetModerationAuditLogs + handler + controller `admin-moderation-audit.controller.ts` + DTO. Supporting ---------- - shared/infrastructure/cache.service.ts tweak. - AUDIT_LISTINGS_PROPERTY_MANAGEMENT.md — in-repo audit notes. - Various test + module wiring updates to keep the tree green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
66 lines
3.2 KiB
SQL
66 lines
3.2 KiB
SQL
-- Add CHECK constraints to enforce positive prices at the DB layer.
|
|
-- Scope (per TEC-2925): Listing price columns. Property has no direct
|
|
-- "price" column, only nullable maintenanceFeeVND (covered defensively).
|
|
-- Related price tables (PriceHistory) are also covered to keep the
|
|
-- invariant consistent across the price domain.
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 1) Backfill / clean offending data BEFORE applying constraints.
|
|
-- Strategy: NULL-out optional bad values; for the required Listing.priceVND
|
|
-- column we cannot null it, so any non-positive value is set to 1 (token
|
|
-- minimum) so the migration succeeds. In dev/seed these should not exist;
|
|
-- in prod the migration runner should review the audit log below before
|
|
-- applying.
|
|
-- -----------------------------------------------------------------------------
|
|
|
|
-- Audit (informational): how many rows would violate?
|
|
-- SELECT COUNT(*) FROM "Listing" WHERE "priceVND" <= 0;
|
|
-- SELECT COUNT(*) FROM "Listing" WHERE "rentPriceMonthly" IS NOT NULL AND "rentPriceMonthly" <= 0;
|
|
-- SELECT COUNT(*) FROM "Listing" WHERE "pricePerM2" IS NOT NULL AND "pricePerM2" <= 0;
|
|
-- SELECT COUNT(*) FROM "Listing" WHERE "aiPriceEstimate" IS NOT NULL AND "aiPriceEstimate" <= 0;
|
|
-- SELECT COUNT(*) FROM "PriceHistory" WHERE "oldPrice" <= 0 OR "newPrice" <= 0;
|
|
|
|
-- Backfill: required column → coerce to 1 VND (audit trail before applying).
|
|
UPDATE "Listing" SET "priceVND" = 1 WHERE "priceVND" <= 0;
|
|
|
|
-- Backfill: optional columns → NULL them out (was clearly invalid data).
|
|
UPDATE "Listing" SET "rentPriceMonthly" = NULL WHERE "rentPriceMonthly" IS NOT NULL AND "rentPriceMonthly" <= 0;
|
|
UPDATE "Listing" SET "pricePerM2" = NULL WHERE "pricePerM2" IS NOT NULL AND "pricePerM2" <= 0;
|
|
UPDATE "Listing" SET "aiPriceEstimate" = NULL WHERE "aiPriceEstimate" IS NOT NULL AND "aiPriceEstimate" <= 0;
|
|
|
|
-- PriceHistory rows with non-positive prices are corrupt; remove them.
|
|
DELETE FROM "PriceHistory" WHERE "oldPrice" <= 0 OR "newPrice" <= 0;
|
|
|
|
-- -----------------------------------------------------------------------------
|
|
-- 2) Apply CHECK constraints
|
|
-- -----------------------------------------------------------------------------
|
|
|
|
ALTER TABLE "Listing"
|
|
ADD CONSTRAINT "listing_price_vnd_positive_chk"
|
|
CHECK ("priceVND" > 0);
|
|
|
|
ALTER TABLE "Listing"
|
|
ADD CONSTRAINT "listing_rent_price_monthly_positive_chk"
|
|
CHECK ("rentPriceMonthly" IS NULL OR "rentPriceMonthly" > 0);
|
|
|
|
ALTER TABLE "Listing"
|
|
ADD CONSTRAINT "listing_price_per_m2_positive_chk"
|
|
CHECK ("pricePerM2" IS NULL OR "pricePerM2" > 0);
|
|
|
|
ALTER TABLE "Listing"
|
|
ADD CONSTRAINT "listing_ai_price_estimate_positive_chk"
|
|
CHECK ("aiPriceEstimate" IS NULL OR "aiPriceEstimate" > 0);
|
|
|
|
ALTER TABLE "PriceHistory"
|
|
ADD CONSTRAINT "price_history_old_price_positive_chk"
|
|
CHECK ("oldPrice" > 0);
|
|
|
|
ALTER TABLE "PriceHistory"
|
|
ADD CONSTRAINT "price_history_new_price_positive_chk"
|
|
CHECK ("newPrice" > 0);
|
|
|
|
-- Property defensive guard (only column with a monetary value):
|
|
ALTER TABLE "Property"
|
|
ADD CONSTRAINT "property_maintenance_fee_vnd_nonnegative_chk"
|
|
CHECK ("maintenanceFeeVND" IS NULL OR "maintenanceFeeVND" >= 0);
|