EventBuzz_Backend/scripts/setup-crest-novel-microsite.js

241 lines
8.0 KiB
JavaScript

#!/usr/bin/env node
/**
* Configure static microsite v2 for an event using crest-novel template + site.json content.
*
* Usage:
* node scripts/setup-crest-novel-microsite.js [--publish] [--event-id=<id>] [--subdomain=<sub>]
*/
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '..', '.env') });
const fs = require('fs');
const mongoose = require('mongoose');
const { connectDB } = require('../src/config/database');
const Event = require('../src/models/Event');
const Form = require('../src/models/Form');
const MicrositeV2Template = require('../src/models/MicrositeV2Template');
const EventStaticMicrosite = require('../src/models/EventStaticMicrosite');
const User = require('../src/models/User');
const { normalizeSubdomain, subdomainAvailability } = require('../src/utils/subdomainAvailability');
const { uploadTemplateAsset } = require('../src/utils/s3MicrositeV2');
const { publishStaticMicrosite } = require('../src/utils/publishStaticMicrosite');
const { resolveConfigAssetUrls } = require('../src/utils/micrositeConfigAssets');
const DEFAULT_EVENT_ID = '6a4cb75a7475c040cc6f2dd7';
const DEFAULT_SUBDOMAIN = 'novel2026-goa-8-9-aug';
const TEMPLATE_SLUG = 'crest-novel';
const SITE_JSON = path.join(__dirname, '..', '..', 'microsites', 'crest-novel', 'site.json');
const PUBLIC_WEB_BASE = (process.env.PUBLIC_WEB_BASE || 'https://web.eventbuzz.live').replace(/\/$/, '');
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 replaceEventIds(obj, oldId, newId) {
if (obj == null) return obj;
if (typeof obj === 'string') {
return obj.split(oldId).join(newId);
}
if (Array.isArray(obj)) return obj.map((v) => replaceEventIds(v, oldId, newId));
if (typeof obj === 'object') {
const out = {};
for (const [k, v] of Object.entries(obj)) out[k] = replaceEventIds(v, oldId, newId);
return out;
}
return obj;
}
function siteJsonToConfig(site, eventId) {
const theme = site.theme || {};
const headingColor = theme.heading?.color || theme.headingColor || '#015AAA';
return {
meta: {
title: site.meta?.title || 'NOVEL Lung Health Summit',
eventId: String(eventId),
},
theme: {
accent: theme.accent || '#189A95',
headingColor,
},
sections: deepClone(site.components || {}),
};
}
async function findSeedUser() {
const userIdArg = argValue('user-id', '');
if (userIdArg) {
const u = await User.findById(userIdArg);
if (u) return u;
}
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 updatedBy/publish. Pass --user-id=<mongoId>');
}
async function findRegistrationForm(eventId) {
return Form.findOne({ event: eventId, type: 'registration', 'settings.isActive': { $ne: false } })
.sort({ createdAt: 1 })
.select('_id name')
.lean();
}
async function findFeedbackForm(eventId) {
return Form.findOne({ event: eventId, type: 'feedback', 'settings.isActive': { $ne: false } })
.sort({ createdAt: 1 })
.select('_id name')
.lean();
}
function applyEventUrls(config, eventId, registrationFormId, feedbackFormId, eventDoc) {
const regUrl = registrationFormId
? `${PUBLIC_WEB_BASE}/event/${eventId}/form/${registrationFormId}`
: `${PUBLIC_WEB_BASE}/event/${eventId}/register`;
const feedbackUrl = feedbackFormId
? `${PUBLIC_WEB_BASE}/event/${eventId}/form/${feedbackFormId}`
: null;
const sections = config.sections || {};
if (sections.hero?.cta) {
sections.hero.cta.url = regUrl;
sections.hero.cta.target = '_blank';
}
if (sections.footer?.links && Array.isArray(sections.footer.links)) {
for (const link of sections.footer.links) {
if (link.text && /register/i.test(link.text)) link.url = regUrl;
if (link.text && /feedback/i.test(link.text) && feedbackUrl) link.url = feedbackUrl;
}
}
if (sections.feedback && feedbackUrl) {
sections.feedback.url = feedbackUrl;
}
if (eventDoc?.banner && sections.hero?.image) {
sections.hero.image.url = eventDoc.banner;
}
return config;
}
async function syncTemplateSiteConfigJs() {
const siteConfigPath = path.join(
__dirname,
'..',
'microsite-v2-templates',
TEMPLATE_SLUG,
'assets',
'js',
'site-config.js',
);
if (!fs.existsSync(siteConfigPath)) {
throw new Error(`Missing site-config.js at ${siteConfigPath}`);
}
await uploadTemplateAsset(
TEMPLATE_SLUG,
'js/site-config.js',
fs.readFileSync(siteConfigPath),
'application/javascript',
);
console.log('Uploaded crest-novel site-config.js to template assets');
}
async function main() {
const eventId = argValue('event-id', DEFAULT_EVENT_ID);
const subdomainRaw = argValue('subdomain', DEFAULT_SUBDOMAIN);
const shouldPublish = process.argv.includes('--publish');
if (!mongoose.Types.ObjectId.isValid(eventId)) {
throw new Error(`Invalid event id: ${eventId}`);
}
if (!fs.existsSync(SITE_JSON)) {
throw new Error(`Missing site.json at ${SITE_JSON}`);
}
await connectDB();
const event = await Event.findById(eventId).lean();
if (!event) throw new Error(`Event not found: ${eventId}`);
const template = await MicrositeV2Template.findOne({ slug: TEMPLATE_SLUG, isActive: true });
if (!template) {
throw new Error(`Active template "${TEMPLATE_SLUG}" not found. Run: npm run seed:crest-novel-template`);
}
const user = await findSeedUser();
const subdomain = normalizeSubdomain(subdomainRaw);
if (!subdomain) throw new Error(`Invalid subdomain: ${subdomainRaw}`);
const existing = await EventStaticMicrosite.findOne({ event: eventId });
const avail = await subdomainAvailability(subdomain, eventId, existing?._id?.toString());
if (!avail.available) throw new Error(avail.message || 'Subdomain not available');
const site = JSON.parse(fs.readFileSync(SITE_JSON, 'utf8'));
const oldEventId = site.meta?.eventId || '6a43e05fdc940a0d6e057f7b';
let config = siteJsonToConfig(site, eventId);
config = replaceEventIds(config, oldEventId, eventId);
const registrationForm = await findRegistrationForm(eventId);
const feedbackForm = await findFeedbackForm(eventId);
config = applyEventUrls(config, eventId, registrationForm?._id, feedbackForm?._id, event);
config.sections = resolveConfigAssetUrls(config.sections || {}, template.assetsBaseUrl);
await syncTemplateSiteConfigJs();
const payload = {
event: event._id,
organization: event.organization,
subdomain,
template: template._id,
config,
status: 'draft',
updatedBy: user._id,
};
let siteDoc;
if (existing) {
Object.assign(existing, payload);
await existing.save();
siteDoc = existing;
console.log(`Updated EventStaticMicrosite: ${existing._id}`);
} else {
siteDoc = await EventStaticMicrosite.create(payload);
console.log(`Created EventStaticMicrosite: ${siteDoc._id}`);
}
console.log(`Event: ${event.name} (${eventId})`);
console.log(`Template: ${template.name} (${template.slug})`);
console.log(`Subdomain: ${subdomain}`);
console.log(`Reg form: ${registrationForm?._id || '(none — using /register fallback)'}`);
console.log(`Feedback: ${feedbackForm?._id || '(none)'}`);
if (shouldPublish) {
console.log('Publishing...');
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);
});