# 📦 Customer Portal - Package Structure & Architecture This document provides comprehensive information about the Customer Portal codebase structure, package configurations, dependencies, and build architecture. ## 🏗️ Monorepo Architecture ### Complete Project Structure ``` customer-portal/ ├── apps/ │ ├── portal/ # Next.js 15.5 Frontend Application │ │ ├── src/ │ │ │ ├── app/ # Next.js App Router │ │ │ ├── components/ # React components │ │ │ ├── lib/ # Utility functions │ │ │ └── styles/ # Tailwind CSS styles │ │ ├── public/ # Static assets │ │ ├── package.json # Frontend dependencies │ │ ├── next.config.mjs # Next.js configuration │ │ ├── tailwind.config.js # Tailwind CSS config │ │ ├── tsconfig.json # TypeScript config │ │ └── Dockerfile # Container build file │ │ │ └── bff/ # NestJS Backend for Frontend │ ├── src/ │ │ ├── modules/ # Feature modules │ │ ├── common/ # Shared backend utilities │ │ ├── config/ # Configuration files │ │ └── main.ts # Application entry point │ ├── prisma/ │ │ ├── schema.prisma # Database schema │ │ ├── migrations/ # Database migrations │ │ └── seed.ts # Database seeding │ ├── test/ # Test files │ ├── package.json # Backend dependencies │ ├── tsconfig.json # TypeScript config │ ├── tsconfig.build.json # Build-specific TS config │ └── Dockerfile # Container build file │ ├── packages/ │ └── shared/ # Shared Package │ ├── src/ │ │ ├── types/ # TypeScript type definitions │ │ ├── utils/ # Common utility functions │ │ ├── constants/ # Application constants │ │ ├── schemas/ # Validation schemas (Zod) │ │ └── index.ts # Package exports │ ├── dist/ # Compiled output (generated) │ ├── package.json # Shared package config │ └── tsconfig.json # TypeScript config │ ├── scripts/ # Build and deployment scripts ├── docs/ # Documentation ├── secrets/ # Sensitive configuration files ├── compose-plesk.yaml # Plesk Docker stack │ ├── package.json # Root workspace configuration ├── pnpm-workspace.yaml # pnpm workspace definition ├── pnpm-lock.yaml # Dependency lock file ├── tsconfig.json # Root TypeScript config ├── eslint.config.mjs # ESLint configuration ├── compose-plesk.yaml # Plesk deployment config ├── .env # Environment variables └── README.md # Project documentation ``` ## 🛠️ Plesk Deployment Notes (Recommended) - Proxy rules: - `/` → container `portal-frontend` port `3000` - `/api` → container `portal-backend` port `4000` - Frontend → Backend base URL: set `NEXT_PUBLIC_API_BASE=/api` (same-origin; no CORS needed). - Env split (do not keep secrets under `httpdocs`): - Frontend env (client-safe): `/var/www/vhosts/asolutions.jp/private/env/portal-frontend.env` - Backend env (server-only): `/var/www/vhosts/asolutions.jp/private/env/portal-backend.env` - Compose (`compose-plesk.yaml`) highlights: - Binds app ports to `127.0.0.1` and keeps DB/Redis internal. - Uses `env_file` per service pointing to the private env files above. - Backend waits for Postgres then runs migrations. - Alpine images: backend installs toolchain for bcrypt/Prisma; frontend includes `libc6-compat`. - Health endpoints: Next `/api/health`, Nest `/health`. ### Environment Files (Plesk) - Create two env files on the server (under the domain's private dir): - Frontend: `/var/www/vhosts/asolutions.jp/private/env/portal-frontend.env` — based on `env/portal-frontend.env.sample` - Must include: `NEXT_PUBLIC_API_BASE=/api` - Backend: `/var/www/vhosts/asolutions.jp/private/env/portal-backend.env` — based on `env/portal-backend.env.sample` - Must include: `TRUST_PROXY=true` - `DATABASE_URL` should use `database:5432` - `REDIS_URL` should use `cache:6379` - Set `JWT_SECRET` to a strong value - Salesforce credentials: `SF_LOGIN_URL`, `SF_CLIENT_ID`, `SF_USERNAME` - Salesforce private key: set `SF_PRIVATE_KEY_PATH=/app/secrets/sf-private.key` and mount `/app/secrets` - Webhook secrets: `SF_WEBHOOK_SECRET` (Salesforce), `WHMCS_WEBHOOK_SECRET` (if using WHMCS webhooks) - Webhook tolerances: `WEBHOOK_TIMESTAMP_TOLERANCE=300000` (ms; optional) - Optional IP allowlists: `SF_WEBHOOK_IP_ALLOWLIST`, `WHMCS_WEBHOOK_IP_ALLOWLIST` (CSV of IP/CIDR) - Pricebook: `PORTAL_PRICEBOOK_ID` ### Image Build and Upload Option A — Use the helper script (recommended): ```bash # Build both images, tag :latest and a date+sha tag, and write tarballs scripts/plesk/build-images.sh # Custom tag and output directory scripts/plesk/build-images.sh --tag v1.0.0 --output ./dist # Also push to a registry (e.g., GHCR) scripts/plesk/build-images.sh --tag v1.0.0 --push ghcr.io/ ``` Option B — Manual build commands: ```bash # Frontend docker build -t portal-frontend:latest -f apps/portal/Dockerfile . docker save -o portal-frontend.latest.tar portal-frontend:latest # Backend docker build -t portal-backend:latest -f apps/bff/Dockerfile . docker save -o portal-backend.latest.tar portal-backend:latest ``` In Plesk → Docker → Images, upload both tar files. Then use `compose-plesk.yaml` under Docker → Stacks → Add Stack to deploy the services. Configure Proxy Rules on the domain: - `/` → `portal-frontend` port `3000` - `/api` → `portal-backend` port `4000` ### Webhook Security (Plesk) - Endpoint for Salesforce Quick Action: - `POST /api/orders/{sfOrderId}/fulfill` - Required backend env (see above). Ensure the same HMAC secret is configured in Salesforce. - The backend guard enforces: - HMAC for all webhooks - Salesforce: timestamp + nonce with Redis-backed replay protection - WHMCS: timestamp/nonce optional (validated if present) - Health check `/health` includes `integrations.redis` to verify nonce storage. Alternatively, load via SSH on the Plesk host: ```bash scp portal-frontend.latest.tar portal-backend.latest.tar user@plesk-host:/tmp/ ssh user@plesk-host sudo docker load -i /tmp/portal-frontend.latest.tar sudo docker load -i /tmp/portal-backend.latest.tar ``` Or push to a registry (example: GHCR): ```bash docker tag portal-frontend:latest ghcr.io//portal-frontend:latest docker tag portal-backend:latest ghcr.io//portal-backend:latest docker push ghcr.io//portal-frontend:latest docker push ghcr.io//portal-backend:latest ``` Quick checklist: - Proxy rules added for `/` and `/api`. - `NEXT_PUBLIC_API_BASE=/api` available to the frontend. - DB URL uses `database:5432` (compose service name). - Secrets are under `/var/www/vhosts/asolutions.jp/private` (not `httpdocs`). ### Technology Stack & Versions - **Runtime**: Node.js 22+ (LTS) - **Package Manager**: pnpm 10.15.0 (workspace support) - **Language**: TypeScript 5.9.2 (strict mode) - **Frontend Framework**: Next.js 15.5.0 with React 19.1.1 - **Backend Framework**: NestJS 11.1.6 - **Database ORM**: Prisma 6.14.0 - **Styling**: Tailwind CSS 4.1.12 - **State Management**: Zustand 5.0.8 + TanStack Query 5.85.5 - **Validation**: Zod 4.0.17 - **Authentication**: JWT + bcrypt - **Database**: PostgreSQL 17 - **Cache**: Redis 7 with ioredis 5.7.0 ## 📦 Workspace Configuration ### Root Package.json (Workspace Root) ```json { "name": "customer-portal", "version": "1.0.0", "description": "Customer portal with BFF architecture", "private": true, "packageManager": "pnpm@10.15.0", "engines": { "node": ">=22.0.0", "pnpm": ">=10.0.0" }, "scripts": { "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 --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", "type-check": "pnpm --filter @customer-portal/shared build && pnpm --recursive run type-check", "clean": "pnpm --recursive run clean" }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.34.0", "@types/node": "^24.3.0", "eslint": "^9.33.0", "eslint-config-next": "15.5.0", "eslint-plugin-prettier": "^5.5.4", "globals": "^16.3.0", "husky": "^9.1.7", "prettier": "^3.6.2", "typescript": "^5.9.2", "typescript-eslint": "^8.40.0" }, "dependencies": { "@sendgrid/mail": "^8.1.5" } } ``` ### Workspace Definition (pnpm-workspace.yaml) ```yaml packages: - "apps/*" # Frontend and Backend applications - "packages/*" # Shared libraries and utilities ``` ### Package Dependency Graph ``` @customer-portal/shared (packages/shared) ├── Built first (no dependencies) └── Exports: types, utils, constants, schemas @customer-portal/portal (apps/portal) ├── Depends on: @customer-portal/shared └── Builds: Next.js standalone application @customer-portal/bff (apps/bff) ├── Depends on: @customer-portal/shared ├── Generates: Prisma client └── Builds: NestJS application ``` ## 🎯 Frontend Application (apps/portal) ### Complete Package Configuration ```json { "name": "@customer-portal/portal", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev -p ${NEXT_PORT:-3000}", "build": "next build", "build:turbo": "next build --turbopack", "start": "next start -p ${NEXT_PORT:-3000}", "lint": "eslint .", "lint:fix": "eslint . --fix", "type-check": "tsc --noEmit", "test": "echo 'No tests yet'" }, "dependencies": { "@customer-portal/shared": "workspace:*", "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^5.2.1", "@tanstack/react-query": "^5.85.5", "@tanstack/react-query-devtools": "^5.85.5", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "lucide-react": "^0.540.0", "next": "15.5.0", "react": "19.1.1", "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" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.12", "@types/node": "^24.3.0", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", "tailwindcss": "^4.1.12", "typescript": "^5.9.2" } } ``` ### Frontend Architecture & Dependencies #### Core Framework Stack - **Next.js 15.5.0**: App Router, Server Components, Streaming - **React 19.1.1**: Latest React with concurrent features - **TypeScript 5.9.2**: Strict type checking #### UI & Styling - **Tailwind CSS 4.1.12**: Utility-first CSS framework - **@tailwindcss/postcss**: PostCSS integration - **tailwind-merge**: Conditional class merging - **class-variance-authority**: Component variant management - **clsx**: Conditional className utility - **tw-animate-css**: Tailwind animation utilities #### State Management & Data Fetching - **Zustand 5.0.8**: Lightweight state management - **TanStack Query 5.85.5**: Server state management - **@tanstack/react-query-devtools**: Development tools #### Forms & Validation - **react-hook-form 7.62.0**: Form state management - **@hookform/resolvers 5.2.1**: Validation resolvers - **Zod 4.0.17**: Runtime type validation #### Icons & UI Components - **@heroicons/react 2.2.0**: SVG icon library - **lucide-react 0.540.0**: Additional icon set #### Utilities - **date-fns 4.1.0**: Date manipulation library - **world-countries 5.1.0**: Country data ### Next.js Configuration (next.config.mjs) ```javascript const nextConfig = { // Enable standalone output for production deployment output: process.env.NODE_ENV === "production" ? "standalone" : undefined, // Exclude server-only packages from client bundle serverExternalPackages: [ "pino", "pino-pretty", "pino-abstract-transport", "thread-stream", "sonic-boom", ], // Turbopack configuration (Next.js 15.5+) turbopack: { resolveAlias: { "@": "./src" }, }, // Environment variables validation env: { NEXT_PUBLIC_API_BASE: process.env.NEXT_PUBLIC_API_BASE, NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME, NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION, }, // Image optimization images: { remotePatterns: [{ protocol: "https", hostname: "**" }], }, // Disable ESLint during builds (handled separately) eslint: { ignoreDuringBuilds: true }, // Security headers async headers() { return [ { source: "/(.*)", headers: [ { key: "X-Frame-Options", value: "DENY" }, { key: "X-Content-Type-Options", value: "nosniff" }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, ], }, ]; }, // Production optimizations compiler: { removeConsole: process.env.NODE_ENV === "production", }, }; ``` ### TypeScript Configuration (tsconfig.json) ```json { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./src/*"] }, "plugins": [{ "name": "next" }] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] } ``` ## 🎯 Backend Application (apps/bff) ### Complete Package Configuration ```json { "name": "@customer-portal/bff", "version": "1.0.0", "description": "Backend for Frontend API", "author": "", "private": true, "license": "UNLICENSED", "scripts": { "build": "nest build -c tsconfig.build.json", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "start": "nest start", "dev": "NODE_OPTIONS=\"--no-deprecation\" nest start --watch --preserveWatchOutput -c tsconfig.build.json", "start:debug": "NODE_OPTIONS=\"--no-deprecation\" nest start --debug --watch", "start:prod": "node dist/main", "lint": "eslint .", "lint:fix": "eslint . --fix", "test": "jest", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", "type-check": "tsc --noEmit", "clean": "rm -rf dist", "db:migrate": "prisma migrate dev", "db:generate": "prisma generate", "db:studio": "prisma studio", "db:reset": "prisma migrate reset", "db:seed": "ts-node prisma/seed.ts" }, "dependencies": { "@customer-portal/shared": "workspace:*", "@nestjs/bullmq": "^11.0.3", "@nestjs/common": "^11.1.6", "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.1.6", "@nestjs/jwt": "^11.0.0", "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^11.1.6", "@nestjs/swagger": "^11.2.0", "@nestjs/throttler": "^6.4.0", "@prisma/client": "^6.14.0", "@types/jsonwebtoken": "^9.0.10", "bcrypt": "^6.0.0", "bullmq": "^5.58.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "cookie-parser": "^1.4.7", "helmet": "^8.1.0", "ioredis": "^5.7.0", "jsforce": "^3.10.4", "jsonwebtoken": "^9.0.2", "nestjs-pino": "^4.4.0", "passport": "^0.7.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "pino": "^9.9.0", "pino-http": "^10.5.0", "pino-pretty": "^13.1.1", "prisma": "^6.14.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2", "@sendgrid/mail": "^8.1.3", "speakeasy": "^2.0.0", "uuid": "^11.1.0", "zod": "^4.0.17" }, "devDependencies": { "@nestjs/cli": "^11.0.10", "@nestjs/schematics": "^11.0.7", "@nestjs/testing": "^11.1.6", "@types/bcrypt": "^6.0.0", "@types/cookie-parser": "^1.4.9", "@types/express": "^5.0.3", "@types/jest": "^30.0.0", "@types/node": "^24.3.0", "@types/passport-jwt": "^4.0.1", "@types/passport-local": "^1.0.38", "@types/speakeasy": "^2.0.10", "@types/supertest": "^6.0.3", "@types/uuid": "^10.0.0", "jest": "^30.0.5", "source-map-support": "^0.5.21", "supertest": "^7.1.4", "ts-jest": "^29.4.1", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "typescript": "^5.9.2" } } ``` ### Backend Architecture & Dependencies #### Core NestJS Framework - **@nestjs/core 11.1.6**: Core framework - **@nestjs/common 11.1.6**: Common decorators and utilities - **@nestjs/platform-express 11.1.6**: Express platform adapter - **@nestjs/config 4.0.2**: Configuration management - **reflect-metadata 0.2.2**: Metadata reflection API #### Authentication & Security - **@nestjs/passport 11.0.5**: Passport integration - **@nestjs/jwt 11.0.0**: JWT token handling - **passport 0.7.0**: Authentication middleware - **passport-jwt 4.0.1**: JWT strategy - **passport-local 1.0.0**: Local authentication strategy - **jsonwebtoken 9.0.2**: JWT implementation - **bcrypt 6.0.0**: Password hashing - **helmet 8.1.0**: Security headers - **@nestjs/throttler 6.4.0**: Rate limiting - **speakeasy 2.0.0**: Two-factor authentication #### Database & ORM - **@prisma/client 6.14.0**: Prisma ORM client - **prisma 6.14.0**: Prisma CLI and schema management #### Caching & Queues - **ioredis 5.7.0**: Redis client - **@nestjs/bullmq 11.0.3**: Queue management - **bullmq 5.58.0**: Redis-based queue system #### Validation & Transformation - **class-validator 0.14.2**: Decorator-based validation - **class-transformer 0.5.1**: Object transformation - **zod 4.0.17**: Runtime type validation #### External Integrations - **jsforce 3.10.4**: Salesforce API client - **@sendgrid/mail 8.1.3**: SendGrid email service #### Logging & Monitoring - **nestjs-pino 4.4.0**: NestJS Pino integration - **pino 9.9.0**: High-performance logging - **pino-http 10.5.0**: HTTP request logging - **pino-pretty 13.1.1**: Pretty-printed logs for development #### API Documentation - **@nestjs/swagger 11.2.0**: OpenAPI/Swagger documentation #### Utilities - **rxjs 7.8.2**: Reactive programming - **uuid 11.1.0**: UUID generation - **cookie-parser 1.4.7**: Cookie parsing middleware ### TypeScript Configurations #### Main TypeScript Config (tsconfig.json) ```json { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./", "paths": { "@/*": ["src/*"] }, "experimentalDecorators": true, "emitDecoratorMetadata": true, "allowSyntheticDefaultImports": true, "target": "ES2021", "lib": ["ES2021"], "module": "commonjs", "declaration": true, "removeComments": true, "incremental": true, "skipLibCheck": true, "strictNullChecks": false, "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false } } ``` #### Build TypeScript Config (tsconfig.build.json) ```json { "extends": "./tsconfig.json", "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] } ``` ### Jest Testing Configuration ```json { "moduleFileExtensions": ["js", "json", "ts"], "rootDir": "src", "testRegex": ".*\\.spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" }, "collectCoverageFrom": ["**/*.(t|j)s", "!**/*.spec.ts", "!**/node_modules/**"], "coverageDirectory": "../coverage", "testEnvironment": "node", "moduleNameMapping": { "^@/(.*)$": "/$1" }, "passWithNoTests": true } ``` ## 📦 Shared Package (packages/shared) ### Complete Package Configuration ```json { "name": "@customer-portal/shared", "version": "1.0.0", "description": "Shared utilities and types for Customer Portal", "main": "dist/index.js", "types": "dist/index.d.ts", "private": true, "scripts": { "build": "tsc", "dev": "tsc --watch", "clean": "rm -rf dist", "type-check": "tsc --noEmit" }, "dependencies": { "zod": "^4.0.17" }, "devDependencies": { "typescript": "^5.9.2", "@types/node": "^24.3.0" }, "exports": { ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" } } } ``` ### TypeScript Configuration (tsconfig.json) ```json { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "./dist", "rootDir": "./src", "declaration": true, "declarationMap": true, "sourceMap": true, "composite": true, "incremental": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` ### Package Structure & Exports ```typescript // src/index.ts - Main export file export * from "./types"; export * from "./utils"; export * from "./constants"; export * from "./schemas"; // src/types/index.ts - Type definitions export interface User { id: string; email: string; name: string; role: UserRole; } export type UserRole = "admin" | "customer" | "support"; export interface ApiResponse { success: boolean; data?: T; error?: string; message?: string; } // src/utils/index.ts - Utility functions export const formatCurrency = (amount: number, currency = "USD") => { return new Intl.NumberFormat("en-US", { style: "currency", currency, }).format(amount); }; export const slugify = (text: string): string => { return text .toLowerCase() .replace(/[^\w\s-]/g, "") .replace(/[\s_-]+/g, "-") .replace(/^-+|-+$/g, ""); }; // src/constants/index.ts - Application constants export const API_ENDPOINTS = { AUTH: "/auth", USERS: "/users", PRODUCTS: "/products", ORDERS: "/orders", } as const; export const USER_ROLES = { ADMIN: "admin", CUSTOMER: "customer", SUPPORT: "support", } as const; // src/schemas/index.ts - Zod validation schemas import { z } from "zod"; export const userSchema = z.object({ id: z.string().uuid(), email: z.string().email(), name: z.string().min(1), role: z.enum(["admin", "customer", "support"]), }); export const loginSchema = z.object({ email: z.string().email(), password: z.string().min(8), }); ``` ### Build Output Structure ``` packages/shared/dist/ ├── index.js # Main compiled entry point ├── index.d.ts # Type definitions ├── index.js.map # Source map ├── types/ │ ├── index.js │ └── index.d.ts ├── utils/ │ ├── index.js │ └── index.d.ts ├── constants/ │ ├── index.js │ └── index.d.ts └── schemas/ ├── index.js └── index.d.ts ``` ## 🔨 Build Process & Dependencies ### Complete Build Order ```bash # 1. Install all workspace dependencies pnpm install --frozen-lockfile # 2. Build shared package (required by other packages) cd packages/shared pnpm build # 3. Generate Prisma client (backend requirement) cd ../../apps/bff pnpm prisma generate # 4. Build backend application pnpm build # 5. Build frontend application cd ../portal pnpm build ``` ### Production Build Requirements #### Shared Package Build - **Input**: TypeScript source files in `src/` - **Output**: Compiled JavaScript + type definitions in `dist/` - **Dependencies**: Only Zod for runtime validation - **Build time**: ~5-10 seconds #### Frontend Build - **Input**: Next.js application with React components - **Output**: Standalone server bundle + static assets - **Dependencies**: Shared package must be built first - **Build time**: ~30-60 seconds - **Output size**: ~15-25MB (standalone bundle) #### Backend Build - **Input**: NestJS application with Prisma schema - **Output**: Compiled Node.js application - **Dependencies**: Shared package + generated Prisma client - **Build time**: ~20-40 seconds - **Critical step**: Prisma client generation before TypeScript compilation ### Native Module Compilation These packages require native compilation and rebuilding in production: #### bcrypt (Password Hashing) - **Platform dependent**: Yes (native C++ bindings) - **Rebuild required**: Yes, for target architecture - **Build command**: `pnpm rebuild bcrypt` #### @prisma/client (Database ORM) - **Platform dependent**: Yes (query engine binaries) - **Rebuild required**: Yes, includes platform-specific engines - **Build command**: `pnpm rebuild @prisma/client @prisma/engines` #### @prisma/engines - **Platform dependent**: Yes (database query engines) - **Architecture specific**: linux-musl-x64, linux-gnu-x64, etc. - **Size**: ~50-100MB of engine binaries ### Environment-Specific Configurations #### Development Environment - **Hot reload**: Enabled for all packages - **Source maps**: Generated for debugging - **Type checking**: Strict mode enabled - **Logging**: Pretty-printed with colors #### Production Environment - **Minification**: Enabled for frontend - **Source maps**: Disabled for security - **Console removal**: Automatic in frontend builds - **Logging**: JSON format for structured logging - **Health checks**: Built-in endpoints for monitoring ## 📋 Package Summary ### Dependency Overview ``` Total packages: 3 workspace packages ├── @customer-portal/shared (packages/shared) │ ├── Dependencies: 1 (zod) │ ├── DevDependencies: 2 (typescript, @types/node) │ └── Build output: ~50KB │ ├── @customer-portal/portal (apps/portal) │ ├── Dependencies: 16 (Next.js, React, UI libraries) │ ├── DevDependencies: 5 (Tailwind, TypeScript types) │ └── Build output: ~15-25MB (standalone) │ └── @customer-portal/bff (apps/bff) ├── Dependencies: 25 (NestJS, Prisma, integrations) ├── DevDependencies: 13 (testing, build tools) └── Build output: ~5-10MB ``` ### Critical Build Dependencies 1. **pnpm 10.15.0**: Workspace package manager 2. **TypeScript 5.9.2**: Language compiler for all packages 3. **Node.js 22+**: Runtime environment 4. **Prisma 6.14.0**: Database client generation 5. **Next.js 15.5.0**: Frontend framework with standalone output ### Package Interdependencies ``` Build Order (Critical): 1. packages/shared → Compiles TypeScript to JavaScript 2. apps/bff → Generates Prisma client, then compiles NestJS 3. apps/portal → Compiles Next.js with shared package dependency Runtime Dependencies: - Frontend depends on shared package types and utilities - Backend depends on shared package + generated Prisma client - Both applications require external services (PostgreSQL, Redis) ``` ### Key Configuration Files - **pnpm-workspace.yaml**: Defines monorepo structure - **Root package.json**: Workspace scripts and shared devDependencies - **tsconfig.json**: TypeScript configuration inheritance - **next.config.mjs**: Next.js production optimizations - **prisma/schema.prisma**: Database schema and client generation This architecture provides a scalable, type-safe monorepo with shared utilities, modern frontend framework, robust backend API, and comprehensive external service integrations.