diff --git a/server/helpers.js b/server/helpers.js index ba86bac..7426b23 100644 --- a/server/helpers.js +++ b/server/helpers.js @@ -226,26 +226,36 @@ function sanitizeArchivedSeriesNotes(value) { } function sanitizeColossiansStudySections(value) { + function normalizeReleasedAt(value) { + if (typeof value !== 'string' || !value.trim()) return '' + const parsed = Date.parse(value.trim()) + if (!Number.isFinite(parsed)) return '' + return new Date(parsed).toISOString().replace('.000Z', 'Z') + } + const source = Array.isArray(value) ? value : [] return source .filter(item => item && typeof item === 'object') - .map(item => ({ - id: typeof item.id === 'string' && item.id.trim() ? item.id.trim() : randomUUID(), - chapter: Number.isInteger(item.chapter) && item.chapter >= 1 && item.chapter <= 999 ? item.chapter : 1, - reference: typeof item.reference === 'string' ? item.reference.trim().slice(0, 40) : '', - title: typeof item.title === 'string' ? item.title.trim().slice(0, 160) : '', - audioEmbedUrl: sanitizeUrl(item.audioEmbedUrl), - passageText: typeof item.passageText === 'string' ? item.passageText.trim().slice(0, 12000) : '', - summary: typeof item.summary === 'string' ? item.summary.trim().slice(0, 600) : '', - commentary: typeof item.commentary === 'string' ? item.commentary.trim().slice(0, 12000) : '', - greekNotes: Array.isArray(item.greekNotes) - ? item.greekNotes.filter(note => typeof note === 'string').map(note => note.trim()).filter(Boolean).slice(0, 20) - : [], - studyQuestions: Array.isArray(item.studyQuestions) - ? item.studyQuestions.filter(question => typeof question === 'string').map(question => question.trim()).filter(Boolean).slice(0, 20) - : [], - ...(typeof item.releasedAt === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(item.releasedAt) ? { releasedAt: item.releasedAt } : {}), - })) + .map(item => { + const releasedAt = normalizeReleasedAt(item.releasedAt) + return { + id: typeof item.id === 'string' && item.id.trim() ? item.id.trim() : randomUUID(), + chapter: Number.isInteger(item.chapter) && item.chapter >= 1 && item.chapter <= 999 ? item.chapter : 1, + reference: typeof item.reference === 'string' ? item.reference.trim().slice(0, 40) : '', + title: typeof item.title === 'string' ? item.title.trim().slice(0, 160) : '', + audioEmbedUrl: sanitizeUrl(item.audioEmbedUrl), + passageText: typeof item.passageText === 'string' ? item.passageText.trim().slice(0, 12000) : '', + summary: typeof item.summary === 'string' ? item.summary.trim().slice(0, 600) : '', + commentary: typeof item.commentary === 'string' ? item.commentary.trim().slice(0, 12000) : '', + greekNotes: Array.isArray(item.greekNotes) + ? item.greekNotes.filter(note => typeof note === 'string').map(note => note.trim()).filter(Boolean).slice(0, 20) + : [], + studyQuestions: Array.isArray(item.studyQuestions) + ? item.studyQuestions.filter(question => typeof question === 'string').map(question => question.trim()).filter(Boolean).slice(0, 20) + : [], + ...(releasedAt ? { releasedAt } : {}), + } + }) .filter(item => item.title || item.summary || item.commentary || item.greekNotes.length > 0 || item.studyQuestions.length > 0 || item.passageText) }