2025-11-04 17:24:26 +09:00
|
|
|
import {
|
|
|
|
|
Body,
|
|
|
|
|
Controller,
|
|
|
|
|
Get,
|
2025-12-15 11:10:50 +09:00
|
|
|
NotFoundException,
|
2025-11-04 17:24:26 +09:00
|
|
|
Param,
|
|
|
|
|
Post,
|
|
|
|
|
Request,
|
|
|
|
|
Sse,
|
|
|
|
|
UseGuards,
|
2025-11-17 10:31:33 +09:00
|
|
|
UnauthorizedException,
|
2025-11-04 17:24:26 +09:00
|
|
|
type MessageEvent,
|
|
|
|
|
} from "@nestjs/common";
|
2025-12-11 11:25:23 +09:00
|
|
|
import { RateLimitGuard, RateLimit } from "@bff/core/rate-limiting/index.js";
|
2025-12-10 16:08:34 +09:00
|
|
|
import { OrderOrchestrator } from "./services/order-orchestrator.service.js";
|
|
|
|
|
import type { RequestWithUser } from "@bff/modules/auth/auth.types.js";
|
2025-08-27 10:54:05 +09:00
|
|
|
import { Logger } from "nestjs-pino";
|
2025-12-26 13:04:15 +09:00
|
|
|
import { createZodDto, ZodResponse } from "nestjs-zod";
|
2025-10-02 17:19:39 +09:00
|
|
|
import {
|
2025-12-26 13:04:15 +09:00
|
|
|
checkoutSessionCreateOrderRequestSchema,
|
2025-10-02 17:19:39 +09:00
|
|
|
createOrderRequestSchema,
|
2025-10-22 11:33:23 +09:00
|
|
|
orderCreateResponseSchema,
|
2025-10-02 17:19:39 +09:00
|
|
|
sfOrderIdParamSchema,
|
2025-12-26 13:04:15 +09:00
|
|
|
orderDetailsSchema,
|
|
|
|
|
orderListResponseSchema,
|
2025-10-03 15:09:19 +09:00
|
|
|
} from "@customer-portal/domain/orders";
|
2025-11-04 17:24:26 +09:00
|
|
|
import { Observable } from "rxjs";
|
2025-12-10 16:08:34 +09:00
|
|
|
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";
|
2025-12-17 15:44:46 +09:00
|
|
|
import { CheckoutSessionService } from "./services/checkout-session.service.js";
|
|
|
|
|
|
2025-12-26 13:04:15 +09:00
|
|
|
class CreateOrderRequestDto extends createZodDto(createOrderRequestSchema) {}
|
|
|
|
|
class CheckoutSessionCreateOrderDto extends createZodDto(checkoutSessionCreateOrderRequestSchema) {}
|
|
|
|
|
class SfOrderIdParamDto extends createZodDto(sfOrderIdParamSchema) {}
|
2025-12-26 13:40:10 +09:00
|
|
|
class CreateOrderResponseDto extends createZodDto(orderCreateResponseSchema) {}
|
2025-12-26 13:04:15 +09:00
|
|
|
class OrderDetailsDto extends createZodDto(orderDetailsSchema) {}
|
|
|
|
|
class OrderListResponseDto extends createZodDto(orderListResponseSchema) {}
|
2025-08-20 18:02:50 +09:00
|
|
|
|
2025-08-21 15:24:40 +09:00
|
|
|
@Controller("orders")
|
2025-12-11 11:25:23 +09:00
|
|
|
@UseGuards(RateLimitGuard)
|
2025-08-20 18:02:50 +09:00
|
|
|
export class OrdersController {
|
2025-08-27 10:54:05 +09:00
|
|
|
constructor(
|
2025-08-27 20:01:46 +09:00
|
|
|
private orderOrchestrator: OrderOrchestrator,
|
2025-12-17 15:44:46 +09:00
|
|
|
private readonly checkoutSessions: CheckoutSessionService,
|
2025-11-04 17:24:26 +09:00
|
|
|
private readonly orderEvents: OrderEventsService,
|
2025-08-27 10:54:05 +09:00
|
|
|
private readonly logger: Logger
|
|
|
|
|
) {}
|
2025-08-20 18:02:50 +09:00
|
|
|
|
2025-08-23 17:24:37 +09:00
|
|
|
@Post()
|
2025-11-06 16:32:29 +09:00
|
|
|
@UseGuards(SalesforceWriteThrottleGuard)
|
2025-12-11 11:25:23 +09:00
|
|
|
@RateLimit({ limit: 5, ttl: 60 }) // 5 order creations per minute
|
2025-12-26 13:04:15 +09:00
|
|
|
@ZodResponse({ status: 201, description: "Create order", type: CreateOrderResponseDto })
|
|
|
|
|
async create(@Request() req: RequestWithUser, @Body() body: CreateOrderRequestDto) {
|
2026-01-19 11:25:30 +09:00
|
|
|
return this.orderOrchestrator.createOrder(req.user.id, body);
|
2025-08-27 20:01:46 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-17 15:44:46 +09:00
|
|
|
@Post("from-checkout-session")
|
|
|
|
|
@UseGuards(SalesforceWriteThrottleGuard)
|
|
|
|
|
@RateLimit({ limit: 5, ttl: 60 }) // 5 order creations per minute
|
2025-12-26 13:04:15 +09:00
|
|
|
@ZodResponse({
|
|
|
|
|
status: 201,
|
|
|
|
|
description: "Create order from checkout session",
|
|
|
|
|
type: CreateOrderResponseDto,
|
|
|
|
|
})
|
2025-12-17 15:44:46 +09:00
|
|
|
async createFromCheckoutSession(
|
|
|
|
|
@Request() req: RequestWithUser,
|
2025-12-26 13:04:15 +09:00
|
|
|
@Body() body: CheckoutSessionCreateOrderDto
|
2025-12-17 15:44:46 +09:00
|
|
|
) {
|
|
|
|
|
this.logger.log(
|
|
|
|
|
{
|
|
|
|
|
userId: req.user?.id,
|
|
|
|
|
checkoutSessionId: body.checkoutSessionId,
|
|
|
|
|
},
|
|
|
|
|
"Order creation from checkout session request received"
|
|
|
|
|
);
|
|
|
|
|
|
2026-02-24 11:58:00 +09:00
|
|
|
return this.checkoutSessions.createOrderFromSession(req.user.id, body.checkoutSessionId);
|
2025-12-17 15:44:46 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-27 20:01:46 +09:00
|
|
|
@Get("user")
|
2025-11-06 13:26:30 +09:00
|
|
|
@UseGuards(SalesforceReadThrottleGuard)
|
2025-12-26 13:04:15 +09:00
|
|
|
@ZodResponse({ description: "Get user orders", type: OrderListResponseDto })
|
2025-08-27 20:01:46 +09:00
|
|
|
async getUserOrders(@Request() req: RequestWithUser) {
|
|
|
|
|
return this.orderOrchestrator.getOrdersForUser(req.user.id);
|
2025-08-23 17:24:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Get(":sfOrderId")
|
2025-11-06 13:26:30 +09:00
|
|
|
@UseGuards(SalesforceReadThrottleGuard)
|
2025-12-26 13:04:15 +09:00
|
|
|
@ZodResponse({ description: "Get order details", type: OrderDetailsDto })
|
|
|
|
|
async get(@Request() req: RequestWithUser, @Param() params: SfOrderIdParamDto) {
|
2025-11-17 10:31:33 +09:00
|
|
|
if (!req.user?.id) {
|
|
|
|
|
throw new UnauthorizedException("Authentication required");
|
|
|
|
|
}
|
|
|
|
|
return this.orderOrchestrator.getOrderForUser(params.sfOrderId, req.user.id);
|
2025-08-23 17:24:37 +09:00
|
|
|
}
|
|
|
|
|
|
2025-11-04 17:24:26 +09:00
|
|
|
@Sse(":sfOrderId/events")
|
2025-12-15 11:10:50 +09:00
|
|
|
@UseGuards(SalesforceReadThrottleGuard)
|
|
|
|
|
async streamOrderUpdates(
|
|
|
|
|
@Request() req: RequestWithUser,
|
2025-12-26 13:04:15 +09:00
|
|
|
@Param() params: SfOrderIdParamDto
|
2025-12-15 11:10:50 +09:00
|
|
|
): Promise<Observable<MessageEvent>> {
|
|
|
|
|
// 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");
|
|
|
|
|
}
|
2025-11-04 17:24:26 +09:00
|
|
|
return this.orderEvents.subscribe(params.sfOrderId);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 16:09:17 +09:00
|
|
|
// Note: Order provisioning has been moved to SalesforceProvisioningController
|
|
|
|
|
// This controller now focuses only on customer-facing order operations
|
2025-08-20 18:02:50 +09:00
|
|
|
}
|