diff --git a/package.json b/package.json index 6508560814..26face1df8 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,9 @@ "build": "npm run generate-news && docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", + "deploy:cloudflare:dev": "zsh src/_scripts/_deploy-cloudflare.zsh --env DEV", + "deploy:cloudflare:test": "zsh src/_scripts/_deploy-cloudflare.zsh --env TEST", + "deploy:cloudflare:prod": "zsh src/_scripts/_deploy-cloudflare.zsh --env PROD", "clear": "docusaurus clear", "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", diff --git a/src/_scripts/_build_and_deploy.zsh b/src/_scripts/_build_and_deploy.zsh new file mode 100755 index 0000000000..b3d23e1e3c --- /dev/null +++ b/src/_scripts/_build_and_deploy.zsh @@ -0,0 +1,151 @@ +#!/usr/bin/env zsh + +# Build and Deploy Script +# This script builds the project and optionally exports drawio files and deploys to Cloudflare + +# Exit on error +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="${0:a:h}" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +# Function to print colored messages +print_message() { + local color=$1 + local message=$2 + echo "${color}${message}${NC}" +} + +print_message "$BLUE" "╔═══════════════════════════════════════════════════════════╗" +print_message "$BLUE" "║ Build and Deploy Script ║" +print_message "$BLUE" "╚═══════════════════════════════════════════════════════════╝" + +# Parse command line arguments +DRAWIO_EXPORT=false +PROD_DEPLOY=false + +print_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " -drawio:true Export drawio files before build and deploy to TEST" + echo " -prod:true Deploy to PROD (skips drawio export)" + echo " -h, --help Show this help message" + echo "" + echo "Important:" + echo " - drawio:true and prod:true cannot be used together" + echo " - prod:true always skips drawio export" + echo "" + echo "Examples:" + echo " $0 # Clear cache, build (no deployment)" + echo " $0 -drawio:true # Clear, export drawios, build, deploy to TEST" + echo " $0 -prod:true # Clear, build, deploy to PROD (no drawio export)" +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -drawio:true) + DRAWIO_EXPORT=true + shift + ;; + -prod:true) + PROD_DEPLOY=true + shift + ;; + -h|--help) + print_usage + exit 0 + ;; + *) + print_message "$RED" "Unknown option: $1" + print_usage + exit 1 + ;; + esac +done + +# Validate that drawio and prod are not used together +if [ "$DRAWIO_EXPORT" = true ] && [ "$PROD_DEPLOY" = true ]; then + print_message "$RED" "✗ Error: -drawio:true and -prod:true cannot be used together" + print_message "$YELLOW" " These options are mutually exclusive" + exit 1 +fi + +# Change to project root +cd "$PROJECT_ROOT" + +# Step 1: Clear cache +print_message "$BLUE" "\n→ Step 1: Clearing Docusaurus cache..." +npm run clear + +if [ $? -eq 0 ]; then + print_message "$GREEN" "✓ Cache cleared successfully" +else + print_message "$RED" "✗ Failed to clear cache" + exit 1 +fi + +# Step 2: Export drawios (only if drawio:true and NOT prod:true) +if [ "$DRAWIO_EXPORT" = true ]; then + print_message "$BLUE" "\n→ Step 2: Exporting drawio files..." + node "$SCRIPT_DIR/_export-drawios.js" + + if [ $? -eq 0 ]; then + print_message "$GREEN" "✓ Drawio files exported successfully" + else + print_message "$RED" "✗ Failed to export drawio files" + exit 1 + fi +else + print_message "$YELLOW" "\n→ Step 2: Skipping drawio export" +fi + +# Step 3: Build +print_message "$BLUE" "\n→ Step 3: Building project..." +npm run build + +if [ $? -eq 0 ]; then + print_message "$GREEN" "✓ Build completed successfully" +else + print_message "$RED" "✗ Build failed" + exit 1 +fi + +# Step 4: Deploy +if [ "$DRAWIO_EXPORT" = true ]; then + print_message "$BLUE" "\n→ Step 4: Deploying to TEST environment..." + npm run deploy:cloudflare:test + + if [ $? -eq 0 ]; then + print_message "$GREEN" "✓ Deployed to TEST successfully" + else + print_message "$RED" "✗ Deployment to TEST failed" + exit 1 + fi +elif [ "$PROD_DEPLOY" = true ]; then + print_message "$BLUE" "\n→ Step 4: Deploying to PROD environment..." + npm run deploy:cloudflare:prod + + if [ $? -eq 0 ]; then + print_message "$GREEN" "✓ Deployed to PROD successfully" + else + print_message "$RED" "✗ Deployment to PROD failed" + exit 1 + fi +else + print_message "$YELLOW" "\n→ Step 4: No deployment requested" +fi + +# Final success message +print_message "$GREEN" "\n════════════════════════════════════════════════════════════" +print_message "$GREEN" "✓ All tasks completed successfully!" +print_message "$GREEN" "════════════════════════════════════════════════════════════" diff --git a/src/_scripts/_deploy-cloudflare.zsh b/src/_scripts/_deploy-cloudflare.zsh new file mode 100755 index 0000000000..52e37350cc --- /dev/null +++ b/src/_scripts/_deploy-cloudflare.zsh @@ -0,0 +1,189 @@ +#!/usr/bin/env zsh + +# Cloudflare Pages Deployment Script +# This script deploys the build folder to Cloudflare Pages using Wrangler CLI + +# Exit on error +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="${0:a:h}" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +BUILD_DIR="$PROJECT_ROOT/build" + +# Function to print colored messages +print_message() { + local color=$1 + local message=$2 + echo "${color}${message}${NC}" +} + +print_message "$BLUE" "╔═══════════════════════════════════════════════════════════╗" +print_message "$BLUE" "║ Cloudflare Pages Deployment Script ║" +print_message "$BLUE" "╚═══════════════════════════════════════════════════════════╝" + +# Check if build directory exists +if [ ! -d "$BUILD_DIR" ]; then + print_message "$RED" "✗ Error: Build directory not found at $BUILD_DIR" + print_message "$YELLOW" " Please run 'npm run build' first to create the build directory." + exit 1 +fi + +print_message "$GREEN" "✓ Build directory found: $BUILD_DIR" + +# Check if wrangler is available (via npx) +print_message "$BLUE" "\n→ Checking Wrangler availability..." +if ! command -v npx &> /dev/null; then + print_message "$RED" "✗ Error: npx not found. Please install Node.js and npm." + exit 1 +fi + +print_message "$GREEN" "✓ npx is available" + +# Check Wrangler version +print_message "$BLUE" "\n→ Checking Wrangler version..." +CURRENT_VERSION=$(npx wrangler --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) +LATEST_VERSION=$(npm view wrangler version 2>/dev/null) + +if [ -n "$CURRENT_VERSION" ] && [ -n "$LATEST_VERSION" ]; then + print_message "$BLUE" " Current: $CURRENT_VERSION" + if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then + print_message "$YELLOW" " ⚠ New version available: $LATEST_VERSION" + print_message "$YELLOW" " Consider updating with: npm install -g wrangler@latest" + print_message "$YELLOW" " Or clear npx cache: rm -rf ~/.npm/_npx" + else + print_message "$GREEN" " ✓ Using latest version" + fi +else + print_message "$YELLOW" " ⚠ Could not check for updates" +fi + +# Parse command line arguments +ENVIRONMENT="" +BRANCH="" +PROJECT_NAME="" + +# Environment configuration +declare -A ENV_BRANCHES +ENV_BRANCHES[DEV]="dev" +ENV_BRANCHES[TEST]="test" +ENV_BRANCHES[PROD]="prod" + +declare -A ENV_COLORS +ENV_COLORS[DEV]="$YELLOW" +ENV_COLORS[TEST]="$BLUE" +ENV_COLORS[PROD]="$GREEN" + +print_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --env, -e ENV Environment to deploy to: DEV, TEST, or PROD (required)" + echo " --project, -p NAME Specify project name (optional, uses cached value if not provided)" + echo " --help, -h Show this help message" + echo "" + echo "Environments:" + echo " DEV - Development environment (deploys to dev branch)" + echo " TEST - Test/Staging environment (deploys to test branch)" + echo " PROD - Production environment (deploys to main branch)" + echo "" + echo "Examples:" + echo " $0 --env DEV # Deploy to development" + echo " $0 -e TEST # Deploy to test/staging" + echo " $0 -e PROD # Deploy to production" + echo " $0 -e DEV -p my-site # Deploy to dev with specific project name" +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -e|--env) + ENVIRONMENT="${2:u}" # Convert to uppercase + shift 2 + ;; + -p|--project) + PROJECT_NAME="$2" + shift 2 + ;; + -h|--help) + print_usage + exit 0 + ;; + *) + print_message "$RED" "Unknown option: $1" + print_usage + exit 1 + ;; + esac +done + +# Validate environment +if [ -z "$ENVIRONMENT" ]; then + print_message "$RED" "✗ Error: Environment is required" + print_message "$YELLOW" " Please specify an environment: --env DEV, --env TEST, or --env PROD" + echo "" + print_usage + exit 1 +fi + +if [[ ! "$ENVIRONMENT" =~ ^(DEV|TEST|PROD)$ ]]; then + print_message "$RED" "✗ Error: Invalid environment '$ENVIRONMENT'" + print_message "$YELLOW" " Valid environments are: DEV, TEST, PROD" + exit 1 +fi + +# Set branch based on environment +BRANCH="${ENV_BRANCHES[$ENVIRONMENT]}" +ENV_COLOR="${ENV_COLORS[$ENVIRONMENT]}" + +# Check authentication +print_message "$BLUE" "\n→ Checking Cloudflare authentication..." +if npx wrangler whoami &> /dev/null; then + print_message "$GREEN" "✓ Already authenticated with Cloudflare" +else + print_message "$YELLOW" "⚠ Not authenticated with Cloudflare" + print_message "$BLUE" "→ Starting authentication flow..." + npx wrangler login +fi + +# Display environment information +print_message "$ENV_COLOR" "\n╔═══════════════════════════════════════════════════════════╗" +print_message "$ENV_COLOR" "║ Deploying to: $ENVIRONMENT Environment" +print_message "$ENV_COLOR" "║ Branch: $BRANCH" +print_message "$ENV_COLOR" "╚═══════════════════════════════════════════════════════════╝" + +# Build deployment command +DEPLOY_CMD="npx wrangler pages deploy \"$BUILD_DIR\"" + +if [ -n "$PROJECT_NAME" ]; then + DEPLOY_CMD="$DEPLOY_CMD --project-name=\"$PROJECT_NAME\"" +fi + +if [ -n "$BRANCH" ]; then + DEPLOY_CMD="$DEPLOY_CMD --branch=\"$BRANCH\"" +fi + +print_message "$BLUE" "\n→ Executing: $DEPLOY_CMD" +print_message "$BLUE" "════════════════════════════════════════════════════════════\n" + +# Execute deployment +eval $DEPLOY_CMD + +# Check if deployment was successful +if [ $? -eq 0 ]; then + print_message "$GREEN" "\n════════════════════════════════════════════════════════════" + print_message "$GREEN" "✓ Deployment completed successfully!" + print_message "$GREEN" "════════════════════════════════════════════════════════════" +else + print_message "$RED" "\n════════════════════════════════════════════════════════════" + print_message "$RED" "✗ Deployment failed!" + print_message "$RED" "════════════════════════════════════════════════════════════" + exit 1 +fi diff --git a/src/plugins/security-headers/csp-config.ts b/src/plugins/security-headers/csp-config.ts index d1516c4e79..4547a225ac 100644 --- a/src/plugins/security-headers/csp-config.ts +++ b/src/plugins/security-headers/csp-config.ts @@ -39,6 +39,7 @@ export const baseCSP: CSPDirectives = { "'unsafe-inline'", // REQUIRED: Docusaurus limitation - see header comment 'https://www.googletagmanager.com', 'https://www.google-analytics.com', + 'https://static.cloudflareinsights.com', ], 'style-src': [ "'self'", @@ -51,6 +52,7 @@ export const baseCSP: CSPDirectives = { 'https://www.google-analytics.com', 'https://architecture-center-auth.cfapps.eu10-005.hana.ondemand.com', 'https://architecture-validator-prod-ns1j6yoi-prod-arch-val-pipeline.cfapps.eu10-005.hana.ondemand.com', + 'https://cloudflareinsights.com', ], 'frame-src': ["'self'"], 'frame-ancestors': ["'none'"], @@ -72,6 +74,7 @@ export const strictCSP: CSPDirectives = { // 'nonce-{RANDOM}' will be added dynamically by SSR 'https://www.googletagmanager.com', 'https://www.google-analytics.com', + 'https://static.cloudflareinsights.com', ], 'style-src': [ "'self'", @@ -84,6 +87,7 @@ export const strictCSP: CSPDirectives = { 'https://www.google-analytics.com', 'https://architecture-center-auth.cfapps.eu10-005.hana.ondemand.com', 'https://architecture-validator-prod-ns1j6yoi-prod-arch-val-pipeline.cfapps.eu10-005.hana.ondemand.com', + 'https://cloudflareinsights.com', ], 'frame-src': ["'self'"], 'frame-ancestors': ["'none'"], diff --git a/src/plugins/security-headers/index.js b/src/plugins/security-headers/index.js index 5bc9b59c1c..246ad6895c 100644 --- a/src/plugins/security-headers/index.js +++ b/src/plugins/security-headers/index.js @@ -80,7 +80,7 @@ module.exports = function (_context, _options) { Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(), usb=(), bluetooth=() Strict-Transport-Security: max-age=31536000; includeSubDomains; preload - Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://www.google-analytics.com https://architecture-center-auth.cfapps.eu10-005.hana.ondemand.com https://architecture-validator-prod-ns1j6yoi-prod-arch-val-pipeline.cfapps.eu10-005.hana.ondemand.com; frame-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests; + Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://www.google-analytics.com https://architecture-center-auth.cfapps.eu10-005.hana.ondemand.com https://architecture-validator-prod-ns1j6yoi-prod-arch-val-pipeline.cfapps.eu10-005.hana.ondemand.com https://cloudflareinsights.com; frame-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests; `; const headersPath = path.join(outDir, '_headers'); @@ -120,7 +120,7 @@ module.exports = function (_context, _options) { }, { key: 'Content-Security-Policy', - value: "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://www.google-analytics.com https://architecture-center-auth.cfapps.eu10-005.hana.ondemand.com https://architecture-validator-prod-ns1j6yoi-prod-arch-val-pipeline.cfapps.eu10-005.hana.ondemand.com; frame-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;", + value: "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://www.google-analytics.com https://architecture-center-auth.cfapps.eu10-005.hana.ondemand.com https://architecture-validator-prod-ns1j6yoi-prod-arch-val-pipeline.cfapps.eu10-005.hana.ondemand.com https://cloudflareinsights.com; frame-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;", }, ], }, diff --git a/vercel.json b/vercel.json index 449bc76f97..f158240f40 100644 --- a/vercel.json +++ b/vercel.json @@ -29,7 +29,7 @@ }, { "key": "Content-Security-Policy", - "value": "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://www.google-analytics.com https://architecture-center-auth.cfapps.eu10.hana.ondemand.com; frame-src 'self' https:; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;" + "value": "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://www.google-analytics.com https://architecture-center-auth.cfapps.eu10.hana.ondemand.com https://cloudflareinsights.com; frame-src 'self' https:; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;" } ] }