const mongoose = require('mongoose'); const Booking = require('../models/booking'); const fs = require('fs'); const path = require('path'); require('dotenv').config(); const filePath = path.join(__dirname, '..', 'data', 'booking.json'); let raw = '{}'; try { raw = fs.readFileSync(filePath, 'utf8'); } catch (e) { console.error('Could not read booking.json at', filePath); process.exit(2); } let data; try { data = JSON.parse(raw || '{}'); } catch (e) { console.error('Invalid JSON in booking.json:', e.message); process.exit(3); } // Remove _id fields recursively to avoid conflicts function stripIds(obj) { if (Array.isArray(obj)) return obj.map(i => stripIds(i)); if (obj && typeof obj === 'object') { const out = {}; for (const k in obj) { if (k !== '_id') out[k] = stripIds(obj[k]); } return out; } return obj; } data = stripIds(data); // Normalize vouchers to an array of objects so Mongoose casting won't fail function normalizeVouchers(doc) { if (!doc) return; // support root-level `vouchers` and `configuration.vouchers` let v = doc.vouchers || (doc.configuration && doc.configuration.vouchers); if (!v) return; // Try to parse stringified arrays (may use single quotes or JS literal) if (typeof v === 'string') { try { v = JSON.parse(v); } catch (e1) { try { // try parsing JS object-literal style strings // eslint-disable-next-line no-new-func v = (new Function('return ' + v))(); } catch (e2) { // fallback: attempt to extract codes by splitting on commas v = v.split && v.split(',').map(s => s.trim()).filter(Boolean).map(s => ({ validCodes: s, type: 'unknown', value: null })); } } } // Normalize array items into objects if (Array.isArray(v)) { v = v.map(item => { if (typeof item === 'string') return { validCodes: item, type: 'unknown', value: null }; if (item && typeof item === 'object') { return { validCodes: item.validCodes || item.code || '', type: item.type || '', value: typeof item.value === 'number' ? item.value : (item.amount && Number(item.amount)) || null, }; } return item; }); } // If Booking schema expects array of strings, convert objects -> string codes try { const Booking = require('../models/booking'); const pathType = Booking.schema.path('vouchers') || Booking.schema.path('configuration.vouchers'); if (pathType && pathType.instance === 'Array' && pathType.caster && pathType.caster.instance === 'String') { const mapped = (v || []).map(it => (typeof it === 'string' ? it : (it && typeof it === 'object' ? it.validCodes || JSON.stringify(it) : String(it)))); if (doc.vouchers) doc.vouchers = mapped; else if (doc.configuration) doc.configuration.vouchers = mapped; return; } } catch (e) { // ignore and keep object form } if (doc.vouchers) doc.vouchers = v; else if (doc.configuration) doc.configuration.vouchers = v; } normalizeVouchers(data); // Also normalize discounts (some inputs have them stringified or as objects) function normalizeDiscounts(doc) { if (!doc) return; let d = doc.discounts || (doc.configuration && doc.configuration.discounts); if (!d) return; if (typeof d === 'string') { try { d = JSON.parse(d); } catch (e1) { try { // eslint-disable-next-line no-new-func d = (new Function('return ' + d))(); } catch (e2) { d = d.split && d.split('\n').map(s => s.trim()).filter(Boolean).map(s => ({ id: '', name: s })); } } } if (Array.isArray(d)) { d = d.map(item => { if (typeof item === 'string') return { id: '', name: item }; if (item && typeof item === 'object') return item; return item; }); } try { const Booking = require('../models/booking'); const pathType = Booking.schema.path('discounts') || Booking.schema.path('configuration.discounts'); if (pathType && pathType.instance === 'Array' && pathType.caster && pathType.caster.instance === 'String') { const mapped = (d || []).map(it => (typeof it === 'string' ? it : (it.id || it.name || JSON.stringify(it)))); if (doc.discounts) doc.discounts = mapped; else if (doc.configuration) doc.configuration.discounts = mapped; return; } } catch (e) { // ignore } if (doc.discounts) doc.discounts = d; else if (doc.configuration) doc.configuration.discounts = d; } normalizeDiscounts(data); const dryRun = process.argv.includes('--dry-run') || process.argv.includes('-n'); async function run() { try { const dbUri = process.env.MONGODB_URI || process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/cms'; console.log('Using DB URI:', dbUri); if (dryRun) { console.log('\nDRY RUN - preview of document to upsert:\n'); console.log(JSON.stringify(data, null, 2)); process.exit(0); } await mongoose.connect(dbUri, { useNewUrlParser: true, useUnifiedTopology: true }); console.log('Connected to MongoDB'); const result = await Booking.findOneAndUpdate({}, data, { upsert: true, new: true, setDefaultsOnInsert: true, }); console.log('Upsert complete. Document id:', result._id.toString()); console.log('Summary: programs=', (data.programs || []).length, 'camps=', (data.camps || []).length); await mongoose.disconnect(); process.exit(0); } catch (err) { console.error('Migration failed:', err && err.message ? err.message : err); process.exit(1); } } run();