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.
This commit is contained in:
parent
1dafa7334a
commit
f4d4cb0ab0
@ -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"]
|
||||
|
||||
35
apps/bff/scripts/docker-entrypoint.sh
Executable file
35
apps/bff/scripts/docker-entrypoint.sh
Executable file
@ -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 "$@"
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) {}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -77,19 +77,19 @@ 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';"
|
||||
: [
|
||||
value: [
|
||||
"default-src 'self'",
|
||||
"script-src 'self'",
|
||||
"style-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:",
|
||||
"connect-src 'self'",
|
||||
// Allow API connections
|
||||
"connect-src 'self' https:",
|
||||
"frame-ancestors 'none'",
|
||||
].join("; "),
|
||||
},
|
||||
|
||||
@ -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();
|
||||
|
||||
138
env/dev.env.sample
vendored
Normal file
138
env/dev.env.sample
vendored
Normal file
@ -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
|
||||
|
||||
240
env/portal-backend.env.sample
vendored
240
env/portal-backend.env.sample
vendored
@ -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
|
||||
|
||||
14
env/portal-frontend.env.sample
vendored
14
env/portal-frontend.env.sample
vendored
@ -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
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@ -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
|
||||
|
||||
1
portal-backend.20251201-1dafa73.tar.sha256
Normal file
1
portal-backend.20251201-1dafa73.tar.sha256
Normal file
@ -0,0 +1 @@
|
||||
d56f8408ed1de76e225abd6a8ddb741c32f96102f03b0caf8fef089a30de317b /home/barsa/projects/customer_portal/customer-portal/portal-backend.20251201-1dafa73.tar
|
||||
1
portal-frontend.20251201-1dafa73.tar.sha256
Normal file
1
portal-frontend.20251201-1dafa73.tar.sha256
Normal file
@ -0,0 +1 @@
|
||||
4510c9159622868d3cbbf8212274e08bb374e541876406ba7d0f2d7d4d93983a /home/barsa/projects/customer_portal/customer-portal/portal-frontend.20251201-1dafa73.tar
|
||||
@ -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."
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user