# Customer Portal Project A modern customer portal where users can self-register, log in, browse & buy subscriptions, view/pay invoices, and manage support cases. ## Architecture Overview ### Systems of Record - **WHMCS**: Billing, subscriptions, invoices, and **authoritative address storage** - **Salesforce**: CRM (Accounts, Contacts, Cases) and **order address snapshots** - **Portal**: Modern UI with backend for frontend (BFF) architecture ### Identity Management - Portal-native authentication (email + password, optional MFA) - One-time WHMCS user verification with forced password reset - User mapping: `user_id ↔ whmcs_client_id ↔ sf_contact_id/sf_account_id` ## Tech Stack ### Frontend (Portal UI) - **Next.js 15** with App Router - **Turbopack** for ultra-fast development and builds - **React 19** with TypeScript - **Tailwind CSS** with shadcn/ui components - **TanStack Query** for data fetching and caching - **Zod** for validation - **React Hook Form** for form management ### Backend (BFF API) - **NestJS 11** (Node 24 Current or 22 LTS) - **Prisma 6** ORM - **jsforce** for Salesforce integration - **WHMCS** API client - **BullMQ** for async jobs with ioredis - **OpenAPI/Swagger** for documentation ### Temporarily Disabled Modules - `CasesModule` and `JobsModule` are intentionally excluded from the running NestJS application until their APIs and job processors are fully implemented. See `docs/TEMPORARY-DISABLED-MODULES.md` for re-enablement details and placeholder behaviour. ### Logging - Centralized structured logging via Pino using `nestjs-pino` in the BFF - Sensitive fields are redacted; each request has a correlation ID - Usage pattern in services: - Inject `Logger` from `nestjs-pino`: `constructor(@Inject(Logger) private readonly logger: Logger) {}` - Log with structured objects: `this.logger.error('Message', { error })` - See `docs/LOGGING.md` for full guidelines ### Data & Infrastructure - **PostgreSQL 17** for users, ID mappings, and optional mirrors - **Redis 8** for cache and queues - **Docker** for local development (Postgres/Redis) ## Project Structure ``` new-portal-website/ ├── apps/ │ ├── portal/ # Next.js 15 frontend (React 19, Tailwind, shadcn/ui) │ └── bff/ # NestJS 11 backend (Prisma, BullMQ, OpenAPI) ├── packages/ │ ├── shared/ # Shared types and utilities │ └── api-client/ # Generated OpenAPI client and types ├── scripts/ │ ├── dev/ # Development management scripts │ └── prod/ # Production deployment scripts ├── compose-plesk.yaml # Plesk Docker stack (proxy / and /api) ├── docs/ # Comprehensive documentation ├── secrets/ # Private keys (git ignored) ├── .env.dev.example # Development environment template ├── .env.production.example # Production environment template ├── package.json # Root workspace configuration ├── pnpm-workspace.yaml # pnpm workspace definition └── README.md # This file ``` ## Getting Started ### Prerequisites - **Node.js**: Version 22 (LTS) or 24 (Current) - specified in `package.json` engines - **pnpm**: Version 10.0.0+ (managed via `packageManager` field) - **Docker & Docker Compose**: For local PostgreSQL and Redis services - **Git**: For version control ### Quick Start (2 minutes) 1. **Clone and Install Dependencies** ```bash git clone cd new-portal-website pnpm install ``` 2. **Setup Environment** ```bash # Copy development environment template cp .env.dev.example .env # Edit with your values (most defaults work for local development) nano .env ``` 3. **Start Development Environment** ```bash # Start database and Redis services pnpm dev:start # In another terminal, start the applications with hot reload pnpm dev ``` 4. **Access Your Applications** - **Frontend**: http://localhost:3000 - **Backend API**: http://localhost:4000/api - **API Documentation**: http://localhost:4000/api/docs ### Development Commands ```bash # === Daily Development === pnpm dev:start # Start PostgreSQL + Redis services pnpm dev # Start both apps with hot reload pnpm dev:stop # Stop all services # === Database Management === pnpm dev:migrate # Run database migrations pnpm db:studio # Open Prisma Studio (database GUI) pnpm dev:tools # Start admin tools (Adminer + Redis Commander) # === Utilities === pnpm dev:status # Check service status pnpm dev:logs # View service logs pnpm dev:reset # Reset development environment pnpm lint # Run linting across all packages pnpm type-check # Run TypeScript checks ``` ### Build and Export Images (for Plesk upload) ```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 ``` Upload the tar files in Plesk → Docker → Images → Upload, then deploy using `compose-plesk.yaml` as a stack. ### API Client Codegen 1. Generate OpenAPI spec from BFF: ```bash pnpm --filter @customer-portal/bff run openapi:gen ``` 2. Generate types and client: ```bash pnpm --filter @customer-portal/api-client run codegen && pnpm --filter @customer-portal/api-client build ``` 3. Use in Portal: ```ts import { createClient } from "@customer-portal/api-client"; ``` ### Environment Configuration - Local development: use the root `.env` (see `.env.example`). - Plesk production: use split env files (no secrets under `httpdocs`). - Frontend: `env/portal-frontend.env.sample` → ensure `NEXT_PUBLIC_API_BASE=/api` - Backend: `env/portal-backend.env.sample` → ensure `TRUST_PROXY=true`, DB uses `database:5432`, Redis uses `cache:6379` - See PLESK_DEPLOYMENT.md for full instructions and proxy rule setup. #### Key Environment Variables ```env # === Application === NODE_ENV=development BFF_PORT=4000 NEXT_PORT=3000 # === Database & Cache === DATABASE_URL=postgresql://dev:dev@localhost:5432/portal_dev?schema=public REDIS_URL=redis://localhost:6379 # === Frontend (exposed to browser) === NEXT_PUBLIC_API_BASE=http://localhost:4000 NEXT_PUBLIC_APP_NAME=Customer Portal (Dev) NEXT_PUBLIC_ENABLE_DEVTOOLS=true # === Security === JWT_SECRET=dev_secret_for_local_development_minimum_32_chars_long BCRYPT_ROUNDS=10 # === External APIs (optional for basic development) === WHMCS_BASE_URL=https://demo.whmcs.com WHMCS_API_IDENTIFIER=your_demo_identifier WHMCS_API_SECRET=your_demo_secret SF_LOGIN_URL=https://test.salesforce.com SF_CLIENT_ID=your_dev_client_id SF_PRIVATE_KEY_PATH=./secrets/sf-dev.key SF_USERNAME=dev@yourcompany.com.sandbox ``` #### Salesforce Pub/Sub (Events) ```env # Enable Pub/Sub subscription for order provisioning SF_EVENTS_ENABLED=true SF_PROVISION_EVENT_CHANNEL=/event/Order_Fulfilment_Requested__e SF_EVENTS_REPLAY=LATEST # or ALL for retention replay SF_PUBSUB_ENDPOINT=api.pubsub.salesforce.com:7443 SF_PUBSUB_NUM_REQUESTED=50 # flow control window ``` - Verify subscriber status: `GET /health/sf-events` - `enabled`: whether Pub/Sub is enabled - `channel`: topic name - `replay.lastReplayId`: last committed cursor - `subscriber.status`: connected | disconnected | unknown ### Development Tools Access When running `pnpm dev:tools`, you get access to: - **Adminer** (Database GUI): http://localhost:8080 - Server: `postgres`, User: `dev`, Password: `dev`, Database: `portal_dev` - **Redis Commander**: http://localhost:8081 - User: `admin`, Password: `dev` ## Data Model ### Core Tables (PostgreSQL) - `users` - Portal user accounts with auth credentials - `id_mappings` - Cross-system user ID mappings - `invoices_mirror` - Optional WHMCS invoice cache - `subscriptions_mirror` - Optional WHMCS service cache - `idempotency_keys` - Prevent duplicate operations ## API Surface (BFF) ### Authentication - `POST /api/auth/signup` - Create portal user → WHMCS AddClient → SF upsert - `POST /api/auth/login` - Portal authentication - `POST /api/auth/link-whmcs` - OIDC callback or ValidateLogin - `POST /api/auth/set-password` - Required after WHMCS link ### User Management - `GET /api/me` - Current user profile - `GET /api/me/summary` - Dashboard summary - `PATCH /api/me` - Update profile - `PATCH /api/me/address` - Update address fields ### Catalog & Orders - `GET /api/catalog` - WHMCS GetProducts (cached 5-15m) - `POST /api/orders` - WHMCS AddOrder with idempotency ### Invoices - `GET /api/invoices` - Paginated invoice list (cached 60-120s) - `GET /api/invoices/:id` - Invoice details - `POST /api/invoices/:id/sso-link` - WHMCS CreateSsoToken ### Subscriptions - `GET /api/subscriptions` - WHMCS GetClientsProducts ### Support Cases (Salesforce) - `GET /api/cases` - Cases list (cached 30-60s) - `GET /api/cases/:id` - Case details - `POST /api/cases` - Create new case ### Webhooks - `POST /api/orders/:sfOrderId/fulfill` - Secure Salesforce-initiated order fulfillment - `POST /api/webhooks/whmcs` - WHMCS action hooks → update mirrors + bust cache ## Frontend Pages ### Initial Pages - `/` - Dashboard (next invoice due, active subs, open cases) - `/billing/invoices` - Invoice list - `/billing/invoices/[id]` - Invoice details - `/subscriptions` - Active subscriptions - `/support/cases` - Support cases list - `/support/cases/[id]` - Case details - `/support/new` - Create new case - `/account/profile` - User profile management - `/account/security` - Security settings - `/auth/login` - Sign in - `/auth/signup` - Create account - `/auth/set-password` - Set password after WHMCS link ## Development Milestones ### Milestone 1: Identity & Linking - [ ] Portal login/signup - [ ] One-time WHMCS verification - [ ] Set new portal password - [ ] Store id_mappings ### Milestone 2: Billing - [ ] Product catalog (GetProducts) - [ ] Checkout (AddOrder) - [ ] Invoice list/detail (GetInvoices) - [ ] WHMCS SSO deep links ### Milestone 3: Cases & Webhooks - [ ] Salesforce case list/create - [ ] WHMCS webhooks → cache bust + mirrors - [ ] Nightly reconcile job (optional) ## Security Features - HTTPS only with HttpOnly/SameSite cookies - Optional MFA for portal accounts - Idempotency keys on all mutating operations - Row-level security (user must own resources) - PII minimization with encryption at rest/in transit - No WHMCS/SF credentials exposed to browser ## Caching Strategy - **Invoices**: 60-120s per page; bust on WHMCS webhook - **Cases**: 30-60s; bust after create/update - **Catalog**: 5-15m; manual bust on changes - **Keys include user_id** to prevent cross-user leakage ## Troubleshooting ### Common Issues **Port Already in Use** ```bash # Check what's using the port lsof -i :3000 # or :4000, :5432, :6379 # Kill the process or change ports in .env ``` **Database Connection Issues** ```bash # Check if PostgreSQL is running pnpm dev:status # Restart services pnpm dev:restart # Reset everything pnpm dev:reset ``` **Environment Variables Not Loading** - Ensure `.env` file exists in project root - Restart applications after changing `.env` - Check for typos in variable names - Frontend variables must start with `NEXT_PUBLIC_` **Docker Issues** ```bash # Clean up Docker resources docker system prune -f # Rebuild containers pnpm dev:stop && pnpm dev:start ``` **pnpm Issues** ```bash # Clear pnpm cache pnpm store prune # Reinstall dependencies rm -rf node_modules && pnpm install ``` ### Getting Help 1. Check the logs: `pnpm dev:logs` 2. Verify service status: `pnpm dev:status` 3. Review environment configuration in `.env` 4. Check the documentation in `docs/` folder 5. Look for similar issues in the project's issue tracker ## Documentation - **[Getting Started](docs/GETTING_STARTED.md)** - Detailed setup guide - **[Development Guide](docs/RUN.md)** - Quick reference for daily development - **[Deployment Guide](docs/DEPLOY.md)** - Production deployment instructions - **[Architecture](docs/STRUCTURE.md)** - Code organization and conventions - **[Logging](docs/LOGGING.md)** - Logging configuration and best practices ## Contributing 1. **Setup Development Environment** ```bash cp .env.dev.example .env pnpm install pnpm dev:start ``` 2. **Follow Code Standards** - Run `pnpm lint` before committing - Use `pnpm format` to format code - Ensure `pnpm type-check` passes - Write tests for new features 3. **Development Workflow** - Create feature branches from `main` - Make small, focused commits - Update documentation as needed - Test thoroughly before submitting PRs 4. **Code Quality** - Follow TypeScript strict mode - Use proper error handling [[memory:6689308]] - Implement clean, minimal UI designs [[memory:6676820]] - Avoid 'V2' suffixes in service names [[memory:6676816]] ## Documentation 📚 **[Complete Documentation](docs/README.md)** - Full documentation index ### Quick Links - **[Getting Started](docs/GETTING_STARTED.md)** - Setup and configuration - **[Development Commands](docs/RUN.md)** - Daily workflow - **[Address System](docs/ADDRESS_SYSTEM.md)** - Address management - **[Product Catalog](docs/PRODUCT-CATALOG-ARCHITECTURE.md)** - SKU-based catalog - **[Deployment Guide](docs/DEPLOY.md)** - Production deployment ### Key Features - ✅ **Required address at signup** - No incomplete profiles - ✅ **Order-type specific flows** - Internet orders require verification - ✅ **Real-time WHMCS sync** - Address updates - ✅ **Salesforce snapshots** - Point-in-time order addresses - ✅ **Clean architecture** - Modular, maintainable code ## License [Your License Here] See `docs/RUNBOOK_PROVISIONING.md` for the provisioning runbook.