Features added: - Database schema for pages and media files with content types (Rich Text, HTML, Markdown) - Admin API routes for full page CRUD operations - Image upload functionality with file management - Rich text editor using TinyMCE with image insertion - Admin interface for creating/editing pages with SEO options - Dynamic navigation and footer integration - Public page display routes with proper SEO metadata - Support for featured images and content excerpts Admin features: - Create/edit/delete pages with rich content editor - Upload and manage images through media library - Configure pages to appear in navigation or footer - Set page status (Draft, Published, Archived) - SEO title and description management - Real-time preview of content changes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
import { prisma } from '@/lib/db'
|
|
|
|
export class CacheManager {
|
|
static async get(key: string): Promise<string | null> {
|
|
try {
|
|
const result = await prisma.$queryRaw<{ value: string }[]>`
|
|
SELECT value FROM verse_cache
|
|
WHERE key = ${key}
|
|
AND expires_at > NOW()
|
|
LIMIT 1
|
|
`
|
|
return result[0]?.value || null
|
|
} catch (error) {
|
|
console.error('Cache get error:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
static async set(key: string, value: string, ttl: number = 3600): Promise<void> {
|
|
try {
|
|
const expiresAt = new Date(Date.now() + ttl * 1000)
|
|
await prisma.$executeRaw`
|
|
INSERT INTO verse_cache (key, value, expires_at)
|
|
VALUES (${key}, ${value}, ${expiresAt})
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = EXCLUDED.value, expires_at = EXCLUDED.expires_at
|
|
`
|
|
} catch (error) {
|
|
console.error('Cache set error:', error)
|
|
}
|
|
}
|
|
|
|
static async invalidate(pattern: string): Promise<void> {
|
|
try {
|
|
await prisma.$executeRaw`
|
|
DELETE FROM verse_cache WHERE key LIKE ${pattern}
|
|
`
|
|
} catch (error) {
|
|
console.error('Cache invalidate error:', error)
|
|
}
|
|
}
|
|
|
|
static async clear(): Promise<void> {
|
|
try {
|
|
await prisma.$executeRaw`DELETE FROM verse_cache`
|
|
} catch (error) {
|
|
console.error('Cache clear error:', error)
|
|
}
|
|
}
|
|
|
|
static async cleanup(): Promise<void> {
|
|
try {
|
|
await prisma.$executeRaw`DELETE FROM verse_cache WHERE expires_at < NOW()`
|
|
} catch (error) {
|
|
console.error('Cache cleanup error:', error)
|
|
}
|
|
}
|
|
|
|
// Helper methods for specific cache patterns
|
|
static getChapterKey(bookId: string, chapterNum: number, versionId?: string): string {
|
|
return versionId ? `chapter:${bookId}:${chapterNum}:${versionId}` : `chapter:${bookId}:${chapterNum}`
|
|
}
|
|
|
|
static getSearchKey(query: string, limit: number): string {
|
|
return `search:${query.toLowerCase()}:${limit}`
|
|
}
|
|
|
|
static getUserBookmarksKey(userId: string): string {
|
|
return `bookmarks:${userId}`
|
|
}
|
|
}
|