Remove podcast checklist backend, calendar events and reminder emails; v2.14.3

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-12 08:11:09 -04:00
1 parent 99b19faede
commit 13d0eb6651
12 files changed
+3 -534

No files matched your search

-133
View File
@@ -26,7 +26,6 @@ import {
QR_CODES_FILE,
REPLY_TEMPLATES_FILE,
REPLY_HISTORY_FILE,
PODCAST_CHECKLIST_FILE,
BACKUP_DIR,
UPLOADS_DIR,
UPLOADS_META_FILE,
@@ -34,7 +33,6 @@ import {
EPISODE_PLAYS_FILE,
ANALYTICS_EVENTS_FILE,
EMAIL_SETTINGS_FILE,
CALENDAR_EVENTS_FILE,
AUDIT_LOG_FILE,
WEBHOOKS_FILE,
STUDY_SESSIONS_FILE,
@@ -51,8 +49,6 @@ import {
MAX_STUDY_NOTES_PER_USER,
MAX_STUDY_NOTE_LENGTH,
MAX_STUDY_COMMENTS,
DEFAULT_PODCAST_CHECKLIST_TASKS,
buildDefaultPodcastChecklist,
} from './config.js'
import { state } from './state.js'
@@ -424,36 +420,6 @@ export function loadEmailSettingsFromDisk() {
})
}
// ── Calendar events ────────────────────────────────────────────────────────
export function queueCalendarEventsWrite() {
state.calendarEventsWritePromise = state.calendarEventsWritePromise
.then(async () => {
await mkdir(DATA_DIR, { recursive: true })
await writeFile(
CALENDAR_EVENTS_FILE,
JSON.stringify({ events: state.calendarEvents, updatedAt: new Date().toISOString() }, null, 2),
'utf8',
)
})
.catch(err => {
console.error('[calendar-events] failed to write:', err)
})
}
export function loadCalendarEventsFromDisk() {
return readFile(CALENDAR_EVENTS_FILE, 'utf8')
.then(raw => {
const parsed = JSON.parse(raw)
if (Array.isArray(parsed?.events)) {
state.calendarEvents = parsed.events
}
})
.catch(() => {
// No file yet — start with empty array
})
}
// ── Audit log ─────────────────────────────────────────────────────────────
export function appendAuditEntry(action, details) {
@@ -552,32 +518,6 @@ export async function fireWebhooks(eventType, payload) {
}
}
// ── Podcast checklist ──────────────────────────────────────────────────────
export function queuePodcastChecklistWrite() {
const updatedAt = new Date().toISOString()
state.podcastChecklistWritePromise = state.podcastChecklistWritePromise.then(async () => {
await mkdir(DATA_DIR, { recursive: true })
await writeFile(
PODCAST_CHECKLIST_FILE,
JSON.stringify({ checklist: state.podcastChecklist, updatedAt }, null, 2),
'utf8',
)
})
return state.podcastChecklistWritePromise
}
export function loadPodcastChecklistFromDisk() {
return readFile(PODCAST_CHECKLIST_FILE, 'utf8')
.then(raw => {
const parsed = JSON.parse(raw)
state.podcastChecklist = sanitizePodcastChecklist(parsed?.checklist)
})
.catch(() => {
state.podcastChecklist = buildDefaultPodcastChecklist()
})
}
// ── Study users ────────────────────────────────────────────────────────────
export function queueStudyUsersWrite() {
@@ -1074,7 +1014,6 @@ export async function createBackupSnapshot(reason = 'scheduled') {
reason,
adminContent: null,
draftContent: null,
podcastChecklist: state.podcastChecklist,
publishState: state.publishState,
hitStats: state.hitStats,
visitorStats: state.visitorStats,
@@ -1200,7 +1139,6 @@ export async function restoreFromBackup(filename) {
state.contactSubmissions = sanitizeLoadedContactSubmissions(parsed?.contactSubmissions)
state.replyTemplates = sanitizeReplyTemplates(parsed?.replyTemplates)
state.replyHistory = sanitizeReplyHistory(parsed?.replyHistory)
state.podcastChecklist = sanitizePodcastChecklist(parsed?.podcastChecklist)
queueHitStatsWrite()
queueVisitorStatsWrite()
@@ -1209,7 +1147,6 @@ export async function restoreFromBackup(filename) {
queueContactSubmissionsWrite()
queueReplyTemplatesWrite()
queueReplyHistoryWrite()
queuePodcastChecklistWrite()
await Promise.all([
state.hitStatsWritePromise,
@@ -1219,7 +1156,6 @@ export async function restoreFromBackup(filename) {
state.contactSubmissionsWritePromise,
state.replyTemplatesWritePromise,
state.replyHistoryWritePromise,
state.podcastChecklistWritePromise,
])
await refreshContentCaches()
await createBackupSnapshot('post-restore')
@@ -1393,75 +1329,6 @@ export function sanitizeReplyHistory(value) {
}))
}
function sanitizeChecklistTask(task) {
const label = typeof task?.label === 'string' ? task.label.trim().slice(0, 120) : ''
if (!label) return null
const phase = task?.phase === 'post' ? 'post' : 'pre'
const id = typeof task?.id === 'string' && task.id.trim() ? task.id.trim() : randomUUID()
return { id, label, phase }
}
export function sanitizePodcastChecklist(value) {
const fallback = buildDefaultPodcastChecklist()
const source = value && typeof value === 'object' && !Array.isArray(value) ? value : {}
const taskInput = Array.isArray(source.tasks) ? source.tasks : fallback.tasks
const seenTaskIds = new Set()
const tasks = []
for (const item of taskInput) {
const safeTask = sanitizeChecklistTask(item)
if (!safeTask) continue
if (seenTaskIds.has(safeTask.id)) continue
seenTaskIds.add(safeTask.id)
tasks.push(safeTask)
}
if (tasks.length === 0) {
for (const task of fallback.tasks) {
tasks.push({ ...task })
seenTaskIds.add(task.id)
}
}
const taskIds = tasks.map(task => task.id)
const episodesInput = Array.isArray(source.episodes) ? source.episodes : fallback.episodes
const episodes = []
for (const item of episodesInput) {
if (!item || typeof item !== 'object' || Array.isArray(item)) continue
const id = typeof item.id === 'string' && item.id.trim() ? item.id.trim() : randomUUID()
const series = typeof item.series === 'string' ? item.series.trim().slice(0, 80) : ''
const title = typeof item.title === 'string' ? item.title.trim().slice(0, 180) : ''
const rawEpisodeNumber = Number(item.episodeNumber)
const episodeNumber = Number.isFinite(rawEpisodeNumber) && rawEpisodeNumber >= 0
? Math.round(rawEpisodeNumber)
: null
const datePublished = typeof item.datePublished === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(item.datePublished.trim())
? item.datePublished.trim()
: ''
const sourceTasks = item.tasks && typeof item.tasks === 'object' && !Array.isArray(item.tasks)
? item.tasks
: {}
const taskState = {}
for (const taskId of taskIds) {
taskState[taskId] = sourceTasks[taskId] === true
}
const reminderDays = Number.isFinite(Number(item.reminderDays)) && Number(item.reminderDays) >= 0 ? Number(item.reminderDays) : 0
const reminderSentAt = typeof item.reminderSentAt === 'string' && item.reminderSentAt ? item.reminderSentAt : undefined
const startTime = typeof item.startTime === 'string' && /^\d{2}:\d{2}$/.test(item.startTime) ? item.startTime : undefined
const PROD_STATUSES = ['idea', 'recorded', 'edited', 'scheduled', 'published']
const productionStatus = PROD_STATUSES.includes(item.productionStatus) ? item.productionStatus : undefined
episodes.push({ id, series, episodeNumber, title, datePublished, expanded: item.expanded === true, tasks: taskState, reminderDays, reminderSentAt, ...(startTime ? { startTime } : {}), ...(productionStatus ? { productionStatus } : {}) })
}
if (episodes.length === 0) {
return fallback
}
return { tasks, episodes }
}
function normalizeStudyUsername(value) {
if (typeof value !== 'string') return ''
return value.trim().toLowerCase()