- ListingDetailData: add valuationEstimate (AVM, cached 24 h), agentQualityScore (denormalised tier from Agent.qualityScore), similarCount, and gate inquiryCount (null for public callers; visible to listing owner or ADMIN) - listing-read.queries: select agent.qualityScore, derive tier, count similar listings in the same query via prisma.listing.count - GetListingQuery: add optional CallerContext (userId, role) for access control - GetListingHandler: inject AVM_SERVICE, fire AVM estimation with 24 h valuation cache, gracefully degrade to null on AVM failure, redact inquiryCount for non-privileged callers - OptionalJwtAuthGuard: new guard that sets request.user without throwing for anonymous requests; used on GET :id so the controller can pass caller identity to the query - ListingsModule: import AnalyticsModule so AVM_SERVICE is available for injection - CacheTTL: add VALUATION_LISTING (86400 s / 24 h) - Tests: 14 unit tests + 3 snapshot tests (public / owner / admin roles), all passing Co-Authored-By: Paperclip <noreply@paperclip.ing>
22 lines
835 B
TypeScript
22 lines
835 B
TypeScript
import { Injectable, type ExecutionContext } from '@nestjs/common';
|
|
import { AuthGuard } from '@nestjs/passport';
|
|
|
|
/**
|
|
* JWT guard that does NOT throw when the token is absent or invalid.
|
|
* When no valid token is provided, `request.user` is left as `undefined`.
|
|
* Use this for endpoints that are public but can serve richer data to
|
|
* authenticated callers (e.g. listing detail with access-gated fields).
|
|
*/
|
|
@Injectable()
|
|
export class OptionalJwtAuthGuard extends AuthGuard('jwt') {
|
|
override canActivate(context: ExecutionContext) {
|
|
return super.canActivate(context);
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
override handleRequest<TUser = any>(_err: unknown, user: TUser): TUser {
|
|
// Return whatever passport resolved (may be false/undefined for anonymous requests)
|
|
return user;
|
|
}
|
|
}
|