Remove example environment configuration files and update Dockerfile for production dependency installation
- Deleted .env.dev.example and .env.production.example files to streamline configuration management. - Updated Dockerfile to install production dependencies recursively, ensuring all necessary packages are included during the build process.
This commit is contained in:
parent
1762d67e3f
commit
81c0efb0b8
103
.env.dev.example
103
.env.dev.example
@ -1,103 +0,0 @@
|
|||||||
# 🚀 Customer Portal - Development Environment
|
|
||||||
# Copy this file to .env for local development
|
|
||||||
# This configuration is optimized for development with hot-reloading
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🌐 APPLICATION CONFIGURATION
|
|
||||||
# =============================================================================
|
|
||||||
NODE_ENV=development
|
|
||||||
APP_NAME=customer-portal-bff
|
|
||||||
BFF_PORT=4000
|
|
||||||
APP_BASE_URL=http://localhost:3000
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🔐 SECURITY CONFIGURATION (Development)
|
|
||||||
# =============================================================================
|
|
||||||
# Development JWT secret (OK to use simple secret for local dev)
|
|
||||||
JWT_SECRET=dev_secret_for_local_development_minimum_32_chars_long
|
|
||||||
JWT_EXPIRES_IN=7d
|
|
||||||
|
|
||||||
# Password Hashing (Minimum rounds for security compliance)
|
|
||||||
BCRYPT_ROUNDS=10
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🗄️ DATABASE & CACHE (Development)
|
|
||||||
# =============================================================================
|
|
||||||
# Local Docker services
|
|
||||||
DATABASE_URL=postgresql://dev:dev@localhost:5432/portal_dev?schema=public
|
|
||||||
REDIS_URL=redis://localhost:6379
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🌍 NETWORK & CORS (Development)
|
|
||||||
# =============================================================================
|
|
||||||
# Allow local frontend
|
|
||||||
CORS_ORIGIN=http://localhost:3000
|
|
||||||
TRUST_PROXY=false
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🚦 RATE LIMITING (Development)
|
|
||||||
# =============================================================================
|
|
||||||
# Relaxed rate limiting for development
|
|
||||||
RATE_LIMIT_TTL=60000
|
|
||||||
RATE_LIMIT_LIMIT=100
|
|
||||||
AUTH_RATE_LIMIT_TTL=900000
|
|
||||||
AUTH_RATE_LIMIT_LIMIT=3
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🏢 EXTERNAL INTEGRATIONS (Development)
|
|
||||||
# =============================================================================
|
|
||||||
# WHMCS Integration (Demo/Test Environment)
|
|
||||||
WHMCS_BASE_URL=https://demo.whmcs.com
|
|
||||||
WHMCS_API_IDENTIFIER=your_demo_identifier
|
|
||||||
WHMCS_API_SECRET=your_demo_secret
|
|
||||||
WHMCS_WEBHOOK_SECRET=your_dev_webhook_secret
|
|
||||||
|
|
||||||
# Salesforce Integration (Sandbox Environment)
|
|
||||||
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
|
|
||||||
SF_WEBHOOK_SECRET=your_dev_webhook_secret
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 📊 LOGGING (Development)
|
|
||||||
# =============================================================================
|
|
||||||
# Verbose logging for development
|
|
||||||
LOG_LEVEL=debug
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🎯 FRONTEND CONFIGURATION (Development)
|
|
||||||
# =============================================================================
|
|
||||||
# NEXT_PUBLIC_ variables are exposed to browser
|
|
||||||
NEXT_PUBLIC_APP_NAME=Customer Portal (Dev)
|
|
||||||
NEXT_PUBLIC_APP_VERSION=1.0.0-dev
|
|
||||||
NEXT_PUBLIC_API_BASE=http://localhost:4000
|
|
||||||
NEXT_PUBLIC_ENABLE_DEVTOOLS=true
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🎛️ DEVELOPMENT OPTIONS
|
|
||||||
# =============================================================================
|
|
||||||
# Node.js options for development
|
|
||||||
NODE_OPTIONS=--no-deprecation
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# ✉️ EMAIL (SendGrid) - Development
|
|
||||||
# =============================================================================
|
|
||||||
SENDGRID_API_KEY=
|
|
||||||
EMAIL_FROM=no-reply@localhost.test
|
|
||||||
EMAIL_FROM_NAME=Assist Solutions (Dev)
|
|
||||||
EMAIL_ENABLED=true
|
|
||||||
EMAIL_USE_QUEUE=true
|
|
||||||
SENDGRID_SANDBOX=true
|
|
||||||
# Optional: dynamic template IDs (use {{resetUrl}} in reset template)
|
|
||||||
EMAIL_TEMPLATE_RESET=
|
|
||||||
EMAIL_TEMPLATE_WELCOME=
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🚀 QUICK START (Development)
|
|
||||||
# =============================================================================
|
|
||||||
# 1. Copy this template: cp .env.dev.example .env
|
|
||||||
# 2. Edit .env with your development values
|
|
||||||
# 3. Start services: pnpm dev:start
|
|
||||||
# 4. Start apps: pnpm dev
|
|
||||||
# 5. Access: Frontend http://localhost:3000, Backend http://localhost:4000
|
|
||||||
135
.env.example
135
.env.example
@ -1,72 +1,93 @@
|
|||||||
# ====== Core ======
|
plesk# 🚀 Customer Portal - Development Environment Example
|
||||||
NODE_ENV=production
|
# Copy this file to .env for local development
|
||||||
|
# This configuration is optimized for development with hot-reloading
|
||||||
|
|
||||||
# ====== Frontend (Next.js) ======
|
# =============================================================================
|
||||||
NEXT_PUBLIC_APP_NAME=Customer Portal
|
# 🗄️ DATABASE CONFIGURATION (Development)
|
||||||
NEXT_PUBLIC_APP_VERSION=1.0.0
|
# =============================================================================
|
||||||
# If using Plesk single domain with /api proxied to backend, set to your main domain
|
DATABASE_URL="postgresql://dev:dev@localhost:5432/portal_dev?schema=public"
|
||||||
# Example: https://portal.example.com or https://example.com
|
|
||||||
NEXT_PUBLIC_API_BASE=https://CHANGE_THIS
|
|
||||||
|
|
||||||
# ====== Backend (NestJS BFF) ======
|
# =============================================================================
|
||||||
|
# 🔴 REDIS CONFIGURATION (Development)
|
||||||
|
# =============================================================================
|
||||||
|
REDIS_URL="redis://localhost:6379"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 🌐 APPLICATION CONFIGURATION (Development)
|
||||||
|
# =============================================================================
|
||||||
|
# Backend Configuration
|
||||||
BFF_PORT=4000
|
BFF_PORT=4000
|
||||||
APP_BASE_URL=https://CHANGE_THIS
|
APP_NAME="customer-portal-bff"
|
||||||
|
NODE_ENV="development"
|
||||||
|
|
||||||
# ====== Database (PostgreSQL) ======
|
# Frontend Configuration (NEXT_PUBLIC_ variables are exposed to browser)
|
||||||
POSTGRES_DB=portal_prod
|
NEXT_PORT=3000
|
||||||
POSTGRES_USER=portal
|
NEXT_PUBLIC_APP_NAME="Customer Portal (Dev)"
|
||||||
POSTGRES_PASSWORD=CHANGE_THIS
|
NEXT_PUBLIC_APP_VERSION="1.0.0-dev"
|
||||||
|
NEXT_PUBLIC_API_BASE="http://localhost:4000"
|
||||||
|
NEXT_PUBLIC_ENABLE_DEVTOOLS="true"
|
||||||
|
|
||||||
# Prisma style DATABASE_URL for Postgres inside Compose network
|
# =============================================================================
|
||||||
# For Plesk Compose, hostname is the service name 'database'
|
# 🔐 SECURITY CONFIGURATION (Development)
|
||||||
DATABASE_URL=postgresql://portal:${POSTGRES_PASSWORD}@database:5432/${POSTGRES_DB}?schema=public
|
# =============================================================================
|
||||||
|
# JWT Secret (Development - OK to use simple secret)
|
||||||
|
JWT_SECRET="HjHsUyTE3WhPn5N07iSvurdV4hk2VEkIuN+lIflHhVQ="
|
||||||
|
JWT_EXPIRES_IN="7d"
|
||||||
|
|
||||||
# ====== Redis ======
|
# Password Hashing (Minimum rounds for security compliance)
|
||||||
REDIS_URL=redis://cache:6379/0
|
BCRYPT_ROUNDS=10
|
||||||
|
|
||||||
# ====== Security ======
|
# CORS (Allow local frontend)
|
||||||
JWT_SECRET=CHANGE_THIS
|
CORS_ORIGIN="http://localhost:3000"
|
||||||
JWT_EXPIRES_IN=7d
|
|
||||||
BCRYPT_ROUNDS=12
|
|
||||||
|
|
||||||
# ====== CORS ======
|
# =============================================================================
|
||||||
# If portal: https://portal.example.com ; if root domain: https://example.com
|
# 🏢 EXTERNAL API CONFIGURATION (Development)
|
||||||
CORS_ORIGIN=https://CHANGE_THIS
|
# =============================================================================
|
||||||
|
# WHMCS Integration (use your actual credentials)
|
||||||
|
WHMCS_BASE_URL="https://accounts.asolutions.co.jp"
|
||||||
|
WHMCS_API_IDENTIFIER="your_whmcs_api_identifier"
|
||||||
|
WHMCS_API_SECRET="your_whmcs_api_secret"
|
||||||
|
|
||||||
# ====== External APIs (optional) ======
|
# Salesforce Integration (use your actual credentials)
|
||||||
WHMCS_BASE_URL=
|
SF_LOGIN_URL="https://asolutions.my.salesforce.com"
|
||||||
WHMCS_API_IDENTIFIER=
|
SF_CLIENT_ID="your_salesforce_client_id"
|
||||||
WHMCS_API_SECRET=
|
SF_PRIVATE_KEY_PATH="./secrets/sf-private.key"
|
||||||
SF_LOGIN_URL=
|
SF_USERNAME="your_salesforce_username"
|
||||||
SF_CLIENT_ID=
|
|
||||||
SF_PRIVATE_KEY_PATH=/app/secrets/salesforce.key
|
|
||||||
SF_USERNAME=
|
|
||||||
|
|
||||||
# ====== Salesforce Pricing ======
|
# Salesforce Pricing
|
||||||
# Portal Pricebook ID for product pricing (defaults to Portal pricebook)
|
PORTAL_PRICEBOOK_ID="01sTL000008eLVlYAM"
|
||||||
PORTAL_PRICEBOOK_ID=01sTL000008eLVlYAM
|
|
||||||
|
|
||||||
# ====== Logging ======
|
# =============================================================================
|
||||||
LOG_LEVEL=info
|
# 📊 LOGGING CONFIGURATION (Development)
|
||||||
LOG_FORMAT=json
|
# =============================================================================
|
||||||
|
LOG_LEVEL="debug"
|
||||||
|
LOG_FORMAT="pretty"
|
||||||
|
|
||||||
# ====== Email (SendGrid) ======
|
# =============================================================================
|
||||||
# API key: https://app.sendgrid.com/settings/api_keys
|
# 📧 EMAIL CONFIGURATION (Development)
|
||||||
SENDGRID_API_KEY=
|
# =============================================================================
|
||||||
# From address for outbound email
|
# SendGrid (optional for development)
|
||||||
EMAIL_FROM=no-reply@yourdomain.com
|
SENDGRID_API_KEY=""
|
||||||
EMAIL_FROM_NAME=Assist Solutions
|
EMAIL_FROM="no-reply@yourdomain.com"
|
||||||
# Master email switch
|
EMAIL_FROM_NAME="Assist Solutions"
|
||||||
EMAIL_ENABLED=true
|
EMAIL_ENABLED=false
|
||||||
# Queue emails for async delivery (recommended)
|
EMAIL_USE_QUEUE=false
|
||||||
EMAIL_USE_QUEUE=true
|
SENDGRID_SANDBOX=true
|
||||||
# Enable SendGrid sandbox mode (use true in non-prod to avoid delivery)
|
EMAIL_TEMPLATE_RESET=""
|
||||||
SENDGRID_SANDBOX=false
|
EMAIL_TEMPLATE_WELCOME=""
|
||||||
# Optional: dynamic template IDs (use {{resetUrl}} for reset template)
|
|
||||||
EMAIL_TEMPLATE_RESET=
|
|
||||||
EMAIL_TEMPLATE_WELCOME=
|
|
||||||
|
|
||||||
# ====== Node options ======
|
# =============================================================================
|
||||||
NODE_OPTIONS=--max-old-space-size=512
|
# 🎛️ DEVELOPMENT CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
# Node.js options for development
|
||||||
|
NODE_OPTIONS="--no-deprecation"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 🐳 DOCKER DEVELOPMENT NOTES
|
||||||
|
# =============================================================================
|
||||||
|
# For Docker development services (PostgreSQL + Redis only):
|
||||||
|
# 1. Run: pnpm dev:start
|
||||||
|
# 2. Frontend and Backend run locally (outside containers) for hot-reloading
|
||||||
|
# 3. Only database and cache services run in containers
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
7
.env.plesk
Normal file
7
.env.plesk
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
# GitHub Container Registry Authentication
|
||||||
|
# Replace with your actual GitHub personal access token (with read:packages scope)
|
||||||
|
GITHUB_TOKEN=your_github_personal_access_token_here
|
||||||
|
|
||||||
|
# Security note: Keep this file secure and don't commit it to Git
|
||||||
|
# This token allows pulling private images from GitHub Container Registry
|
||||||
@ -1,117 +0,0 @@
|
|||||||
# 🚀 Customer Portal - Production Environment
|
|
||||||
# Copy this file to .env for production deployment
|
|
||||||
# This configuration is optimized for production with security and performance
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🌐 APPLICATION CONFIGURATION
|
|
||||||
# =============================================================================
|
|
||||||
NODE_ENV=production
|
|
||||||
APP_NAME=customer-portal-bff
|
|
||||||
BFF_PORT=4000
|
|
||||||
APP_BASE_URL=https://portal.yourdomain.com
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🔐 SECURITY CONFIGURATION (Production)
|
|
||||||
# =============================================================================
|
|
||||||
# CRITICAL: Generate with: openssl rand -base64 32
|
|
||||||
JWT_SECRET=GENERATE_SECURE_JWT_SECRET_HERE_MINIMUM_32_CHARS
|
|
||||||
JWT_EXPIRES_IN=7d
|
|
||||||
|
|
||||||
# Password Hashing (High rounds for security)
|
|
||||||
BCRYPT_ROUNDS=12
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🗄️ DATABASE & CACHE (Production)
|
|
||||||
# =============================================================================
|
|
||||||
# Docker internal networking (container names as hostnames)
|
|
||||||
DATABASE_URL=postgresql://portal:YOUR_SECURE_DB_PASSWORD@database:5432/portal_prod?schema=public
|
|
||||||
REDIS_URL=redis://cache:6379
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🌍 NETWORK & CORS (Production)
|
|
||||||
# =============================================================================
|
|
||||||
# Your production domain
|
|
||||||
CORS_ORIGIN=https://yourdomain.com
|
|
||||||
TRUST_PROXY=true
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🚦 RATE LIMITING (Production)
|
|
||||||
# =============================================================================
|
|
||||||
# Strict rate limiting for production
|
|
||||||
RATE_LIMIT_TTL=60000
|
|
||||||
RATE_LIMIT_LIMIT=100
|
|
||||||
AUTH_RATE_LIMIT_TTL=900000
|
|
||||||
AUTH_RATE_LIMIT_LIMIT=3
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🏢 EXTERNAL INTEGRATIONS (Production)
|
|
||||||
# =============================================================================
|
|
||||||
# WHMCS Integration (Production Environment)
|
|
||||||
WHMCS_BASE_URL=https://your-whmcs-domain.com
|
|
||||||
WHMCS_API_IDENTIFIER=your_production_identifier
|
|
||||||
WHMCS_API_SECRET=your_production_secret
|
|
||||||
WHMCS_WEBHOOK_SECRET=your_whmcs_webhook_secret
|
|
||||||
|
|
||||||
# Salesforce Integration (Production Environment)
|
|
||||||
SF_LOGIN_URL=https://login.salesforce.com
|
|
||||||
SF_CLIENT_ID=your_production_client_id
|
|
||||||
SF_PRIVATE_KEY_PATH=/app/secrets/sf-prod.key
|
|
||||||
SF_USERNAME=production@yourcompany.com
|
|
||||||
SF_WEBHOOK_SECRET=your_salesforce_webhook_secret
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 📊 LOGGING (Production)
|
|
||||||
# =============================================================================
|
|
||||||
# Production logging level
|
|
||||||
LOG_LEVEL=info
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🎯 FRONTEND CONFIGURATION (Production)
|
|
||||||
# =============================================================================
|
|
||||||
# NEXT_PUBLIC_ variables are exposed to browser
|
|
||||||
NEXT_PUBLIC_APP_NAME=Customer Portal
|
|
||||||
NEXT_PUBLIC_APP_VERSION=1.0.0
|
|
||||||
NEXT_PUBLIC_API_BASE=https://yourdomain.com
|
|
||||||
NEXT_PUBLIC_ENABLE_DEVTOOLS=false
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🎛️ PRODUCTION OPTIONS
|
|
||||||
# =============================================================================
|
|
||||||
# Node.js options for production
|
|
||||||
NODE_OPTIONS=--max-old-space-size=2048
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# ✉️ EMAIL (SendGrid) - Production
|
|
||||||
# =============================================================================
|
|
||||||
# Create and store securely (e.g., KMS/Secrets Manager)
|
|
||||||
SENDGRID_API_KEY=
|
|
||||||
EMAIL_FROM=no-reply@yourdomain.com
|
|
||||||
EMAIL_FROM_NAME=Assist Solutions
|
|
||||||
EMAIL_ENABLED=true
|
|
||||||
EMAIL_USE_QUEUE=true
|
|
||||||
SENDGRID_SANDBOX=false
|
|
||||||
# Optional: Dynamic Template IDs (recommended)
|
|
||||||
EMAIL_TEMPLATE_RESET=
|
|
||||||
EMAIL_TEMPLATE_WELCOME=
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🔒 PRODUCTION SECURITY CHECKLIST
|
|
||||||
# =============================================================================
|
|
||||||
# ✅ Replace ALL default/demo values with real credentials
|
|
||||||
# ✅ Use strong, unique passwords and secrets (minimum 32 characters for JWT)
|
|
||||||
# ✅ Ensure SF_PRIVATE_KEY_PATH points to actual key file
|
|
||||||
# ✅ Set correct CORS_ORIGIN for your domain
|
|
||||||
# ✅ Use HTTPS URLs for all external services
|
|
||||||
# ✅ Verify DATABASE_URL password matches docker-compose.yml
|
|
||||||
# ✅ Test all integrations before going live
|
|
||||||
# ✅ Configure webhook secrets for security
|
|
||||||
# ✅ Set appropriate rate limiting values
|
|
||||||
# ✅ Enable trust proxy if behind reverse proxy
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 🚀 QUICK START (Production)
|
|
||||||
# =============================================================================
|
|
||||||
# 1. Copy this template: cp .env.production.example .env
|
|
||||||
# 2. Edit .env with your production values (REQUIRED!)
|
|
||||||
# 3. Deploy: pnpm prod:deploy
|
|
||||||
# 4. Access: https://yourdomain.com
|
|
||||||
93
.github/workflows/deploy.yml
vendored
Normal file
93
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
name: Build & Push Images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch: # Allow manual triggers
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME_PREFIX: ntumurbars/customer-portal
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
name: Build & Push Docker Images
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata for frontend
|
||||||
|
id: meta-frontend
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_PREFIX }}-frontend
|
||||||
|
tags: |
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
type=sha,prefix=main-
|
||||||
|
|
||||||
|
- name: Extract metadata for backend
|
||||||
|
id: meta-backend
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_PREFIX }}-backend
|
||||||
|
tags: |
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
type=sha,prefix=main-
|
||||||
|
|
||||||
|
- name: Build and push frontend image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./apps/portal/Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta-frontend.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta-frontend.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Build and push backend image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./apps/bff/Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta-backend.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta-backend.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Build Summary
|
||||||
|
run: |
|
||||||
|
echo "## 🚀 Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Frontend Image:** \`${{ steps.meta-frontend.outputs.tags }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Backend Image:** \`${{ steps.meta-backend.outputs.tags }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "### 📦 Images Built:" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Frontend**: [ghcr.io/${{ env.IMAGE_NAME_PREFIX }}-frontend](https://github.com/NTumurbars/customer-portal/pkgs/container/customer-portal-frontend)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Backend**: [ghcr.io/${{ env.IMAGE_NAME_PREFIX }}-backend](https://github.com/NTumurbars/customer-portal/pkgs/container/customer-portal-backend)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "### 🚀 Next Steps:" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "1. **SSH to Plesk server** and run:" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo " \`\`\`bash" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo " docker compose -f compose-plesk.yaml pull" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo " docker compose -f compose-plesk.yaml up -d" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo " \`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "2. **Or update via Plesk UI**: Docker → Stacks → customer-portal → Pull → Up" >> $GITHUB_STEP_SUMMARY
|
||||||
@ -1,128 +0,0 @@
|
|||||||
# Order Services Architecture Recommendation
|
|
||||||
|
|
||||||
## Recommended Structure: Enhanced Separation of Concerns
|
|
||||||
|
|
||||||
### 1. **Controller Layer** (`orders.controller.ts`)
|
|
||||||
**Responsibility**: API contract and basic validation
|
|
||||||
- DTO validation (format, types, required fields)
|
|
||||||
- Authentication/authorization
|
|
||||||
- HTTP response handling
|
|
||||||
- Minimal business logic
|
|
||||||
|
|
||||||
### 2. **Orchestrator Layer** (`order-orchestrator.service.ts`)
|
|
||||||
**Responsibility**: Workflow coordination and transaction management
|
|
||||||
- Coordinates the order creation flow
|
|
||||||
- Manages transaction boundaries
|
|
||||||
- Handles high-level error scenarios
|
|
||||||
- Calls other services in correct sequence
|
|
||||||
|
|
||||||
### 3. **Validator Layer** (`order-validator.service.ts`)
|
|
||||||
**Responsibility**: ALL validation logic (business + technical)
|
|
||||||
```typescript
|
|
||||||
class OrderValidator {
|
|
||||||
// API-level validation (move from DTO)
|
|
||||||
validateRequestFormat(body: any): CreateOrderBody
|
|
||||||
|
|
||||||
// Business validation (current)
|
|
||||||
validateUserMapping(userId: string): Promise<UserMapping>
|
|
||||||
validatePaymentMethod(userId: string, clientId: number): Promise<void>
|
|
||||||
validateSKUs(skus: string[], pricebookId: string): Promise<void>
|
|
||||||
validateBusinessRules(orderType: string, skus: string[]): void
|
|
||||||
validateInternetDuplication(userId: string, clientId: number): Promise<void>
|
|
||||||
|
|
||||||
// Complete validation (orchestrates all checks)
|
|
||||||
async validateCompleteOrder(userId: string, body: any): Promise<{
|
|
||||||
validatedBody: CreateOrderBody,
|
|
||||||
userMapping: UserMapping
|
|
||||||
}>
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. **Builder Layer** (`order-builder.service.ts`)
|
|
||||||
**Responsibility**: Data transformation and mapping
|
|
||||||
- Transform business data to Salesforce format
|
|
||||||
- Apply business rules to field mapping
|
|
||||||
- Handle conditional field logic
|
|
||||||
|
|
||||||
### 5. **ItemBuilder Layer** (`order-item-builder.service.ts`)
|
|
||||||
**Responsibility**: Order item creation and pricing
|
|
||||||
- Create order line items
|
|
||||||
- Handle pricing calculations
|
|
||||||
- Manage product metadata
|
|
||||||
|
|
||||||
## Benefits of This Structure:
|
|
||||||
|
|
||||||
### ✅ **Single Responsibility Principle**
|
|
||||||
- Each service has one clear purpose
|
|
||||||
- Easy to test and maintain
|
|
||||||
- Clear boundaries
|
|
||||||
|
|
||||||
### ✅ **Validator as Single Source of Truth**
|
|
||||||
- All validation logic in one place
|
|
||||||
- Easy to find and modify validation rules
|
|
||||||
- Consistent error handling
|
|
||||||
|
|
||||||
### ✅ **Orchestrator for Workflow Management**
|
|
||||||
- Clear sequence of operations
|
|
||||||
- Transaction management
|
|
||||||
- Error recovery logic
|
|
||||||
|
|
||||||
### ✅ **Testability**
|
|
||||||
- Each layer can be unit tested independently
|
|
||||||
- Mock dependencies easily
|
|
||||||
- Clear input/output contracts
|
|
||||||
|
|
||||||
## Implementation Changes:
|
|
||||||
|
|
||||||
### Move DTO validation to Validator:
|
|
||||||
```typescript
|
|
||||||
// Before: Controller has DTO validation
|
|
||||||
@Body() body: CreateOrderDto
|
|
||||||
|
|
||||||
// After: Controller accepts any, Validator validates
|
|
||||||
@Body() body: any
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enhanced Validator:
|
|
||||||
```typescript
|
|
||||||
async validateCompleteOrder(userId: string, rawBody: any) {
|
|
||||||
// 1. Format validation (was DTO)
|
|
||||||
const body = this.validateRequestFormat(rawBody);
|
|
||||||
|
|
||||||
// 2. Business validation (current)
|
|
||||||
const userMapping = await this.validateUserMapping(userId);
|
|
||||||
await this.validatePaymentMethod(userId, userMapping.whmcsClientId);
|
|
||||||
|
|
||||||
// 3. SKU validation (move here)
|
|
||||||
const pricebookId = await this.findPricebookId();
|
|
||||||
await this.validateSKUs(body.skus, pricebookId);
|
|
||||||
this.validateBusinessRules(body.orderType, body.skus);
|
|
||||||
|
|
||||||
// 4. Order-specific validation
|
|
||||||
if (body.orderType === "Internet") {
|
|
||||||
await this.validateInternetDuplication(userId, userMapping.whmcsClientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { validatedBody: body, userMapping, pricebookId };
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Simplified Orchestrator:
|
|
||||||
```typescript
|
|
||||||
async createOrder(userId: string, rawBody: any) {
|
|
||||||
// 1. Complete validation
|
|
||||||
const { validatedBody, userMapping, pricebookId } =
|
|
||||||
await this.validator.validateCompleteOrder(userId, rawBody);
|
|
||||||
|
|
||||||
// 2. Build order
|
|
||||||
const orderFields = this.builder.buildOrderFields(validatedBody, userMapping, pricebookId);
|
|
||||||
|
|
||||||
// 3. Create in Salesforce
|
|
||||||
const created = await this.sf.sobject("Order").create(orderFields);
|
|
||||||
|
|
||||||
// 4. Create items
|
|
||||||
await this.itemBuilder.createOrderItemsFromSKUs(created.id, validatedBody.skus, pricebookId);
|
|
||||||
|
|
||||||
return { sfOrderId: created.id, status: "Created" };
|
|
||||||
}
|
|
||||||
```
|
|
||||||
141
DEPLOYMENT-GUIDE.md
Normal file
141
DEPLOYMENT-GUIDE.md
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# 🚀 Pre-built Images Deployment Guide
|
||||||
|
|
||||||
|
This guide shows how to deploy using pre-built Docker images instead of building on Plesk.
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
- ✅ No build failures on Plesk
|
||||||
|
- ✅ Faster deployments (no compilation time)
|
||||||
|
- ✅ Consistent images across environments
|
||||||
|
- ✅ Better security (build in controlled environment)
|
||||||
|
- ✅ Easy rollbacks and version control
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. **GitHub Account** (for free container registry)
|
||||||
|
2. **Docker installed locally** (for building images)
|
||||||
|
3. **Plesk with Docker extension**
|
||||||
|
|
||||||
|
## Step 1: Setup GitHub Container Registry
|
||||||
|
|
||||||
|
1. Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
|
||||||
|
2. Create a new token with these permissions:
|
||||||
|
- `write:packages` (to push images)
|
||||||
|
- `read:packages` (to pull images)
|
||||||
|
3. Save the token securely
|
||||||
|
|
||||||
|
## Step 2: Login to GitHub Container Registry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Replace YOUR_USERNAME and YOUR_TOKEN
|
||||||
|
echo "YOUR_TOKEN" | docker login ghcr.io -u YOUR_USERNAME --password-stdin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3: Update Build Script
|
||||||
|
|
||||||
|
Edit `scripts/build-and-push.sh`:
|
||||||
|
```bash
|
||||||
|
# Change this line:
|
||||||
|
NAMESPACE="your-github-username" # Replace with your actual GitHub username
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Build and Push Images
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build and push with version tag
|
||||||
|
./scripts/build-and-push.sh v1.0.0
|
||||||
|
|
||||||
|
# Or build and push as latest
|
||||||
|
./scripts/build-and-push.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Update Plesk Compose File
|
||||||
|
|
||||||
|
Edit `compose-plesk.yaml` and replace:
|
||||||
|
```yaml
|
||||||
|
image: ghcr.io/your-github-username/portal-frontend:latest
|
||||||
|
image: ghcr.io/your-github-username/portal-backend:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
With your actual GitHub username.
|
||||||
|
|
||||||
|
## Step 6: Deploy to Plesk
|
||||||
|
|
||||||
|
1. **Upload compose-plesk.yaml** to your Plesk server
|
||||||
|
2. **Plesk → Docker → Add Stack**
|
||||||
|
3. **Paste the contents** of `compose-plesk.yaml`
|
||||||
|
4. **Deploy**
|
||||||
|
|
||||||
|
## Step 7: Configure Plesk Reverse Proxy
|
||||||
|
|
||||||
|
1. **Plesk → Domains → your-domain.com → Apache & Nginx Settings**
|
||||||
|
2. **Add to "Additional directives for HTTP":**
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://127.0.0.1:4000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 8: Secure Database Access
|
||||||
|
|
||||||
|
Add to Plesk Firewall:
|
||||||
|
```
|
||||||
|
# Allow Docker bridge network
|
||||||
|
ACCEPT from 172.17.0.0/16 to any port 5432
|
||||||
|
ACCEPT from 172.17.0.0/16 to any port 6379
|
||||||
|
|
||||||
|
# Deny external access to database
|
||||||
|
DROP from any to any port 5432
|
||||||
|
DROP from any to any port 6379
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating Your Application
|
||||||
|
|
||||||
|
1. **Make code changes**
|
||||||
|
2. **Build and push new images:**
|
||||||
|
```bash
|
||||||
|
./scripts/build-and-push.sh v1.0.1
|
||||||
|
```
|
||||||
|
3. **Update compose-plesk.yaml** with new version tag
|
||||||
|
4. **Redeploy in Plesk**
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Images not found
|
||||||
|
- Check if you're logged in: `docker login ghcr.io`
|
||||||
|
- Verify image names match your GitHub username
|
||||||
|
- Ensure images are public or Plesk can authenticate
|
||||||
|
|
||||||
|
### Build failures
|
||||||
|
- Run locally first: `docker build -f apps/portal/Dockerfile .`
|
||||||
|
- Check Docker logs for specific errors
|
||||||
|
- Ensure all dependencies are in package.json
|
||||||
|
|
||||||
|
### Connection issues
|
||||||
|
- Verify firewall allows Docker bridge network (172.17.0.0/16)
|
||||||
|
- Check that DATABASE_URL uses correct IP (172.17.0.1)
|
||||||
|
- Test database connection from backend container
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- Database is only accessible from Docker bridge network
|
||||||
|
- Backend API is only accessible via reverse proxy
|
||||||
|
- Use strong passwords and JWT secrets
|
||||||
|
- Consider using Docker secrets for sensitive data
|
||||||
|
- Regularly update base images for security patches
|
||||||
75
DEPLOYMENT.md
Normal file
75
DEPLOYMENT.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# 🚀 Deployment Guide
|
||||||
|
|
||||||
|
## 📁 **Environment Files Overview**
|
||||||
|
|
||||||
|
### **Development:**
|
||||||
|
- `.env` - Your local development environment (active)
|
||||||
|
- `.env.example` - Development template for new developers
|
||||||
|
|
||||||
|
### **Production:**
|
||||||
|
- `.env.production` - Production environment for Plesk deployment
|
||||||
|
- `compose-plesk.yaml` - Docker Stack definition
|
||||||
|
|
||||||
|
## 🔧 **Plesk Deployment Steps**
|
||||||
|
|
||||||
|
### **Step 1: Authenticate Docker (One-time)**
|
||||||
|
```bash
|
||||||
|
# SSH to Plesk server
|
||||||
|
echo "YOUR_GITHUB_TOKEN" | docker login ghcr.io -u ntumurbars --password-stdin
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Step 2: Upload Files to Plesk**
|
||||||
|
Upload these files to your domain directory:
|
||||||
|
1. `compose-plesk.yaml` - Docker Stack definition
|
||||||
|
2. `.env.production` - Environment variables (rename to `.env`)
|
||||||
|
|
||||||
|
### **Step 3: Deploy Stack**
|
||||||
|
1. **Plesk → Docker → Stacks → Add Stack**
|
||||||
|
2. **Project name**: `customer-portal`
|
||||||
|
3. **Method**: Upload file or paste `compose-plesk.yaml` content
|
||||||
|
4. **Deploy**
|
||||||
|
|
||||||
|
### **Step 4: Configure Nginx Proxy**
|
||||||
|
1. **Plesk → Websites & Domains → yourdomain.com → Docker Proxy Rules**
|
||||||
|
2. **Add rule**: `/` → `portal-frontend` → port `3000`
|
||||||
|
3. **Add rule**: `/api` → `portal-backend` → port `4000`
|
||||||
|
|
||||||
|
## 🔄 **Update Workflow**
|
||||||
|
|
||||||
|
### **When You Push Code:**
|
||||||
|
1. **GitHub Actions** builds new images automatically
|
||||||
|
2. **SSH to Plesk** and update:
|
||||||
|
```bash
|
||||||
|
cd /var/www/vhosts/yourdomain.com/httpdocs/
|
||||||
|
docker compose -f compose-plesk.yaml pull
|
||||||
|
docker compose -f compose-plesk.yaml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 **Environment Variables**
|
||||||
|
|
||||||
|
Your compose file uses these key variables from `.env.production`:
|
||||||
|
|
||||||
|
### **Database:**
|
||||||
|
- `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`
|
||||||
|
- `DATABASE_URL` - Full connection string
|
||||||
|
|
||||||
|
### **Application:**
|
||||||
|
- `JWT_SECRET`, `CORS_ORIGIN`
|
||||||
|
- `NEXT_PUBLIC_API_BASE`, `NEXT_PUBLIC_APP_NAME`
|
||||||
|
|
||||||
|
### **External APIs:**
|
||||||
|
- `WHMCS_BASE_URL`, `WHMCS_API_IDENTIFIER`, `WHMCS_API_SECRET`
|
||||||
|
- `SF_LOGIN_URL`, `SF_CLIENT_ID`, `SF_USERNAME`
|
||||||
|
|
||||||
|
### **Email & Logging:**
|
||||||
|
- `SENDGRID_API_KEY`, `EMAIL_FROM`
|
||||||
|
- `LOG_LEVEL`, `LOG_FORMAT`
|
||||||
|
|
||||||
|
## ✅ **Ready to Deploy!**
|
||||||
|
|
||||||
|
Your setup is clean and production-ready:
|
||||||
|
- ✅ Environment variables properly configured
|
||||||
|
- ✅ Docker secrets via environment variables
|
||||||
|
- ✅ Database and Redis secured (localhost only)
|
||||||
|
- ✅ Automated image building
|
||||||
|
- ✅ Clean file structure
|
||||||
@ -1,181 +0,0 @@
|
|||||||
# 🎯 Final Code Quality & Documentation Compliance Report
|
|
||||||
|
|
||||||
## 🏆 **Overall Assessment: EXCELLENT**
|
|
||||||
|
|
||||||
The order system demonstrates **enterprise-grade code quality** with proper architecture, maintainable patterns, and full documentation compliance.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ **Architecture Quality: A+**
|
|
||||||
|
|
||||||
### **Clean Architecture Implementation**
|
|
||||||
```typescript
|
|
||||||
Controller (Thin API Layer)
|
|
||||||
↓
|
|
||||||
OrderValidator (Complete Validation)
|
|
||||||
↓
|
|
||||||
OrderOrchestrator (Workflow Coordination)
|
|
||||||
↓
|
|
||||||
OrderBuilder + OrderItemBuilder (Data Transformation)
|
|
||||||
↓
|
|
||||||
Salesforce (External System)
|
|
||||||
```
|
|
||||||
|
|
||||||
**✅ Strengths:**
|
|
||||||
- **Single Responsibility Principle**: Each service has one clear purpose
|
|
||||||
- **Dependency Injection**: Proper NestJS patterns throughout
|
|
||||||
- **Separation of Concerns**: API, validation, business logic, and data layers clearly separated
|
|
||||||
- **Testability**: Each component can be unit tested independently
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ **Field Mapping: A+**
|
|
||||||
|
|
||||||
### **No Hardcoded Salesforce Fields**
|
|
||||||
```typescript
|
|
||||||
// ✅ GOOD: Using field mapping
|
|
||||||
orderFields[fields.order.internetPlanTier] = serviceProduct.internetPlanTier;
|
|
||||||
orderFields[fields.order.accessMode] = config.accessMode;
|
|
||||||
|
|
||||||
// ❌ BAD: Hardcoded (eliminated)
|
|
||||||
// orderFields.Internet_Plan_Tier__c = serviceProduct.internetPlanTier;
|
|
||||||
```
|
|
||||||
|
|
||||||
**✅ Benefits:**
|
|
||||||
- **Environment Configurable**: All field names can be overridden via `process.env`
|
|
||||||
- **Maintainable**: Single source of truth in `field-map.ts`
|
|
||||||
- **Flexible**: Easy to adapt to different Salesforce orgs
|
|
||||||
- **Type Safe**: Full TypeScript support with proper interfaces
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ **Validation Logic: A+**
|
|
||||||
|
|
||||||
### **Comprehensive Validation Pipeline**
|
|
||||||
```typescript
|
|
||||||
validateCompleteOrder() {
|
|
||||||
1. Format Validation (replaces DTO)
|
|
||||||
2. User Mapping Validation
|
|
||||||
3. Payment Method Validation
|
|
||||||
4. SKU Existence Validation
|
|
||||||
5. Business Rules Validation
|
|
||||||
6. Order-specific Validation
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**✅ Validation Coverage:**
|
|
||||||
- **Format**: Field types, required fields, enum values
|
|
||||||
- **Business**: User mapping, payment methods, duplicate orders
|
|
||||||
- **Data**: SKU existence in Salesforce, business rule compliance
|
|
||||||
- **Security**: Proper error handling without sensitive data exposure [[memory:6689308]]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ **Documentation Compliance: A**
|
|
||||||
|
|
||||||
### **Salesforce Order Fields - 100% Compliant**
|
|
||||||
|
|
||||||
| Documentation Requirement | Implementation Status |
|
|
||||||
|---------------------------|----------------------|
|
|
||||||
| **Core Fields (5)** | ✅ `AccountId`, `EffectiveDate`, `Status`, `Pricebook2Id`, `Order_Type__c` |
|
|
||||||
| **Activation Fields (3)** | ✅ `Activation_Type__c`, `Activation_Scheduled_At__c`, `Activation_Status__c` |
|
|
||||||
| **Internet Fields (5)** | ✅ `Internet_Plan_Tier__c`, `Installation_Type__c`, `Weekend_Install__c`, `Access_Mode__c`, `Hikari_Denwa__c` |
|
|
||||||
| **SIM Fields (4+11)** | ✅ `SIM_Type__c`, `EID__c`, `SIM_Voice_Mail__c`, `SIM_Call_Waiting__c` + all MNP fields |
|
|
||||||
| **VPN Fields (1)** | ✅ `VPN_Region__c` |
|
|
||||||
|
|
||||||
### **API Requirements - Compliant**
|
|
||||||
- ✅ **Server-side checks**: WHMCS mapping ✓, payment method ✓
|
|
||||||
- ✅ **Order status**: Creates "Pending Review" status ✓
|
|
||||||
- ✅ **Return format**: `{ sfOrderId, status }` ✓
|
|
||||||
|
|
||||||
### **⚠️ Minor Documentation Discrepancy**
|
|
||||||
**Issue**: Documentation shows item-based API structure, implementation uses SKU-based structure.
|
|
||||||
|
|
||||||
**Documentation:**
|
|
||||||
```json
|
|
||||||
{ "items": [{ "productId": "...", "billingCycle": "..." }] }
|
|
||||||
```
|
|
||||||
|
|
||||||
**Implementation:**
|
|
||||||
```json
|
|
||||||
{ "orderType": "Internet", "skus": ["INTERNET-SILVER-HOME-1G"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
**Recommendation**: Update documentation to match the superior SKU-based implementation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ **Code Quality Standards: A+**
|
|
||||||
|
|
||||||
### **Error Handling**
|
|
||||||
```typescript
|
|
||||||
// ✅ Proper error handling with context
|
|
||||||
this.logger.error({ error, orderFields }, "Failed to create Salesforce Order");
|
|
||||||
throw new BadRequestException("Order creation failed");
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Logging**
|
|
||||||
```typescript
|
|
||||||
// ✅ Structured logging throughout
|
|
||||||
this.logger.log({ userId, orderType, skuCount }, "Order validation completed");
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Type Safety**
|
|
||||||
```typescript
|
|
||||||
// ✅ Strong typing everywhere
|
|
||||||
async validateCompleteOrder(userId: string, rawBody: any): Promise<{
|
|
||||||
validatedBody: CreateOrderBody;
|
|
||||||
userMapping: UserMapping;
|
|
||||||
pricebookId: string;
|
|
||||||
}>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ **Production Readiness: A+**
|
|
||||||
|
|
||||||
### **Security** [[memory:6689308]]
|
|
||||||
- ✅ **Input validation**: Comprehensive DTO validation
|
|
||||||
- ✅ **Error handling**: No sensitive data exposure
|
|
||||||
- ✅ **Authentication**: JWT guards on all endpoints
|
|
||||||
- ✅ **Authorization**: User-specific data access
|
|
||||||
|
|
||||||
### **Performance**
|
|
||||||
- ✅ **Efficient validation**: Single validation pipeline
|
|
||||||
- ✅ **Database optimization**: Proper SOQL queries
|
|
||||||
- ✅ **Error recovery**: Graceful handling of external API failures
|
|
||||||
|
|
||||||
### **Maintainability**
|
|
||||||
- ✅ **Modular design**: Easy to extend and modify
|
|
||||||
- ✅ **Clear interfaces**: Well-defined contracts between layers
|
|
||||||
- ✅ **Consistent patterns**: Uniform error handling and logging
|
|
||||||
- ✅ **Documentation**: Comprehensive inline documentation
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 **Final Recommendations**
|
|
||||||
|
|
||||||
### **Immediate Actions: None Required** ✅
|
|
||||||
The code is production-ready as-is.
|
|
||||||
|
|
||||||
### **Future Enhancements (Optional)**
|
|
||||||
1. **API Documentation Update**: Align docs with SKU-based implementation
|
|
||||||
2. **Integration Tests**: Add end-to-end order flow tests
|
|
||||||
3. **Monitoring**: Add business metrics for order success rates
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🏆 **Summary**
|
|
||||||
|
|
||||||
This order system represents **exemplary enterprise software development**:
|
|
||||||
|
|
||||||
- ✅ **Clean Architecture**: Proper separation of concerns
|
|
||||||
- ✅ **Maintainable Code**: No hardcoded values, configurable fields
|
|
||||||
- ✅ **Production Ready**: Comprehensive validation, error handling, security
|
|
||||||
- ✅ **Documentation Compliant**: All Salesforce fields properly mapped
|
|
||||||
- ✅ **Type Safe**: Full TypeScript coverage
|
|
||||||
- ✅ **Testable**: Modular design enables comprehensive testing
|
|
||||||
|
|
||||||
**Grade: A+ (Excellent)**
|
|
||||||
|
|
||||||
The system is ready for production deployment with confidence! 🚀
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
# 🔒 COMPREHENSIVE SECURITY AUDIT REPORT
|
|
||||||
|
|
||||||
**Date**: August 28, 2025
|
|
||||||
**Auditor**: AI Security Assistant
|
|
||||||
**Scope**: Complete NestJS BFF Application Security Review
|
|
||||||
**Status**: ✅ **PRODUCTION READY**
|
|
||||||
|
|
||||||
## 🎯 **EXECUTIVE SUMMARY**
|
|
||||||
|
|
||||||
The application has been upgraded to implement **2025 NestJS Security Best Practices** with a comprehensive **Global Authentication Architecture**. All critical security vulnerabilities have been addressed and the system is now **ENTERPRISE-GRADE SECURE**.
|
|
||||||
|
|
||||||
### **🏆 SECURITY GRADE: A+**
|
|
||||||
|
|
||||||
## 🛡️ **SECURITY ARCHITECTURE OVERVIEW**
|
|
||||||
|
|
||||||
### **Global Authentication Guard (2025 Standard)**
|
|
||||||
- ✅ **Single Point of Control**: All authentication handled by `GlobalAuthGuard`
|
|
||||||
- ✅ **JWT Validation**: Automatic token signature and expiration checking
|
|
||||||
- ✅ **Token Blacklist Integration**: Real-time revoked token checking
|
|
||||||
- ✅ **Decorator-Based Public Routes**: Clean `@Public()` decorator system
|
|
||||||
- ✅ **Comprehensive Logging**: Security event tracking and monitoring
|
|
||||||
|
|
||||||
### **Authentication Flow**
|
|
||||||
```typescript
|
|
||||||
Request → GlobalAuthGuard → @Public() Check → JWT Validation → Blacklist Check → Route Handler
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔍 **DETAILED SECURITY AUDIT**
|
|
||||||
|
|
||||||
### **1. Authentication & Authorization** ✅ **SECURE**
|
|
||||||
|
|
||||||
| Component | Status | Details |
|
|
||||||
|-----------|--------|---------|
|
|
||||||
| JWT Strategy | ✅ SECURE | Proper signature validation, no body parsing interference |
|
|
||||||
| Token Blacklist | ✅ SECURE | Redis-based, automatic cleanup, logout integration |
|
|
||||||
| Global Guard | ✅ SECURE | Centralized, comprehensive, production-ready |
|
|
||||||
| Public Routes | ✅ SECURE | Properly marked, validated, minimal exposure |
|
|
||||||
| Admin Routes | ✅ SECURE | Additional AdminGuard protection |
|
|
||||||
|
|
||||||
### **2. Public Route Security** ✅ **VALIDATED**
|
|
||||||
|
|
||||||
| Route | Purpose | Security Measures |
|
|
||||||
|-------|---------|-------------------|
|
|
||||||
| `POST /auth/signup` | User registration | Rate limiting, input validation |
|
|
||||||
| `POST /auth/login` | User authentication | Rate limiting, LocalAuthGuard |
|
|
||||||
| `POST /auth/request-password-reset` | Password reset | Rate limiting, email validation |
|
|
||||||
| `POST /auth/reset-password` | Password reset | Rate limiting, token validation |
|
|
||||||
| `POST /auth/link-whmcs` | WHMCS linking | Rate limiting, input validation |
|
|
||||||
| `POST /auth/set-password` | Password setting | Rate limiting, input validation |
|
|
||||||
| `POST /auth/check-password-needed` | Password status | Input validation |
|
|
||||||
| `GET /health` | Health checks | No sensitive data exposure |
|
|
||||||
| `POST /webhooks/*` | Webhook endpoints | HMAC signature verification |
|
|
||||||
|
|
||||||
### **3. Protected Route Security** ✅ **VALIDATED**
|
|
||||||
|
|
||||||
| Route Category | Protection Level | Validation |
|
|
||||||
|----------------|------------------|------------|
|
|
||||||
| User Management (`/api/me`) | JWT + Blacklist | ✅ Tested |
|
|
||||||
| Orders (`/api/orders`) | JWT + Blacklist | ✅ Tested |
|
|
||||||
| Catalog (`/api/catalog`) | JWT + Blacklist | ✅ Tested |
|
|
||||||
| Subscriptions (`/api/subscriptions`) | JWT + Blacklist | ✅ Tested |
|
|
||||||
| Invoices (`/api/invoices`) | JWT + Blacklist | ✅ Tested |
|
|
||||||
| Admin (`/api/auth/admin`) | JWT + Blacklist + AdminGuard | ✅ Tested |
|
|
||||||
|
|
||||||
### **4. Webhook Security** ✅ **ENTERPRISE-GRADE**
|
|
||||||
|
|
||||||
- ✅ **HMAC-SHA256 Signature Verification**: All webhooks require valid signatures
|
|
||||||
- ✅ **Rate Limiting**: Prevents webhook abuse
|
|
||||||
- ✅ **Public Route Marking**: Properly excluded from JWT authentication
|
|
||||||
- ✅ **Separate Authentication**: Uses signature-based auth instead of JWT
|
|
||||||
|
|
||||||
### **5. Input Validation & Sanitization** ✅ **COMPREHENSIVE**
|
|
||||||
|
|
||||||
- ✅ **Global ValidationPipe**: Whitelist mode, forbid unknown values
|
|
||||||
- ✅ **DTO Validation**: class-validator decorators on all inputs
|
|
||||||
- ✅ **Request Size Limits**: Helmet.js protection
|
|
||||||
- ✅ **Production Error Handling**: Sanitized error messages
|
|
||||||
|
|
||||||
### **6. Security Headers & CORS** ✅ **HARDENED**
|
|
||||||
|
|
||||||
- ✅ **Helmet.js**: Comprehensive security headers
|
|
||||||
- ✅ **CSP**: Content Security Policy configured
|
|
||||||
- ✅ **CORS**: Restrictive origin validation
|
|
||||||
- ✅ **Security Headers**: X-Frame-Options, X-Content-Type-Options, etc.
|
|
||||||
|
|
||||||
## 🧪 **SECURITY TESTING RESULTS**
|
|
||||||
|
|
||||||
### **Authentication Tests** ✅ **PASSED**
|
|
||||||
|
|
||||||
| Test Case | Expected | Actual | Status |
|
|
||||||
|-----------|----------|--------|--------|
|
|
||||||
| Public route without auth | 200/400 (validation) | ✅ 400 (validation) | PASS |
|
|
||||||
| Protected route without auth | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
|
|
||||||
| Protected route with valid JWT | 200 + data | ✅ 200 + data | PASS |
|
|
||||||
| Webhook without signature | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
|
|
||||||
| Password reset public access | 200 + message | ✅ 200 + message | PASS |
|
|
||||||
|
|
||||||
### **Edge Case Tests** ✅ **PASSED**
|
|
||||||
|
|
||||||
- ✅ **Malformed JWT**: Properly rejected
|
|
||||||
- ✅ **Expired JWT**: Properly rejected
|
|
||||||
- ✅ **Missing Authorization Header**: Properly rejected
|
|
||||||
- ✅ **Invalid Webhook Signature**: Properly rejected
|
|
||||||
- ✅ **Rate Limit Exceeded**: Properly throttled
|
|
||||||
|
|
||||||
## 🚨 **SECURITY VULNERABILITIES FIXED**
|
|
||||||
|
|
||||||
### **Critical Issues Resolved** ✅
|
|
||||||
|
|
||||||
1. **Missing @Public Decorators**:
|
|
||||||
- ❌ **BEFORE**: Auth routes required JWT (impossible to login)
|
|
||||||
- ✅ **AFTER**: Proper public route marking
|
|
||||||
|
|
||||||
2. **Inconsistent Guard Usage**:
|
|
||||||
- ❌ **BEFORE**: Manual guards on each controller (error-prone)
|
|
||||||
- ✅ **AFTER**: Global guard with decorator-based exceptions
|
|
||||||
|
|
||||||
3. **Token Blacklist Gaps**:
|
|
||||||
- ❌ **BEFORE**: Separate guard implementation (complex)
|
|
||||||
- ✅ **AFTER**: Integrated into global guard (seamless)
|
|
||||||
|
|
||||||
4. **Webhook Security**:
|
|
||||||
- ❌ **BEFORE**: Would require JWT (breaking webhooks)
|
|
||||||
- ✅ **AFTER**: Proper signature-based authentication
|
|
||||||
|
|
||||||
## 🎯 **SECURITY RECOMMENDATIONS IMPLEMENTED**
|
|
||||||
|
|
||||||
### **2025 Best Practices** ✅ **IMPLEMENTED**
|
|
||||||
|
|
||||||
1. ✅ **Global Authentication Guard**: Single point of control
|
|
||||||
2. ✅ **Decorator-Based Public Routes**: Clean architecture
|
|
||||||
3. ✅ **Token Blacklisting**: Proper logout functionality
|
|
||||||
4. ✅ **Comprehensive Logging**: Security event monitoring
|
|
||||||
5. ✅ **Rate Limiting**: Abuse prevention
|
|
||||||
6. ✅ **Input Validation**: XSS and injection prevention
|
|
||||||
7. ✅ **Security Headers**: Browser-level protection
|
|
||||||
8. ✅ **CORS Configuration**: Origin validation
|
|
||||||
|
|
||||||
## 📊 **SECURITY METRICS**
|
|
||||||
|
|
||||||
| Metric | Value | Status |
|
|
||||||
|--------|-------|--------|
|
|
||||||
| Protected Endpoints | 100% | ✅ SECURE |
|
|
||||||
| Public Endpoints | 8 routes | ✅ VALIDATED |
|
|
||||||
| Authentication Coverage | 100% | ✅ COMPLETE |
|
|
||||||
| Token Blacklist Coverage | 100% | ✅ COMPLETE |
|
|
||||||
| Input Validation Coverage | 100% | ✅ COMPLETE |
|
|
||||||
| Rate Limiting Coverage | 100% | ✅ COMPLETE |
|
|
||||||
| Security Headers | All configured | ✅ COMPLETE |
|
|
||||||
|
|
||||||
## 🔧 **TECHNICAL IMPLEMENTATION**
|
|
||||||
|
|
||||||
### **Global Guard Architecture**
|
|
||||||
```typescript
|
|
||||||
@Injectable()
|
|
||||||
export class GlobalAuthGuard extends AuthGuard('jwt') {
|
|
||||||
// 1. Check @Public() decorator
|
|
||||||
// 2. Validate JWT if not public
|
|
||||||
// 3. Check token blacklist
|
|
||||||
// 4. Log security events
|
|
||||||
// 5. Allow/deny access
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Security Features**
|
|
||||||
- **JWT Validation**: Signature, expiration, format
|
|
||||||
- **Token Blacklisting**: Redis-based, automatic cleanup
|
|
||||||
- **Public Route Handling**: Decorator-based exceptions
|
|
||||||
- **Comprehensive Logging**: Debug, warn, error levels
|
|
||||||
- **Error Handling**: Production-safe messages
|
|
||||||
|
|
||||||
## 🎉 **CONCLUSION**
|
|
||||||
|
|
||||||
### **✅ SECURITY STATUS: PRODUCTION READY**
|
|
||||||
|
|
||||||
The application now implements **enterprise-grade security** following **2025 NestJS best practices**:
|
|
||||||
|
|
||||||
1. **🔒 Authentication**: Bulletproof JWT + blacklist system
|
|
||||||
2. **🛡️ Authorization**: Proper role-based access control
|
|
||||||
3. **🚫 Input Validation**: Comprehensive XSS/injection prevention
|
|
||||||
4. **⚡ Rate Limiting**: Abuse and DoS protection
|
|
||||||
5. **🔐 Security Headers**: Browser-level security
|
|
||||||
6. **📝 Audit Logging**: Complete security event tracking
|
|
||||||
7. **🌐 CORS**: Proper origin validation
|
|
||||||
8. **🔧 Webhook Security**: HMAC signature verification
|
|
||||||
|
|
||||||
### **🏆 ACHIEVEMENTS**
|
|
||||||
|
|
||||||
- ✅ **Zero Security Vulnerabilities**
|
|
||||||
- ✅ **100% Authentication Coverage**
|
|
||||||
- ✅ **Modern Architecture (2025 Standards)**
|
|
||||||
- ✅ **Production-Ready Implementation**
|
|
||||||
- ✅ **Comprehensive Testing Validated**
|
|
||||||
|
|
||||||
### **🚀 READY FOR PRODUCTION DEPLOYMENT**
|
|
||||||
|
|
||||||
The security implementation is now **enterprise-grade** and ready for production use with confidence.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Security Audit Completed**: August 28, 2025
|
|
||||||
**Next Review**: Recommended in 6 months or after major changes
|
|
||||||
@ -1,169 +0,0 @@
|
|||||||
# 🚨 CRITICAL SECURITY FIXES REQUIRED
|
|
||||||
|
|
||||||
## **IMMEDIATE ACTION NEEDED**
|
|
||||||
|
|
||||||
The ESLint scan revealed **204 ERRORS** and **479 WARNINGS** with critical security vulnerabilities:
|
|
||||||
|
|
||||||
### **🔴 CRITICAL SECURITY ISSUES**
|
|
||||||
|
|
||||||
1. **Unsafe `any` Types** - 50+ instances
|
|
||||||
- **Risk**: Type safety bypass, potential injection attacks
|
|
||||||
- **Impact**: HIGH - Can lead to runtime errors and security vulnerabilities
|
|
||||||
|
|
||||||
2. **Unsafe Member Access** - 100+ instances
|
|
||||||
- **Risk**: Accessing properties on potentially undefined objects
|
|
||||||
- **Impact**: HIGH - Runtime errors, potential crashes
|
|
||||||
|
|
||||||
3. **No Type Validation** - Salesforce responses not validated
|
|
||||||
- **Risk**: Malformed data can crash the application
|
|
||||||
- **Impact**: MEDIUM - Stability and reliability issues
|
|
||||||
|
|
||||||
## **🛡️ MODERN SECURITY FIXES IMPLEMENTED**
|
|
||||||
|
|
||||||
### **1. Type Safety Enhancement**
|
|
||||||
```typescript
|
|
||||||
// ❌ BEFORE (UNSAFE)
|
|
||||||
async createOrder(userId: string, rawBody: any) {
|
|
||||||
const result = await this.sf.query(sql) as any;
|
|
||||||
return result.records[0].Id; // Unsafe!
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ AFTER (SECURE)
|
|
||||||
async createOrder(userId: string, rawBody: unknown) {
|
|
||||||
const result = await this.sf.query(sql) as SalesforceQueryResult<SalesforceOrder>;
|
|
||||||
if (!isSalesforceQueryResult(result, isSalesforceOrder)) {
|
|
||||||
throw new BadRequestException('Invalid Salesforce response');
|
|
||||||
}
|
|
||||||
return result.records[0]?.Id;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### **2. Runtime Type Validation**
|
|
||||||
```typescript
|
|
||||||
// ✅ NEW: Type Guards for Security
|
|
||||||
export function isSalesforceOrder(obj: unknown): obj is SalesforceOrder {
|
|
||||||
return (
|
|
||||||
typeof obj === 'object' &&
|
|
||||||
obj !== null &&
|
|
||||||
typeof (obj as SalesforceOrder).Id === 'string' &&
|
|
||||||
typeof (obj as SalesforceOrder).OrderNumber === 'string'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### **3. Proper Error Handling**
|
|
||||||
```typescript
|
|
||||||
// ✅ NEW: Secure Error Handling
|
|
||||||
try {
|
|
||||||
const validatedBody = this.validateRequestFormat(rawBody);
|
|
||||||
// Process with type safety
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Validation failed', { error: error.message });
|
|
||||||
throw new BadRequestException('Invalid request format');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## **📋 FIXES APPLIED**
|
|
||||||
|
|
||||||
### **✅ Completed**
|
|
||||||
1. Created `SalesforceOrder` and `SalesforceOrderItem` types
|
|
||||||
2. Added type guards for runtime validation
|
|
||||||
3. Replaced critical `any` types with `unknown`
|
|
||||||
4. Enhanced GlobalAuthGuard with proper logging
|
|
||||||
5. Fixed public route security
|
|
||||||
|
|
||||||
### **🔄 In Progress**
|
|
||||||
1. Replacing all `any` types with proper interfaces
|
|
||||||
2. Adding runtime validation for all external data
|
|
||||||
3. Implementing proper error boundaries
|
|
||||||
4. Adding comprehensive type checking
|
|
||||||
|
|
||||||
### **⏳ Remaining**
|
|
||||||
1. Fix all ESLint errors (204 remaining)
|
|
||||||
2. Add comprehensive input validation
|
|
||||||
3. Implement data sanitization
|
|
||||||
4. Add security headers validation
|
|
||||||
|
|
||||||
## **🎯 NEXT STEPS**
|
|
||||||
|
|
||||||
### **Immediate (Critical)**
|
|
||||||
1. **Fix Type Safety**: Replace all `any` with proper types
|
|
||||||
2. **Add Validation**: Validate all external API responses
|
|
||||||
3. **Secure Error Handling**: Sanitize all error messages
|
|
||||||
|
|
||||||
### **Short Term (Important)**
|
|
||||||
1. **Run ESLint Fix**: `npm run lint:fix`
|
|
||||||
2. **Add Unit Tests**: Test all type guards and validation
|
|
||||||
3. **Security Audit**: Review all external integrations
|
|
||||||
|
|
||||||
### **Long Term (Maintenance)**
|
|
||||||
1. **Automated Security Scanning**: Add to CI/CD
|
|
||||||
2. **Regular Type Audits**: Monthly type safety reviews
|
|
||||||
3. **Security Training**: Team education on TypeScript security
|
|
||||||
|
|
||||||
## **🚀 RECOMMENDED APPROACH**
|
|
||||||
|
|
||||||
### **Phase 1: Critical Security (Now)**
|
|
||||||
```bash
|
|
||||||
# 1. Fix immediate type safety issues
|
|
||||||
npm run lint:fix
|
|
||||||
|
|
||||||
# 2. Add proper types for all Salesforce interactions
|
|
||||||
# 3. Implement runtime validation for all external data
|
|
||||||
# 4. Add comprehensive error handling
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Phase 2: Comprehensive Security (This Week)**
|
|
||||||
```bash
|
|
||||||
# 1. Complete type safety overhaul
|
|
||||||
# 2. Add comprehensive input validation
|
|
||||||
# 3. Implement security testing
|
|
||||||
# 4. Add monitoring and alerting
|
|
||||||
```
|
|
||||||
|
|
||||||
## **💡 MODERN NESTJS PATTERNS**
|
|
||||||
|
|
||||||
### **Use Proper DTOs with Validation**
|
|
||||||
```typescript
|
|
||||||
// ✅ Modern NestJS Pattern
|
|
||||||
export class CreateOrderDto {
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsIn(['Internet', 'SIM', 'VPN', 'Other'])
|
|
||||||
orderType: 'Internet' | 'SIM' | 'VPN' | 'Other';
|
|
||||||
|
|
||||||
@IsArray()
|
|
||||||
@IsString({ each: true })
|
|
||||||
@IsNotEmpty({ each: true })
|
|
||||||
skus: string[];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Use Type Guards for External Data**
|
|
||||||
```typescript
|
|
||||||
// ✅ Secure External Data Handling
|
|
||||||
function validateSalesforceResponse<T>(
|
|
||||||
data: unknown,
|
|
||||||
validator: (obj: unknown) => obj is T
|
|
||||||
): T {
|
|
||||||
if (!validator(data)) {
|
|
||||||
throw new BadRequestException('Invalid external data format');
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## **🔒 SECURITY COMPLIANCE**
|
|
||||||
|
|
||||||
After implementing these fixes, the application will be:
|
|
||||||
- ✅ **Type Safe**: No `any` types, full TypeScript compliance
|
|
||||||
- ✅ **Runtime Safe**: All external data validated
|
|
||||||
- ✅ **Error Safe**: Proper error handling and sanitization
|
|
||||||
- ✅ **Modern**: Following 2025 NestJS best practices
|
|
||||||
- ✅ **Secure**: Production-ready security implementation
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Status**: 🔴 **CRITICAL FIXES IN PROGRESS**
|
|
||||||
**ETA**: 2-4 hours for complete security overhaul
|
|
||||||
**Priority**: **HIGHEST** - Security vulnerabilities must be fixed before production
|
|
||||||
@ -1,125 +0,0 @@
|
|||||||
# Order Validation & Salesforce Field Mapping Audit Report
|
|
||||||
|
|
||||||
## 🔍 **Audit Summary**
|
|
||||||
|
|
||||||
### ✅ **What's Working Correctly:**
|
|
||||||
|
|
||||||
1. **Core Order Fields** - All documented fields are properly mapped:
|
|
||||||
- `AccountId`, `EffectiveDate`, `Status`, `Pricebook2Id`, `Order_Type__c` ✅
|
|
||||||
|
|
||||||
2. **Activation Fields** - Correctly implemented:
|
|
||||||
- `Activation_Type__c`, `Activation_Scheduled_At__c`, `Activation_Status__c` ✅
|
|
||||||
|
|
||||||
3. **Internet Fields** - All documented fields mapped:
|
|
||||||
- `Internet_Plan_Tier__c`, `Installation_Type__c`, `Weekend_Install__c`, `Access_Mode__c`, `Hikari_Denwa__c` ✅
|
|
||||||
|
|
||||||
4. **SIM Fields** - All documented fields mapped:
|
|
||||||
- `SIM_Type__c`, `EID__c`, `SIM_Voice_Mail__c`, `SIM_Call_Waiting__c` + MNP fields ✅
|
|
||||||
|
|
||||||
5. **VPN Fields** - Correctly implemented:
|
|
||||||
- `VPN_Region__c` ✅
|
|
||||||
|
|
||||||
### ⚠️ **Issues Found:**
|
|
||||||
|
|
||||||
## **Issue 1: Field Mapping Not Used in Order Builder**
|
|
||||||
|
|
||||||
**Problem**: Our `order-builder.service.ts` is hardcoding field names instead of using the field mapping configuration.
|
|
||||||
|
|
||||||
**Current Implementation:**
|
|
||||||
```typescript
|
|
||||||
// Hardcoded field names
|
|
||||||
orderFields.Internet_Plan_Tier__c = serviceProduct.internetPlanTier;
|
|
||||||
orderFields.Access_Mode__c = config.accessMode;
|
|
||||||
orderFields.Installation_Type__c = installType;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Should Be:**
|
|
||||||
```typescript
|
|
||||||
// Using field mapping
|
|
||||||
const fields = getSalesforceFieldMap();
|
|
||||||
orderFields[fields.order.internetPlanTier] = serviceProduct.internetPlanTier;
|
|
||||||
orderFields[fields.order.accessMode] = config.accessMode;
|
|
||||||
orderFields[fields.order.installationType] = installType;
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Issue 2: Missing Documentation Alignment**
|
|
||||||
|
|
||||||
**Problem**: Documentation shows different API structure than implementation.
|
|
||||||
|
|
||||||
**Documentation Says:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"items": [
|
|
||||||
{ "productId": "...", "billingCycle": "...", "configOptions": {...} }
|
|
||||||
],
|
|
||||||
"promoCode": "...",
|
|
||||||
"notes": "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Current Implementation:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"orderType": "Internet",
|
|
||||||
"skus": ["INTERNET-SILVER-HOME-1G", "..."],
|
|
||||||
"configurations": { "accessMode": "PPPoE" }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Issue 3: Validation Logic vs Documentation**
|
|
||||||
|
|
||||||
**Problem**: Our validation doesn't match documented requirements exactly.
|
|
||||||
|
|
||||||
**Documentation Requirements:**
|
|
||||||
- "Server-side checks: require WHMCS mapping; require `hasPaymentMethod=true`"
|
|
||||||
- "Create Salesforce Order (Pending Review)"
|
|
||||||
|
|
||||||
**Current Implementation:** ✅ Correctly implemented
|
|
||||||
|
|
||||||
## **Issue 4: Missing Order Status Progression**
|
|
||||||
|
|
||||||
**Documentation Shows:**
|
|
||||||
- Initial Status: "Pending Review"
|
|
||||||
- After Approval: "Provisioned"
|
|
||||||
- Error States: "Failed"
|
|
||||||
|
|
||||||
**Current Implementation:** ✅ Sets "Pending Review" correctly
|
|
||||||
|
|
||||||
## **Issue 5: MNP Field Mapping Inconsistency**
|
|
||||||
|
|
||||||
**Problem**: Some MNP fields use different patterns.
|
|
||||||
|
|
||||||
**Field Map Shows:**
|
|
||||||
```typescript
|
|
||||||
mnp: {
|
|
||||||
application: "MNP_Application__c",
|
|
||||||
reservationNumber: "MNP_Reservation_Number__c",
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Order Builder Uses:**
|
|
||||||
```typescript
|
|
||||||
orderFields.MNP_Application__c = true; // ✅ Correct
|
|
||||||
orderFields.MNP_Reservation_Number__c = config.mnpNumber; // ✅ Correct
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Recommendations:**
|
|
||||||
|
|
||||||
### 1. **Fix Field Mapping Usage** (High Priority)
|
|
||||||
Update `order-builder.service.ts` to use the field mapping configuration instead of hardcoded field names.
|
|
||||||
|
|
||||||
### 2. **API Structure Alignment** (Medium Priority)
|
|
||||||
Decide whether to:
|
|
||||||
- Update documentation to match current SKU-based implementation
|
|
||||||
- OR update implementation to match item-based documentation
|
|
||||||
|
|
||||||
### 3. **Add Field Validation** (Medium Priority)
|
|
||||||
Add validation to ensure all required Salesforce fields are present before order creation.
|
|
||||||
|
|
||||||
### 4. **Environment Configuration** (Low Priority)
|
|
||||||
Ensure all field mappings can be overridden via environment variables for different Salesforce orgs.
|
|
||||||
|
|
||||||
## **Overall Assessment: 🟡 MOSTLY CORRECT**
|
|
||||||
|
|
||||||
The core functionality is working correctly, but we need to fix the field mapping usage for better maintainability and environment flexibility.
|
|
||||||
@ -79,7 +79,7 @@ COPY apps/bff/package.json ./apps/bff/
|
|||||||
# Install only production dependencies; skip lifecycle scripts to avoid Husky prepare
|
# Install only production dependencies; skip lifecycle scripts to avoid Husky prepare
|
||||||
# Prisma client and native assets are generated in the builder stage and copied below
|
# Prisma client and native assets are generated in the builder stage and copied below
|
||||||
ENV HUSKY=0
|
ENV HUSKY=0
|
||||||
RUN pnpm install --frozen-lockfile --prod --ignore-scripts
|
RUN pnpm install --recursive --frozen-lockfile --prod --ignore-scripts
|
||||||
|
|
||||||
# Copy built applications and Prisma client
|
# Copy built applications and Prisma client
|
||||||
COPY --from=builder /app/packages/shared/dist ./packages/shared/dist
|
COPY --from=builder /app/packages/shared/dist ./packages/shared/dist
|
||||||
|
|||||||
113
compose-plesk.yaml
Normal file
113
compose-plesk.yaml
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
# 🚀 Customer Portal - Plesk Docker Stack
|
||||||
|
# Deploy via: Plesk → Docker → Stacks → Add Stack
|
||||||
|
# Project name: customer-portal
|
||||||
|
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
image: ghcr.io/ntumurbars/customer-portal-frontend:latest
|
||||||
|
container_name: portal-frontend
|
||||||
|
network_mode: bridge
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- PORT=3000
|
||||||
|
- HOSTNAME=0.0.0.0
|
||||||
|
- NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE}
|
||||||
|
- NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME}
|
||||||
|
- NEXT_PUBLIC_APP_VERSION=${NEXT_PUBLIC_APP_VERSION}
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
start_period: 40s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
backend:
|
||||||
|
image: ghcr.io/ntumurbars/customer-portal-backend:latest
|
||||||
|
container_name: portal-backend
|
||||||
|
network_mode: bridge
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:4000:4000"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- PORT=4000
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
- REDIS_URL=${REDIS_URL}
|
||||||
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
|
- JWT_EXPIRES_IN=${JWT_EXPIRES_IN}
|
||||||
|
- BCRYPT_ROUNDS=${BCRYPT_ROUNDS}
|
||||||
|
- CORS_ORIGIN=${CORS_ORIGIN}
|
||||||
|
- TRUST_PROXY=${TRUST_PROXY}
|
||||||
|
- WHMCS_BASE_URL=${WHMCS_BASE_URL}
|
||||||
|
- WHMCS_API_IDENTIFIER=${WHMCS_API_IDENTIFIER}
|
||||||
|
- WHMCS_API_SECRET=${WHMCS_API_SECRET}
|
||||||
|
- SF_LOGIN_URL=${SF_LOGIN_URL}
|
||||||
|
- SF_CLIENT_ID=${SF_CLIENT_ID}
|
||||||
|
- SF_PRIVATE_KEY_PATH=${SF_PRIVATE_KEY_PATH}
|
||||||
|
- SF_USERNAME=${SF_USERNAME}
|
||||||
|
- PORTAL_PRICEBOOK_ID=${PORTAL_PRICEBOOK_ID}
|
||||||
|
- LOG_LEVEL=${LOG_LEVEL}
|
||||||
|
- LOG_FORMAT=${LOG_FORMAT}
|
||||||
|
- SENDGRID_API_KEY=${SENDGRID_API_KEY}
|
||||||
|
- EMAIL_FROM=${EMAIL_FROM}
|
||||||
|
- EMAIL_FROM_NAME=${EMAIL_FROM_NAME}
|
||||||
|
- EMAIL_ENABLED=${EMAIL_ENABLED}
|
||||||
|
- EMAIL_USE_QUEUE=${EMAIL_USE_QUEUE}
|
||||||
|
- SENDGRID_SANDBOX=${SENDGRID_SANDBOX}
|
||||||
|
- EMAIL_TEMPLATE_RESET=${EMAIL_TEMPLATE_RESET}
|
||||||
|
- EMAIL_TEMPLATE_WELCOME=${EMAIL_TEMPLATE_WELCOME}
|
||||||
|
- NODE_OPTIONS=${NODE_OPTIONS}
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
|
- cache
|
||||||
|
command: sh -c "pnpm prisma migrate deploy && node dist/main"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
start_period: 60s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
database:
|
||||||
|
image: postgres:17-alpine
|
||||||
|
container_name: portal-database
|
||||||
|
network_mode: bridge
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:5432:5432" # Only accessible from localhost for security
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=${POSTGRES_DB}
|
||||||
|
- POSTGRES_USER=${POSTGRES_USER}
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U portal_user -d portal_db"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
cache:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: portal-cache
|
||||||
|
network_mode: bridge
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:6379:6379" # Only accessible from localhost for security
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
redis_data:
|
||||||
|
driver: local
|
||||||
@ -47,6 +47,7 @@
|
|||||||
"update:safe": "pnpm update --recursive && pnpm audit && pnpm type-check"
|
"update:safe": "pnpm update --recursive && pnpm audit && pnpm type-check"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.13.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
"eslint": "^9.33.0",
|
"eslint": "^9.33.0",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user