Backfill release dates on startup and hard-lock undated lessons
This commit is contained in:
+108
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user