diff --git a/pages/admin.vue b/pages/admin.vue index a8ae4e9..e152a99 100644 --- a/pages/admin.vue +++ b/pages/admin.vue @@ -378,18 +378,42 @@ {{ retentionPolicySuccess }} -
- -

- Manually trigger the cleanup process to delete sermons according to the retention policy. - This will also run automatically on a daily schedule. -

+
+
+ +

+ Automatically archive sermons that are 1 day past their most recent date. + This moves them to the "Previous Sermons" list on the home page. + This also runs automatically on a daily schedule. +

+
+ +
+ +

+ Manually trigger the cleanup process to delete sermons according to the retention policy. + This will also run automatically on a daily schedule. +

+
+
+ +
+ {{ autoArchiveError }} +
+
+ {{ autoArchiveSuccess }}
@@ -450,6 +474,9 @@ const retentionPolicy = ref('forever') const savingRetentionPolicy = ref(false) const retentionPolicyError = ref('') const retentionPolicySuccess = ref('') +const autoArchiving = ref(false) +const autoArchiveError = ref('') +const autoArchiveSuccess = ref('') const cleaningUp = ref(false) const cleanupError = ref('') const cleanupSuccess = ref('') @@ -756,6 +783,27 @@ async function handleRetentionPolicyChange() { } } +async function handleAutoArchive() { + autoArchiveError.value = '' + autoArchiveSuccess.value = '' + autoArchiving.value = true + + try { + const result = await $fetch('/api/sermons/auto-archive', { + method: 'POST' + }) + + autoArchiveSuccess.value = result.message + + // Refresh sermon list after auto-archive + await refreshSermons() + } catch (e: any) { + autoArchiveError.value = e.data?.message || 'Failed to run auto-archive' + } finally { + autoArchiving.value = false + } +} + async function handleManualCleanup() { if (!confirm('Are you sure you want to run the cleanup process?\n\nThis will permanently delete all sermons older than the retention policy. This action cannot be undone.')) { return diff --git a/server/api/sermons/auto-archive.post.ts b/server/api/sermons/auto-archive.post.ts new file mode 100644 index 0000000..42f1980 --- /dev/null +++ b/server/api/sermons/auto-archive.post.ts @@ -0,0 +1,41 @@ +import { getSessionUsername } from '~/server/utils/auth' +import { autoArchiveOldSermons, getUserByUsername } from '~/server/utils/database' + +export default defineEventHandler(async (event) => { + // Check authentication + const username = await getSessionUsername(event) + + if (!username) { + throw createError({ + statusCode: 401, + message: 'Unauthorized' + }) + } + + // Check admin role + const user = getUserByUsername(username) + + if (!user || user.is_admin !== 1) { + throw createError({ + statusCode: 403, + message: 'Forbidden - Admin access required' + }) + } + + try { + const result = autoArchiveOldSermons() + + return { + success: true, + message: result.archivedCount > 0 + ? `Successfully auto-archived ${result.archivedCount} sermon(s)` + : 'No sermons needed to be archived', + archivedCount: result.archivedCount + } + } catch (error: any) { + throw createError({ + statusCode: 500, + message: error.message || 'Failed to auto-archive sermons' + }) + } +}) diff --git a/server/plugins/sermon-cleanup.ts b/server/plugins/sermon-cleanup.ts index 72aa638..e1ea12e 100644 --- a/server/plugins/sermon-cleanup.ts +++ b/server/plugins/sermon-cleanup.ts @@ -1,4 +1,4 @@ -import { getSetting, deleteOldSermons, deleteExpiredSessions, getDatabase } from '../utils/database' +import { getSetting, deleteOldSermons, deleteExpiredSessions, autoArchiveOldSermons, getDatabase } from '../utils/database' // Map retention policy to days const retentionDaysMap: Record = { @@ -12,6 +12,20 @@ const retentionDaysMap: Record = { '10_years': 3650 } +async function runSermonAutoArchive() { + try { + const result = autoArchiveOldSermons() + + if (result.archivedCount > 0) { + console.log(`[Sermon Auto-Archive] Archived ${result.archivedCount} sermon(s) that passed their most recent date`) + } else { + console.log(`[Sermon Auto-Archive] No sermons needed archiving`) + } + } catch (error) { + console.error('[Sermon Auto-Archive] Error running auto-archive:', error) + } +} + async function runSermonCleanup() { try { // Get the retention policy setting @@ -75,6 +89,7 @@ async function runPasswordResetCleanup() { async function runAllCleanupTasks() { console.log('[Cleanup] Starting scheduled cleanup tasks') + await runSermonAutoArchive() await runSermonCleanup() await runSessionCleanup() await runRateLimitCleanup() diff --git a/server/utils/database.ts b/server/utils/database.ts index 65b0eb6..8f508b0 100644 --- a/server/utils/database.ts +++ b/server/utils/database.ts @@ -372,6 +372,62 @@ export function archiveSermon(id: number) { return db.prepare('UPDATE sermons SET archived = 1 WHERE id = ?').run(id) } +export function unarchiveSermon(id: number) { + const db = getDatabase() + return db.prepare('UPDATE sermons SET archived = 0 WHERE id = ?').run(id) +} + +// Get the most recent date from a sermon (checking both primary date and additional dates) +function getMostRecentSermonDate(sermon: any): Date { + const dates: string[] = [sermon.date] + + // Add additional dates if they exist + if (sermon.dates) { + try { + const additionalDates = JSON.parse(sermon.dates) + dates.push(...additionalDates) + } catch { + // If parsing fails, just use primary date + } + } + + // Convert all dates to Date objects and find the most recent + const dateTimes = dates.map(dateStr => { + const date = new Date(dateStr + 'T00:00:00') + return date.getTime() + }) + + return new Date(Math.max(...dateTimes)) +} + +export function autoArchiveOldSermons(): { archivedCount: number } { + const db = getDatabase() + + // Get all non-archived sermons + const sermons = db.prepare('SELECT * FROM sermons WHERE archived = 0').all() as any[] + + const now = new Date() + now.setHours(0, 0, 0, 0) // Start of today + + let archivedCount = 0 + + for (const sermon of sermons) { + const mostRecentDate = getMostRecentSermonDate(sermon) + + // Calculate days difference + const oneDayAfterSermon = new Date(mostRecentDate) + oneDayAfterSermon.setDate(oneDayAfterSermon.getDate() + 1) + + // If it's been at least 1 day since the most recent sermon date, archive it + if (now >= oneDayAfterSermon) { + archiveSermon(sermon.id!) + archivedCount++ + } + } + + return { archivedCount } +} + export function getSermonBySlug(slug: string) { const db = getDatabase() return db.prepare('SELECT * FROM sermons WHERE slug = ?').get(slug) as Sermon | undefined