perf: Comprehensive efficiency optimizations

Implemented all 5 critical efficiency improvements to optimize
performance, reduce resource usage, and improve scalability.

## 1. Database Indexes
- Added indexes on sermon_notes foreign keys (user_id, sermon_id)
- Added composite index on sermons (archived, date DESC)
- Added indexes on frequently queried columns across all tables
- Impact: Faster queries as data grows, better JOIN performance

## 2. Eliminated N+1 Query Pattern
- Reduced 2 API calls to 1 on home page load
- Changed from separate active/archived fetches to single call
- Filter archived sermons client-side using computed properties
- Impact: 50% reduction in HTTP requests per page load

## 3. Scheduled Database Cleanup
- Extended existing plugin to clean expired sessions hourly
- Added cleanup for expired rate limits every hour
- Added cleanup for expired password reset codes every hour
- Sermon cleanup continues to run daily based on retention policy
- Impact: Prevents database table growth, better performance

## 4. Multi-stage Docker Build
- Implemented 3-stage build: deps -> builder -> runtime
- Separated build-time and runtime dependencies
- Added non-root user (nuxt:nodejs) for security
- Integrated dumb-init for proper signal handling
- Added health check endpoint at /api/health
- Impact: Smaller image size, faster deployments, better security

## 5. HTTP Caching
- Static assets: 1 year cache (immutable)
- Logos/images: 1 year cache (immutable)
- API routes: No cache (always fresh)
- HTML pages: 10 minute cache with revalidation
- Impact: Reduced bandwidth, faster page loads, less server load

All optimizations follow best practices and maintain backward
compatibility with existing functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-06 08:01:45 -05:00
parent 0126b7e835
commit 287284c2fe
6 changed files with 180 additions and 37 deletions

View File

@@ -200,17 +200,20 @@ const greeting = computed(() => {
return `${timeGreeting}, ${firstName.value}!`
})
// Fetch non-archived sermons for the most recent sermon
const { data: activeSermons } = await useFetch('/api/sermons?includeArchived=false')
// Fetch archived sermons for the previous sermons dropdown
const { data: archivedSermons } = await useFetch('/api/sermons?includeArchived=true')
// Fetch all sermons in a single request (more efficient than two separate calls)
const { data: allSermons } = await useFetch('/api/sermons?includeArchived=true')
const selectedSermonSlug = ref('')
// Filter active (non-archived) sermons client-side
const activeSermons = computed(() => {
if (!allSermons.value) return []
return allSermons.value.filter((s: any) => s.archived !== 1)
})
// Get today's sermon (if one exists for today's date)
const todaysSermon = computed(() => {
if (!activeSermons.value || activeSermons.value.length === 0) return null
if (activeSermons.value.length === 0) return null
const today = new Date()
today.setHours(0, 0, 0, 0)
@@ -222,7 +225,7 @@ const todaysSermon = computed(() => {
if (sermonDate.getTime() === today.getTime()) {
return { ...s, displayDate: s.date }
}
// Check additional dates if they exist
if (s.dates) {
try {
@@ -232,7 +235,7 @@ const todaysSermon = computed(() => {
additionalDate.setHours(0, 0, 0, 0)
return additionalDate.getTime() === today.getTime()
})
if (matchingDate) {
return { ...s, displayDate: matchingDate }
}
@@ -241,13 +244,13 @@ const todaysSermon = computed(() => {
}
}
}
return null
})
// Get upcoming sermons (future dates)
const upcomingSermons = computed(() => {
if (!activeSermons.value) return []
if (activeSermons.value.length === 0) return []
const today = new Date()
today.setHours(0, 0, 0, 0)
@@ -289,8 +292,8 @@ const upcomingSermons = computed(() => {
// Get archived sermons only for the previous sermons dropdown
const previousSermons = computed(() => {
if (!archivedSermons.value) return []
return archivedSermons.value.filter((s: any) => s.archived === 1)
if (!allSermons.value) return []
return allSermons.value.filter((s: any) => s.archived === 1)
})
// Check if there are any current or upcoming sermons