Update package configurations and enhance BFF module structure
- Modified build command in package.json for improved reporting. - Updated pnpm-lock.yaml to remove obsolete dependencies and add new ones. - Enhanced TypeScript configuration in BFF for better compatibility with ES2024. - Refactored app.module.ts for clearer module organization and added comments for better understanding. - Adjusted next.config.mjs to conditionally enable standalone output based on environment. - Improved InternetPlansPage to fetch and display installation options alongside internet plans. - Cleaned up unnecessary comments and code in VPN plans page for better readability. - Updated manage.sh script to build shared packages and BFF before starting development applications.
This commit is contained in:
parent
9a7c1a06bb
commit
cde7cb3e3c
@ -1,85 +1,76 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { RouterModule } from "@nestjs/core";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { ThrottlerModule } from "@nestjs/throttler";
|
||||
import { AuthModule } from "./auth/auth.module";
|
||||
import { UsersModule } from "./users/users.module";
|
||||
import { MappingsModule } from "./mappings/mappings.module";
|
||||
import { CatalogModule } from "./catalog/catalog.module";
|
||||
import { OrdersModule } from "./orders/orders.module";
|
||||
import { InvoicesModule } from "./invoices/invoices.module";
|
||||
import { SubscriptionsModule } from "./subscriptions/subscriptions.module";
|
||||
import { CasesModule } from "./cases/cases.module";
|
||||
import { WebhooksModule } from "./webhooks/webhooks.module";
|
||||
import { VendorsModule } from "./vendors/vendors.module";
|
||||
import { JobsModule } from "./jobs/jobs.module";
|
||||
import { HealthModule } from "./health/health.module";
|
||||
import { EmailModule } from "./common/email/email.module";
|
||||
import { PrismaModule } from "./common/prisma/prisma.module";
|
||||
import { AuditModule } from "./common/audit/audit.module";
|
||||
import { RedisModule } from "./common/redis/redis.module";
|
||||
import { LoggingModule } from "./common/logging/logging.module";
|
||||
import { CacheModule } from "./common/cache/cache.module";
|
||||
import * as path from "path";
|
||||
import { validateEnv } from "./common/config/env.validation";
|
||||
import { Module } from '@nestjs/common';
|
||||
import { RouterModule } from '@nestjs/core';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { ThrottlerModule } from '@nestjs/throttler';
|
||||
|
||||
// Support multiple .env files across environments and run contexts
|
||||
const repoRoot = path.resolve(__dirname, "../../../..");
|
||||
const nodeEnv = process.env.NODE_ENV || "development";
|
||||
const envFilePath = [
|
||||
// Prefer repo root env files
|
||||
path.resolve(repoRoot, `.env.${nodeEnv}.local`),
|
||||
path.resolve(repoRoot, `.env.${nodeEnv}`),
|
||||
path.resolve(repoRoot, ".env.local"),
|
||||
path.resolve(repoRoot, ".env"),
|
||||
// Fallback to local working directory
|
||||
`.env.${nodeEnv}.local`,
|
||||
`.env.${nodeEnv}`,
|
||||
".env.local",
|
||||
".env",
|
||||
];
|
||||
// Configuration
|
||||
import { appConfig } from './common/config/app.config';
|
||||
import { createThrottlerConfig } from './common/config/throttler.config';
|
||||
import { apiRoutes } from './common/config/router.config';
|
||||
|
||||
// Core Infrastructure Modules
|
||||
import { LoggingModule } from './common/logging/logging.module';
|
||||
import { PrismaModule } from './common/prisma/prisma.module';
|
||||
import { RedisModule } from './common/redis/redis.module';
|
||||
import { CacheModule } from './common/cache/cache.module';
|
||||
import { AuditModule } from './common/audit/audit.module';
|
||||
import { EmailModule } from './common/email/email.module';
|
||||
|
||||
// External Integration Modules
|
||||
import { VendorsModule } from './vendors/vendors.module';
|
||||
import { JobsModule } from './jobs/jobs.module';
|
||||
|
||||
// Feature Modules
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { MappingsModule } from './mappings/mappings.module';
|
||||
import { CatalogModule } from './catalog/catalog.module';
|
||||
import { OrdersModule } from './orders/orders.module';
|
||||
import { InvoicesModule } from './invoices/invoices.module';
|
||||
import { SubscriptionsModule } from './subscriptions/subscriptions.module';
|
||||
import { CasesModule } from './cases/cases.module';
|
||||
import { WebhooksModule } from './webhooks/webhooks.module';
|
||||
|
||||
// System Modules
|
||||
import { HealthModule } from './health/health.module';
|
||||
|
||||
/**
|
||||
* Main application module
|
||||
*
|
||||
* Architecture:
|
||||
* - Core infrastructure modules provide foundational services
|
||||
* - External integration modules handle third-party services
|
||||
* - Feature modules implement business logic
|
||||
* - System modules provide monitoring and health checks
|
||||
*
|
||||
* All feature modules are grouped under "/api" prefix via RouterModule
|
||||
* Health endpoints remain at root level for monitoring tools
|
||||
*/
|
||||
@Module({
|
||||
imports: [
|
||||
// Configuration
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath,
|
||||
validate: validateEnv,
|
||||
}),
|
||||
// === CONFIGURATION ===
|
||||
ConfigModule.forRoot(appConfig),
|
||||
|
||||
// Logging
|
||||
// === INFRASTRUCTURE ===
|
||||
LoggingModule,
|
||||
|
||||
// Rate limiting with environment-based configuration
|
||||
ThrottlerModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => [
|
||||
{
|
||||
ttl: configService.get("RATE_LIMIT_TTL", 60000),
|
||||
limit: configService.get("RATE_LIMIT_LIMIT", 100),
|
||||
},
|
||||
// Stricter rate limiting for auth endpoints
|
||||
{
|
||||
ttl: configService.get("AUTH_RATE_LIMIT_TTL", 900000),
|
||||
limit: configService.get("AUTH_RATE_LIMIT_LIMIT", 3),
|
||||
name: "auth",
|
||||
},
|
||||
],
|
||||
useFactory: createThrottlerConfig,
|
||||
}),
|
||||
|
||||
// Core modules
|
||||
// === CORE SERVICES ===
|
||||
PrismaModule,
|
||||
RedisModule,
|
||||
CacheModule,
|
||||
AuditModule,
|
||||
VendorsModule,
|
||||
JobsModule,
|
||||
HealthModule,
|
||||
EmailModule,
|
||||
|
||||
// Feature modules
|
||||
// === EXTERNAL INTEGRATIONS ===
|
||||
VendorsModule,
|
||||
JobsModule,
|
||||
|
||||
// === FEATURE MODULES ===
|
||||
AuthModule,
|
||||
UsersModule,
|
||||
MappingsModule,
|
||||
@ -89,18 +80,12 @@ const envFilePath = [
|
||||
SubscriptionsModule,
|
||||
CasesModule,
|
||||
WebhooksModule,
|
||||
// Route grouping: apply "/api" prefix to all feature modules except health
|
||||
RouterModule.register([
|
||||
{ path: "api", module: AuthModule },
|
||||
{ path: "api", module: UsersModule },
|
||||
{ path: "api", module: MappingsModule },
|
||||
{ path: "api", module: CatalogModule },
|
||||
{ path: "api", module: OrdersModule },
|
||||
{ path: "api", module: InvoicesModule },
|
||||
{ path: "api", module: SubscriptionsModule },
|
||||
{ path: "api", module: CasesModule },
|
||||
{ path: "api", module: WebhooksModule },
|
||||
]),
|
||||
|
||||
// === SYSTEM MODULES ===
|
||||
HealthModule,
|
||||
|
||||
// === ROUTING ===
|
||||
RouterModule.register(apiRoutes),
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule {}
|
||||
47
apps/bff/src/common/config/app.config.ts
Normal file
47
apps/bff/src/common/config/app.config.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { ConfigModuleOptions } from '@nestjs/config';
|
||||
import { validateEnv } from './env.validation';
|
||||
import * as path from 'path';
|
||||
|
||||
/**
|
||||
* Resolves the project root directory
|
||||
* Works both in development (src/) and production (dist/) environments
|
||||
*/
|
||||
function getProjectRoot(): string {
|
||||
// In development: process.cwd() should be the project root
|
||||
// In production: we need to navigate up from the compiled location
|
||||
const cwd = process.cwd();
|
||||
|
||||
// If we're running from apps/bff directory, go up to project root
|
||||
if (cwd.endsWith('/apps/bff')) {
|
||||
return path.resolve(cwd, '../..');
|
||||
}
|
||||
|
||||
// If we're running from project root, use it directly
|
||||
return cwd;
|
||||
}
|
||||
|
||||
const projectRoot = getProjectRoot();
|
||||
const nodeEnv = process.env.NODE_ENV || 'development';
|
||||
|
||||
/**
|
||||
* Application configuration for NestJS ConfigModule
|
||||
* Handles environment file loading with proper fallbacks
|
||||
*/
|
||||
export const appConfig: ConfigModuleOptions = {
|
||||
isGlobal: true,
|
||||
envFilePath: [
|
||||
// Project root environment files (highest priority)
|
||||
path.resolve(projectRoot, `.env.${nodeEnv}.local`),
|
||||
path.resolve(projectRoot, `.env.${nodeEnv}`),
|
||||
path.resolve(projectRoot, '.env.local'),
|
||||
path.resolve(projectRoot, '.env'),
|
||||
// Fallback to relative paths
|
||||
`.env.${nodeEnv}.local`,
|
||||
`.env.${nodeEnv}`,
|
||||
'.env.local',
|
||||
'.env',
|
||||
],
|
||||
validate: validateEnv,
|
||||
// Enable expansion of variables (e.g., ${VAR_NAME})
|
||||
expandVariables: true,
|
||||
};
|
||||
32
apps/bff/src/common/config/router.config.ts
Normal file
32
apps/bff/src/common/config/router.config.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Routes } from '@nestjs/core';
|
||||
import { AuthModule } from '../../auth/auth.module';
|
||||
import { UsersModule } from '../../users/users.module';
|
||||
import { MappingsModule } from '../../mappings/mappings.module';
|
||||
import { CatalogModule } from '../../catalog/catalog.module';
|
||||
import { OrdersModule } from '../../orders/orders.module';
|
||||
import { InvoicesModule } from '../../invoices/invoices.module';
|
||||
import { SubscriptionsModule } from '../../subscriptions/subscriptions.module';
|
||||
import { CasesModule } from '../../cases/cases.module';
|
||||
import { WebhooksModule } from '../../webhooks/webhooks.module';
|
||||
|
||||
/**
|
||||
* API routing configuration
|
||||
* Groups feature modules under the "/api" prefix
|
||||
* Health endpoints remain at root level for monitoring tools
|
||||
*/
|
||||
export const apiRoutes: Routes = [
|
||||
{
|
||||
path: 'api',
|
||||
children: [
|
||||
{ path: '', module: AuthModule },
|
||||
{ path: '', module: UsersModule },
|
||||
{ path: '', module: MappingsModule },
|
||||
{ path: '', module: CatalogModule },
|
||||
{ path: '', module: OrdersModule },
|
||||
{ path: '', module: InvoicesModule },
|
||||
{ path: '', module: SubscriptionsModule },
|
||||
{ path: '', module: CasesModule },
|
||||
{ path: '', module: WebhooksModule },
|
||||
],
|
||||
},
|
||||
];
|
||||
22
apps/bff/src/common/config/throttler.config.ts
Normal file
22
apps/bff/src/common/config/throttler.config.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ThrottlerModuleOptions } from '@nestjs/throttler';
|
||||
|
||||
/**
|
||||
* Throttler configuration factory
|
||||
* Provides rate limiting configuration based on environment variables
|
||||
*/
|
||||
export const createThrottlerConfig = (
|
||||
configService: ConfigService,
|
||||
): ThrottlerModuleOptions => [
|
||||
// General rate limiting
|
||||
{
|
||||
ttl: configService.get<number>('RATE_LIMIT_TTL', 60000), // 1 minute
|
||||
limit: configService.get<number>('RATE_LIMIT_LIMIT', 100), // 100 requests
|
||||
},
|
||||
// Stricter rate limiting for authentication endpoints
|
||||
{
|
||||
name: 'auth',
|
||||
ttl: configService.get<number>('AUTH_RATE_LIMIT_TTL', 900000), // 15 minutes
|
||||
limit: configService.get<number>('AUTH_RATE_LIMIT_LIMIT', 3), // 3 attempts
|
||||
},
|
||||
];
|
||||
@ -2,11 +2,15 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "./tsconfig.build.tsbuildinfo",
|
||||
"outDir": "./dist",
|
||||
"sourceMap": true,
|
||||
"declaration": false
|
||||
"declaration": false,
|
||||
"module": "CommonJS",
|
||||
"target": "ES2024"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "test", "**/*.spec.ts"]
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"removeComments": true,
|
||||
"target": "ES2024",
|
||||
|
||||
// Path mappings
|
||||
"paths": {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/* eslint-env node */
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// Enable standalone output for production deployment
|
||||
output: "standalone",
|
||||
// Enable standalone output only for production deployment
|
||||
output: process.env.NODE_ENV === "production" ? "standalone" : undefined,
|
||||
|
||||
// Turbopack configuration (Next.js 15.5+)
|
||||
turbopack: {
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
"react-dom": "19.1.1",
|
||||
"react-hook-form": "^7.62.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tw-animate-css": "^1.3.7",
|
||||
"world-countries": "^5.1.0",
|
||||
"zod": "^4.0.17",
|
||||
"zustand": "^5.0.8"
|
||||
|
||||
@ -13,13 +13,14 @@ import {
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { authenticatedApi } from "@/lib/api";
|
||||
|
||||
import { InternetPlan } from "@/shared/types/catalog.types";
|
||||
import { InternetPlan, InternetInstallation } from "@/shared/types/catalog.types";
|
||||
import { LoadingSpinner } from "@/components/catalog/loading-spinner";
|
||||
import { AnimatedCard } from "@/components/catalog/animated-card";
|
||||
import { AnimatedButton } from "@/components/catalog/animated-button";
|
||||
|
||||
export default function InternetPlansPage() {
|
||||
const [plans, setPlans] = useState<InternetPlan[]>([]);
|
||||
const [installations, setInstallations] = useState<InternetInstallation[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [eligibility, setEligibility] = useState<string>("");
|
||||
@ -31,11 +32,15 @@ export default function InternetPlansPage() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const plans = await authenticatedApi.get<InternetPlan[]>("/catalog/internet/plans");
|
||||
const [plans, installations] = await Promise.all([
|
||||
authenticatedApi.get<InternetPlan[]>("/catalog/internet/plans"),
|
||||
authenticatedApi.get<InternetInstallation[]>("/catalog/internet/installations")
|
||||
]);
|
||||
|
||||
if (mounted) {
|
||||
// Plans are now ordered by Salesforce Display_Order__c field
|
||||
setPlans(plans);
|
||||
setInstallations(installations);
|
||||
if (plans.length > 0) {
|
||||
setEligibility(plans[0].offeringType || "Home 1G");
|
||||
}
|
||||
@ -144,7 +149,7 @@ export default function InternetPlansPage() {
|
||||
<>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{plans.map(plan => (
|
||||
<InternetPlanCard key={plan.id} plan={plan} />
|
||||
<InternetPlanCard key={plan.id} plan={plan} installations={installations} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -189,7 +194,7 @@ export default function InternetPlansPage() {
|
||||
);
|
||||
}
|
||||
|
||||
function InternetPlanCard({ plan }: { plan: InternetPlan }) {
|
||||
function InternetPlanCard({ plan, installations }: { plan: InternetPlan; installations: InternetInstallation[] }) {
|
||||
const isGold = plan.tier === "Gold";
|
||||
const isPlatinum = plan.tier === "Platinum";
|
||||
const isSilver = plan.tier === "Silver";
|
||||
@ -274,7 +279,12 @@ function InternetPlanCard({ plan }: { plan: InternetPlan }) {
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
Monthly: ¥{plan.monthlyPrice?.toLocaleString()} | One-time: ¥{plan.setupFee?.toLocaleString() || '22,800'}
|
||||
Monthly: ¥{plan.monthlyPrice?.toLocaleString()}
|
||||
{installations.length > 0 && (
|
||||
<span className="text-gray-600 text-sm ml-2">
|
||||
(+ installation from ¥{Math.min(...installations.map(i => i.price || 0)).toLocaleString()})
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -135,19 +135,7 @@ export default function VpnPlansPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{plan.features && plan.features.length > 0 && (
|
||||
<div className="mb-6">
|
||||
<h4 className="font-medium text-gray-900 mb-3">Features:</h4>
|
||||
<ul className="text-sm text-gray-700 space-y-1">
|
||||
{plan.features.map((feature, index) => (
|
||||
<li key={index} className="flex items-start">
|
||||
<CheckIcon className="h-4 w-4 text-green-500 mr-2 mt-0.5 flex-shrink-0" />
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{/* VPN plans don't have features defined in the type structure */}
|
||||
|
||||
<AnimatedButton
|
||||
href={`/catalog/vpn/configure?plan=${plan.sku}`}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"predev": "pnpm --filter @customer-portal/shared build",
|
||||
"dev": "./scripts/dev/manage.sh apps",
|
||||
"dev:all": "pnpm --parallel --filter @customer-portal/shared --filter @customer-portal/portal --filter @customer-portal/bff run dev",
|
||||
"build": "pnpm --recursive -w --if-present run build",
|
||||
"build": "pnpm --recursive --reporter=default run build",
|
||||
"start": "pnpm --parallel --filter @customer-portal/portal --filter @customer-portal/bff run start",
|
||||
"test": "pnpm --recursive run test",
|
||||
"lint": "pnpm --recursive run lint",
|
||||
@ -50,7 +50,6 @@
|
||||
"dev:watch": "pnpm --parallel --filter @customer-portal/shared --filter @customer-portal/portal --filter @customer-portal/bff run dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.13.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.34.0",
|
||||
"@types/node": "^24.3.0",
|
||||
|
||||
32
pnpm-lock.yaml
generated
32
pnpm-lock.yaml
generated
@ -205,9 +205,6 @@ importers:
|
||||
ts-jest:
|
||||
specifier: ^29.4.1
|
||||
version: 29.4.1(@babel/core@7.28.3)(@jest/transform@30.0.5)(@jest/types@30.0.5)(babel-jest@30.0.5(@babel/core@7.28.3))(jest-util@30.0.5)(jest@30.0.5(@types/node@24.3.0)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.9.2)))(typescript@5.9.2)
|
||||
ts-loader:
|
||||
specifier: ^9.5.2
|
||||
version: 9.5.2(typescript@5.9.2)(webpack@5.100.2)
|
||||
ts-node:
|
||||
specifier: ^10.9.2
|
||||
version: 10.9.2(@types/node@24.3.0)(typescript@5.9.2)
|
||||
@ -262,6 +259,9 @@ importers:
|
||||
tailwind-merge:
|
||||
specifier: ^3.3.1
|
||||
version: 3.3.1
|
||||
tw-animate-css:
|
||||
specifier: ^1.3.7
|
||||
version: 1.3.7
|
||||
world-countries:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
@ -287,9 +287,6 @@ importers:
|
||||
tailwindcss:
|
||||
specifier: ^4.1.12
|
||||
version: 4.1.12
|
||||
tw-animate-css:
|
||||
specifier: ^1.3.7
|
||||
version: 1.3.7
|
||||
typescript:
|
||||
specifier: ^5.9.2
|
||||
version: 5.9.2
|
||||
@ -4400,10 +4397,6 @@ packages:
|
||||
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
source-map@0.7.6:
|
||||
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
speakeasy@2.0.0:
|
||||
resolution: {integrity: sha512-lW2A2s5LKi8rwu77ewisuUOtlCydF/hmQSOJjpTqTj1gZLkNgTaYnyvfxy2WBr4T/h+9c4g8HIITfj83OkFQFw==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
@ -4673,13 +4666,6 @@ packages:
|
||||
jest-util:
|
||||
optional: true
|
||||
|
||||
ts-loader@9.5.2:
|
||||
resolution: {integrity: sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
webpack: ^5.0.0
|
||||
|
||||
ts-node@10.9.2:
|
||||
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
|
||||
hasBin: true
|
||||
@ -9690,8 +9676,6 @@ snapshots:
|
||||
|
||||
source-map@0.7.4: {}
|
||||
|
||||
source-map@0.7.6: {}
|
||||
|
||||
speakeasy@2.0.0:
|
||||
dependencies:
|
||||
base32.js: 0.0.1
|
||||
@ -9960,16 +9944,6 @@ snapshots:
|
||||
babel-jest: 30.0.5(@babel/core@7.28.3)
|
||||
jest-util: 30.0.5
|
||||
|
||||
ts-loader@9.5.2(typescript@5.9.2)(webpack@5.100.2):
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
enhanced-resolve: 5.18.3
|
||||
micromatch: 4.0.8
|
||||
semver: 7.7.2
|
||||
source-map: 0.7.6
|
||||
typescript: 5.9.2
|
||||
webpack: 5.100.2
|
||||
|
||||
ts-node@10.9.2(@types/node@24.3.0)(typescript@5.9.2):
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
|
||||
@ -102,6 +102,14 @@ start_apps() {
|
||||
source "$ENV_FILE" 2>/dev/null || true
|
||||
set +a
|
||||
|
||||
# Build shared package first (required by both apps)
|
||||
log "🔨 Building shared package..."
|
||||
pnpm --filter @customer-portal/shared build
|
||||
|
||||
# Build BFF first to ensure dist directory exists for watch mode
|
||||
log "🔨 Building BFF for initial setup..."
|
||||
pnpm --filter @customer-portal/bff build
|
||||
|
||||
# Show startup information
|
||||
log "🎯 Starting development applications..."
|
||||
log "🔗 BFF API: http://localhost:${BFF_PORT:-4000}/api"
|
||||
@ -111,8 +119,7 @@ start_apps() {
|
||||
log "📚 API Docs: http://localhost:${BFF_PORT:-4000}/api/docs"
|
||||
log "Starting apps with hot-reload..."
|
||||
|
||||
# Start Prisma Studio (opens browser)
|
||||
(cd "$PROJECT_ROOT/apps/bff" && pnpm db:studio &)
|
||||
# Prisma Studio can be started manually with: pnpm db:studio
|
||||
|
||||
# Start apps (portal + bff) with hot reload in parallel
|
||||
pnpm --parallel --filter @customer-portal/portal --filter @customer-portal/bff run dev
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user