Files
biblical-guide.com/app/api/bible/seo-url/route.ts
Andrei 61a5180e2f feat: implement SEO-friendly URLs for Bible reader
- Add dynamic route structure /[locale]/bible/[version]/[book]/[chapter]
- Convert UUID-based URLs to readable format (e.g., /en/bible/eng-kjv/genesis/1)
- Implement automatic redirects from old URLs to new SEO-friendly format
- Add SEO metadata generation with proper titles, descriptions, and OpenGraph tags
- Create API endpoint for URL conversion between formats
- Update navigation in search results, bookmarks, and internal links
- Fix PWA manifest icons to correct dimensions (192x192, 512x512)
- Resolve JavaScript parameter passing issues between server and client components
- Maintain backward compatibility with existing bookmark and search functionality

Benefits:
- Improved SEO with descriptive URLs
- Better user experience with readable URLs
- Enhanced social media sharing
- Maintained full backward compatibility
2025-09-28 23:17:58 +00:00

151 lines
3.5 KiB
TypeScript

import { NextResponse } from 'next/server'
import { prisma } from '@/lib/db'
export const runtime = 'nodejs'
// Convert UUIDs to SEO-friendly slugs
export async function POST(request: Request) {
try {
const { versionId, bookId, chapter, locale } = await request.json()
if (!versionId || !bookId || !chapter) {
return NextResponse.json({
success: false,
error: 'Missing required parameters'
}, { status: 400 })
}
const version = await prisma.bibleVersion.findUnique({
where: { id: versionId }
})
const book = await prisma.bibleBook.findUnique({
where: { id: bookId }
})
if (!version || !book) {
return NextResponse.json({
success: false,
error: 'Version or book not found'
}, { status: 404 })
}
const versionSlug = version.abbreviation.toLowerCase()
const bookSlug = book.bookKey.toLowerCase()
const seoUrl = `/${locale}/bible/${versionSlug}/${bookSlug}/${chapter}`
return NextResponse.json({
success: true,
seoUrl,
version: {
id: version.id,
name: version.name,
abbreviation: version.abbreviation,
slug: versionSlug
},
book: {
id: book.id,
name: book.name,
bookKey: book.bookKey,
slug: bookSlug
}
})
} catch (error) {
console.error('Error generating SEO URL:', error)
return NextResponse.json({
success: false,
error: 'Internal server error'
}, { status: 500 })
}
}
// Convert SEO-friendly slugs to UUIDs
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const versionSlug = searchParams.get('version')
const bookSlug = searchParams.get('book')
const chapter = searchParams.get('chapter')
if (!versionSlug || !bookSlug || !chapter) {
return NextResponse.json({
success: false,
error: 'Missing required parameters'
}, { status: 400 })
}
const version = await prisma.bibleVersion.findFirst({
where: {
abbreviation: {
equals: versionSlug,
mode: 'insensitive'
}
}
})
if (!version) {
return NextResponse.json({
success: false,
error: 'Version not found'
}, { status: 404 })
}
const book = await prisma.bibleBook.findFirst({
where: {
versionId: version.id,
bookKey: {
equals: bookSlug,
mode: 'insensitive'
}
}
})
if (!book) {
return NextResponse.json({
success: false,
error: 'Book not found'
}, { status: 404 })
}
// Validate chapter exists
const chapterNum = parseInt(chapter)
const chapterRecord = await prisma.bibleChapter.findFirst({
where: {
bookId: book.id,
chapterNum: chapterNum
}
})
if (!chapterRecord) {
return NextResponse.json({
success: false,
error: 'Chapter not found'
}, { status: 404 })
}
return NextResponse.json({
success: true,
versionId: version.id,
bookId: book.id,
chapter: chapterNum,
version: {
id: version.id,
name: version.name,
abbreviation: version.abbreviation
},
book: {
id: book.id,
name: book.name,
bookKey: book.bookKey,
testament: book.testament
}
})
} catch (error) {
console.error('Error resolving SEO URL:', error)
return NextResponse.json({
success: false,
error: 'Internal server error'
}, { status: 500 })
}
}