/** * Align Goa 7–8 Aug microsite with Goa 8–9 Aug format and publish. * * Usage: * node scripts/setup-goa-7-8-microsite.js [--publish] [--csv=path] */ const path = require('path'); const fs = require('fs'); require('dotenv').config({ path: path.join(__dirname, '..', '.env') }); const mongoose = require('mongoose'); const { connectDB } = require('../src/config/database'); const Event = require('../src/models/Event'); const Form = require('../src/models/Form'); const EventStaticMicrosite = require('../src/models/EventStaticMicrosite'); const User = require('../src/models/User'); const { normalizeSubdomain, subdomainAvailability } = require('../src/utils/subdomainAvailability'); const { publishStaticMicrosite } = require('../src/utils/publishStaticMicrosite'); const { parseNovelScheduleCsv } = require('../src/utils/parseNovelScheduleCsv'); const { importNovelScheduleToAgenda } = require('../src/utils/importNovelScheduleAgenda'); const SOURCE_SUBDOMAIN = 'novel2026-goa-8-9-aug'; const TARGET_EVENT_ID = '6a43e05fdc940a0d6e057f7b'; const TARGET_SUBDOMAIN = 'novel2026-goa-7-8-aug'; const SOURCE_EVENT_ID = '6a4cb75a7475c040cc6f2dd7'; const DEFAULT_CSV = path.join(__dirname, '..', '..', 'novel2026-goa-7-8-aug.csv'); const PUBLIC_WEB_BASE = (process.env.PUBLIC_WEB_BASE || 'https://web.eventbuzz.live').replace(/\/$/, ''); const TARGET = { name: 'NOVEL Lung Health Summit - Goa - 7th & 8th August 2026', shortPlace: 'Goa', dateLabel: '7th & 8th August 2026', dateShort: 'Aug 7–8, 2026', countdownDate: '2026-08-07', scheduleDay1: 'August 7, 2026', scheduleDay2: 'August 8, 2026', location: 'DoubleTree by Hilton Panjim, Goa', mapQuery: 'DoubleTree+by+Hilton+Goa+Panaji,+Chimbel,+Goa', }; function argValue(name, fallback) { const hit = process.argv.find((a) => a.startsWith(`--${name}=`)); return hit ? hit.split('=').slice(1).join('=').trim() : fallback; } function deepClone(obj) { return JSON.parse(JSON.stringify(obj)); } function replaceAllStrings(obj, replacements) { if (obj == null) return obj; if (typeof obj === 'string') { let out = obj; for (const [from, to] of replacements) { if (from) out = out.split(from).join(to); } return out; } if (Array.isArray(obj)) return obj.map((item) => replaceAllStrings(item, replacements)); if (typeof obj === 'object') { const out = {}; for (const [k, v] of Object.entries(obj)) out[k] = replaceAllStrings(v, replacements); return out; } return obj; } async function findForm(eventId, type) { return Form.findOne({ event: eventId, type, 'settings.isActive': { $ne: false } }) .sort({ createdAt: 1 }) .select('_id name') .lean(); } async function findContactForm(eventId) { return Form.findOne({ event: eventId, type: 'other', name: /contact/i, 'settings.isActive': { $ne: false } }) .sort({ createdAt: 1 }) .select('_id name') .lean(); } async function findPublishUser() { const platform = await User.findOne({ platform_admin: 'yes', isActive: { $ne: false } }).sort({ createdAt: 1 }); if (platform) return platform; const superAdmin = await User.findOne({ role: 'super_admin' }).sort({ createdAt: 1 }); if (superAdmin) return superAdmin; throw new Error('No user found for publish'); } function loadScheduleFromCsv(csvPath) { if (!fs.existsSync(csvPath)) { throw new Error(`Schedule CSV not found: ${csvPath}`); } const text = fs.readFileSync(csvPath, 'utf8'); const { days } = parseNovelScheduleCsv(text, { dayDates: [TARGET.scheduleDay1, TARGET.scheduleDay2], }); return days; } function applyScheduleToConfig(config, days) { const next = deepClone(config); const sections = next.sections || {}; sections.event_schedule = sections.event_schedule || {}; sections.event_schedule.show = sections.event_schedule.show !== false; sections.event_schedule.heading = sections.event_schedule.heading || 'Event Schedule'; sections.event_schedule.subtitle = sections.event_schedule.subtitle || 'Scientific programme at DoubleTree by Hilton Panjim, Goa.'; sections.event_schedule.days = days; next.sections = sections; return next; } function applyTargetOverrides(config, targetEventId, forms, heroBannerUrl) { const next = deepClone(config); next.meta = next.meta || {}; next.meta.title = TARGET.name; next.meta.eventId = String(targetEventId); const regUrl = forms.registration ? `${PUBLIC_WEB_BASE}/event/${targetEventId}/form/${forms.registration._id}` : `${PUBLIC_WEB_BASE}/event/${targetEventId}/register`; const feedbackUrl = forms.feedback ? `${PUBLIC_WEB_BASE}/event/${targetEventId}/form/${forms.feedback._id}` : null; const sections = next.sections || {}; if (sections.hero) { sections.hero.event_name = `${TARGET.shortPlace} - ${TARGET.dateLabel}`; sections.hero.date_venue = TARGET.location; if (heroBannerUrl && sections.hero.image) { sections.hero.image.url = heroBannerUrl; } if (sections.hero.cta) { sections.hero.cta.url = regUrl; sections.hero.cta.target = '_blank'; } if (sections.hero.countdown) { sections.hero.countdown.date = TARGET.countdownDate; } } if (sections.informationBar?.items) { for (const item of sections.informationBar.items) { if (item.label === 'Location') item.value = TARGET.location; if (item.label === 'Date & Time') { item.value = TARGET.dateShort; if (item.calendar) { item.calendar.title = TARGET.name; item.calendar.start = TARGET.countdownDate; item.calendar.end = '2026-08-08'; item.calendar.location = TARGET.location; } } } } if (sections.event_schedule?.days) { if (sections.event_schedule.days[0]) sections.event_schedule.days[0].date = TARGET.scheduleDay1; if (sections.event_schedule.days[1]) sections.event_schedule.days[1].date = TARGET.scheduleDay2; } if (sections.map) { sections.map.embedUrl = `https://maps.google.com/maps?q=${TARGET.mapQuery}&hl=en&z=15&output=embed`; } if (sections.contact && forms.contact) { sections.contact.show = true; sections.contact.formId = String(forms.contact._id); sections.contact.apiBaseUrl = sections.contact.apiBaseUrl || 'https://api.eventbuzz.live/api'; } if (sections.feedback && feedbackUrl) { sections.feedback.url = feedbackUrl; } if (sections.footer?.links) { for (const link of sections.footer.links) { if (link.text && /register/i.test(link.text)) { link.url = regUrl; link.target = '_blank'; } if (link.text && /feedback/i.test(link.text) && feedbackUrl) { link.url = feedbackUrl; link.target = '_blank'; } } } next.sections = sections; return next; } async function main() { const shouldPublish = process.argv.includes('--publish'); const csvPath = argValue('csv', DEFAULT_CSV); await connectDB(); const targetEvent = await Event.findById(TARGET_EVENT_ID).lean(); if (!targetEvent) throw new Error(`Target event not found: ${TARGET_EVENT_ID}`); const sourceSite = await EventStaticMicrosite.findOne({ subdomain: SOURCE_SUBDOMAIN }).populate('template').lean(); if (!sourceSite) throw new Error(`Source microsite not found: ${SOURCE_SUBDOMAIN}`); const scheduleDays = loadScheduleFromCsv(csvPath); const agendaResult = await importNovelScheduleToAgenda(TARGET_EVENT_ID, scheduleDays); console.log( `Agenda: deleted ${agendaResult.deleted}, created ${agendaResult.created} session(s) on track ${agendaResult.trackId}`, ); const existingTarget = await EventStaticMicrosite.findOne({ event: TARGET_EVENT_ID }); const previousTarget = existingTarget ? deepClone(existingTarget.config) : null; const heroBannerUrl = previousTarget?.sections?.hero?.image?.url || null; const subdomain = normalizeSubdomain(TARGET_SUBDOMAIN); const avail = await subdomainAvailability(subdomain, TARGET_EVENT_ID, existingTarget?._id?.toString()); if (!avail.available) throw new Error(avail.message || 'Subdomain not available'); const [registration, feedback, contact] = await Promise.all([ findForm(TARGET_EVENT_ID, 'registration'), findForm(TARGET_EVENT_ID, 'feedback'), findContactForm(TARGET_EVENT_ID), ]); const sourceForms = await Promise.all([ findForm(SOURCE_EVENT_ID, 'registration'), findForm(SOURCE_EVENT_ID, 'feedback'), findContactForm(SOURCE_EVENT_ID), ]); const replacements = [ [SOURCE_EVENT_ID, TARGET_EVENT_ID], [sourceForms[0]?._id ? String(sourceForms[0]._id) : '', registration?._id ? String(registration._id) : ''], [sourceForms[1]?._id ? String(sourceForms[1]._id) : '', feedback?._id ? String(feedback._id) : ''], ['NOVEL Lung Health Summit - Goa - 8th & 9th August 2026', TARGET.name], ['Goa - 8th & 9th August 2026', `${TARGET.shortPlace} - ${TARGET.dateLabel}`], ['Aug 8–9, 2026', TARGET.dateShort], ].filter(([from]) => from); let config = replaceAllStrings(sourceSite.config, replacements); config = applyScheduleToConfig(config, scheduleDays); config = applyTargetOverrides(config, TARGET_EVENT_ID, { registration, feedback, contact }, heroBannerUrl); const user = shouldPublish ? await findPublishUser() : null; const templateId = sourceSite.template?._id || sourceSite.template; const payload = { event: targetEvent._id, organization: targetEvent.organization, subdomain, template: templateId, config, updatedBy: user?._id, }; let siteDoc; if (existingTarget) { Object.assign(existingTarget, payload); existingTarget.markModified('config'); await existingTarget.save(); siteDoc = existingTarget; console.log(`Updated microsite ${existingTarget._id}`); } else { siteDoc = await EventStaticMicrosite.create({ ...payload, status: 'draft' }); console.log(`Created microsite ${siteDoc._id}`); } console.log(`Event: ${targetEvent.name}`); console.log(`Subdomain: ${subdomain}`); console.log(`Template: ${sourceSite.template?.slug || templateId}`); console.log(`CSV: ${csvPath}`); console.log(`Reg form: ${registration?._id || '(none)'}`); console.log(`Feedback: ${feedback?._id || '(none)'}`); console.log(`Contact: ${contact?._id || '(none)'}`); if (shouldPublish) { const populated = await EventStaticMicrosite.findById(siteDoc._id).populate('template'); const result = await publishStaticMicrosite({ staticMicrosite: populated, template: populated.template, userId: user._id, }); Object.assign(populated, result); await populated.save(); console.log(`Published: ${result.publicUrl}`); } else { console.log('Saved as draft. Re-run with --publish to publish.'); } await mongoose.disconnect(); } main().catch((err) => { console.error(err); process.exit(1); });