import { Body, Controller, Get, Post, Req, UseGuards, UsePipes } from "@nestjs/common"; import { ZodValidationPipe } from "nestjs-zod"; import { z } from "zod"; import type { RequestWithUser } from "@bff/modules/auth/auth.types.js"; import { RateLimit, RateLimitGuard } from "@bff/core/rate-limiting/index.js"; import { InternetCatalogService } from "./services/internet-catalog.service.js"; import { addressSchema } from "@customer-portal/domain/customer"; import type { InternetEligibilityDetails } from "@customer-portal/domain/catalog"; const eligibilityRequestSchema = z.object({ notes: z.string().trim().max(2000).optional(), address: addressSchema.partial().optional(), }); type EligibilityRequest = z.infer; /** * Internet Eligibility Controller * * Authenticated endpoints for: * - fetching current Salesforce eligibility value * - requesting a (manual) eligibility/availability check * * Note: CatalogController is @Public, so we keep these endpoints in a separate controller * to ensure GlobalAuthGuard enforces authentication. */ @Controller("catalog/internet") @UseGuards(RateLimitGuard) export class InternetEligibilityController { constructor(private readonly internetCatalog: InternetCatalogService) {} @Get("eligibility") @RateLimit({ limit: 60, ttl: 60 }) // 60/min per IP (cheap) async getEligibility(@Req() req: RequestWithUser): Promise { return this.internetCatalog.getEligibilityDetailsForUser(req.user.id); } @Post("eligibility-request") @RateLimit({ limit: 5, ttl: 300 }) // 5 per 5 minutes per IP @UsePipes(new ZodValidationPipe(eligibilityRequestSchema)) async requestEligibility( @Req() req: RequestWithUser, @Body() body: EligibilityRequest ): Promise<{ requestId: string }> { const requestId = await this.internetCatalog.requestEligibilityCheckForUser(req.user.id, { email: req.user.email, ...body, }); return { requestId }; } }