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
|
# Strip build toolchain to shrink image
|
||||||
RUN apk del --no-cache python3 make g++ pkgconfig && rm -rf /root/.cache /var/cache/apk/*
|
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]]
|
# Create non-root user for security [[memory:6689308]]
|
||||||
RUN addgroup --system --gid 1001 nodejs && \
|
RUN addgroup --system --gid 1001 nodejs && \
|
||||||
adduser --system --uid 1001 nestjs
|
adduser --system --uid 1001 nestjs
|
||||||
@ -122,7 +126,7 @@ RUN mkdir -p /app/secrets /app/logs && \
|
|||||||
# Switch to non-root user
|
# Switch to non-root user
|
||||||
USER nestjs
|
USER nestjs
|
||||||
|
|
||||||
# Expose port (required for Plesk port mapping)
|
# Expose port
|
||||||
EXPOSE 4000
|
EXPOSE 4000
|
||||||
|
|
||||||
# Environment variables
|
# Environment variables
|
||||||
@ -136,6 +140,6 @@ WORKDIR /app/apps/bff
|
|||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||||
CMD wget --no-verbose --tries=1 --spider http://localhost:4000/health || exit 1
|
CMD wget --no-verbose --tries=1 --spider http://localhost:4000/health || exit 1
|
||||||
|
|
||||||
# Use dumb-init for proper signal handling in containers
|
# Use dumb-init for proper signal handling, then entrypoint script
|
||||||
ENTRYPOINT ["dumb-init", "--"]
|
ENTRYPOINT ["dumb-init", "--", "/app/docker-entrypoint.sh"]
|
||||||
CMD ["node", "dist/main.js"]
|
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_BASE_URL: z.string().url().optional(),
|
||||||
WHMCS_API_IDENTIFIER: z.string().optional(),
|
WHMCS_API_IDENTIFIER: z.string().optional(),
|
||||||
WHMCS_API_SECRET: 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_BASE_URL: z.string().url().optional(),
|
||||||
WHMCS_DEV_API_IDENTIFIER: z.string().optional(),
|
WHMCS_DEV_API_IDENTIFIER: z.string().optional(),
|
||||||
WHMCS_DEV_API_SECRET: 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_LOGIN_URL: z.string().url().optional(),
|
||||||
SF_USERNAME: z.string().optional(),
|
SF_USERNAME: z.string().optional(),
|
||||||
SF_CLIENT_ID: z.string().optional(),
|
SF_CLIENT_ID: z.string().optional(),
|
||||||
SF_PRIVATE_KEY_PATH: 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_AUTH_TIMEOUT_MS: z.coerce.number().int().positive().default(30000),
|
||||||
SF_TOKEN_TTL_MS: z.coerce.number().int().positive().default(720000),
|
SF_TOKEN_TTL_MS: z.coerce.number().int().positive().default(720000),
|
||||||
SF_TOKEN_REFRESH_BUFFER_MS: z.coerce.number().int().positive().default(60000),
|
SF_TOKEN_REFRESH_BUFFER_MS: z.coerce.number().int().positive().default(60000),
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import { Controller, Get } from "@nestjs/common";
|
import { Controller, Get } from "@nestjs/common";
|
||||||
import { WhmcsRequestQueueService } from "@bff/core/queue/services/whmcs-request-queue.service";
|
import { WhmcsRequestQueueService } from "@bff/core/queue/services/whmcs-request-queue.service";
|
||||||
import { SalesforceRequestQueueService } from "@bff/core/queue/services/salesforce-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")
|
@Controller("health/queues")
|
||||||
|
@Public()
|
||||||
export class QueueHealthController {
|
export class QueueHealthController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly whmcsQueue: WhmcsRequestQueueService,
|
private readonly whmcsQueue: WhmcsRequestQueueService,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Controller, Get } from "@nestjs/common";
|
import { Controller, Get } from "@nestjs/common";
|
||||||
import { CatalogCacheService, CatalogCacheSnapshot } from "./services/catalog-cache.service";
|
import { CatalogCacheService, CatalogCacheSnapshot } from "./services/catalog-cache.service";
|
||||||
|
import { Public } from "@bff/modules/auth/decorators/public.decorator";
|
||||||
|
|
||||||
interface CatalogCacheHealthResponse {
|
interface CatalogCacheHealthResponse {
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@ -13,6 +14,7 @@ interface CatalogCacheHealthResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Controller("health/catalog")
|
@Controller("health/catalog")
|
||||||
|
@Public()
|
||||||
export class CatalogHealthController {
|
export class CatalogHealthController {
|
||||||
constructor(private readonly catalogCache: CatalogCacheService) {}
|
constructor(private readonly catalogCache: CatalogCacheService) {}
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,10 @@ import { Controller, Get, Inject } from "@nestjs/common";
|
|||||||
import { Logger } from "nestjs-pino";
|
import { Logger } from "nestjs-pino";
|
||||||
import { PrismaService } from "@bff/infra/database/prisma.service";
|
import { PrismaService } from "@bff/infra/database/prisma.service";
|
||||||
import { CacheService } from "@bff/infra/cache/cache.service";
|
import { CacheService } from "@bff/infra/cache/cache.service";
|
||||||
|
import { Public } from "@bff/modules/auth/decorators/public.decorator";
|
||||||
|
|
||||||
@Controller("health")
|
@Controller("health")
|
||||||
|
@Public()
|
||||||
export class HealthController {
|
export class HealthController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
|
|||||||
@ -77,21 +77,21 @@ const nextConfig = {
|
|||||||
key: "X-XSS-Protection",
|
key: "X-XSS-Protection",
|
||||||
value: "1; mode=block",
|
value: "1; mode=block",
|
||||||
},
|
},
|
||||||
// Content Security Policy - development-friendly
|
// Content Security Policy - allows Next.js inline scripts/styles
|
||||||
{
|
{
|
||||||
key: "Content-Security-Policy",
|
key: "Content-Security-Policy",
|
||||||
value:
|
value: [
|
||||||
process.env.NODE_ENV === "development"
|
"default-src 'self'",
|
||||||
? "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';"
|
// Next.js requires unsafe-inline for hydration scripts
|
||||||
: [
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
|
||||||
"default-src 'self'",
|
// Next.js/Tailwind requires unsafe-inline for styles
|
||||||
"script-src 'self'",
|
"style-src 'self' 'unsafe-inline'",
|
||||||
"style-src 'self'",
|
"img-src 'self' data: https:",
|
||||||
"img-src 'self' data: https:",
|
"font-src 'self' data:",
|
||||||
"font-src 'self' data:",
|
// Allow API connections
|
||||||
"connect-src 'self'",
|
"connect-src 'self' https:",
|
||||||
"frame-ancestors 'none'",
|
"frame-ancestors 'none'",
|
||||||
].join("; "),
|
].join("; "),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -68,7 +68,8 @@ export function ReissueSimModal({
|
|||||||
setValidationError(null);
|
setValidationError(null);
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
await simActionsService.reissueEsim(String(subscriptionId), {
|
await simActionsService.reissueSim(String(subscriptionId), {
|
||||||
|
simType: "esim",
|
||||||
newEid: newEid.trim() || undefined,
|
newEid: newEid.trim() || undefined,
|
||||||
});
|
});
|
||||||
onSuccess();
|
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
|
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
|
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
|
# Core Application
|
||||||
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.
|
|
||||||
|
|
||||||
|
APP_NAME=customer-portal-bff
|
||||||
|
APP_BASE_URL=https://your-domain.com
|
||||||
|
BFF_PORT=4000
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
# Security
|
# Security
|
||||||
JWT_SECRET=CHANGE_ME
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Redis cache (required for production token management)
|
||||||
|
REDIS_URL=redis://cache:6379/0
|
||||||
|
|
||||||
|
# JWT configuration
|
||||||
JWT_EXPIRES_IN=7d
|
JWT_EXPIRES_IN=7d
|
||||||
BCRYPT_ROUNDS=12
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
# CSRF Protection
|
# CORS - set to your frontend domain
|
||||||
CSRF_TOKEN_EXPIRY=3600000
|
CORS_ORIGIN=https://your-domain.com
|
||||||
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
|
|
||||||
TRUST_PROXY=true
|
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_TTL=60
|
||||||
RATE_LIMIT_LIMIT=100
|
RATE_LIMIT_LIMIT=100
|
||||||
AUTH_RATE_LIMIT_TTL=900
|
AUTH_RATE_LIMIT_TTL=900
|
||||||
AUTH_RATE_LIMIT_LIMIT=3
|
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_TTL=900
|
||||||
LOGIN_RATE_LIMIT_LIMIT=5
|
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_PROVIDER=none
|
||||||
AUTH_CAPTCHA_SECRET=
|
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
|
EXPOSE_VALIDATION_ERRORS=false
|
||||||
|
|
||||||
# WHMCS Credentials
|
# -----------------------------------------------------------------------------
|
||||||
|
# WHMCS Integration
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
WHMCS_BASE_URL=https://accounts.asolutions.co.jp
|
WHMCS_BASE_URL=https://accounts.asolutions.co.jp
|
||||||
WHMCS_API_IDENTIFIER=
|
WHMCS_API_IDENTIFIER=
|
||||||
WHMCS_API_SECRET=
|
WHMCS_API_SECRET=
|
||||||
# Optional webhook security for WHMCS webhooks
|
|
||||||
WHMCS_WEBHOOK_SECRET=
|
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_LOGIN_URL=https://asolutions.my.salesforce.com
|
||||||
SF_CLIENT_ID=
|
SF_CLIENT_ID=
|
||||||
SF_PRIVATE_KEY_PATH=/app/secrets/sf-private.key
|
|
||||||
SF_USERNAME=
|
SF_USERNAME=
|
||||||
|
SF_PRIVATE_KEY_PATH=/app/secrets/sf-private.key
|
||||||
SF_WEBHOOK_SECRET=
|
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
|
# Queue throttling
|
||||||
WHMCS_QUEUE_CONCURRENCY=15
|
|
||||||
WHMCS_QUEUE_INTERVAL_CAP=300
|
|
||||||
WHMCS_QUEUE_TIMEOUT_MS=30000
|
|
||||||
SF_QUEUE_CONCURRENCY=15
|
SF_QUEUE_CONCURRENCY=15
|
||||||
SF_QUEUE_LONG_RUNNING_CONCURRENCY=22
|
|
||||||
SF_QUEUE_INTERVAL_CAP=600
|
|
||||||
SF_QUEUE_TIMEOUT_MS=30000
|
SF_QUEUE_TIMEOUT_MS=30000
|
||||||
SF_QUEUE_LONG_RUNNING_TIMEOUT_MS=600000
|
SF_QUEUE_LONG_RUNNING_TIMEOUT_MS=600000
|
||||||
|
|
||||||
# Salesforce Platform Events (Provisioning)
|
# Platform Events
|
||||||
SF_EVENTS_ENABLED=true
|
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_EVENTS_REPLAY=LATEST
|
||||||
SF_PUBSUB_NUM_REQUESTED=25
|
|
||||||
SF_PUBSUB_QUEUE_MAX=100
|
|
||||||
SF_PUBSUB_ENDPOINT=api.pubsub.salesforce.com:7443
|
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)
|
# Freebit SIM Management
|
||||||
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
|
|
||||||
|
|
||||||
# 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_BASE_URL=https://i1.mvno.net/emptool/api
|
||||||
FREEBIT_OEM_ID=PASI
|
FREEBIT_OEM_ID=PASI
|
||||||
FREEBIT_OEM_KEY=
|
FREEBIT_OEM_KEY=
|
||||||
FREEBIT_TIMEOUT=30000
|
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
|
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
|
# ADVANCED CONFIGURATION (rarely need to change)
|
||||||
ACCOUNT_PORTAL_STATUS_FIELD=Portal_Status__c
|
# =============================================================================
|
||||||
ACCOUNT_PORTAL_STATUS_SOURCE_FIELD=Portal_Registration_Source__c
|
# The following variables have sensible defaults and only need to be set
|
||||||
ACCOUNT_PORTAL_LAST_SIGNED_IN_FIELD=Portal_Last_SignIn__c
|
# 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
|
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_NAME=Assist Solutions Portal
|
||||||
NEXT_PUBLIC_APP_VERSION=1.0.0
|
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
|
NEXT_PUBLIC_API_BASE=/api
|
||||||
|
|
||||||
|
# Disable React Query devtools in production
|
||||||
|
NEXT_PUBLIC_ENABLE_DEVTOOLS=false
|
||||||
|
|||||||
@ -35,6 +35,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@customer-portal/domain": "workspace:*",
|
"@customer-portal/domain": "workspace:*",
|
||||||
"@nestjs/common": "^11.1.6",
|
"@nestjs/common": "^11.1.6",
|
||||||
|
"nestjs-pino": "^4.4.0",
|
||||||
"nestjs-zod": "^5.0.1",
|
"nestjs-zod": "^5.0.1",
|
||||||
"zod": "^4.1.9"
|
"zod": "^4.1.9"
|
||||||
},
|
},
|
||||||
@ -58,7 +59,6 @@
|
|||||||
"jest": "^30.0.5",
|
"jest": "^30.0.5",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"nestjs-zod": "^5.0.1",
|
"nestjs-zod": "^5.0.1",
|
||||||
"nestjs-pino": "^4.4.0",
|
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"@types/express": "^5.0.3"
|
"@types/express": "^5.0.3"
|
||||||
}
|
}
|
||||||
|
|||||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@ -382,6 +382,9 @@ importers:
|
|||||||
'@nestjs/common':
|
'@nestjs/common':
|
||||||
specifier: ^11.1.6
|
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)
|
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:
|
nestjs-zod:
|
||||||
specifier: ^5.0.1
|
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)
|
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:
|
jest:
|
||||||
specifier: ^30.0.5
|
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))
|
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:
|
react:
|
||||||
specifier: 19.1.1
|
specifier: 19.1.1
|
||||||
version: 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 "$FRONT_TAR_TAGGED" "${IMAGE_FRONTEND_NAME}:${IMAGE_TAG}"
|
||||||
docker save -o "$BACK_TAR_TAGGED" "${IMAGE_BACKEND_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:"
|
log "✅ Wrote:"
|
||||||
echo " - $FRONT_TAR_LATEST"
|
echo " - $FRONT_TAR_LATEST"
|
||||||
echo " - $BACK_TAR_LATEST"
|
echo " - $BACK_TAR_LATEST"
|
||||||
echo " - $FRONT_TAR_TAGGED"
|
echo " - $FRONT_TAR_TAGGED"
|
||||||
echo " - $BACK_TAR_TAGGED"
|
echo " - $BACK_TAR_TAGGED"
|
||||||
|
echo " - ${FRONT_TAR_TAGGED}.sha256"
|
||||||
|
echo " - ${BACK_TAR_TAGGED}.sha256"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${PUSH_REMOTE}" ]]; then
|
if [[ -n "${PUSH_REMOTE}" ]]; then
|
||||||
@ -142,5 +148,13 @@ if [[ -n "${PUSH_REMOTE}" ]]; then
|
|||||||
docker push "$BE_REMOTE_TAGGED"
|
docker push "$BE_REMOTE_TAGGED"
|
||||||
fi
|
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