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 { data: T } const API_KEY = process.env.API_BIBLE_KEY || '7b42606f8f809e155c9b0742c4f1849b' const API_BASE = 'https://api.scripture.api.bible/v1' // English Bible for standard structure const BIBLE_ID = 'bba9f40183526463-01' // Berean Standard Bible async function apiFetch(endpoint: string): Promise { 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 { // Remove HTML tags and extract plain text 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 { // Extract verse number from ID like "GEN.1.5" const parts = verseId.split('.') return parseInt(parts[2]) || 1 } function parseChapterNumber(chapterId: string): number { // Extract chapter number from ID like "GEN.1" const parts = chapterId.split('.') return parseInt(parts[1]) || 1 } function getTestament(bookId: string): string { // Old Testament books (standard order) 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 { // Standard Biblical book order const bookOrder: Record = { // 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 } async function importFromApiBible() { console.log('Starting API.Bible import...') try { // Get all books for the Bible console.log('Fetching books...') const booksResponse = await apiFetch>(`/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 for (const book of books.slice(0, 2)) { // Limit to first 2 books for sample structure console.log(`Processing ${book.name} (${book.id})...`) const orderNum = getBookOrderNumber(book.id) const testament = getTestament(book.id) // Create book const createdBook = await prisma.bibleBook.upsert({ where: { id: orderNum }, update: {}, create: { id: orderNum, name: book.name, testament: testament, orderNum: orderNum } }) // Get chapters for this book const chaptersResponse = await apiFetch>(`/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`) for (const chapter of chapters.slice(0, 2)) { // Limit to first 2 chapters for sample const chapterNum = parseChapterNumber(chapter.id) console.log(` Processing chapter ${chapterNum}...`) // Create chapter const createdChapter = await prisma.bibleChapter.upsert({ where: { bookId_chapterNum: { bookId: orderNum, chapterNum: chapterNum } }, update: {}, create: { bookId: orderNum, chapterNum: chapterNum } }) // Get verses for this chapter const versesResponse = await apiFetch>(`/bibles/${BIBLE_ID}/chapters/${chapter.id}/verses`) console.log(` Found ${versesResponse.data.length} verses`) // Process only first 5 verses for sample structure const sampleVerses = versesResponse.data.slice(0, 5) for (let i = 0; i < sampleVerses.length; i += 5) { const verseBatch = sampleVerses.slice(i, i + 5) for (const verseRef of verseBatch) { try { // Get full verse content const verseResponse = await apiFetch>(`/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 verse await prisma.bibleVerse.upsert({ where: { chapterId_verseNum_version: { chapterId: createdChapter.id, verseNum: verseNum, version: 'EN' } }, update: {}, create: { chapterId: createdChapter.id, verseNum: verseNum, text: cleanText, version: 'EN' } }) totalVersesImported++ } // Rate limiting - small delay between requests await new Promise(resolve => setTimeout(resolve, 100)) } catch (error) { console.warn(` Warning: Failed to fetch verse ${verseRef.id}:`, error) } } } } } console.log(`\nAPI.Bible import completed! Imported ${totalVersesImported} verses.`) // Create search function for English content console.log('Creating English search function...') await prisma.$executeRaw` CREATE OR REPLACE FUNCTION search_verses_en(search_query TEXT, limit_count INT DEFAULT 10) RETURNS TABLE( verse_id TEXT, book_name TEXT, chapter_num INT, verse_num INT, verse_text TEXT, rank REAL ) AS $$ BEGIN RETURN QUERY SELECT v.id::TEXT, b.name, c."chapterNum", v."verseNum", v.text, CASE WHEN v.text ILIKE '%' || search_query || '%' THEN 1.0 ELSE ts_rank(to_tsvector('english', v.text), plainto_tsquery('english', search_query)) END as rank FROM "BibleVerse" v JOIN "BibleChapter" c ON v."chapterId" = c.id JOIN "BibleBook" b ON c."bookId" = b.id WHERE v.version = 'EN' AND (v.text ILIKE '%' || search_query || '%' OR to_tsvector('english', v.text) @@ plainto_tsquery('english', search_query)) ORDER BY rank DESC, b."orderNum", c."chapterNum", v."verseNum" LIMIT limit_count; END; $$ LANGUAGE plpgsql; ` console.log('English search function created successfully!') } catch (error) { console.error('Error importing from API.Bible:', error) throw error } } // Run the import importFromApiBible() .then(() => { console.log('API.Bible import completed successfully!') process.exit(0) }) .catch((error) => { console.error('Import failed:', error) process.exit(1) }) .finally(() => prisma.$disconnect())