- 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
151 lines
3.5 KiB
TypeScript
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 })
|
|
}
|
|
}
|