Files
biblical-guide.com/scripts/old/import-english-versioned.ts
Andrei 95070e5369 Add comprehensive page management system to admin dashboard
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>
2025-09-24 07:26:25 +00:00

286 lines
9.5 KiB
TypeScript

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
interface ApiBibleBook {
id: string
bibleId: string
abbreviation: string
name: string
nameLong: string
}
interface ApiBibleChapter {
id: string
bibleId: string
bookId: string
number: string
reference: string
}
interface ApiBibleVerse {
id: string
orgId: string
bookId: string
chapterId: string
bibleId: string
reference: string
content: string
verseCount: number
}
interface ApiBibleResponse<T> {
data: T
}
const API_KEY = process.env.API_BIBLE_KEY || '7b42606f8f809e155c9b0742c4f1849b'
const API_BASE = 'https://api.scripture.api.bible/v1'
const BIBLE_ID = 'bba9f40183526463-01' // Berean Standard Bible
async function apiFetch<T>(endpoint: string): Promise<T> {
const response = await fetch(`${API_BASE}${endpoint}`, {
headers: {
'api-key': API_KEY
}
})
if (!response.ok) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`)
}
const data = await response.json()
return data
}
function cleanHtmlContent(htmlContent: string): string {
return htmlContent
.replace(/<[^>]*>/g, '') // Remove HTML tags
.replace(/\s+/g, ' ') // Normalize whitespace
.replace(/^\d+\s*/, '') // Remove verse numbers at start
.trim()
}
function parseVerseNumber(verseId: string): number {
const parts = verseId.split('.')
return parseInt(parts[2]) || 1
}
function parseChapterNumber(chapterId: string): number {
const parts = chapterId.split('.')
return parseInt(parts[1]) || 1
}
function getTestament(bookId: string): string {
const oldTestamentBooks = [
'GEN', 'EXO', 'LEV', 'NUM', 'DEU', 'JOS', 'JDG', 'RUT',
'1SA', '2SA', '1KI', '2KI', '1CH', '2CH', 'EZR', 'NEH',
'EST', 'JOB', 'PSA', 'PRO', 'ECC', 'SNG', 'ISA', 'JER',
'LAM', 'EZK', 'DAN', 'HOS', 'JOL', 'AMO', 'OBA', 'JON',
'MIC', 'NAM', 'HAB', 'ZEP', 'HAG', 'ZEC', 'MAL'
]
return oldTestamentBooks.includes(bookId) ? 'Old Testament' : 'New Testament'
}
function getBookOrderNumber(bookId: string): number {
const bookOrder: Record<string, number> = {
// Old Testament
'GEN': 1, 'EXO': 2, 'LEV': 3, 'NUM': 4, 'DEU': 5, 'JOS': 6, 'JDG': 7, 'RUT': 8,
'1SA': 9, '2SA': 10, '1KI': 11, '2KI': 12, '1CH': 13, '2CH': 14, 'EZR': 15, 'NEH': 16,
'EST': 17, 'JOB': 18, 'PSA': 19, 'PRO': 20, 'ECC': 21, 'SNG': 22, 'ISA': 23, 'JER': 24,
'LAM': 25, 'EZK': 26, 'DAN': 27, 'HOS': 28, 'JOL': 29, 'AMO': 30, 'OBA': 31, 'JON': 32,
'MIC': 33, 'NAM': 34, 'HAB': 35, 'ZEP': 36, 'HAG': 37, 'ZEC': 38, 'MAL': 39,
// New Testament
'MAT': 40, 'MRK': 41, 'LUK': 42, 'JHN': 43, 'ACT': 44, 'ROM': 45, '1CO': 46, '2CO': 47,
'GAL': 48, 'EPH': 49, 'PHP': 50, 'COL': 51, '1TH': 52, '2TH': 53, '1TI': 54, '2TI': 55,
'TIT': 56, 'PHM': 57, 'HEB': 58, 'JAS': 59, '1PE': 60, '2PE': 61, '1JN': 62, '2JN': 63,
'3JN': 64, 'JUD': 65, 'REV': 66
}
return bookOrder[bookId] || 999
}
function getBookKey(bookId: string): string {
const keyMap: Record<string, string> = {
'GEN': 'genesis', 'EXO': 'exodus', 'LEV': 'leviticus', 'NUM': 'numbers', 'DEU': 'deuteronomy',
'JOS': 'joshua', 'JDG': 'judges', 'RUT': 'ruth', '1SA': '1_samuel', '2SA': '2_samuel',
'1KI': '1_kings', '2KI': '2_kings', '1CH': '1_chronicles', '2CH': '2_chronicles',
'EZR': 'ezra', 'NEH': 'nehemiah', 'EST': 'esther', 'JOB': 'job', 'PSA': 'psalms',
'PRO': 'proverbs', 'ECC': 'ecclesiastes', 'SNG': 'song_of_songs', 'ISA': 'isaiah',
'JER': 'jeremiah', 'LAM': 'lamentations', 'EZK': 'ezekiel', 'DAN': 'daniel',
'HOS': 'hosea', 'JOL': 'joel', 'AMO': 'amos', 'OBA': 'obadiah', 'JON': 'jonah',
'MIC': 'micah', 'NAM': 'nahum', 'HAB': 'habakkuk', 'ZEP': 'zephaniah',
'HAG': 'haggai', 'ZEC': 'zechariah', 'MAL': 'malachi',
'MAT': 'matthew', 'MRK': 'mark', 'LUK': 'luke', 'JHN': 'john', 'ACT': 'acts',
'ROM': 'romans', '1CO': '1_corinthians', '2CO': '2_corinthians', 'GAL': 'galatians',
'EPH': 'ephesians', 'PHP': 'philippians', 'COL': 'colossians', '1TH': '1_thessalonians',
'2TH': '2_thessalonians', '1TI': '1_timothy', '2TI': '2_timothy', 'TIT': 'titus',
'PHM': 'philemon', 'HEB': 'hebrews', 'JAS': 'james', '1PE': '1_peter', '2PE': '2_peter',
'1JN': '1_john', '2JN': '2_john', '3JN': '3_john', 'JUD': 'jude', 'REV': 'revelation'
}
return keyMap[bookId] || bookId.toLowerCase()
}
async function importEnglishBible() {
console.log('Starting English Bible import with versioned schema...')
try {
// Step 1: Create English Bible version
console.log('Creating English Bible version...')
const englishVersion = await prisma.bibleVersion.upsert({
where: {
abbreviation_language: {
abbreviation: 'BSB',
language: 'en'
}
},
update: {},
create: {
name: 'Berean Standard Bible',
abbreviation: 'BSB',
language: 'en',
description: 'The Berean Standard Bible in English',
isDefault: true
}
})
console.log(`Created English version: ${englishVersion.id}`)
// Step 2: Get all books for the Bible
console.log('Fetching books from API.Bible...')
const booksResponse = await apiFetch<ApiBibleResponse<ApiBibleBook[]>>(`/bibles/${BIBLE_ID}/books`)
const books = booksResponse.data.filter(book =>
book.id !== 'INT' && // Skip introduction
!book.id.includes('intro') // Skip intro chapters
)
console.log(`Found ${books.length} books`)
let totalVersesImported = 0
// Import first 5 books to respect API rate limits
for (const book of books.slice(0, 5)) {
console.log(`Processing ${book.name} (${book.id})...`)
const orderNum = getBookOrderNumber(book.id)
const testament = getTestament(book.id)
const bookKey = getBookKey(book.id)
// Create or update book
const createdBook = await prisma.bibleBook.upsert({
where: {
versionId_orderNum: {
versionId: englishVersion.id,
orderNum: orderNum
}
},
update: {},
create: {
versionId: englishVersion.id,
name: book.name,
testament: testament,
orderNum: orderNum,
bookKey: bookKey
}
})
// Get chapters for this book
const chaptersResponse = await apiFetch<ApiBibleResponse<ApiBibleChapter[]>>(`/bibles/${BIBLE_ID}/books/${book.id}/chapters`)
const chapters = chaptersResponse.data.filter(chapter =>
chapter.number !== 'intro' && // Skip introduction chapters
!isNaN(parseInt(chapter.number)) // Only numeric chapters
)
console.log(` Found ${chapters.length} chapters`)
// Import first 3 chapters to respect API rate limits
for (const chapter of chapters.slice(0, 3)) {
const chapterNum = parseChapterNumber(chapter.id)
console.log(` Processing chapter ${chapterNum}...`)
// Create or update chapter
const createdChapter = await prisma.bibleChapter.upsert({
where: {
bookId_chapterNum: {
bookId: createdBook.id,
chapterNum: chapterNum
}
},
update: {},
create: {
bookId: createdBook.id,
chapterNum: chapterNum
}
})
// Get verses for this chapter
const versesResponse = await apiFetch<ApiBibleResponse<ApiBibleVerse[]>>(`/bibles/${BIBLE_ID}/chapters/${chapter.id}/verses`)
console.log(` Found ${versesResponse.data.length} verses`)
// Process verses in smaller batches to respect API rate limits
const limitedVerses = versesResponse.data.slice(0, 15) // Limit to first 15 verses per chapter
for (let i = 0; i < limitedVerses.length; i += 5) {
const verseBatch = limitedVerses.slice(i, i + 5)
for (const verseRef of verseBatch) {
try {
// Get full verse content
const verseResponse = await apiFetch<ApiBibleResponse<ApiBibleVerse>>(`/bibles/${BIBLE_ID}/verses/${verseRef.id}`)
const verse = verseResponse.data
const verseNum = parseVerseNumber(verse.id)
const cleanText = cleanHtmlContent(verse.content)
if (cleanText.length > 0) {
// Create or update verse
await prisma.bibleVerse.upsert({
where: {
chapterId_verseNum: {
chapterId: createdChapter.id,
verseNum: verseNum
}
},
update: { text: cleanText },
create: {
chapterId: createdChapter.id,
verseNum: verseNum,
text: cleanText
}
})
totalVersesImported++
}
// Rate limiting - longer delay between requests to respect API limits
await new Promise(resolve => setTimeout(resolve, 300))
} catch (error) {
console.warn(` Warning: Failed to fetch verse ${verseRef.id}:`, error)
}
}
}
}
}
console.log(`\nEnglish Bible import completed! Imported ${totalVersesImported} verses.`)
} catch (error) {
console.error('Error importing English Bible:', error)
throw error
}
}
// Run the import
importEnglishBible()
.then(() => {
console.log('English Bible import completed successfully!')
process.exit(0)
})
.catch((error) => {
console.error('Import failed:', error)
process.exit(1)
})
.finally(() => prisma.$disconnect())