diff --git a/docker-compose.yml b/docker-compose.yml index 39bffeae..1404f670 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,8 @@ services: - CONTAINER_PATH=${CONTAINER_PATH} - CMS_DATA_PATH=${CMS_DATA_PATH} - AEM_TEMPLATES_DIR=${AEM_TEMPLATES_DIR:-templates} + extra_hosts: + - "host.docker.internal:host-gateway" networks: - migration-network security_opt: @@ -39,6 +41,16 @@ services: - CMS_DATA_PATH=${CMS_DATA_PATH} - NODE_BACKEND_API=${NODE_BACKEND_API:-http://migration-api:5001} - AEM_TEMPLATES_DIR=${AEM_TEMPLATES_DIR:-templates} + - CMS_LOCAL_PATH=${CMS_LOCAL_PATH:-} + - MYSQL_HOST=${MYSQL_HOST:-} + - MYSQL_USER=${MYSQL_USER:-} + - MYSQL_PASSWORD=${MYSQL_PASSWORD:-} + - MYSQL_DATABASE=${MYSQL_DATABASE:-} + - MYSQL_PORT=${MYSQL_PORT:-3306} + - DRUPAL_ASSETS_BASE_URL=${DRUPAL_ASSETS_BASE_URL:-} + - DRUPAL_ASSETS_PUBLIC_PATH=${DRUPAL_ASSETS_PUBLIC_PATH:-} + extra_hosts: + - "host.docker.internal:host-gateway" networks: - migration-network security_opt: diff --git a/fileUpdate.js b/fileUpdate.js index 7b26be4e..faba4c29 100644 --- a/fileUpdate.js +++ b/fileUpdate.js @@ -1,27 +1,12 @@ const fs = require('fs'); const path = require('path'); const { cliux, messageHandler } = require('@contentstack/cli-utilities'); + const isEmpty = (value) => value === null || value === undefined || (typeof value === 'object' && Object.keys(value).length === 0) || (typeof value === 'string' && value.trim().length === 0); -const config = { - plan: { - dropdown: { optionLimit: 100 } - }, - cmsType: null, - isLocalPath: true, - awsData: { - awsRegion: 'us-east-2', - awsAccessKeyId: '', - awsSecretAccessKey: '', - awsSessionToken: '', - bucketName: '', - bucketKey: '' - }, - localPath: null -}; -const configFilePath = path.resolve(path?.join?.('upload-api', 'src', 'config', 'index.ts')); +const envFilePath = path.resolve(path.join('upload-api', '.env')); const ensureDirectoryExists = (filePath) => { const dir = path.dirname(filePath); @@ -31,6 +16,31 @@ const ensureDirectoryExists = (filePath) => { } }; +const readEnvFile = () => { + const vars = {}; + if (fs.existsSync(envFilePath)) { + const content = fs.readFileSync(envFilePath, 'utf8'); + for (const line of content.split('\n')) { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + const eqIndex = trimmed.indexOf('='); + if (eqIndex > 0) { + vars[trimmed.substring(0, eqIndex)] = trimmed.substring(eqIndex + 1); + } + } + } + } + return vars; +}; + +const writeEnvFile = (newVars) => { + ensureDirectoryExists(envFilePath); + const existing = readEnvFile(); + const merged = { ...existing, ...newVars }; + const lines = Object.entries(merged).map(([k, v]) => `${k}=${v}`); + fs.writeFileSync(envFilePath, lines.join('\n') + '\n', 'utf8'); +}; + const inquireRequireFieldValidation = (input) => { if (isEmpty(input)) { return messageHandler.parse('Please enter the path'); @@ -41,80 +51,83 @@ const inquireRequireFieldValidation = (input) => { return true; }; -const typeSwitcher = async (type) => { - switch (type) { - case 'Aws S3': { - const awsData = { - awsRegion: await cliux.inquire({ - type: 'input', - message: 'Enter AWS Region', - name: 'awsRegion', - validate: inquireRequireFieldValidation - }), - awsAccessKeyId: await cliux.inquire({ - type: 'input', - message: 'Enter AWS Access Key Id', - name: 'awsAccessKeyId', - validate: inquireRequireFieldValidation - }), - awsSecretAccessKey: await cliux.inquire({ - type: 'input', - message: 'Enter AWS Secret Access Key', - name: 'awsSecretAccessKey', - validate: inquireRequireFieldValidation - }), - }; - const isSessionToken = await cliux.inquire({ - choices: ['yes', 'no'], - type: 'list', - name: 'isSessionToken', - message: 'Do you have a Session Token?' - }); - if (isSessionToken === 'yes') { - awsData.awsSessionToken = await cliux.inquire({ - type: 'input', - message: 'Enter AWS Session Token', - name: 'awsSessionToken', - validate: inquireRequireFieldValidation - }); - } - return awsData; - } - case 'Locale Path': { - return await cliux.inquire({ - type: 'input', - message: 'Enter file path', - name: 'filePath', - validate: inquireRequireFieldValidation - }); - } - default: - console.log('⚠️ Invalid type provided'); - return; - } -}; - const XMLMigration = async () => { const typeOfcms = await cliux.inquire({ - choices: ['sitecore', 'contentful', 'wordpress', 'aem'], + choices: ['sitecore', 'contentful', 'wordpress', 'aem', 'drupal'], type: 'list', name: 'value', message: 'Choose the option to proceed with your legacy CMS:' }); - const data = await typeSwitcher('Locale Path'); - if (typeof typeOfcms === 'string') { - config.cmsType = typeOfcms; - } else { + if (typeof typeOfcms !== 'string') { console.log('⚠️ Error: Expected a string for typeOfcms but got an object.'); + return; } - if (typeof data === 'string') { - config.localPath = data; + + const envVars = { CMS_TYPE: typeOfcms }; + + if (typeOfcms === 'drupal') { + console.log('\nDrupal uses a MySQL database connection. Please provide your database details:'); + + envVars.MYSQL_HOST = await cliux.inquire({ + type: 'input', + message: 'Enter MySQL Host', + name: 'mysqlHost', + validate: (input) => isEmpty(input) ? 'Please enter the MySQL host' : true + }); + envVars.MYSQL_USER = await cliux.inquire({ + type: 'input', + message: 'Enter MySQL User', + name: 'mysqlUser', + validate: (input) => isEmpty(input) ? 'Please enter the MySQL user' : true + }); + envVars.MYSQL_PASSWORD = await cliux.inquire({ + type: 'password', + message: 'Enter MySQL Password (can be empty)', + name: 'mysqlPassword' + }); + envVars.MYSQL_DATABASE = await cliux.inquire({ + type: 'input', + message: 'Enter MySQL Database Name', + name: 'mysqlDatabase', + validate: (input) => isEmpty(input) ? 'Please enter the MySQL database name' : true + }); + const portInput = await cliux.inquire({ + type: 'input', + message: 'Enter MySQL Port [3306]', + name: 'mysqlPort' + }); + envVars.MYSQL_PORT = portInput || '3306'; + + envVars.DRUPAL_ASSETS_BASE_URL = await cliux.inquire({ + type: 'input', + message: 'Enter Drupal Assets Base URL (e.g. https://example.com)', + name: 'assetsBaseUrl' + }); + envVars.DRUPAL_ASSETS_PUBLIC_PATH = await cliux.inquire({ + type: 'input', + message: 'Enter Drupal Assets Public Path (e.g. sites/default/files)', + name: 'assetsPublicPath' + }); + + envVars.CMS_LOCAL_PATH = 'sql'; } else { - console.log('⚠️ Error: Expected a string for localPath but got an object.'); + const localPath = await cliux.inquire({ + type: 'input', + message: 'Enter file path', + name: 'filePath', + validate: inquireRequireFieldValidation + }); + if (typeof localPath === 'string') { + envVars.CMS_LOCAL_PATH = localPath; + } else { + console.log('⚠️ Error: Expected a string for localPath but got an object.'); + return; + } } - ensureDirectoryExists(configFilePath); - fs.writeFileSync(configFilePath, `export default ${JSON.stringify(config, null, 2)};`, 'utf8'); + + writeEnvFile(envVars); + console.log(`✅ Configuration written to ${envFilePath}`); }; XMLMigration(); diff --git a/setup-docker.sh b/setup-docker.sh index 08afcb8f..3a259490 100755 --- a/setup-docker.sh +++ b/setup-docker.sh @@ -1,7 +1,7 @@ #!/bin/bash echo "Choose the CMS (numbers):" -select CMS_TYPE in "sitecore" "contentful" "wordpress" "aem"; do +select CMS_TYPE in "sitecore" "contentful" "wordpress" "aem" "drupal"; do case $CMS_TYPE in sitecore) EXAMPLE_FILE="sitecore.zip" @@ -19,90 +19,16 @@ select CMS_TYPE in "sitecore" "contentful" "wordpress" "aem"; do EXAMPLE_FILE="aem_data_structure" break ;; + drupal) + EXAMPLE_FILE="drupal.sql" + break + ;; *) - echo "Invalid option. Please select 1, 2, 3, or 4." + echo "Invalid option. Please select 1, 2, 3, 4, or 5." ;; esac done -# Prompt for the CMS data path -echo "Enter the path to your $CMS_TYPE data:" -if [[ "$CMS_TYPE" == "aem" ]]; then - echo "(Path should contain a 'templates' folder with JSON files, or provide direct path to templates folder)" -fi -read -r CMS_DATA_PATH - -# Store original path for Docker (Windows paths work with Docker Desktop) -ORIGINAL_PATH="$CMS_DATA_PATH" -DOCKER_MOUNT_PATH="$CMS_DATA_PATH" # Path to mount in Docker - -# Convert Windows path to Unix format for Git Bash file operations ONLY -UNIX_PATH="$CMS_DATA_PATH" -if [[ "$CMS_DATA_PATH" =~ ^[A-Za-z]:\\ ]]; then - # Replace backslashes with forward slashes - UNIX_PATH=$(echo "$CMS_DATA_PATH" | sed 's/\\/\//g') - # Convert C: to /c/ format for Git Bash - UNIX_PATH=$(echo "$UNIX_PATH" | sed 's/^\([A-Za-z]\):/\/\L\1/') -fi - -# Check if file/directory exists using the converted path -if [[ "$CMS_TYPE" == "aem" ]]; then - # For AEM, check if it's a directory - if [ ! -d "$UNIX_PATH" ]; then - echo "❌ Directory does not exist: $UNIX_PATH" - echo "Please provide the path to your AEM data structure folder" - exit 1 - fi - - # Check if the provided path IS the templates folder - if [[ "$(basename "$UNIX_PATH")" == "templates" ]]; then - # User provided the templates folder directly - TEMPLATES_PATH="$UNIX_PATH" - # Get parent directory for Docker mounting - UNIX_MOUNT_PATH="$(dirname "$UNIX_PATH")" - DOCKER_MOUNT_PATH="$(dirname "$ORIGINAL_PATH")" - echo "ℹ️ Detected direct templates path, using parent folder for Docker mounting" - else - # User provided the parent folder - TEMPLATES_PATH="$UNIX_PATH/templates" - UNIX_MOUNT_PATH="$UNIX_PATH" - - # Verify templates folder exists - if [ ! -d "$TEMPLATES_PATH" ]; then - echo "❌ 'templates' folder not found in: $UNIX_PATH" - echo "Expected structure: your-folder/templates/*.json" - echo "Or provide the direct path to the templates folder" - exit 1 - fi - fi - - # Check if templates folder contains JSON files - JSON_COUNT=$(find "$TEMPLATES_PATH" -maxdepth 1 -name "*.json" -type f 2>/dev/null | wc -l) - if [ "$JSON_COUNT" -eq 0 ]; then - echo "❌ No JSON files found in templates folder: $TEMPLATES_PATH" - echo "Please ensure your templates folder contains template JSON files" - exit 1 - fi - - echo "✅ Found $JSON_COUNT JSON template file(s) in templates folder" - - FILENAME=$(basename "$UNIX_MOUNT_PATH") - CONTAINER_PATH="/data/$FILENAME" -else - # For other CMS types, check if it's a file - if [ ! -f "$UNIX_PATH" ]; then - echo "❌ File does not exist: $UNIX_PATH" - exit 1 - fi - - FILENAME=$(basename "$UNIX_PATH") - CONTAINER_PATH="/data/$FILENAME" -fi - -export CMS_TYPE -export CMS_DATA_PATH="$ORIGINAL_PATH" -export CONTAINER_PATH - ENV_PATH="./upload-api/.env" set_env_var() { @@ -114,7 +40,7 @@ set_env_var() { if grep -q "^${VAR_NAME}=" "$ENV_PATH" 2>/dev/null; then # Update existing variable - escape special characters for sed - ESCAPED_VALUE=$(printf '%s\n' "$VAR_VALUE" | sed 's/[[\.*^$()+?{|]/\\&/g') + ESCAPED_VALUE=$(printf '%s\n' "$VAR_VALUE" | sed 's/[[\.*^$()+?{|&]/\\&/g') sed -i.bak "s|^${VAR_NAME}=.*|${VAR_NAME}=${ESCAPED_VALUE}|" "$ENV_PATH" rm -f "$ENV_PATH.bak" else @@ -123,18 +49,137 @@ set_env_var() { fi } -set_env_var "CMS_TYPE" "$CMS_TYPE" -# Save the full path to templates (what user provided) -set_env_var "CMS_DATA_PATH" "$ORIGINAL_PATH" -# Save the Docker mount path (may be parent directory for AEM) -set_env_var "DOCKER_MOUNT_PATH" "$DOCKER_MOUNT_PATH" -set_env_var "CONTAINER_PATH" "$CONTAINER_PATH" -set_env_var "NODE_BACKEND_API" "http://migration-api:5001" - -# Set AEM-specific environment variables -if [[ "$CMS_TYPE" == "aem" ]]; then - set_env_var "AEM_TEMPLATES_DIR" "templates" - echo "ℹ️ Set AEM_TEMPLATES_DIR to: templates" +# Drupal uses MySQL connection instead of a data file +if [[ "$CMS_TYPE" == "drupal" ]]; then + echo "" + echo "Drupal uses a MySQL database connection. Please provide your database details:" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + read -rp "MySQL Host (e.g. host.docker.internal for host DB): " MYSQL_HOST + read -rp "MySQL User: " MYSQL_USER + read -rsp "MySQL Password: " MYSQL_PASSWORD + echo "" + read -rp "MySQL Database: " MYSQL_DATABASE + read -rp "MySQL Port [3306]: " MYSQL_PORT + MYSQL_PORT="${MYSQL_PORT:-3306}" + + echo "" + echo "Drupal assets configuration (for resolving media/file URLs):" + read -rp "Assets Base URL (e.g. https://example.com): " DRUPAL_ASSETS_BASE_URL + read -rp "Assets Public Path (e.g. sites/default/files): " DRUPAL_ASSETS_PUBLIC_PATH + + # Drupal doesn't mount a data file; use a placeholder for Docker volume + ORIGINAL_PATH="$(pwd)" + DOCKER_MOUNT_PATH="$(pwd)" + CONTAINER_PATH="/data/drupal" + CMS_LOCAL_PATH="sql" + + export CMS_TYPE + export CMS_DATA_PATH="$ORIGINAL_PATH" + export CONTAINER_PATH + export CMS_LOCAL_PATH + export MYSQL_HOST + export MYSQL_USER + export MYSQL_PASSWORD + export MYSQL_DATABASE + export MYSQL_PORT + export DRUPAL_ASSETS_BASE_URL + export DRUPAL_ASSETS_PUBLIC_PATH + + set_env_var "CMS_TYPE" "$CMS_TYPE" + set_env_var "CMS_DATA_PATH" "$ORIGINAL_PATH" + set_env_var "DOCKER_MOUNT_PATH" "$DOCKER_MOUNT_PATH" + set_env_var "CONTAINER_PATH" "$CONTAINER_PATH" + set_env_var "CMS_LOCAL_PATH" "$CMS_LOCAL_PATH" + set_env_var "NODE_BACKEND_API" "http://migration-api:5001" + set_env_var "MYSQL_HOST" "$MYSQL_HOST" + set_env_var "MYSQL_USER" "$MYSQL_USER" + set_env_var "MYSQL_PASSWORD" "$MYSQL_PASSWORD" + set_env_var "MYSQL_DATABASE" "$MYSQL_DATABASE" + set_env_var "MYSQL_PORT" "$MYSQL_PORT" + set_env_var "DRUPAL_ASSETS_BASE_URL" "$DRUPAL_ASSETS_BASE_URL" + set_env_var "DRUPAL_ASSETS_PUBLIC_PATH" "$DRUPAL_ASSETS_PUBLIC_PATH" + +else + # Non-Drupal CMS types: prompt for data file/folder path + echo "Enter the path to your $CMS_TYPE data:" + if [[ "$CMS_TYPE" == "aem" ]]; then + echo "(Path should contain a 'templates' folder with JSON files, or provide direct path to templates folder)" + fi + read -r CMS_DATA_PATH + + # Store original path for Docker (Windows paths work with Docker Desktop) + ORIGINAL_PATH="$CMS_DATA_PATH" + DOCKER_MOUNT_PATH="$CMS_DATA_PATH" # Path to mount in Docker + + # Convert Windows path to Unix format for Git Bash file operations ONLY + UNIX_PATH="$CMS_DATA_PATH" + if [[ "$CMS_DATA_PATH" =~ ^[A-Za-z]:\\ ]]; then + UNIX_PATH=$(echo "$CMS_DATA_PATH" | sed 's/\\/\//g') + UNIX_PATH=$(echo "$UNIX_PATH" | sed 's/^\([A-Za-z]\):/\/\L\1/') + fi + + # Check if file/directory exists using the converted path + if [[ "$CMS_TYPE" == "aem" ]]; then + if [ ! -d "$UNIX_PATH" ]; then + echo "❌ Directory does not exist: $UNIX_PATH" + echo "Please provide the path to your AEM data structure folder" + exit 1 + fi + + if [[ "$(basename "$UNIX_PATH")" == "templates" ]]; then + TEMPLATES_PATH="$UNIX_PATH" + UNIX_MOUNT_PATH="$(dirname "$UNIX_PATH")" + DOCKER_MOUNT_PATH="$(dirname "$ORIGINAL_PATH")" + echo "ℹ️ Detected direct templates path, using parent folder for Docker mounting" + else + TEMPLATES_PATH="$UNIX_PATH/templates" + UNIX_MOUNT_PATH="$UNIX_PATH" + + if [ ! -d "$TEMPLATES_PATH" ]; then + echo "❌ 'templates' folder not found in: $UNIX_PATH" + echo "Expected structure: your-folder/templates/*.json" + echo "Or provide the direct path to the templates folder" + exit 1 + fi + fi + + JSON_COUNT=$(find "$TEMPLATES_PATH" -maxdepth 1 -name "*.json" -type f 2>/dev/null | wc -l) + if [ "$JSON_COUNT" -eq 0 ]; then + echo "❌ No JSON files found in templates folder: $TEMPLATES_PATH" + echo "Please ensure your templates folder contains template JSON files" + exit 1 + fi + + echo "✅ Found $JSON_COUNT JSON template file(s) in templates folder" + + FILENAME=$(basename "$UNIX_MOUNT_PATH") + CONTAINER_PATH="/data/$FILENAME" + else + if [ ! -f "$UNIX_PATH" ]; then + echo "❌ File does not exist: $UNIX_PATH" + exit 1 + fi + + FILENAME=$(basename "$UNIX_PATH") + CONTAINER_PATH="/data/$FILENAME" + fi + + export CMS_TYPE + export CMS_DATA_PATH="$ORIGINAL_PATH" + export CONTAINER_PATH + + set_env_var "CMS_TYPE" "$CMS_TYPE" + set_env_var "CMS_DATA_PATH" "$ORIGINAL_PATH" + set_env_var "DOCKER_MOUNT_PATH" "$DOCKER_MOUNT_PATH" + set_env_var "CONTAINER_PATH" "$CONTAINER_PATH" + set_env_var "NODE_BACKEND_API" "http://migration-api:5001" + + # Set AEM-specific environment variables + if [[ "$CMS_TYPE" == "aem" ]]; then + set_env_var "AEM_TEMPLATES_DIR" "templates" + echo "ℹ️ Set AEM_TEMPLATES_DIR to: templates" + fi fi # Check if docker-compose.yml exists before running @@ -158,12 +203,20 @@ echo "" echo "✅ Starting Docker Compose with the following configuration:" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "CMS_TYPE: $CMS_TYPE" -echo "CMS_DATA_PATH: $ORIGINAL_PATH" -if [[ "$CMS_TYPE" == "aem" ]]; then +if [[ "$CMS_TYPE" == "drupal" ]]; then + echo "MySQL Host: $MYSQL_HOST" + echo "MySQL User: $MYSQL_USER" + echo "MySQL Database: $MYSQL_DATABASE" + echo "MySQL Port: $MYSQL_PORT" + echo "Assets Base URL: $DRUPAL_ASSETS_BASE_URL" + echo "Assets Public Path: $DRUPAL_ASSETS_PUBLIC_PATH" +elif [[ "$CMS_TYPE" == "aem" ]]; then + echo "CMS_DATA_PATH: $ORIGINAL_PATH" echo "DOCKER_MOUNT_PATH: $DOCKER_MOUNT_PATH" echo "CONTAINER_PATH: $CONTAINER_PATH" echo "Templates accessible at: $CONTAINER_PATH/templates" else + echo "CMS_DATA_PATH: $ORIGINAL_PATH" echo "CONTAINER_PATH: $CONTAINER_PATH" fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/upload-api/src/config/index.ts b/upload-api/src/config/index.ts index a56ce060..f117ef3e 100644 --- a/upload-api/src/config/index.ts +++ b/upload-api/src/config/index.ts @@ -13,15 +13,15 @@ export default { bucketKey: '' }, mysql: { - host: 'host_name', - user: 'user_name', - password: '', - database: 'database_name', - port: 'port_number' + host: process.env.MYSQL_HOST || 'host_name', + user: process.env.MYSQL_USER || 'user_name', + password: process.env.MYSQL_PASSWORD || '', + database: process.env.MYSQL_DATABASE || 'database_name', + port: process.env.MYSQL_PORT || 'port_number' }, assetsConfig: { - base_url: process.env.DRUPAL_ASSETS_BASE_URL || 'drupal_assets_base_url', // Dynamic: Can be any domain, with/without trailing slash - public_path: process.env.DRUPAL_ASSETS_PUBLIC_PATH || 'drupal_assets_public_path' // Dynamic: Can be any path, with/without slashes + base_url: process.env.DRUPAL_ASSETS_BASE_URL || 'drupal_assets_base_url', + public_path: process.env.DRUPAL_ASSETS_PUBLIC_PATH || 'drupal_assets_public_path' }, - localPath: process.env.CONTAINER_PATH || 'localPath' + localPath: process.env.CMS_LOCAL_PATH || process.env.CONTAINER_PATH || 'localPath' };