UNPKG

pame-core-cli

Version:

PAME.AI Core Operating System CLI - Open Source AI Platform for Agentic Commerce

884 lines • 43.2 kB
import { Command } from 'commander'; import chalk from 'chalk'; import ora from 'ora'; import inquirer from 'inquirer'; import * as fs from 'fs'; import * as path from 'path'; import { GoDaddyManager } from '../services/godaddy-manager.js'; import { VercelManager } from '../services/vercel-manager.js'; import { VercelSDKManager } from '../services/vercel-sdk-manager.js'; export const subdomainCommand = new Command('subdomain') .description('Manage subdomains with integrated Vercel and GoDaddy DNS configuration') .addCommand(new Command('create') .description('Create a new subdomain with Vercel project and GoDaddy DNS setup') .argument('[subdomain]', 'Subdomain name (e.g., "crm" for crm.pame.ai)') .option('-f, --framework <framework>', 'Project framework', 'nextjs') .option('-p, --production', 'Deploy to production immediately', false) .option('--dry-run', 'Show what would be done without making changes', false) .option('--skip-vercel', 'Skip Vercel project creation', false) .option('--skip-godaddy', 'Skip GoDaddy DNS configuration', false) .option('--skip-tests', 'Skip endpoint testing', false) .option('--use-sdk', 'Use Vercel SDK instead of direct API calls', false) .action(async (subdomainArg, options) => { try { console.log(chalk.blue('🌐 PAME.AI Subdomain Creation Wizard\n')); if (options.dryRun) { console.log(chalk.yellow('šŸ” DRY RUN MODE - No changes will be made\n')); } // Get subdomain name let subdomain = subdomainArg || options.subdomain; if (!subdomain) { const { inputSubdomain } = await inquirer.prompt([{ type: 'input', name: 'inputSubdomain', message: 'Enter subdomain name (e.g., "crm" for crm.pame.ai):', validate: (input) => { if (!input) return 'Subdomain name is required'; if (!/^[a-z0-9-]+$/.test(input)) return 'Subdomain can only contain lowercase letters, numbers, and hyphens'; if (input.length > 63) return 'Subdomain must be 63 characters or less'; return true; } }]); subdomain = inputSubdomain; } // Validate subdomain format if (!subdomain || !/^[a-z0-9-]+$/.test(subdomain)) { console.error(chalk.red('āŒ Invalid subdomain format. Use only lowercase letters, numbers, and hyphens.')); process.exit(1); } // Get framework if not specified let framework = options.framework; if (!framework) { const { selectedFramework } = await inquirer.prompt([{ type: 'list', name: 'selectedFramework', message: 'Select project framework:', choices: [ { name: 'Next.js (Recommended for PAME)', value: 'nextjs' }, { name: 'React', value: 'react' }, { name: 'Vue.js', value: 'vue' }, { name: 'Static Site', value: 'static' } ], default: 'nextjs' }]); framework = selectedFramework; } // Confirm creation if (!options.dryRun) { const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: `Create ${subdomain}.pame.ai with ${framework} framework?`, default: true }]); if (!confirm) { console.log(chalk.yellow('Operation cancelled.')); return; } } // Initialize managers let godaddyManager = null; let vercelManager = null; let vercelSDKManager = null; if (!options.skipGodaddy) { try { godaddyManager = new GoDaddyManager(); if (!options.dryRun) { await godaddyManager.testConnection(); } else { console.log(chalk.green('šŸ” [DRY RUN] Would test GoDaddy API connection...')); } } catch (error) { console.error(chalk.red('āŒ GoDaddy connection failed:'), error instanceof Error ? error.message : String(error)); if (!options.dryRun) process.exit(1); } } if (!options.skipVercel) { try { if (options.useSdk) { console.log(chalk.blue('šŸš€ Using Vercel SDK for enhanced domain management...')); vercelSDKManager = new VercelSDKManager(); if (!options.dryRun) { await vercelSDKManager.testConnection(); } else { console.log(chalk.green('šŸ” [DRY RUN] Would test Vercel SDK connection...')); } } else { vercelManager = new VercelManager(); if (!options.dryRun) { await vercelManager.testConnection(); } else { console.log(chalk.green('šŸ” [DRY RUN] Would test Vercel API connection...')); } } } catch (error) { console.error(chalk.red('āŒ Vercel connection failed:'), error instanceof Error ? error.message : String(error)); if (!options.dryRun) process.exit(1); } } // Start creation process const report = { subdomain, recommendations: [], issues: [] }; console.log(chalk.blue(`\nšŸš€ Creating subdomain: ${subdomain}.pame.ai\n`)); // Step 1: Create Vercel project if (!options.skipVercel) { if (options.useSdk && vercelSDKManager) { // Use SDK for enhanced domain management await createSubdomainWithSDK(vercelSDKManager, subdomain, framework, options, report); } else if (vercelManager) { // Use traditional HTTP API await createVercelProject(vercelManager, subdomain, framework, options, report); } } // Step 2: Configure DNS in GoDaddy if (!options.skipGodaddy && godaddyManager && report.vercelProject) { await configureDNS(godaddyManager, vercelManager, subdomain, options, report); } // Step 3: Verify and test endpoints if (!options.skipTests) { await testEndpoints(vercelManager, subdomain, options, report); } // Generate final report await generateReport(report, options); } catch (error) { console.error(chalk.red('\nāŒ Subdomain creation failed:'), error instanceof Error ? error.message : String(error)); process.exit(1); } })) .addCommand(new Command('list') .description('List all existing subdomains and their status') .option('--godaddy-only', 'Show only GoDaddy DNS records') .option('--vercel-only', 'Show only Vercel projects') .action(async (options) => { try { console.log(chalk.blue('šŸ“‹ Current Subdomains for pame.ai\n')); if (!options.vercelOnly) { const godaddyManager = new GoDaddyManager(); const subdomains = await godaddyManager.listSubdomains(); console.log(chalk.cyan('🌐 GoDaddy DNS Records:')); if (subdomains.length === 0) { console.log(chalk.gray(' No subdomains found')); } else { subdomains.forEach(sub => { console.log(chalk.white(` ${sub.subdomain}.pame.ai`) + chalk.gray(` → ${sub.target} (TTL: ${sub.ttl}s)`)); }); } console.log(); } if (!options.godaddyOnly) { const vercelManager = new VercelManager(); const projects = await vercelManager.listProjects(); console.log(chalk.cyan('ā–² Vercel Projects:')); if (projects.length === 0) { console.log(chalk.gray(' No projects found')); } else { projects.forEach(project => { const domain = project.targets?.production?.domain || 'No domain'; console.log(chalk.white(` ${project.name}`) + chalk.gray(` → ${domain} (${project.framework})`)); }); } } } catch (error) { console.error(chalk.red('āŒ Failed to list subdomains:'), error instanceof Error ? error.message : String(error)); process.exit(1); } })) .addCommand(new Command('status') .description('Check status of DNS and Vercel configuration') .option('-s, --subdomain <subdomain>', 'Check specific subdomain') .action(async (options) => { try { console.log(chalk.blue('šŸ” Checking DNS and Vercel Status\n')); const godaddyManager = new GoDaddyManager(); const vercelManager = new VercelManager(); if (options.subdomain) { // Check specific subdomain await checkSubdomainStatus(godaddyManager, vercelManager, options.subdomain); } else { // Overall status const dnsStatus = await godaddyManager.getDNSStatus(); console.log(chalk.cyan('šŸ“Š Overall Status:')); console.log(chalk.white(` Total DNS Records: ${dnsStatus.totalRecords}`)); console.log(chalk.white(` Active Subdomains: ${dnsStatus.subdomains}`)); console.log(chalk.white(` TXT Records: ${dnsStatus.txtRecords}`)); console.log(chalk.white(` Vercel Records: ${dnsStatus.vercelRecords}`)); const projects = await vercelManager.listProjects(); console.log(chalk.white(` Vercel Projects: ${projects.length}`)); } } catch (error) { console.error(chalk.red('āŒ Status check failed:'), error instanceof Error ? error.message : String(error)); process.exit(1); } })) .addCommand(new Command('test') .description('Test subdomain connectivity and performance') .argument('<subdomain>', 'Subdomain to test') .action(async (subdomain) => { try { console.log(chalk.blue(`šŸ” Testing ${subdomain}.pame.ai\n`)); const vercelManager = new VercelManager(); // Test custom domain const customDomainResult = await vercelManager.testEndpoint(`https://${subdomain}.pame.ai`); console.log(chalk.cyan('🌐 Custom Domain Test:')); if (customDomainResult.success) { console.log(chalk.green(` āœ… ${subdomain}.pame.ai responding (${customDomainResult.responseTime}ms)`)); } else { console.log(chalk.red(` āŒ ${subdomain}.pame.ai failed: ${customDomainResult.error}`)); } // Performance recommendations if (customDomainResult.responseTime > 2000) { console.log(chalk.yellow('āš ļø Slow response time detected. Consider optimizing your application.')); } } catch (error) { console.error(chalk.red('āŒ Test failed:'), error instanceof Error ? error.message : String(error)); process.exit(1); } })) .addCommand(new Command('update') .description('Update DNS records for an existing subdomain') .argument('<subdomain>', 'Subdomain to update (e.g., "gcp" for gcp.pame.ai)') .option('--target <target>', 'New CNAME target (e.g., new-deployment.vercel.app)') .option('--add-domain', 'Add domain to Vercel project using SDK', false) .option('--add-txt <value>', 'Add TXT verification record') .option('--dry-run', 'Show what would be done without making changes', false) .action(async (subdomain, options) => { try { console.log(chalk.blue(`šŸ”„ Updating DNS for ${subdomain}.pame.ai\n`)); if (options.dryRun) { console.log(chalk.yellow('šŸ” DRY RUN MODE - No changes will be made\n')); } // Initialize managers const godaddyManager = new GoDaddyManager(); let vercelSDKManager = null; if (options.addDomain) { console.log(chalk.blue('šŸš€ Using Vercel SDK for domain management...')); vercelSDKManager = new VercelSDKManager(); await vercelSDKManager.testConnection(); } // Test GoDaddy connection console.log(chalk.blue('šŸ” Testing GoDaddy API connection...')); const connected = await godaddyManager.testConnection(); if (!connected) { console.error(chalk.red('āŒ Failed to connect to GoDaddy API')); process.exit(1); } // Add domain to Vercel project if requested if (options.addDomain && vercelSDKManager) { const projectName = `pame-${subdomain}`; const domain = `${subdomain}.pame.ai`; console.log(chalk.blue(`🌐 Adding domain ${domain} to Vercel project ${projectName}...`)); try { // Check current domains first const currentDomains = await vercelSDKManager.getProjectDomains(projectName); const existingDomain = currentDomains.find(d => d.name === domain); if (existingDomain) { console.log(chalk.yellow(`āš ļø Domain ${domain} already exists in project`)); console.log(chalk.gray(` Verified: ${existingDomain.verified ? 'Yes' : 'No'}`)); if (!existingDomain.verified) { console.log(chalk.blue('šŸ’” Domain needs verification. Get TXT record from Vercel dashboard.')); } } else { if (!options.dryRun) { const domainResult = await vercelSDKManager.addDomainToProject(projectName, domain); console.log(chalk.green(`āœ… Domain added: ${domainResult.name}`)); } else { console.log(chalk.blue(`šŸ” [DRY RUN] Would add domain ${domain} to project ${projectName}`)); } } // Get updated domain status const updatedDomains = await vercelSDKManager.getProjectDomains(projectName); const targetDomain = updatedDomains.find(d => d.name === domain); if (targetDomain) { console.log(chalk.cyan('\nšŸ“‹ Vercel Domain Status:')); console.log(chalk.white(` Domain: ${targetDomain.name}`)); console.log(chalk.white(` Verified: ${targetDomain.verified ? 'āœ… Yes' : 'āš ļø No'}`)); if (!targetDomain.verified) { console.log(chalk.blue('\nšŸ’” To complete verification:')); console.log(' 1. Go to Vercel dashboard → pame-gcp → Domains'); console.log(' 2. Copy the TXT verification record'); console.log(' 3. Add it using: --add-txt "verification-value"'); } } } catch (error) { if (error instanceof Error && error.message.includes('already exists')) { console.log(chalk.yellow('āš ļø Domain already exists in Vercel project')); } else { console.error(chalk.red('āŒ Failed to add domain to Vercel:'), error instanceof Error ? error.message : String(error)); } } } // Add TXT verification record if provided if (options.addTxt) { console.log(chalk.blue('šŸ“ Adding TXT verification record...')); const txtRecord = [{ type: 'TXT', name: '_vercel', data: options.addTxt, ttl: 3600 }]; if (!options.dryRun) { await godaddyManager.updateRecords('TXT', '_vercel', txtRecord); console.log(chalk.green(`āœ… TXT record added: _vercel.pame.ai → "${options.addTxt}"`)); } else { console.log(chalk.blue(`šŸ” [DRY RUN] Would add TXT record: _vercel.pame.ai → "${options.addTxt}"`)); } } // Check current CNAME record console.log(chalk.blue('šŸ” Checking current CNAME record...')); const currentCNAME = await godaddyManager.getRecords('CNAME', subdomain); if (currentCNAME.length === 0) { console.log(chalk.red(`āŒ No CNAME record found for ${subdomain}.pame.ai`)); console.log(chalk.yellow('šŸ’” Use "pame-core subdomain create" to create a new subdomain')); process.exit(1); } const currentTarget = currentCNAME[0].data; console.log(chalk.gray(` Current: ${subdomain}.pame.ai → ${currentTarget}`)); // Update CNAME if target provided if (options.target) { const newTarget = options.target; console.log(chalk.gray(` Target: ${subdomain}.pame.ai → ${newTarget}`)); // Check if update is needed if (currentTarget === newTarget) { console.log(chalk.green('āœ… CNAME record already points to the correct target!')); } else { // Confirm update if (!options.dryRun) { const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: `Update ${subdomain}.pame.ai from ${currentTarget} to ${newTarget}?`, default: true }]); if (!confirm) { console.log(chalk.yellow('Update cancelled.')); return; } } // Update the CNAME record if (options.dryRun) { console.log(chalk.blue(`šŸ” [DRY RUN] Would update CNAME record:`)); console.log(chalk.gray(` ${subdomain}.pame.ai → ${newTarget}`)); } else { console.log(chalk.blue(`šŸ”„ Updating CNAME record...`)); const newRecord = [{ type: 'CNAME', name: subdomain, data: newTarget, ttl: 3600 }]; await godaddyManager.updateRecords('CNAME', subdomain, newRecord); // Verify the update console.log(chalk.blue('šŸ” Verifying update...')); const updatedCNAME = await godaddyManager.getRecords('CNAME', subdomain); if (updatedCNAME.length > 0 && updatedCNAME[0].data === newTarget) { console.log(chalk.green('āœ… CNAME record updated successfully!')); console.log(chalk.gray(` ${subdomain}.pame.ai → ${newTarget} (TTL: ${updatedCNAME[0].ttl}s)`)); } else { console.log(chalk.red('āŒ CNAME record update verification failed')); process.exit(1); } } } } // Check TXT verification record status console.log(chalk.blue('\nšŸ” Checking TXT verification record...')); const txtRecords = await godaddyManager.getRecords('TXT', '_vercel'); const subdomainTxtRecord = txtRecords.find(record => record.data.includes(`${subdomain}.pame.ai`)); if (subdomainTxtRecord) { console.log(chalk.green('āœ… TXT verification record found')); console.log(chalk.gray(` _vercel.pame.ai → "${subdomainTxtRecord.data}"`)); } else { console.log(chalk.yellow('āš ļø No TXT verification record found')); if (!options.addDomain) { console.log(chalk.blue('\nšŸ’” To add domain verification:')); console.log(` pame-core subdomain update ${subdomain} --add-domain`); } } // Final summary console.log(chalk.green('\nāœ… DNS update completed!')); console.log(chalk.blue('\nšŸ“‹ Summary:')); if (options.target) { if (options.dryRun) { console.log(` • Would update: ${subdomain}.pame.ai → ${options.target}`); } else { console.log(` • Updated: ${subdomain}.pame.ai → ${options.target} āœ…`); } } if (options.addDomain) { console.log(` • Vercel domain: ${options.dryRun ? 'Would add' : 'Added'} āœ…`); } if (options.addTxt) { console.log(` • TXT record: ${options.dryRun ? 'Would add' : 'Added'} āœ…`); } console.log(` • TXT verification: ${subdomainTxtRecord ? 'āœ… Found' : 'āš ļø May be needed'}`); console.log(' • DNS propagation: up to 24 hours'); console.log('\nšŸ”— Test URLs:'); const target = options.target || currentTarget; console.log(` • Direct: https://${target}`); console.log(` • Custom: https://${subdomain}.pame.ai (after propagation)`); } catch (error) { console.error(chalk.red('\nāŒ Update failed:'), error instanceof Error ? error.message : String(error)); process.exit(1); } })) .addCommand(new Command('verify') .description('Get verification details for a subdomain') .argument('<subdomain>', 'Subdomain to check verification for (e.g., "gcp" for gcp.pame.ai)') .action(async (subdomain) => { try { console.log(chalk.blue(`šŸ” Getting verification details for ${subdomain}.pame.ai\n`)); // Initialize Vercel SDK Manager const vercelSDKManager = new VercelSDKManager(); await vercelSDKManager.testConnection(); const projectName = `pame-${subdomain}`; const domain = `${subdomain}.pame.ai`; // Get project domains with detailed verification info console.log(chalk.blue(`🌐 Checking project ${projectName}...`)); const domains = await vercelSDKManager.getProjectDomains(projectName); const targetDomain = domains.find(d => d.name === domain); if (!targetDomain) { console.log(chalk.red(`āŒ Domain ${domain} not found in project ${projectName}`)); console.log(chalk.yellow('šŸ’” Available domains:')); domains.forEach(d => console.log(chalk.gray(` ${d.name}`))); process.exit(1); } console.log(chalk.cyan('\nšŸ“‹ Domain Verification Details:')); console.log(chalk.white(` Domain: ${targetDomain.name}`)); console.log(chalk.white(` Verified: ${targetDomain.verified ? 'āœ… Yes' : 'āš ļø No'}`)); console.log(chalk.white(` Created: ${targetDomain.createdAt}`)); if (targetDomain.verification && targetDomain.verification.length > 0) { console.log(chalk.cyan('\nšŸ” Verification Requirements:')); targetDomain.verification.forEach((req, i) => { console.log(chalk.white(` ${i + 1}. Type: ${req.type}`)); console.log(chalk.white(` Domain: ${req.domain}`)); console.log(chalk.white(` Value: ${req.value}`)); console.log(chalk.white(` Reason: ${req.reason || 'Domain verification'}`)); if (req.type === 'TXT') { console.log(chalk.yellow('\nšŸŽÆ TXT Record Required:')); console.log(chalk.white(` Host: ${req.domain}`)); console.log(chalk.white(` Value: ${req.value}`)); console.log(chalk.green(`\nšŸ“ Add with this command:`)); console.log(chalk.cyan(` npx pame-core-cli subdomain update ${subdomain} --add-txt "${req.value}"`)); } }); } else if (!targetDomain.verified) { console.log(chalk.yellow('\nāš ļø Domain not verified but no verification requirements found')); console.log(chalk.blue('šŸ’” Check the Vercel dashboard for manual verification steps:')); console.log(chalk.cyan(` https://vercel.com/getaifactory/${projectName}/domains`)); } else { console.log(chalk.green('\nāœ… Domain is fully verified!')); } } catch (error) { console.error(chalk.red('\nāŒ Verification check failed:'), error instanceof Error ? error.message : String(error)); process.exit(1); } })); // Helper function to create subdomain using Vercel SDK async function createSubdomainWithSDK(vercelSDKManager, subdomain, framework, options, report) { const spinner = ora('Creating subdomain with Vercel SDK...').start(); try { if (options.dryRun) { spinner.stop(); console.log(chalk.blue(`šŸ” [DRY RUN] Would create subdomain: ${subdomain}.pame.ai using SDK`)); console.log(chalk.gray(` - Framework: ${framework}`)); console.log(chalk.gray(` - Enhanced features: Domain verification, status monitoring`)); // Mock report data for dry run report.vercelProject = { id: 'mock-sdk-project-id', name: `pame-${subdomain}`, url: `https://pame-${subdomain}.vercel.app`, domain: `${subdomain}.pame.ai` }; return; } const result = await vercelSDKManager.createSubdomainWithSDK({ subdomain, framework, envVars: { NEXT_PUBLIC_SUBDOMAIN: subdomain, NEXT_PUBLIC_DOMAIN: `${subdomain}.pame.ai`, } }); spinner.stop(); if (result.success && result.project) { console.log(chalk.green(`āœ… SDK subdomain creation successful!`)); console.log(chalk.gray(` Project: ${result.project.name} (${result.project.id})`)); console.log(chalk.gray(` Domain: ${result.domain?.name || `${subdomain}.pame.ai`}`)); console.log(chalk.gray(` Domains found: ${result.domains?.length || 0}`)); report.vercelProject = { id: result.project.id, name: result.project.name, url: `https://${result.project.name}.vercel.app`, domain: `${subdomain}.pame.ai` }; if (!report.recommendations) report.recommendations = []; report.recommendations.push('SDK-based creation provides enhanced domain management features'); } else { throw new Error(result.error || 'SDK subdomain creation failed'); } } catch (error) { spinner.stop(); console.error(chalk.red('āŒ SDK subdomain creation failed:'), error instanceof Error ? error.message : String(error)); if (!report.issues) report.issues = []; report.issues.push(`SDK creation failed: ${error instanceof Error ? error.message : String(error)}`); throw error; } } async function createVercelProject(vercelManager, subdomain, framework, options, report) { const spinner = ora('Creating Vercel project...').start(); try { const projectName = `pame-${subdomain}`; const domain = `${subdomain}.pame.ai`; // Handle dry-run mode first, before any API calls if (options.dryRun) { spinner.stop(); console.log(chalk.blue(`šŸ” [DRY RUN] Would create Vercel project: ${projectName}`)); console.log(chalk.gray(` - Framework: ${framework}`)); console.log(chalk.gray(` - Domain: ${domain}`)); console.log(chalk.gray(` - Environment: NEXT_PUBLIC_PLATFORM=${subdomain}`)); // Mock the report data for dry run report.vercelProject = { id: 'mock-project-id', name: projectName, url: `https://${projectName}.vercel.app`, domain: domain }; // Mock DNS configuration for subsequent dry-run steps report.godaddyDNS = { cname: `${projectName}.vercel.app`, txtRecord: 'mock-txt-verification-record', verified: false }; spinner.succeed(`āœ… [DRY RUN] Would create project: ${projectName}`); return; } // Check if project already exists (only in real mode) const existingProject = await vercelManager.getProject(projectName); if (existingProject) { spinner.stop(); console.log(chalk.yellow(`āš ļø Project ${projectName} already exists. Using existing project.`)); report.vercelProject = { id: existingProject.id, name: existingProject.name, url: existingProject.targets?.production?.url || '', domain: existingProject.targets?.production?.domain || domain }; return; } // Create project configuration const projectConfig = { name: projectName, subdomain, framework, envVars: { NEXT_PUBLIC_PLATFORM: subdomain, NEXT_PUBLIC_API_URL: 'https://api.pame.ai' }, rewrites: [ // Block access to other platform routes { source: '/core/(.*)', destination: '/404' }, { source: '/saas/(.*)', destination: '/404' } ] }; // Create Vercel project spinner.text = 'Creating Vercel project...'; const project = await vercelManager.createProject(projectConfig); // Add custom domain spinner.text = 'Adding custom domain...'; await vercelManager.addDomain(project.id, domain); spinner.succeed(`āœ… Vercel project created: ${projectName}`); console.log(chalk.green(`šŸŽ‰ Domain ${domain} has been added to project!`)); console.log(chalk.blue(`šŸ“” Webhook automation will handle DNS verification automatically`)); report.vercelProject = { id: project.id, name: project.name, url: `https://${projectName}.vercel.app`, domain }; // Set up DNS configuration for automated webhook processing // The webhook at api.pame.ai will handle the actual DNS records report.godaddyDNS = { cname: `${projectName}.vercel.app`, txtRecord: `Webhook automation will handle verification`, verified: false }; console.log(chalk.gray(` Project ID: ${project.id}`)); console.log(chalk.gray(` Domain: ${domain}`)); console.log(chalk.gray(` Framework: ${framework}`)); console.log(chalk.yellow(` šŸ“‹ Note: DNS records will be configured automatically via webhook`)); } catch (error) { spinner.fail('āŒ Failed to create Vercel project'); throw error; } } async function configureDNS(godaddyManager, vercelManager, subdomain, options, report) { const spinner = ora('Configuring DNS records...').start(); try { // Check if webhook automation is handling this if (report.godaddyDNS?.txtRecord === 'Webhook automation will handle verification') { spinner.stop(); console.log(chalk.blue('šŸ“” Webhook automation is handling DNS configuration')); console.log(chalk.yellow('šŸ”„ DNS records will be created automatically when webhook triggers')); console.log(chalk.gray(' The webhook at api.pame.ai will process domain events from Vercel')); console.log(chalk.gray(' and automatically create the required DNS records in GoDaddy')); report.godaddyDNS.verified = false; // Will be updated by webhook return; } if (!report.godaddyDNS?.cname || !report.godaddyDNS?.txtRecord) { spinner.fail('āŒ Missing DNS configuration from Vercel'); throw new Error('DNS configuration not available from Vercel step'); } if (options.dryRun) { spinner.stop(); console.log(chalk.blue('šŸ” Would configure DNS records:')); console.log(chalk.gray(` CNAME: ${subdomain} → ${report.godaddyDNS.cname}`)); console.log(chalk.gray(` TXT: _vercel → ${report.godaddyDNS.txtRecord}`)); return; } const subdomainSetup = { subdomain, cname: report.godaddyDNS.cname, txtRecord: report.godaddyDNS.txtRecord, vercelDomain: `${subdomain}.pame.ai` }; spinner.text = 'Adding DNS records to GoDaddy...'; await godaddyManager.createSubdomainSetup(subdomainSetup); // Wait a bit for DNS propagation spinner.text = 'Waiting for DNS propagation...'; await new Promise(resolve => setTimeout(resolve, 5000)); // Verify DNS configuration spinner.text = 'Verifying DNS configuration...'; const verified = await godaddyManager.verifySubdomain(subdomain, report.godaddyDNS.cname, report.godaddyDNS.txtRecord); if (verified) { // Try to verify domain in Vercel spinner.text = 'Verifying domain in Vercel...'; const vercelVerified = await vercelManager.verifyDomain(`${subdomain}.pame.ai`); report.godaddyDNS.verified = vercelVerified; } spinner.succeed('āœ… DNS records configured successfully'); console.log(chalk.gray(` CNAME: ${subdomain}.pame.ai → ${report.godaddyDNS.cname}`)); console.log(chalk.gray(` TXT: _vercel → ${report.godaddyDNS.txtRecord}`)); console.log(chalk.gray(` Verified: ${report.godaddyDNS.verified ? 'Yes' : 'Pending'}`)); if (!report.godaddyDNS.verified) { report.recommendations?.push('DNS verification is pending. It may take up to 24 hours for DNS changes to propagate globally.'); } } catch (error) { spinner.fail('āŒ Failed to configure DNS'); throw error; } } async function testEndpoints(vercelManager, subdomain, options, report) { const spinner = ora('Testing endpoints...').start(); try { if (options.dryRun) { spinner.stop(); console.log(chalk.blue('šŸ” Would test endpoints:')); console.log(chalk.gray(` https://${report.vercelProject?.name}.vercel.app`)); console.log(chalk.gray(` https://${subdomain}.pame.ai`)); return; } report.endpoints = { vercelUrl: { url: '', status: 0, responseTime: 0, success: false }, customDomain: { url: '', status: 0, responseTime: 0, success: false } }; // Test Vercel URL if (report.vercelProject?.name) { spinner.text = 'Testing Vercel URL...'; const vercelUrl = `https://${report.vercelProject.name}.vercel.app`; const vercelResult = await vercelManager.testEndpoint(vercelUrl); report.endpoints.vercelUrl = { url: vercelUrl, status: vercelResult.status, responseTime: vercelResult.responseTime, success: vercelResult.success, error: vercelResult.error }; } // Test custom domain spinner.text = 'Testing custom domain...'; const customUrl = `https://${subdomain}.pame.ai`; const customResult = await vercelManager.testEndpoint(customUrl); report.endpoints.customDomain = { url: customUrl, status: customResult.status, responseTime: customResult.responseTime, success: customResult.success, error: customResult.error }; spinner.succeed('āœ… Endpoint testing completed'); // Log results if (report.endpoints.vercelUrl.success) { console.log(chalk.green(` āœ… Vercel URL: ${report.endpoints.vercelUrl.status} (${report.endpoints.vercelUrl.responseTime}ms)`)); } else { console.log(chalk.red(` āŒ Vercel URL: ${report.endpoints.vercelUrl.error}`)); } if (report.endpoints.customDomain.success) { console.log(chalk.green(` āœ… Custom Domain: ${report.endpoints.customDomain.status} (${report.endpoints.customDomain.responseTime}ms)`)); } else { console.log(chalk.yellow(` āš ļø Custom Domain: ${report.endpoints.customDomain.error} (This is normal for new domains)`)); report.recommendations?.push('Custom domain may take time to become available after DNS propagation.'); } } catch (error) { spinner.fail('āŒ Endpoint testing failed'); report.issues?.push(`Endpoint testing failed: ${error instanceof Error ? error.message : String(error)}`); } } async function generateReport(report, options) { console.log(chalk.blue('\nšŸ“Š Subdomain Creation Report\n')); // Summary console.log(chalk.cyan('šŸ“‹ Summary:')); console.log(chalk.white(` Subdomain: ${report.subdomain}.pame.ai`)); if (report.vercelProject) { console.log(chalk.white(` Vercel Project: ${report.vercelProject.name} (${report.vercelProject.id})`)); } // DNS Status if (report.godaddyDNS) { console.log(chalk.cyan('\n🌐 DNS Configuration:')); console.log(chalk.white(` CNAME Record: ${report.subdomain} → ${report.godaddyDNS.cname}`)); console.log(chalk.white(` TXT Record: _vercel → ${report.godaddyDNS.txtRecord}`)); console.log(chalk.white(` Verification: ${report.godaddyDNS.verified ? 'āœ… Verified' : 'ā³ Pending'}`)); } // Endpoint Status if (report.endpoints) { console.log(chalk.cyan('\nšŸ” Endpoint Status:')); if (report.endpoints.vercelUrl.url) { const status = report.endpoints.vercelUrl.success ? 'āœ…' : 'āŒ'; console.log(chalk.white(` Vercel URL: ${status} ${report.endpoints.vercelUrl.url}`)); } if (report.endpoints.customDomain.url) { const status = report.endpoints.customDomain.success ? 'āœ…' : 'āš ļø'; console.log(chalk.white(` Custom Domain: ${status} ${report.endpoints.customDomain.url}`)); } } // Recommendations if (report.recommendations && report.recommendations.length > 0) { console.log(chalk.cyan('\nšŸ’” Recommendations:')); report.recommendations.forEach(rec => { console.log(chalk.yellow(` • ${rec}`)); }); } // Issues if (report.issues && report.issues.length > 0) { console.log(chalk.cyan('\nāš ļø Issues:')); report.issues.forEach(issue => { console.log(chalk.red(` • ${issue}`)); }); } // Next Steps console.log(chalk.cyan('\nšŸš€ Next Steps:')); if (!options.production) { console.log(chalk.white(' 1. Deploy your application code to the Vercel project')); console.log(chalk.white(` 2. Run: pame-core deploy ${report.vercelProject?.name} --production`)); } console.log(chalk.white(' 3. Wait for DNS propagation (up to 24 hours)')); console.log(chalk.white(` 4. Test your site: https://${report.subdomain}.pame.ai`)); console.log(chalk.white(' 5. Configure SSL certificate (automatic with Vercel)')); // Save report to file if (!options.dryRun) { const reportPath = path.join(process.cwd(), `${report.subdomain}-subdomain-report.json`); fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); console.log(chalk.gray(`\nšŸ“„ Full report saved to: ${reportPath}`)); } console.log(chalk.green(`\nāœ… Subdomain ${report.subdomain}.pame.ai setup completed!`)); } async function checkSubdomainStatus(godaddyManager, vercelManager, subdomain) { console.log(chalk.cyan(`šŸ” Checking status for ${subdomain}.pame.ai\n`)); try { // Check DNS records const cnameRecords = await godaddyManager.getRecords('CNAME', subdomain); const txtRecords = await godaddyManager.getRecords('TXT', '_vercel'); console.log(chalk.cyan('🌐 DNS Records:')); if (cnameRecords.length > 0) { cnameRecords.forEach(record => { console.log(chalk.white(` CNAME: ${record.name} → ${record.data} (TTL: ${record.ttl}s)`)); }); } else { console.log(chalk.red(' āŒ No CNAME record found')); } const relevantTxtRecords = txtRecords.filter(record => record.data.includes(`${subdomain}.pame.ai`)); if (relevantTxtRecords.length > 0) { relevantTxtRecords.forEach(record => { console.log(chalk.white(` TXT: ${record.name} → ${record.data}`)); }); } else { console.log(chalk.red(` āŒ No TXT verification record found for ${subdomain}.pame.ai`)); } // Check Vercel project const projectName = `pame-${subdomain}`; const project = await vercelManager.getProject(projectName); console.log(chalk.cyan('\nā–² Vercel Project:')); if (project) { console.log(chalk.white(` Name: ${project.name}`)); console.log(chalk.white(` ID: ${project.id}`)); console.log(chalk.white(` Framework: ${project.framework}`)); if (project.targets?.production) { console.log(chalk.white(` Production URL: ${project.targets.production.url}`)); console.log(chalk.white(` Production Domain: ${project.targets.production.domain}`)); } } else { console.log(chalk.red(` āŒ No Vercel project found for ${projectName}`)); } // Test connectivity console.log(chalk.cyan('\nšŸ” Connectivity Test:')); const result = await vercelManager.testEndpoint(`https://${subdomain}.pame.ai`); if (result.success) { console.log(chalk.green(` āœ… ${subdomain}.pame.ai is responding (${result.responseTime}ms)`)); } else { console.log(chalk.red(` āŒ ${subdomain}.pame.ai is not responding: ${result.error}`)); } } catch (error) { console.error(chalk.red('āŒ Status check failed:'), error instanceof Error ? error.message : String(error)); } } //# sourceMappingURL=subdomain.js.map