diff --git a/entrypoint.sh b/entrypoint.sh index 841b6e0..dfd41fa 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -102,4 +102,112 @@ NODE merge_seed_platform_links "$DATA_DIR/admin-content.json" "$SEED_DIR/admin-content.json" merge_seed_platform_links "$DATA_DIR/admin-content-draft.json" "$SEED_DIR/admin-content-draft.json" +# Backfill missing lesson releasedAt values from seeded defaults on upgrades. +# This keeps persistent volumes compatible with release-date access gating. +merge_seed_study_release_dates() { + target_file="$1" + seed_file="$2" + + [ -f "$target_file" ] || return 0 + [ -f "$seed_file" ] || return 0 + + node - "$target_file" "$seed_file" <<'NODE' +const fs = require('fs') + +const [targetFile, seedFile] = process.argv.slice(2) + +function readJson(file) { + return JSON.parse(fs.readFileSync(file, 'utf8')) +} + +function asArray(value) { + return Array.isArray(value) ? value : [] +} + +function mergeStudyDates(targetStudies, seedStudies) { + let changed = false + const seedBySlug = new Map() + + for (const seedStudy of asArray(seedStudies)) { + if (!seedStudy || typeof seedStudy !== 'object') continue + if (typeof seedStudy.slug !== 'string' || !seedStudy.slug) continue + seedBySlug.set(seedStudy.slug, seedStudy) + } + + for (const targetStudy of asArray(targetStudies)) { + if (!targetStudy || typeof targetStudy !== 'object') continue + if (typeof targetStudy.slug !== 'string' || !targetStudy.slug) continue + + const seedStudy = seedBySlug.get(targetStudy.slug) + if (!seedStudy) continue + + const seedSectionById = new Map() + for (const seedSection of asArray(seedStudy.sections)) { + if (!seedSection || typeof seedSection !== 'object') continue + if (typeof seedSection.id !== 'string' || !seedSection.id) continue + seedSectionById.set(seedSection.id, seedSection) + } + + for (const targetSection of asArray(targetStudy.sections)) { + if (!targetSection || typeof targetSection !== 'object') continue + if (typeof targetSection.id !== 'string' || !targetSection.id) continue + if (typeof targetSection.releasedAt === 'string' && targetSection.releasedAt.trim()) continue + + const seedSection = seedSectionById.get(targetSection.id) + if (!seedSection || typeof seedSection.releasedAt !== 'string' || !seedSection.releasedAt.trim()) continue + + targetSection.releasedAt = seedSection.releasedAt + changed = true + } + } + + return changed +} + +function mergeLegacyColossiansDates(targetSections, seedSections) { + let changed = false + const seedById = new Map() + + for (const seedSection of asArray(seedSections)) { + if (!seedSection || typeof seedSection !== 'object') continue + if (typeof seedSection.id !== 'string' || !seedSection.id) continue + seedById.set(seedSection.id, seedSection) + } + + for (const targetSection of asArray(targetSections)) { + if (!targetSection || typeof targetSection !== 'object') continue + if (typeof targetSection.id !== 'string' || !targetSection.id) continue + if (typeof targetSection.releasedAt === 'string' && targetSection.releasedAt.trim()) continue + + const seedSection = seedById.get(targetSection.id) + if (!seedSection || typeof seedSection.releasedAt !== 'string' || !seedSection.releasedAt.trim()) continue + + targetSection.releasedAt = seedSection.releasedAt + changed = true + } + + return changed +} + +const target = readJson(targetFile) +const seed = readJson(seedFile) + +if (!target.siteContent || !seed.siteContent) process.exit(0) + +const targetContent = target.siteContent +const seedContent = seed.siteContent + +const changedStudies = mergeStudyDates(targetContent.studies, seedContent.studies) +const changedLegacy = mergeLegacyColossiansDates(targetContent.colossiansStudySections, seedContent.colossiansStudySections) + +if (!changedStudies && !changedLegacy) process.exit(0) + +fs.writeFileSync(targetFile, `${JSON.stringify(target, null, 2)}\n`) +console.log(`[siteforge] Backfilled missing releasedAt values in ${targetFile}`) +NODE +} + +merge_seed_study_release_dates "$DATA_DIR/admin-content.json" "$SEED_DIR/admin-content.json" +merge_seed_study_release_dates "$DATA_DIR/admin-content-draft.json" "$SEED_DIR/admin-content-draft.json" + exec node server.js diff --git a/server.js b/server.js index f53846b..8763847 100644 --- a/server.js +++ b/server.js @@ -113,9 +113,9 @@ function sanitizeUrl(value) { function isSectionReleased(section) { if (!section || typeof section !== 'object') return false - if (typeof section.releasedAt !== 'string' || !section.releasedAt.trim()) return true + if (typeof section.releasedAt !== 'string' || !section.releasedAt.trim()) return false const releaseTime = Date.parse(section.releasedAt) - if (!Number.isFinite(releaseTime)) return true + if (!Number.isFinite(releaseTime)) return false return releaseTime <= Date.now() } diff --git a/src/colossiansStudy.tsx b/src/colossiansStudy.tsx index 5efa383..f19399c 100644 --- a/src/colossiansStudy.tsx +++ b/src/colossiansStudy.tsx @@ -79,9 +79,9 @@ function isComingSoon(section: StudySection): boolean { } function isSectionReleased(section: StudySection): boolean { - if (!section.releasedAt) return true + if (!section.releasedAt) return false const releaseDate = new Date(section.releasedAt) - if (Number.isNaN(releaseDate.getTime())) return true + if (Number.isNaN(releaseDate.getTime())) return false return releaseDate <= new Date() }