Complete sermon management system with Nuxt 4, authentication, SQLite database, QR codes, and Docker deployment

This commit is contained in:
Ryderjj89
2025-09-29 18:59:31 -04:00
commit c033410c2e
25 changed files with 1510 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
import { authenticateUser, createJWT } from '~/server/utils/auth'
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const { username, password } = body
if (!username || !password) {
throw createError({
statusCode: 400,
statusMessage: 'Username and password are required'
})
}
const user = await authenticateUser(username, password)
if (!user) {
throw createError({
statusCode: 401,
statusMessage: 'Invalid credentials'
})
}
const token = await createJWT(user)
setCookie(event, 'auth_token', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 // 7 days
})
return {
user: {
id: user.id,
username: user.username
}
}
})

View File

@@ -0,0 +1,6 @@
export default defineEventHandler(async (event) => {
// Clear the auth cookie
deleteCookie(event, 'auth_token')
return { success: true }
})

View File

@@ -0,0 +1,27 @@
import { verifyJWT } from '~/server/utils/auth'
export default defineEventHandler(async (event) => {
const token = getCookie(event, 'auth_token')
if (!token) {
throw createError({
statusCode: 401,
statusMessage: 'No authentication token provided'
})
}
const payload = await verifyJWT(token)
if (!payload) {
throw createError({
statusCode: 401,
statusMessage: 'Invalid authentication token'
})
}
return {
user: {
id: payload.userId,
username: payload.username
}
}
})

View File

@@ -0,0 +1,28 @@
import { getDatabase } from '~/server/utils/database'
export default defineEventHandler(async (event) => {
const db = await getDatabase()
const slug = getRouterParam(event, 'slug')
if (!slug) {
throw createError({
statusCode: 400,
statusMessage: 'Sermon slug is required'
})
}
const sermon = db.prepare('SELECT * FROM sermons WHERE slug = ?').get(slug) as any
if (!sermon) {
throw createError({
statusCode: 404,
statusMessage: 'Sermon not found'
})
}
// Parse JSON fields
return {
...sermon,
bibleReferences: sermon.bible_references ? JSON.parse(sermon.bible_references) : []
}
})

View File

@@ -0,0 +1,39 @@
import { getDatabase } from '~/server/utils/database'
import { verifyJWT } from '~/server/utils/auth'
export default defineEventHandler(async (event) => {
const db = await getDatabase()
// Check for time filter
const query = getQuery(event)
const timeFilter = query.time as string || '3months'
let dateFilter = ''
const now = new Date()
switch (timeFilter) {
case '3months':
const threeMonthsAgo = new Date(now.getFullYear(), now.getMonth() - 3, now.getDate())
dateFilter = threeMonthsAgo.toISOString()
break
case '6months':
const sixMonthsAgo = new Date(now.getFullYear(), now.getMonth() - 6, now.getDate())
dateFilter = sixMonthsAgo.toISOString()
break
case '1year':
const oneYearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate())
dateFilter = oneYearAgo.toISOString()
break
case 'all':
dateFilter = '1970-01-01'
break
}
const sermons = db.prepare(`
SELECT * FROM sermons
WHERE date >= ?
ORDER BY date DESC
`).all(dateFilter) as any[]
return sermons
})

View File

@@ -0,0 +1,43 @@
import { getDatabase } from '~/server/utils/database'
import { verifyJWT, generateSlug } from '~/server/utils/auth'
export default defineEventHandler(async (event) => {
const db = await getDatabase()
const body = await readBody(event)
const { title, date, bibleReferences, personalApplication, pastorChallenge } = body
if (!title || !date) {
throw createError({
statusCode: 400,
statusMessage: 'Title and date are required'
})
}
const slug = generateSlug(title, date)
try {
const result = db.prepare(`
INSERT INTO sermons (title, date, slug, bible_references, personal_application, pastor_challenge)
VALUES (?, ?, ?, ?, ?, ?)
`).run(title, date, slug, JSON.stringify(bibleReferences || []), personalApplication || '', pastorChallenge || '')
return {
id: result.lastInsertRowid,
title,
date,
slug,
bibleReferences: bibleReferences || [],
personalApplication: personalApplication || '',
pastorChallenge: pastorChallenge || ''
}
} catch (error: any) {
if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
throw createError({
statusCode: 409,
statusMessage: 'A sermon with this date already exists'
})
}
throw error
}
})