import { Body, Controller, Get, NotFoundException, Param, Post, Request, Sse, UseGuards, UnauthorizedException, type MessageEvent, } from "@nestjs/common"; import { RateLimitGuard, RateLimit } from "@bff/core/rate-limiting/index.js"; import { OrderOrchestrator } from "./services/order-orchestrator.service.js"; import type { RequestWithUser } from "@bff/modules/auth/auth.types.js"; import { Logger } from "nestjs-pino"; import { createZodDto, ZodResponse } from "nestjs-zod"; import { checkoutSessionCreateOrderRequestSchema, createOrderRequestSchema, orderCreateResponseSchema, sfOrderIdParamSchema, orderDetailsSchema, orderListResponseSchema, } from "@customer-portal/domain/orders"; import { Observable } from "rxjs"; import { OrderEventsService } from "./services/order-events.service.js"; import { SalesforceReadThrottleGuard } from "@bff/integrations/salesforce/guards/salesforce-read-throttle.guard.js"; import { SalesforceWriteThrottleGuard } from "@bff/integrations/salesforce/guards/salesforce-write-throttle.guard.js"; import { CheckoutSessionService } from "./services/checkout-session.service.js"; class CreateOrderRequestDto extends createZodDto(createOrderRequestSchema) {} class CheckoutSessionCreateOrderDto extends createZodDto(checkoutSessionCreateOrderRequestSchema) {} class SfOrderIdParamDto extends createZodDto(sfOrderIdParamSchema) {} class CreateOrderResponseDto extends createZodDto(orderCreateResponseSchema) {} class OrderDetailsDto extends createZodDto(orderDetailsSchema) {} class OrderListResponseDto extends createZodDto(orderListResponseSchema) {} @Controller("orders") @UseGuards(RateLimitGuard) export class OrdersController { constructor( private orderOrchestrator: OrderOrchestrator, private readonly checkoutSessions: CheckoutSessionService, private readonly orderEvents: OrderEventsService, private readonly logger: Logger ) {} @Post() @UseGuards(SalesforceWriteThrottleGuard) @RateLimit({ limit: 5, ttl: 60 }) // 5 order creations per minute @ZodResponse({ status: 201, description: "Create order", type: CreateOrderResponseDto }) async create(@Request() req: RequestWithUser, @Body() body: CreateOrderRequestDto) { return this.orderOrchestrator.createOrder(req.user.id, body); } @Post("from-checkout-session") @UseGuards(SalesforceWriteThrottleGuard) @RateLimit({ limit: 5, ttl: 60 }) // 5 order creations per minute @ZodResponse({ status: 201, description: "Create order from checkout session", type: CreateOrderResponseDto, }) async createFromCheckoutSession( @Request() req: RequestWithUser, @Body() body: CheckoutSessionCreateOrderDto ) { this.logger.log( { userId: req.user?.id, checkoutSessionId: body.checkoutSessionId, }, "Order creation from checkout session request received" ); return this.checkoutSessions.createOrderFromSession(req.user.id, body.checkoutSessionId); } @Get("user") @UseGuards(SalesforceReadThrottleGuard) @ZodResponse({ description: "Get user orders", type: OrderListResponseDto }) async getUserOrders(@Request() req: RequestWithUser) { return this.orderOrchestrator.getOrdersForUser(req.user.id); } @Get(":sfOrderId") @UseGuards(SalesforceReadThrottleGuard) @ZodResponse({ description: "Get order details", type: OrderDetailsDto }) async get(@Request() req: RequestWithUser, @Param() params: SfOrderIdParamDto) { if (!req.user?.id) { throw new UnauthorizedException("Authentication required"); } return this.orderOrchestrator.getOrderForUser(params.sfOrderId, req.user.id); } @Sse(":sfOrderId/events") @UseGuards(SalesforceReadThrottleGuard) async streamOrderUpdates( @Request() req: RequestWithUser, @Param() params: SfOrderIdParamDto ): Promise> { // Ensure caller is allowed to access this order stream (avoid leaking existence) try { await this.orderOrchestrator.getOrderForUser(params.sfOrderId, req.user.id); } catch { throw new NotFoundException("Order not found"); } return this.orderEvents.subscribe(params.sfOrderId); } // Note: Order provisioning has been moved to SalesforceProvisioningController // This controller now focuses only on customer-facing order operations }