Skip to content

feat(stellar-wave): GDPR export, account deletion, fraud geo/fingerprint/velocity/impossible-travel, i18n - #1010

Merged
nanaf6203-bit merged 4 commits into
MettaChain:mainfrom
walexjnr:feat/stellar-wave-batch
Jul 28, 2026
Merged

feat(stellar-wave): GDPR export, account deletion, fraud geo/fingerprint/velocity/impossible-travel, i18n#1010
nanaf6203-bit merged 4 commits into
MettaChain:mainfrom
walexjnr:feat/stellar-wave-batch

Conversation

@walexjnr

Copy link
Copy Markdown
Contributor

Summary

Implements the four Stellar Wave issues assigned to me, behind a single feature branch so the maintainers can review them together.

  • Closes Implement GDPR-compliant user data export endpoint #959 — GDPR-compliant personal data export.

    • src/users/data-export.service.ts bundles every user-owned record (profile + preferences, activity logs, login history, search history + analytics, fraud alerts, sessions, properties, transactions, documents, notifications, saved filters, password history, digest preference, etc.) into a single schema-versioned JSON document, then compresses it with archiver into propchain-data-export-<job>.zip. The export_jobs row is updated to COMPLETED and a download-ready email is sent.
    • Self-service endpoint at POST /users/me/request-export and GET /users/me/exports/:jobId/stream (returns a StreamableFile for Nest's response pipeline).
  • Closes Add user account deletion workflow with configurable retention periods #960 — Account deletion workflow with retention + legal-hold override + audit log.

    • src/users/account-deletion.service.ts exposes requestDeletion, cancelDeletion, performScheduledDeletion. Retention defaults to 30 days, clamped to the configurable 7–90 range via ACCOUNT_DELETION_RETENTION_DAYS. Legal-hold users are blocked immediately and the attempt is written to the new account_deletion_audit table. Successful deletes also write a PERFORMED audit entry.
    • src/users/scheduled-deletion.service.ts now delegates to accountDeletionService.performScheduledDeletion() so the existing daily cron at 02:00 stays the entry point.
    • New columns legalHold, deletionReason on User and a dedicated AccountDeletionAudit table back the auditability requirement.
    • Self-service endpoints at POST /users/me/request-deletion and POST /users/me/cancel-deletion. Admin POST /users/delete-scheduled is preserved and now routes through the same service.
  • Closes Enhance fraud detection with IP geolocation and device fingerprinting #961 — Fraud detection enhancements: IP geolocation + device fingerprinting + velocity + impossible-travel.

    • New src/fraud/geo-location.service.ts — extracts the originating IP from x-forwarded-for / request.ip and maps a hard-coded set of common IPv4 prefixes to ISO country codes + cities. No external dependency, so it works offline and in unit tests.
    • New src/fraud/device-fingerprint.service.ts — stable SHA-256 over (user-agent, accept-language, ip-address) plus a parsed browserFamily / osFamily / isMobile / isBot summary.
    • FraudService gains recordLoginContext, evaluateVelocity, evaluateImpossibleTravel and evaluateDeviceFingerprint. FraudPattern enum is extended with VELOCITY_EXCEEDED, IMPOSSIBLE_TRAVEL and DEVICE_FINGERPRINT_MISMATCH (purely additive migration, no DROP / RENAME / SET NOT NULL).
    • The Session table gains deviceFingerprint, geoCountryCode and geoCity columns so context is persisted alongside the session row.
    • AuthService.login() calls fraudService.recordLoginContext(user.id, { ipAddress, userAgent }) after the existing evaluateSuccessfulLogin pass, so the new evaluations piggyback on every successful authentication.
  • Closes Add localized error messages with i18n support for API responses #964 — Localized error messages with i18n.

    • New src/i18n/ module: I18nService synchronously loads en.json and es.json catalogues at module init, parses Accept-Language headers (RFC 7231, with quality factors), and exposes t(key, lang, params) + translate(key, { userPreference, acceptLanguageHeader }) API. Resolution order is user preference → header → English default. Missing keys fall back to English silently so translations can ship incrementally.
    • HttpExceptionFilter, PrismaExceptionFilter and AllExceptionsFilter are updated to translate every error message and surface a language field on the response JSON.
    • main.ts adds a ValidationPipe exceptionFactory that pipes class-validator messages through the same I18nService.
    • All three filters are registered through APP_FILTER providers in AppModule so I18nService is correctly injected via Nest Dependency Injection (the existing app.useGlobalFilters(...) had to be removed to avoid double-registration).

Migration

prisma/migrations/20260801000000_add_security_compliance_features/migration.sql:

  • ALTER TABLE "users" ADD COLUMN "deletion_reason" TEXT, ADD COLUMN "legal_hold" BOOLEAN NOT NULL DEFAULT FALSE (+ index)
  • CREATE TABLE "account_deletion_audit" (...) (+ indexes)
  • ALTER TABLE "sessions" ADD COLUMN "device_fingerprint" TEXT, "geo_country_code" TEXT, "geo_city" TEXT (+ indexes)
  • ALTER TYPE "FraudPattern" ADD VALUE 'VELOCITY_EXCEEDED', 'IMPOSSIBLE_TRAVEL', 'DEVICE_FINGERPRINT_MISMATCH'

npx ts-node scripts/validate-migrations.ts reports no destructive changes (✅ No destructive changes detected. All migrations are safe.).

Testing

  • Unit specs cover the new bits: account-deletion.service.spec.ts, data-export.service.spec.ts, i18n.service.spec.ts, geo-location.service.spec.ts, device-fingerprint.service.spec.ts. The two strict-lint spec rewrites also exercise the new types and signature changes.
  • npm run lint -- --max-warnings=0 is green on the working tree.
  • The CI pipeline is unchanged: lint -> validate-migrations -> test (postgres service) -> build. No new CI surface area.

Risks worth flagging during review

  • AppModule registers the three filters as APP_FILTER providers. If the production app registers additional filters elsewhere they'll need to be deduplicated by the maintainers.
  • GeoLocationService is intentionally a static IPv4 prefix map (offline-friendly). A real-world rollout would swap this for MaxMind or an external API.
  • DataExportService writes the archive under /tmp/propchain-data-export and emails the resulting path. Files there are not garbage-collected today — that's a follow-up.
  • AccountDeletionService.performScheduledDeletion has a small TOCTOU window for concurrent reactivation; wrappers in a transaction are a sensible follow-up.

Closes #959, Closes #960, Closes #961, Closes #964

…ity/impossible-travel, and i18n error messages

Adds the self-service data export endpoint (issue MettaChain#959), an
account-deletion workflow with retention, audit log and legal-hold
override (issue MettaChain#960), and IP geolocation, device fingerprinting,
velocity checks and impossible-travel alerts in the login pipeline
(issue MettaChain#961). Localises ValidationPipe and exception-filter
messages via user preference / Accept-Language with English and
Spanish catalogues (issue MettaChain#964).

Mirrors the existing module patterns and stays inside NestJS
conventions. The new migration is purely additive, so
scripts/validate-migrations.ts still passes.
@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@walexjnr 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

walexjnr added 3 commits July 28, 2026 17:50
…irectly

The unit spec for performScheduledDeletion destructure an identifier
that buildHarness does not return, so the call/expectation threw
undefined is not a function. Asserting on prisma.user.delete, which
is the jest.fn the harness already wires, is the smaller change and
keeps the harness return shape minimal.
The Stellar Wave branch (MettaChain#959 MettaChain#960 MettaChain#961 MettaChain#964) had inadvertently
removed five top-level models and their supporting enums that were
delivered on earlier issues (MettaChain#955 Support Tickets, MettaChain#957 Property
Tours, MettaChain#958 Webhooks). This broke prisma generate with eight
type-resolution validation errors in CI (Type Webhook, TourRequest,
AgentAvailability, SupportTicket, SupportTicketNote).

Restores the verbatim content of those models + enums from origin/main
(append-only edit, no destructive change) so that prisma generate
succeeds and the test + benchmark jobs can progress past the schema
generation step. The additive migration in
prisma/migrations/20260801000000_add_security_compliance_features/ is
unchanged.
…e controllers

- src/fraud/fraud.service.spec.ts: register GeoLocationService and
  DeviceFingerprintService in the TestingModule. The FraudService
  constructor gained these in MettaChain#961 fraud-context work, so the suite
  was hanging on module compile when Nest could not resolve
  index [4]/[5].

- test/e2e/users-profile.e2e.spec.ts: register AccountDeletionService
  and DataExportService in the TestingModule. UsersController picked
  up these deps in MettaChain#959 / MettaChain#960, and the e2e spec crashed in beforeAll
  because the TestingModule could no longer resolve the controller's
  constructor (account-deletion at index [2], data-export at [3]).

Both specs now pass when jest is invoked with the same module list
the CI test job uses. No source code, schema, or migration changes;
this is a test-only fixup.

@nanaf6203-bit nanaf6203-bit left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nanaf6203-bit

Copy link
Copy Markdown
Contributor

@walexjnr Thanks for contributing.

@nanaf6203-bit
nanaf6203-bit merged commit cca1823 into MettaChain:main Jul 28, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants