#!/usr/bin/env node /** * Миграционный скрипт для добавления arrowHint и accentedOption настроек * к существующим trialChoice экранам. * * Этот скрипт: * 1. Подключается к MongoDB * 2. Находит все воронки с экранами template: "trialChoice" * 3. Добавляет дефолтные arrowHint и accentedOption настройки если их нет * * Запуск: * node scripts/migrate-trial-choice-arrow-hint.mjs * * Опции: * --dry-run Показать изменения без записи в базу */ import mongoose from 'mongoose'; import dotenv from 'dotenv'; // Load environment variables dotenv.config({ path: '.env.local' }); const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/witlab-funnel'; const isDryRun = process.argv.includes('--dry-run'); // Дефолтные значения для arrowHint const DEFAULT_ARROW_HINT = { text: "It costs us {{maxTrialPrice}} to compensate our team for the work involved in creating your personalized portrait and guide, but please choose the amount you are comfortable with.", position: "bottom-right" }; // Дефолтное значение для accentedOption const DEFAULT_ACCENTED_OPTION = "server"; // Mongoose schema (minimal for migration) const FunnelSchema = new mongoose.Schema({ funnelData: mongoose.Schema.Types.Mixed, name: String, status: String, }, { timestamps: true, collection: 'funnels' }); const FunnelModel = mongoose.models.Funnel || mongoose.model('Funnel', FunnelSchema); async function connectToDatabase() { try { await mongoose.connect(MONGODB_URI, { bufferCommands: false, maxPoolSize: 10, serverSelectionTimeoutMS: 5000, socketTimeoutMS: 45000, }); console.log('✅ Connected to MongoDB'); } catch (error) { console.error('❌ Failed to connect to MongoDB:', error.message); process.exit(1); } } async function migrateFunnels() { // Находим все воронки с trialChoice экранами без arrowHint const funnels = await FunnelModel.find({ 'funnelData.screens': { $elemMatch: { template: 'trialChoice' } } }); console.log(`\n📋 Found ${funnels.length} funnels with trialChoice screens\n`); let updatedCount = 0; let screensUpdated = 0; for (const funnel of funnels) { const funnelId = funnel.funnelData?.meta?.id || funnel._id; const funnelName = funnel.name || funnelId; let hasChanges = false; const screens = funnel.funnelData?.screens || []; for (let i = 0; i < screens.length; i++) { const screen = screens[i]; if (screen.template === 'trialChoice') { const needsArrowHint = !screen.arrowHint; const needsAccentedOption = screen.accentedOption === undefined; if (needsArrowHint || needsAccentedOption) { const updates = []; if (needsArrowHint) updates.push('arrowHint'); if (needsAccentedOption) updates.push('accentedOption'); console.log(` 📍 [${funnelName}] Screen "${screen.id}" needs: ${updates.join(', ')}`); if (!isDryRun) { screens[i] = { ...screen, ...(needsArrowHint && { arrowHint: DEFAULT_ARROW_HINT }), ...(needsAccentedOption && { accentedOption: DEFAULT_ACCENTED_OPTION }), }; } hasChanges = true; screensUpdated++; } else { console.log(` ✅ [${funnelName}] Screen "${screen.id}" already has all settings`); } } } if (hasChanges) { if (!isDryRun) { funnel.funnelData.screens = screens; funnel.markModified('funnelData'); await funnel.save(); console.log(` 💾 [${funnelName}] Saved changes`); } else { console.log(` 🔍 [${funnelName}] Would update (dry-run)`); } updatedCount++; } } return { updatedCount, screensUpdated, total: funnels.length }; } async function main() { console.log('🚀 Starting trialChoice arrowHint migration...'); if (isDryRun) { console.log('⚠️ DRY RUN MODE - No changes will be saved\n'); } await connectToDatabase(); const { updatedCount, screensUpdated, total } = await migrateFunnels(); console.log('\n📊 Migration Summary:'); console.log('====================='); console.log(`📁 Total funnels with trialChoice: ${total}`); console.log(`📝 Funnels updated: ${updatedCount}`); console.log(`🎯 Screens updated: ${screensUpdated}`); if (isDryRun) { console.log('\n⚠️ This was a dry run. Run without --dry-run to apply changes.'); } else if (updatedCount > 0) { console.log('\n✅ Migration completed successfully!'); } else { console.log('\n✨ All trialChoice screens already have arrowHint configured!'); } await mongoose.connection.close(); console.log('\n👋 Database connection closed.'); } main().catch(error => { console.error('\n💥 Fatal error:', error); process.exit(1); });