Backend:
- Auth controller sets httpOnly secure cookies (access_token, refresh_token, goodgo_authenticated) on login/register/refresh
- JWT strategy reads token from cookie first, falls back to Authorization header
- Added POST /auth/logout to clear auth cookies
- Added POST /auth/exchange-token for OAuth callback token-to-cookie exchange
- Refresh endpoint reads refresh_token from cookie (body fallback for backwards compat)
- CSRF middleware excludes auth endpoints (login, register, refresh, exchange-token, logout)
Frontend:
- Removed all localStorage token storage (goodgo_tokens key)
- Removed authGet/authPost/authPatch helpers from api-client (tokens sent via cookies)
- All API calls use credentials:'include' for cookie-based auth
- Updated auth-store: no more token state, uses isAuthenticated flag from cookie
- Updated admin-api, listings-api to remove explicit token parameters
- Updated all pages (admin dashboard, users, KYC, moderation, listings) to remove token passing
- OAuth callbacks use exchange-token endpoint to convert URL tokens to cookies
- Auth provider simplified (no client-side cookie management needed)
Security improvements:
- JWT no longer accessible via JavaScript (XSS-safe)
- Refresh token scoped to /auth path only
- Server-side goodgo_authenticated cookie with SameSite=Lax
- Access token cookie with SameSite=Strict
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add dedicated idempotencyKey column with unique constraint (userId, provider, idempotencyKey) to prevent duplicate payments at DB level
- Add @Min(1) @Max(100B) validators on amountVND in CreatePaymentDto to reject invalid amounts at API boundary
- Replace read-check-write callback handler with atomic updateIfStatus to eliminate race condition on concurrent callbacks
- Add magic byte verification in FileValidationPipe to validate file content matches declared MIME type server-side
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add dumb-init + --timeout-graceful-shutdown 30 to AI service Dockerfile
- Add slowapi rate limiting (configurable via AI_RATE_LIMIT) and X-API-Key auth middleware
- Pin all Python dependencies to exact versions for reproducible builds
- Move Grafana admin credentials from env vars to Docker secrets in production compose
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add startup env validation that fails fast in production if critical vars
(JWT_SECRET, JWT_REFRESH_SECRET, DATABASE_URL, CORS_ORIGINS, REDIS_HOST)
are missing
- Fix CORS_ORIGINS to throw in production instead of defaulting to localhost
- Replace hardcoded dev passwords in .env.example with CHANGE_ME placeholders
- Add missing vars to .env.example (CORS_ORIGINS, SMTP_*, FIREBASE, LOG_LEVEL)
- Warn on missing optional payment/storage vars at startup
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add take: 10 on unbounded media include in findByIdWithProperty
- Add take: 100 + orderBy on user listings include in getUserDetail
- Convert GetUsersQueryDto page/limit from string to validated integers with @Min(1) @Max(100)
- Add @Max(100) to BillingHistoryParamsDto limit field
- Refactor admin controller to use GetUsersQueryDto with class-validator pipeline
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Fix DI issues: circular MCP module dependency, EventBus type import,
SearchModule provider, CacheService metric counters placement
- Fix Express 5 readonly req.query in SanitizeInputMiddleware
- Fix Typesense client lazy initialization (getter instead of constructor)
- Fix MinIO bucket init error handling (non-fatal on 403)
- Fix missing class-validator decorators on bigint DTO fields (priceVND, amountVND)
- Fix subscription plan 404 (was returning 500 for invalid tier)
- Disable CSRF and raise rate limits in test environment
- Update E2E tests to match actual API response shapes
- Update CI workflow with Redis, Typesense, MinIO services and env vars
All 101 API E2E tests now pass against Docker dev environment.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Dashboard overview: stats cards (listings, views, inquiries, market avg price), Recharts bar chart for district pricing, recent listings feed with engagement metrics
- Analytics page: tabbed layout (overview/trends/districts), interactive bar chart for district comparison, line chart for price trend over quarters with dual Y-axis, clickable heatmap cards
- Listings management: grid/table view toggle, status filter, stats summary cards, table view with thumbnails and engagement data
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- scripts/seed-districts.ts: Vietnam district/ward data for HCM, Hanoi, Da Nang with sample properties
- scripts/seed-plans.ts: Subscription plans (FREE, AGENT_PRO, INVESTOR, ENTERPRISE)
- scripts/import-market-data.ts: Market index data across all 3 cities with realistic pricing
- All scripts are idempotent (upsert/ON CONFLICT DO NOTHING)
- Refactored prisma/seed.ts to import shared data from scripts, removing duplication
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add CSRF middleware with double-submit cookie pattern for all
state-changing requests. Integrate cookie-parser, update CORS
headers, and add client-side CSRF token handling.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Install @nestjs/swagger, configure Swagger UI at /api/docs with JWT bearer
auth, and add ApiTags/ApiOperation/ApiResponse/ApiProperty decorators to
all 8 controllers (50+ endpoints) and 31 DTOs across auth, listings,
search, payments, subscriptions, admin, notifications, and analytics modules.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add pg-backup container with daily automated pg_dump (02:00 UTC) and 7-day retention
- Add backup/restore scripts with documented recovery procedure
- Add Loki + Promtail for centralized log aggregation from all Docker containers
- Add Loki as Grafana datasource with correlation ID derived fields
- Add Grafana logs dashboard with volume, error rate, HTTP request, and log viewer panels
- Configure Promtail to parse Pino structured JSON logs with level/context labels
- Enhance LoggerService with string-level formatter and service base field
- Configure 15-day log retention in Loki
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Multi-stage Dockerfile for apps/api (NestJS) and apps/web (Next.js standalone)
- Production docker-compose.prod.yml with all services, health checks, and security
- Real deploy.yml pipeline: build → push to GHCR → deploy staging/production
- .dockerignore for optimized build context
- Enable Next.js standalone output mode
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add @@index([sellerId]) and @@index([propertyId]) to Listing model
- Add @@index([transactionId]) to Payment model
- Add @@index([planId]) to Subscription model
- Prevents full table scans on frequently-queried foreign keys
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The auth module fell back to a publicly-known secret string when JWT_SECRET
was unset, creating a critical authentication bypass risk. Both jwt.strategy.ts
and auth.module.ts now throw at startup if JWT_SECRET is missing.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
All 23 tasks across Phases 0-3 are now complete. Phase 3 items
(Analytics, AI/ML Services, MCP Servers, Monitoring) were already
committed but tracker was not updated.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Build the complete admin panel UI at apps/web/app/(admin)/:
- Admin layout with sidebar navigation and ADMIN role guard
- Dashboard page with stats cards and revenue chart
- User management with search, filters, pagination, detail panel, ban/unban
- Listings moderation queue with approve/reject/bulk actions
- KYC review page with document viewer and approve/reject flow
- New reusable UI components: Dialog, Table
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add missing admin backend endpoints:
- User management: list users (paginated/filterable), user detail view, update user status
- KYC approval: pending KYC queue, approve/reject KYC with comments
- Bulk moderation: approve/reject multiple listings in one request
- Domain events for KYC lifecycle (approved/rejected)
- Unit tests for all new handlers (35 tests passing)
All endpoints protected by ADMIN role guard via JwtAuthGuard + RolesGuard.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add /auth/callback/google and /auth/callback/zalo pages that extract
tokens from query params and persist them via the auth store
- Add handleOAuthCallback method to Zustand auth store
- Update middleware to allow /auth/callback/* as public routes
- Show OAuth error messages on login page when redirected back
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Commands: ApproveListing, RejectListing, BanUser, AdjustSubscription
- Queries: GetModerationQueue, GetDashboardStats, GetRevenueStats
- Admin-only guards via @Roles('ADMIN') on all endpoints
- Prisma-based admin query repository for dashboard aggregations
- 14 unit tests covering all command handlers and query handlers
- Added activate() method to UserEntity for unban support
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add init migration with all 23 models including PostGIS
- Add seed script for districts, plans, and sample data
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add Helmet with CSP, HSTS, referrer policy
- Configure CORS with environment-based origins
- Add global validation pipe with whitelist mode
- Add SanitizeInputMiddleware for XSS prevention
- Add ThrottlerBehindProxyGuard for rate limiting
- Add FileValidationPipe for upload security
- Set request body size limit to 1MB
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Create (public) route group with landing page (hero, featured listings, district links, stats, CTA)
- Create search page with filter sidebar, list/map/split view modes, URL-synced filters, pagination
- Build ListingMap component with CSS-based marker visualization and popup details
- Build FilterBar with transaction type, property type, city, price range, area, bedrooms filters
- Build PropertyCard and SearchResults components with responsive grid layout
- Update middleware to allow public access to / and /search routes
- Move dashboard home to /dashboard to avoid route conflict
- All content in Vietnamese, mobile responsive
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Multi-step wizard for listing creation (basic info, location, details, pricing, images)
- Listing detail page with image gallery, property specs, seller/agent info, stats
- Listings index page with filters (transaction type, property type) and pagination
- Edit page with tab-based form (read-only until backend PATCH endpoint available)
- Drag & drop image upload component with preview and multi-file support
- Dashboard layout with navigation bar
- New UI primitives: textarea, select, badge, tabs
- Listings API client with typed endpoints matching backend contract
- Zod validation schemas for all form steps
- Status badges with Vietnamese labels for all listing states
- Responsive design across all pages
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- TypesenseClient service with configurable connection
- Collection schema for listings with facets, geo-point, and Vietnamese text
- ListingIndexer service with PostGIS coordinate extraction for geo search
- CQRS commands: SyncListing, ReindexAll (batch with pagination)
- CQRS queries: SearchProperties (filters, sorting), GeoSearch (radius)
- Event handlers for listing.approved/updated/deactivated auto-sync
- REST endpoints: GET /search, GET /search/geo, POST /search/reindex (admin)
- DTOs with class-validator validation and pagination
Co-Authored-By: Paperclip <noreply@paperclip.ing>