From f4d4cb0ab057a5e104f8715517dc18dc6ae8a88c Mon Sep 17 00:00:00 2001 From: barsa Date: Mon, 1 Dec 2025 15:30:04 +0900 Subject: [PATCH] Update pnpm-lock.yaml, Dockerfile, and environment configurations - Added nestjs-pino dependency to pnpm-lock.yaml for improved logging capabilities. - Updated Dockerfile to include a custom entrypoint script for better container management. - Modified health controllers to include @Public() decorator for public access. - Cleaned up environment variable samples for clarity and added new variables for Freebit integration. - Adjusted Content Security Policy in next.config.mjs to allow inline scripts/styles for Next.js compatibility. - Refactored ReissueSimModal to specify simType during eSIM reissue requests. --- apps/bff/Dockerfile | 10 +- apps/bff/scripts/docker-entrypoint.sh | 35 +++ apps/bff/src/core/config/env.validation.ts | 3 - .../core/health/queue-health.controller.ts | 2 + .../catalog/catalog-health.controller.ts | 2 + .../src/modules/health/health.controller.ts | 2 + apps/portal/next.config.mjs | 26 +- .../components/ReissueSimModal.tsx | 3 +- env/dev.env.sample | 138 ++++++++++ env/portal-backend.env.sample | 240 +++++++++++------- env/portal-frontend.env.sample | 14 +- packages/validation/package.json | 2 +- pnpm-lock.yaml | 6 +- portal-backend.20251201-1dafa73.tar.sha256 | 1 + portal-frontend.20251201-1dafa73.tar.sha256 | 1 + scripts/plesk/build-images.sh | 16 +- 16 files changed, 377 insertions(+), 124 deletions(-) create mode 100755 apps/bff/scripts/docker-entrypoint.sh create mode 100644 env/dev.env.sample create mode 100644 portal-backend.20251201-1dafa73.tar.sha256 create mode 100644 portal-frontend.20251201-1dafa73.tar.sha256 diff --git a/apps/bff/Dockerfile b/apps/bff/Dockerfile index 65f8c0a2..80197d03 100644 --- a/apps/bff/Dockerfile +++ b/apps/bff/Dockerfile @@ -111,6 +111,10 @@ RUN pnpm dlx prisma@6.14.0 generate # Strip build toolchain to shrink image RUN apk del --no-cache python3 make g++ pkgconfig && rm -rf /root/.cache /var/cache/apk/* +# Copy entrypoint script +COPY apps/bff/scripts/docker-entrypoint.sh /app/docker-entrypoint.sh +RUN chmod +x /app/docker-entrypoint.sh + # Create non-root user for security [[memory:6689308]] RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nestjs @@ -122,7 +126,7 @@ RUN mkdir -p /app/secrets /app/logs && \ # Switch to non-root user USER nestjs -# Expose port (required for Plesk port mapping) +# Expose port EXPOSE 4000 # Environment variables @@ -136,6 +140,6 @@ WORKDIR /app/apps/bff HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:4000/health || exit 1 -# Use dumb-init for proper signal handling in containers -ENTRYPOINT ["dumb-init", "--"] +# Use dumb-init for proper signal handling, then entrypoint script +ENTRYPOINT ["dumb-init", "--", "/app/docker-entrypoint.sh"] CMD ["node", "dist/main.js"] diff --git a/apps/bff/scripts/docker-entrypoint.sh b/apps/bff/scripts/docker-entrypoint.sh new file mode 100755 index 00000000..8a6578c8 --- /dev/null +++ b/apps/bff/scripts/docker-entrypoint.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -e + +# ============================================================================= +# Docker Entrypoint Script +# ============================================================================= +# Handles runtime setup before starting the application: +# - Decodes SF_PRIVATE_KEY_BASE64 to file if provided +# - Runs Prisma migrations if DATABASE_URL is set +# ============================================================================= + +echo "🚀 Starting Customer Portal Backend..." + +# Handle Salesforce private key from base64 environment variable +if [ -n "$SF_PRIVATE_KEY_BASE64" ]; then + echo "📝 Decoding Salesforce private key..." + mkdir -p /app/secrets + echo "$SF_PRIVATE_KEY_BASE64" | base64 -d > /app/secrets/sf-private.key + chmod 600 /app/secrets/sf-private.key + export SF_PRIVATE_KEY_PATH=/app/secrets/sf-private.key + echo "✅ Salesforce private key configured" +fi + +# Run database migrations if enabled +if [ "$RUN_MIGRATIONS" = "true" ] && [ -n "$DATABASE_URL" ]; then + echo "🗄️ Running database migrations..." + npx prisma migrate deploy --schema=/app/apps/bff/prisma/schema.prisma + echo "✅ Migrations complete" +fi + +echo "🌐 Starting server on port ${PORT:-4000}..." + +# Execute the main command (node dist/main.js) +exec "$@" + diff --git a/apps/bff/src/core/config/env.validation.ts b/apps/bff/src/core/config/env.validation.ts index 5b1f9718..33a1da72 100644 --- a/apps/bff/src/core/config/env.validation.ts +++ b/apps/bff/src/core/config/env.validation.ts @@ -53,17 +53,14 @@ export const envSchema = z.object({ WHMCS_BASE_URL: z.string().url().optional(), WHMCS_API_IDENTIFIER: z.string().optional(), WHMCS_API_SECRET: z.string().optional(), - WHMCS_WEBHOOK_SECRET: z.string().optional(), WHMCS_DEV_BASE_URL: z.string().url().optional(), WHMCS_DEV_API_IDENTIFIER: z.string().optional(), WHMCS_DEV_API_SECRET: z.string().optional(), - WHMCS_DEV_WEBHOOK_SECRET: z.string().optional(), SF_LOGIN_URL: z.string().url().optional(), SF_USERNAME: z.string().optional(), SF_CLIENT_ID: z.string().optional(), SF_PRIVATE_KEY_PATH: z.string().optional(), - SF_WEBHOOK_SECRET: z.string().optional(), SF_AUTH_TIMEOUT_MS: z.coerce.number().int().positive().default(30000), SF_TOKEN_TTL_MS: z.coerce.number().int().positive().default(720000), SF_TOKEN_REFRESH_BUFFER_MS: z.coerce.number().int().positive().default(60000), diff --git a/apps/bff/src/core/health/queue-health.controller.ts b/apps/bff/src/core/health/queue-health.controller.ts index 48d7e5ce..92c2cfa7 100644 --- a/apps/bff/src/core/health/queue-health.controller.ts +++ b/apps/bff/src/core/health/queue-health.controller.ts @@ -1,8 +1,10 @@ import { Controller, Get } from "@nestjs/common"; import { WhmcsRequestQueueService } from "@bff/core/queue/services/whmcs-request-queue.service"; import { SalesforceRequestQueueService } from "@bff/core/queue/services/salesforce-request-queue.service"; +import { Public } from "@bff/modules/auth/decorators/public.decorator"; @Controller("health/queues") +@Public() export class QueueHealthController { constructor( private readonly whmcsQueue: WhmcsRequestQueueService, diff --git a/apps/bff/src/modules/catalog/catalog-health.controller.ts b/apps/bff/src/modules/catalog/catalog-health.controller.ts index 5c072c70..4a3176fe 100644 --- a/apps/bff/src/modules/catalog/catalog-health.controller.ts +++ b/apps/bff/src/modules/catalog/catalog-health.controller.ts @@ -1,5 +1,6 @@ import { Controller, Get } from "@nestjs/common"; import { CatalogCacheService, CatalogCacheSnapshot } from "./services/catalog-cache.service"; +import { Public } from "@bff/modules/auth/decorators/public.decorator"; interface CatalogCacheHealthResponse { timestamp: string; @@ -13,6 +14,7 @@ interface CatalogCacheHealthResponse { } @Controller("health/catalog") +@Public() export class CatalogHealthController { constructor(private readonly catalogCache: CatalogCacheService) {} diff --git a/apps/bff/src/modules/health/health.controller.ts b/apps/bff/src/modules/health/health.controller.ts index 1145405a..244b5f33 100644 --- a/apps/bff/src/modules/health/health.controller.ts +++ b/apps/bff/src/modules/health/health.controller.ts @@ -2,8 +2,10 @@ import { Controller, Get, Inject } from "@nestjs/common"; import { Logger } from "nestjs-pino"; import { PrismaService } from "@bff/infra/database/prisma.service"; import { CacheService } from "@bff/infra/cache/cache.service"; +import { Public } from "@bff/modules/auth/decorators/public.decorator"; @Controller("health") +@Public() export class HealthController { constructor( private readonly prisma: PrismaService, diff --git a/apps/portal/next.config.mjs b/apps/portal/next.config.mjs index 4a55bbe3..b194732e 100644 --- a/apps/portal/next.config.mjs +++ b/apps/portal/next.config.mjs @@ -77,21 +77,21 @@ const nextConfig = { key: "X-XSS-Protection", value: "1; mode=block", }, - // Content Security Policy - development-friendly + // Content Security Policy - allows Next.js inline scripts/styles { key: "Content-Security-Policy", - value: - process.env.NODE_ENV === "development" - ? "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https: http://localhost:* ws://localhost:*; frame-ancestors 'none';" - : [ - "default-src 'self'", - "script-src 'self'", - "style-src 'self'", - "img-src 'self' data: https:", - "font-src 'self' data:", - "connect-src 'self'", - "frame-ancestors 'none'", - ].join("; "), + value: [ + "default-src 'self'", + // Next.js requires unsafe-inline for hydration scripts + "script-src 'self' 'unsafe-inline' 'unsafe-eval'", + // Next.js/Tailwind requires unsafe-inline for styles + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: https:", + "font-src 'self' data:", + // Allow API connections + "connect-src 'self' https:", + "frame-ancestors 'none'", + ].join("; "), }, ], }, diff --git a/apps/portal/src/features/sim-management/components/ReissueSimModal.tsx b/apps/portal/src/features/sim-management/components/ReissueSimModal.tsx index 653fd01b..5e0a5d1c 100644 --- a/apps/portal/src/features/sim-management/components/ReissueSimModal.tsx +++ b/apps/portal/src/features/sim-management/components/ReissueSimModal.tsx @@ -68,7 +68,8 @@ export function ReissueSimModal({ setValidationError(null); setSubmitting(true); try { - await simActionsService.reissueEsim(String(subscriptionId), { + await simActionsService.reissueSim(String(subscriptionId), { + simType: "esim", newEid: newEid.trim() || undefined, }); onSuccess(); diff --git a/env/dev.env.sample b/env/dev.env.sample new file mode 100644 index 00000000..4d830232 --- /dev/null +++ b/env/dev.env.sample @@ -0,0 +1,138 @@ +# ============================================================================= +# Customer Portal - Development Environment +# ============================================================================= +# Copy to .env in project root for local development +# This file configures both frontend and backend for dev mode +# ============================================================================= + +# ----------------------------------------------------------------------------- +# Core +# ----------------------------------------------------------------------------- + +NODE_ENV=development +APP_NAME=customer-portal-bff +APP_BASE_URL=http://localhost:3000 + +# Ports +BFF_PORT=4000 +NEXT_PORT=3000 + +# ----------------------------------------------------------------------------- +# Frontend (Next.js) - Browser-exposed variables +# ----------------------------------------------------------------------------- + +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 + +# ----------------------------------------------------------------------------- +# Database & Cache +# ----------------------------------------------------------------------------- + +DATABASE_URL=postgresql://dev:dev@localhost:5432/portal_dev?schema=public +REDIS_URL=redis://localhost:6379 + +# ----------------------------------------------------------------------------- +# Security +# ----------------------------------------------------------------------------- + +# Generate with: openssl rand -base64 32 +JWT_SECRET=HjHsUyTE3WhPn5N07iSvurdV4hk2VEkIuN+lIflHhVQ= +JWT_EXPIRES_IN=7d +BCRYPT_ROUNDS=12 + +CORS_ORIGIN=http://localhost:3000 +TRUST_PROXY=false + +# Redis token handling (relaxed for dev) +AUTH_ALLOW_REDIS_TOKEN_FAILOPEN=false +AUTH_REQUIRE_REDIS_FOR_TOKENS=false +AUTH_MAINTENANCE_MODE=false + +# ----------------------------------------------------------------------------- +# Rate Limiting (relaxed for dev) +# ----------------------------------------------------------------------------- + +RATE_LIMIT_TTL=60 +RATE_LIMIT_LIMIT=1000 +AUTH_RATE_LIMIT_TTL=900 +AUTH_RATE_LIMIT_LIMIT=10 + +# Show detailed validation errors in dev +EXPOSE_VALIDATION_ERRORS=true + +# ----------------------------------------------------------------------------- +# WHMCS Integration (dev credentials) +# ----------------------------------------------------------------------------- + +WHMCS_DEV_BASE_URL= +WHMCS_DEV_API_IDENTIFIER= +WHMCS_DEV_API_SECRET= +WHMCS_QUEUE_CONCURRENCY=15 +WHMCS_QUEUE_TIMEOUT_MS=30000 + +# ----------------------------------------------------------------------------- +# Salesforce Integration +# ----------------------------------------------------------------------------- + +SF_LOGIN_URL= +SF_CLIENT_ID= +SF_PRIVATE_KEY_PATH=./secrets/sf-private.key +SF_USERNAME= + +# Timeouts +SF_AUTH_TIMEOUT_MS=30000 +SF_TOKEN_TTL_MS=720000 +SF_TOKEN_REFRESH_BUFFER_MS=60000 + +# Queue throttling +SF_QUEUE_CONCURRENCY=15 +SF_QUEUE_TIMEOUT_MS=30000 +SF_QUEUE_LONG_RUNNING_TIMEOUT_MS=600000 + +# Platform Events +SF_EVENTS_ENABLED=true +SF_EVENTS_REPLAY=LATEST +SF_PUBSUB_ENDPOINT=api.pubsub.salesforce.com:7443 + +# ----------------------------------------------------------------------------- +# Freebit SIM Management +# ----------------------------------------------------------------------------- + +FREEBIT_BASE_URL= +FREEBIT_OEM_ID= +FREEBIT_OEM_KEY= +FREEBIT_TIMEOUT=30000 + +# ----------------------------------------------------------------------------- +# Email +# ----------------------------------------------------------------------------- + +EMAIL_ENABLED=true +EMAIL_USE_QUEUE=true +EMAIL_FROM=no-reply@asolutions.co.jp +EMAIL_FROM_NAME=Assist Solutions + +# ----------------------------------------------------------------------------- +# Portal Configuration +# ----------------------------------------------------------------------------- + +PORTAL_PRICEBOOK_ID=01sTL000008eLVlYAM +PORTAL_PRICEBOOK_NAME=Portal + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- + +LOG_LEVEL=debug +DISABLE_HTTP_LOGGING=false + +# ----------------------------------------------------------------------------- +# Local Dev Bypasses (NEVER enable in production!) +# ----------------------------------------------------------------------------- + +DISABLE_CSRF=false +DISABLE_RATE_LIMIT=false +DISABLE_ACCOUNT_LOCKING=false + diff --git a/env/portal-backend.env.sample b/env/portal-backend.env.sample index 761c1f70..24f6f9b9 100644 --- a/env/portal-backend.env.sample +++ b/env/portal-backend.env.sample @@ -1,144 +1,190 @@ +# ============================================================================= +# Customer Portal Backend (BFF) - Production Environment +# ============================================================================= +# Copy to portal-backend.env and configure values marked with CHANGE_ME +# Variables with defaults can be omitted unless you need to override them +# ============================================================================= + +# ----------------------------------------------------------------------------- +# REQUIRED - Must be configured +# ----------------------------------------------------------------------------- + NODE_ENV=production - -# App -APP_BASE_URL=https://asolutions.jp -BFF_PORT=4000 -APP_NAME=customer-portal-bff - -# Database (PostgreSQL) -POSTGRES_DB=portal_prod -POSTGRES_USER=portal -POSTGRES_PASSWORD=CHANGE_ME DATABASE_URL=postgresql://portal:CHANGE_ME@database:5432/portal_prod?schema=public +JWT_SECRET=CHANGE_ME_GENERATE_WITH_openssl_rand_base64_32 -# Cache (Redis) -REDIS_URL=redis://cache:6379/0 -AUTH_ALLOW_REDIS_TOKEN_FAILOPEN=false -# Redis-required token flow (when enabled, tokens require Redis to be available) -AUTH_REQUIRE_REDIS_FOR_TOKENS=false -# Maintenance mode for authentication service -AUTH_MAINTENANCE_MODE=false -AUTH_MAINTENANCE_MESSAGE=Authentication service is temporarily unavailable for maintenance. Please try again later. +# ----------------------------------------------------------------------------- +# Core Application +# ----------------------------------------------------------------------------- +APP_NAME=customer-portal-bff +APP_BASE_URL=https://your-domain.com +BFF_PORT=4000 + +# ----------------------------------------------------------------------------- # Security -JWT_SECRET=CHANGE_ME +# ----------------------------------------------------------------------------- + +# Redis cache (required for production token management) +REDIS_URL=redis://cache:6379/0 + +# JWT configuration JWT_EXPIRES_IN=7d BCRYPT_ROUNDS=12 -# CSRF Protection -CSRF_TOKEN_EXPIRY=3600000 -CSRF_SECRET_KEY=CHANGE_ME_AT_LEAST_32_CHARACTERS_LONG -CSRF_COOKIE_NAME=csrf-secret -CSRF_HEADER_NAME=X-CSRF-Token - -# CORS / Proxy -CORS_ORIGIN=https://asolutions.jp +# CORS - set to your frontend domain +CORS_ORIGIN=https://your-domain.com TRUST_PROXY=true -# Rate Limiting (optional; defaults shown - ttl values in seconds) +# CSRF Protection (generate secret: openssl rand -base64 32) +CSRF_SECRET_KEY=CHANGE_ME_GENERATE_WITH_openssl_rand_base64_32 + +# Redis token handling +AUTH_ALLOW_REDIS_TOKEN_FAILOPEN=false +AUTH_REQUIRE_REDIS_FOR_TOKENS=false + +# Maintenance mode (enable during deployments) +AUTH_MAINTENANCE_MODE=false + +# ----------------------------------------------------------------------------- +# Rate Limiting +# ----------------------------------------------------------------------------- + RATE_LIMIT_TTL=60 RATE_LIMIT_LIMIT=100 AUTH_RATE_LIMIT_TTL=900 AUTH_RATE_LIMIT_LIMIT=3 -AUTH_REFRESH_RATE_LIMIT_TTL=300 -AUTH_REFRESH_RATE_LIMIT_LIMIT=10 LOGIN_RATE_LIMIT_TTL=900 LOGIN_RATE_LIMIT_LIMIT=5 -LOGIN_CAPTCHA_AFTER_ATTEMPTS=3 -SIGNUP_RATE_LIMIT_TTL=900 -SIGNUP_RATE_LIMIT_LIMIT=5 -PASSWORD_RESET_RATE_LIMIT_TTL=900 -PASSWORD_RESET_RATE_LIMIT_LIMIT=5 -# CAPTCHA Configuration +# CAPTCHA (optional - set provider to 'turnstile' or 'hcaptcha' to enable) AUTH_CAPTCHA_PROVIDER=none AUTH_CAPTCHA_SECRET= -AUTH_CAPTCHA_THRESHOLD=0 -AUTH_CAPTCHA_ALWAYS_ON=false -# Validation error visibility (set true to show field-level errors to clients) +# Hide validation errors from clients in production EXPOSE_VALIDATION_ERRORS=false -# WHMCS Credentials +# ----------------------------------------------------------------------------- +# WHMCS Integration +# ----------------------------------------------------------------------------- + WHMCS_BASE_URL=https://accounts.asolutions.co.jp WHMCS_API_IDENTIFIER= WHMCS_API_SECRET= -# Optional webhook security for WHMCS webhooks WHMCS_WEBHOOK_SECRET= -# Salesforce Credentials + +# Queue throttling +WHMCS_QUEUE_CONCURRENCY=15 +WHMCS_QUEUE_TIMEOUT_MS=30000 + +# ----------------------------------------------------------------------------- +# Salesforce Integration +# ----------------------------------------------------------------------------- + SF_LOGIN_URL=https://asolutions.my.salesforce.com SF_CLIENT_ID= -SF_PRIVATE_KEY_PATH=/app/secrets/sf-private.key SF_USERNAME= +SF_PRIVATE_KEY_PATH=/app/secrets/sf-private.key SF_WEBHOOK_SECRET= -# Salesforce Authentication Timeouts (in milliseconds) -SF_AUTH_TIMEOUT_MS=30000 -SF_TOKEN_TTL_MS=720000 -SF_TOKEN_REFRESH_BUFFER_MS=60000 -# Queue Throttling Configuration -WHMCS_QUEUE_CONCURRENCY=15 -WHMCS_QUEUE_INTERVAL_CAP=300 -WHMCS_QUEUE_TIMEOUT_MS=30000 +# Queue throttling SF_QUEUE_CONCURRENCY=15 -SF_QUEUE_LONG_RUNNING_CONCURRENCY=22 -SF_QUEUE_INTERVAL_CAP=600 SF_QUEUE_TIMEOUT_MS=30000 SF_QUEUE_LONG_RUNNING_TIMEOUT_MS=600000 -# Salesforce Platform Events (Provisioning) +# Platform Events SF_EVENTS_ENABLED=true -SF_CATALOG_EVENT_CHANNEL=/event/Product_and_Pricebook_Change__e -SF_ACCOUNT_EVENT_CHANNEL=/event/Account_Internet_Eligibility_Update__e SF_EVENTS_REPLAY=LATEST -SF_PUBSUB_NUM_REQUESTED=25 -SF_PUBSUB_QUEUE_MAX=100 SF_PUBSUB_ENDPOINT=api.pubsub.salesforce.com:7443 -# Salesforce Change Data Capture (CDC) for Catalog Cache Invalidation -# These use /data/ prefix for built-in CDC events (no setup needed in Salesforce) -SF_CATALOG_PRODUCT_CDC_CHANNEL=/data/Product2ChangeEvent -SF_CATALOG_PRICEBOOKENTRY_CDC_CHANNEL=/data/PricebookEntryChangeEvent -# Optional: Platform Event for account eligibility updates (requires Salesforce setup) -SF_ACCOUNT_ELIGIBILITY_CHANNEL=/event/Account_Internet_Eligibility_Update__e +# ----------------------------------------------------------------------------- +# Freebit SIM Management +# ----------------------------------------------------------------------------- -# Salesforce Change Data Capture (CDC) for Order Cache Invalidation -# These use /data/ prefix for built-in CDC events (no setup needed in Salesforce) -# Smart filtering: Only invalidates cache for customer-facing field changes, NOT internal fulfillment fields -SF_ORDER_CDC_CHANNEL=/data/OrderChangeEvent -SF_ORDER_ITEM_CDC_CHANNEL=/data/OrderItemChangeEvent - -# Salesforce Pricing -PORTAL_PRICEBOOK_ID= - -# Logging -LOG_LEVEL=info -LOG_FORMAT=json - -# Email (SendGrid) -SENDGRID_API_KEY= -EMAIL_FROM=no-reply@asolutions.jp -EMAIL_FROM_NAME=Assist Solutions -EMAIL_ENABLED=true -EMAIL_USE_QUEUE=true -SENDGRID_SANDBOX=false -EMAIL_TEMPLATE_RESET= -EMAIL_TEMPLATE_WELCOME= - -# Freebit (SIM management; optional) FREEBIT_BASE_URL=https://i1.mvno.net/emptool/api FREEBIT_OEM_ID=PASI FREEBIT_OEM_KEY= FREEBIT_TIMEOUT=30000 -FREEBIT_RETRY_ATTEMPTS=3 -FREEBIT_DETAILS_ENDPOINT=/master/getAcnt/ -# Node Options +# ----------------------------------------------------------------------------- +# Email (SendGrid) +# ----------------------------------------------------------------------------- + +EMAIL_ENABLED=true +EMAIL_FROM=no-reply@asolutions.jp +EMAIL_FROM_NAME=Assist Solutions +SENDGRID_API_KEY= +SENDGRID_SANDBOX=false + +# ----------------------------------------------------------------------------- +# Portal Configuration +# ----------------------------------------------------------------------------- + +PORTAL_PRICEBOOK_ID= +PORTAL_PRICEBOOK_NAME=Portal + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- + +LOG_LEVEL=info + +# ----------------------------------------------------------------------------- +# Node Runtime +# ----------------------------------------------------------------------------- + NODE_OPTIONS=--max-old-space-size=512 -# NOTE: Frontend (Next.js) uses a separate env file (portal-frontend.env) -# Do not include NEXT_PUBLIC_* variables here. -# Salesforce Account Portal Flags -ACCOUNT_PORTAL_STATUS_FIELD=Portal_Status__c -ACCOUNT_PORTAL_STATUS_SOURCE_FIELD=Portal_Registration_Source__c -ACCOUNT_PORTAL_LAST_SIGNED_IN_FIELD=Portal_Last_SignIn__c + +# ============================================================================= +# ADVANCED CONFIGURATION (rarely need to change) +# ============================================================================= +# The following variables have sensible defaults and only need to be set +# if your Salesforce org uses non-standard field API names. +# Uncomment and modify only if needed. +# ============================================================================= + +# --- Salesforce Field Mappings - Account --- +# ACCOUNT_INTERNET_ELIGIBILITY_FIELD=Internet_Eligibility__c +# ACCOUNT_CUSTOMER_NUMBER_FIELD=SF_Account_No__c +# ACCOUNT_PORTAL_STATUS_FIELD=Portal_Status__c +# ACCOUNT_PORTAL_STATUS_SOURCE_FIELD=Portal_Registration_Source__c +# ACCOUNT_PORTAL_LAST_SIGNED_IN_FIELD=Portal_Last_SignIn__c + +# --- Salesforce Field Mappings - Product --- +# PRODUCT_SKU_FIELD=StockKeepingUnit +# PRODUCT_PORTAL_CATEGORY_FIELD=Product2Categories1__c +# PRODUCT_PORTAL_CATALOG_FIELD=Portal_Catalog__c +# PRODUCT_PORTAL_ACCESSIBLE_FIELD=Portal_Accessible__c +# PRODUCT_ITEM_CLASS_FIELD=Item_Class__c +# PRODUCT_BILLING_CYCLE_FIELD=Billing_Cycle__c +# PRODUCT_WHMCS_PRODUCT_ID_FIELD=WH_Product_ID__c +# PRODUCT_WHMCS_PRODUCT_NAME_FIELD=WH_Product_Name__c +# PRODUCT_INTERNET_PLAN_TIER_FIELD=Internet_Plan_Tier__c +# PRODUCT_INTERNET_OFFERING_TYPE_FIELD=Internet_Offering_Type__c +# PRODUCT_DISPLAY_ORDER_FIELD=Catalog_Order__c +# PRODUCT_BUNDLED_ADDON_FIELD=Bundled_Addon__c +# PRODUCT_IS_BUNDLED_ADDON_FIELD=Is_Bundled_Addon__c +# PRODUCT_SIM_DATA_SIZE_FIELD=SIM_Data_Size__c +# PRODUCT_SIM_PLAN_TYPE_FIELD=SIM_Plan_Type__c +# PRODUCT_SIM_HAS_FAMILY_DISCOUNT_FIELD=SIM_Has_Family_Discount__c +# PRODUCT_VPN_REGION_FIELD=VPN_Region__c + +# --- Salesforce Field Mappings - Order --- +# ORDER_TYPE_FIELD=Type +# ORDER_ACTIVATION_TYPE_FIELD=Activation_Type__c +# ORDER_ACTIVATION_SCHEDULED_AT_FIELD=Activation_Scheduled_At__c +# ORDER_ACTIVATION_STATUS_FIELD=Activation_Status__c +# ORDER_SIM_TYPE_FIELD=SIM_Type__c +# ORDER_EID_FIELD=EID__c +# ORDER_MNP_APPLICATION_FIELD=MNP_Application__c +# ORDER_MNP_RESERVATION_FIELD=MNP_Reservation_Number__c +# ORDER_MNP_EXPIRY_FIELD=MNP_Expiry_Date__c +# ORDER_MNP_PHONE_FIELD=MNP_Phone_Number__c +# ORDER_WHMCS_ORDER_ID_FIELD=WHMCS_Order_ID__c + +# --- Salesforce CDC Channels (Change Data Capture) --- +# SF_CATALOG_PRODUCT_CDC_CHANNEL=/data/Product2ChangeEvent +# SF_CATALOG_PRICEBOOKENTRY_CDC_CHANNEL=/data/PricebookEntryChangeEvent +# SF_ORDER_CDC_CHANNEL=/data/OrderChangeEvent +# SF_ORDER_ITEM_CDC_CHANNEL=/data/OrderItemChangeEvent diff --git a/env/portal-frontend.env.sample b/env/portal-frontend.env.sample index 8d70310a..8cbe014c 100644 --- a/env/portal-frontend.env.sample +++ b/env/portal-frontend.env.sample @@ -1,8 +1,18 @@ +# ============================================================================= +# Customer Portal Frontend (Next.js) - Production Environment +# ============================================================================= +# Copy to portal-frontend.env +# Note: NEXT_PUBLIC_* variables are embedded at build time +# ============================================================================= + NODE_ENV=production -# Frontend (Next.js) — public runtime vars only +# Application name shown in UI NEXT_PUBLIC_APP_NAME=Assist Solutions Portal NEXT_PUBLIC_APP_VERSION=1.0.0 -# Same-origin API behind Plesk proxy rule + +# API endpoint - use /api for same-origin requests behind reverse proxy NEXT_PUBLIC_API_BASE=/api +# Disable React Query devtools in production +NEXT_PUBLIC_ENABLE_DEVTOOLS=false diff --git a/packages/validation/package.json b/packages/validation/package.json index 91f0077c..e4b3a66a 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -35,6 +35,7 @@ "dependencies": { "@customer-portal/domain": "workspace:*", "@nestjs/common": "^11.1.6", + "nestjs-pino": "^4.4.0", "nestjs-zod": "^5.0.1", "zod": "^4.1.9" }, @@ -58,7 +59,6 @@ "jest": "^30.0.5", "@types/jest": "^30.0.0", "nestjs-zod": "^5.0.1", - "nestjs-pino": "^4.4.0", "express": "^5.1.0", "@types/express": "^5.0.3" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bcfa3433..f21439ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -382,6 +382,9 @@ importers: '@nestjs/common': specifier: ^11.1.6 version: 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + nestjs-pino: + specifier: ^4.4.0 + version: 4.4.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@10.5.0)(pino@9.9.5)(rxjs@7.8.2) nestjs-zod: specifier: ^5.0.1 version: 5.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.1.9) @@ -404,9 +407,6 @@ importers: jest: specifier: ^30.0.5 version: 30.1.3(@types/node@24.3.1)(ts-node@10.9.2(@types/node@24.3.1)(typescript@5.9.2)) - nestjs-pino: - specifier: ^4.4.0 - version: 4.4.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@10.5.0)(pino@9.9.5)(rxjs@7.8.2) react: specifier: 19.1.1 version: 19.1.1 diff --git a/portal-backend.20251201-1dafa73.tar.sha256 b/portal-backend.20251201-1dafa73.tar.sha256 new file mode 100644 index 00000000..443d54e3 --- /dev/null +++ b/portal-backend.20251201-1dafa73.tar.sha256 @@ -0,0 +1 @@ +d56f8408ed1de76e225abd6a8ddb741c32f96102f03b0caf8fef089a30de317b /home/barsa/projects/customer_portal/customer-portal/portal-backend.20251201-1dafa73.tar diff --git a/portal-frontend.20251201-1dafa73.tar.sha256 b/portal-frontend.20251201-1dafa73.tar.sha256 new file mode 100644 index 00000000..9de57c5f --- /dev/null +++ b/portal-frontend.20251201-1dafa73.tar.sha256 @@ -0,0 +1 @@ +4510c9159622868d3cbbf8212274e08bb374e541876406ba7d0f2d7d4d93983a /home/barsa/projects/customer_portal/customer-portal/portal-frontend.20251201-1dafa73.tar diff --git a/scripts/plesk/build-images.sh b/scripts/plesk/build-images.sh index ef369cb9..7d680c72 100755 --- a/scripts/plesk/build-images.sh +++ b/scripts/plesk/build-images.sh @@ -116,11 +116,17 @@ if [[ "${SAVE_TARS}" -eq 1 ]]; then docker save -o "$FRONT_TAR_TAGGED" "${IMAGE_FRONTEND_NAME}:${IMAGE_TAG}" docker save -o "$BACK_TAR_TAGGED" "${IMAGE_BACKEND_NAME}:${IMAGE_TAG}" + log "🔐 Generating checksums for integrity verification..." + sha256sum "$FRONT_TAR_TAGGED" > "${FRONT_TAR_TAGGED}.sha256" + sha256sum "$BACK_TAR_TAGGED" > "${BACK_TAR_TAGGED}.sha256" + log "✅ Wrote:" echo " - $FRONT_TAR_LATEST" echo " - $BACK_TAR_LATEST" echo " - $FRONT_TAR_TAGGED" echo " - $BACK_TAR_TAGGED" + echo " - ${FRONT_TAR_TAGGED}.sha256" + echo " - ${BACK_TAR_TAGGED}.sha256" fi if [[ -n "${PUSH_REMOTE}" ]]; then @@ -142,5 +148,13 @@ if [[ -n "${PUSH_REMOTE}" ]]; then docker push "$BE_REMOTE_TAGGED" fi -log "🎉 Done. Use config/docker/compose-plesk.yaml in Plesk and upload the .tar files." +log "🎉 Done!" +log "" +log "Next steps:" +log " 1. Upload .tar files to your Plesk server" +log " 2. Load images: docker load -i portal-frontend.${IMAGE_TAG}.tar" +log " 3. Verify checksums: sha256sum -c portal-frontend.${IMAGE_TAG}.tar.sha256" +log " 4. Update Portainer stack with new image tag: ${IMAGE_TAG}" +log "" +log "See docker/portainer/PORTAINER-GUIDE.md for detailed instructions."