2025-10-29 13:29:28 +09:00
|
|
|
import { Controller, Get, Request, UseGuards, Header } 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 type { RequestWithUser } from "@bff/modules/auth/auth.types.js";
|
2025-12-17 14:07:22 +09:00
|
|
|
import { Public } from "@bff/modules/auth/decorators/public.decorator.js";
|
2025-10-20 13:53:35 +09:00
|
|
|
import {
|
|
|
|
|
parseInternetCatalog,
|
2025-10-21 18:24:47 +09:00
|
|
|
parseSimCatalog,
|
2025-10-20 13:53:35 +09:00
|
|
|
type InternetAddonCatalogItem,
|
|
|
|
|
type InternetInstallationCatalogItem,
|
|
|
|
|
type InternetPlanCatalogItem,
|
|
|
|
|
type SimActivationFeeCatalogItem,
|
2025-10-21 18:24:47 +09:00
|
|
|
type SimCatalogCollection,
|
2025-10-20 13:53:35 +09:00
|
|
|
type SimCatalogProduct,
|
|
|
|
|
type VpnCatalogProduct,
|
2025-12-25 13:20:45 +09:00
|
|
|
} from "@customer-portal/domain/services";
|
2025-12-10 16:08:34 +09:00
|
|
|
import { InternetCatalogService } from "./services/internet-catalog.service.js";
|
|
|
|
|
import { SimCatalogService } from "./services/sim-catalog.service.js";
|
|
|
|
|
import { VpnCatalogService } from "./services/vpn-catalog.service.js";
|
|
|
|
|
import { SalesforceReadThrottleGuard } from "@bff/integrations/salesforce/guards/salesforce-read-throttle.guard.js";
|
2025-08-20 18:02:50 +09:00
|
|
|
|
2025-12-25 13:20:45 +09:00
|
|
|
@Controller("services")
|
|
|
|
|
@Public() // Allow public access - services can be browsed without authentication
|
2025-12-11 11:25:23 +09:00
|
|
|
@UseGuards(SalesforceReadThrottleGuard, RateLimitGuard)
|
2025-12-25 13:20:45 +09:00
|
|
|
export class ServicesController {
|
2025-08-27 20:01:46 +09:00
|
|
|
constructor(
|
|
|
|
|
private internetCatalog: InternetCatalogService,
|
|
|
|
|
private simCatalog: SimCatalogService,
|
|
|
|
|
private vpnCatalog: VpnCatalogService
|
|
|
|
|
) {}
|
2025-08-20 18:02:50 +09:00
|
|
|
|
2025-08-28 16:57:57 +09:00
|
|
|
@Get("internet/plans")
|
2025-12-11 11:25:23 +09:00
|
|
|
@RateLimit({ limit: 20, ttl: 60 }) // 20 requests per minute
|
2025-12-15 11:55:55 +09:00
|
|
|
@Header("Cache-Control", "private, no-store") // Personalised responses: avoid browser caching (realtime invalidation relies on refetch)
|
2025-10-07 17:38:39 +09:00
|
|
|
async getInternetPlans(@Request() req: RequestWithUser): Promise<{
|
2025-09-29 11:00:56 +09:00
|
|
|
plans: InternetPlanCatalogItem[];
|
|
|
|
|
installations: InternetInstallationCatalogItem[];
|
|
|
|
|
addons: InternetAddonCatalogItem[];
|
|
|
|
|
}> {
|
2025-08-27 20:01:46 +09:00
|
|
|
const userId = req.user?.id;
|
|
|
|
|
if (!userId) {
|
2025-10-20 13:53:35 +09:00
|
|
|
const catalog = await this.internetCatalog.getCatalogData();
|
|
|
|
|
return parseInternetCatalog(catalog);
|
2025-08-27 20:01:46 +09:00
|
|
|
}
|
2025-10-02 17:19:39 +09:00
|
|
|
|
2025-09-29 11:00:56 +09:00
|
|
|
const [plans, installations, addons] = await Promise.all([
|
|
|
|
|
this.internetCatalog.getPlansForUser(userId),
|
|
|
|
|
this.internetCatalog.getInstallations(),
|
|
|
|
|
this.internetCatalog.getAddons(),
|
|
|
|
|
]);
|
2025-10-02 17:19:39 +09:00
|
|
|
|
2025-10-20 13:53:35 +09:00
|
|
|
return parseInternetCatalog({ plans, installations, addons });
|
2025-08-27 10:54:05 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:57:57 +09:00
|
|
|
@Get("internet/addons")
|
2025-11-06 16:32:29 +09:00
|
|
|
@Header("Cache-Control", "public, max-age=300, s-maxage=300") // 5 minutes
|
2025-09-18 11:22:22 +09:00
|
|
|
async getInternetAddons(): Promise<InternetAddonCatalogItem[]> {
|
2025-08-27 20:01:46 +09:00
|
|
|
return this.internetCatalog.getAddons();
|
2025-08-27 10:54:05 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:57:57 +09:00
|
|
|
@Get("internet/installations")
|
2025-11-06 16:32:29 +09:00
|
|
|
@Header("Cache-Control", "public, max-age=300, s-maxage=300") // 5 minutes
|
2025-09-18 11:22:22 +09:00
|
|
|
async getInternetInstallations(): Promise<InternetInstallationCatalogItem[]> {
|
2025-08-27 20:01:46 +09:00
|
|
|
return this.internetCatalog.getInstallations();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:57:57 +09:00
|
|
|
@Get("sim/plans")
|
2025-12-11 11:25:23 +09:00
|
|
|
@RateLimit({ limit: 20, ttl: 60 }) // 20 requests per minute
|
2025-12-15 11:55:55 +09:00
|
|
|
@Header("Cache-Control", "private, no-store") // Personalised responses: avoid browser caching (realtime invalidation relies on refetch)
|
2025-10-21 18:24:47 +09:00
|
|
|
async getSimCatalogData(@Request() req: RequestWithUser): Promise<SimCatalogCollection> {
|
2025-08-27 20:01:46 +09:00
|
|
|
const userId = req.user?.id;
|
|
|
|
|
if (!userId) {
|
2025-10-21 18:24:47 +09:00
|
|
|
const catalog = await this.simCatalog.getCatalogData();
|
|
|
|
|
return parseSimCatalog({
|
|
|
|
|
...catalog,
|
|
|
|
|
plans: catalog.plans.filter(plan => !plan.simHasFamilyDiscount),
|
|
|
|
|
});
|
2025-08-27 20:01:46 +09:00
|
|
|
}
|
2025-10-21 18:24:47 +09:00
|
|
|
|
|
|
|
|
const [plans, activationFees, addons] = await Promise.all([
|
|
|
|
|
this.simCatalog.getPlansForUser(userId),
|
|
|
|
|
this.simCatalog.getActivationFees(),
|
|
|
|
|
this.simCatalog.getAddons(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return parseSimCatalog({ plans, activationFees, addons });
|
2025-08-27 20:01:46 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:57:57 +09:00
|
|
|
@Get("sim/activation-fees")
|
2025-11-06 16:32:29 +09:00
|
|
|
@Header("Cache-Control", "public, max-age=300, s-maxage=300") // 5 minutes
|
2025-09-18 11:22:22 +09:00
|
|
|
async getSimActivationFees(): Promise<SimActivationFeeCatalogItem[]> {
|
2025-08-27 20:01:46 +09:00
|
|
|
return this.simCatalog.getActivationFees();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:57:57 +09:00
|
|
|
@Get("sim/addons")
|
2025-11-06 16:32:29 +09:00
|
|
|
@Header("Cache-Control", "public, max-age=300, s-maxage=300") // 5 minutes
|
2025-09-24 18:00:49 +09:00
|
|
|
async getSimAddons(): Promise<SimCatalogProduct[]> {
|
2025-08-27 20:01:46 +09:00
|
|
|
return this.simCatalog.getAddons();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:57:57 +09:00
|
|
|
@Get("vpn/plans")
|
2025-12-11 11:25:23 +09:00
|
|
|
@RateLimit({ limit: 20, ttl: 60 }) // 20 requests per minute
|
2025-11-06 16:32:29 +09:00
|
|
|
@Header("Cache-Control", "public, max-age=300, s-maxage=300") // 5 minutes
|
2025-09-24 18:00:49 +09:00
|
|
|
async getVpnPlans(): Promise<VpnCatalogProduct[]> {
|
2025-08-27 20:01:46 +09:00
|
|
|
return this.vpnCatalog.getPlans();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 16:57:57 +09:00
|
|
|
@Get("vpn/activation-fees")
|
2025-11-06 16:32:29 +09:00
|
|
|
@Header("Cache-Control", "public, max-age=300, s-maxage=300") // 5 minutes
|
2025-09-24 18:00:49 +09:00
|
|
|
async getVpnActivationFees(): Promise<VpnCatalogProduct[]> {
|
2025-08-27 20:01:46 +09:00
|
|
|
return this.vpnCatalog.getActivationFees();
|
2025-08-27 10:54:05 +09:00
|
|
|
}
|
2025-08-28 16:57:57 +09:00
|
|
|
}
|