Skip to main content
Glama
database.ts•6.22 kB
import { EGWDatabase } from '@surgbc/egw-writings-shared'; import { SearchResult, BookSummary, SearchFilters } from '@/types'; class DatabaseService { private db: EGWDatabase; constructor() { this.db = new EGWDatabase(); } // Search functionality async searchWritings( query: string, filters: Partial<SearchFilters> = {}, limit = 20, offset = 0 ): Promise<{ results: SearchResult[]; total: number }> { try { const rawResults = await this.db.search(query, limit, offset); const total = await this.db.searchCount(query); const results: SearchResult[] = rawResults.map((result: any, index) => ({ id: `${result.para_id || index}`, title: result.refcode_short || 'Untitled', excerpt: this.extractExcerpt(result.snippet || result.content), bookTitle: result.pub_name || 'Unknown Book', author: result.author || 'Ellen G. White', reference: result.refcode_long || result.refcode_short || '', relevanceScore: 1 - (index / rawResults.length), // Simple relevance scoring highlightedText: this.highlightSearchTerms(result.snippet || result.content, query) })); return { results, total }; } catch (error) { console.error('Search error:', error); throw new Error('Failed to search writings'); } } // Book management async getBooks(language = 'en', limit = 50): Promise<BookSummary[]> { try { const books = await this.db.getBooks(language); const limitedBooks = books.slice(0, limit); const bookPromises = limitedBooks.map(async (book: any) => ({ id: book.book_id, title: book.title, author: book.author, year: book.pub_year, pages: book.npages, language: book.lang, description: book.description || '', categories: this.extractCategories(book), downloadedContent: await this.hasDownloadedContent(book.book_id) })); return await Promise.all(bookPromises); } catch (error) { console.error('Error fetching books:', error); throw new Error('Failed to fetch books'); } } async getBook(bookId: number): Promise<BookSummary | null> { try { const book = await this.db.getBook(bookId); if (!book) return null; const typedBook = book as any; return { id: typedBook.book_id, title: typedBook.title, author: typedBook.author, year: typedBook.pub_year, pages: typedBook.npages, language: typedBook.lang, description: typedBook.description || '', categories: this.extractCategories(typedBook), downloadedContent: await this.hasDownloadedContent(typedBook.book_id) }; } catch (error) { console.error('Error fetching book:', error); return null; } } // Content retrieval async getBookContent(bookId: number, limit = 50, offset = 0): Promise<any[]> { try { return await this.db.getParagraphs(bookId, limit, offset); } catch (error) { console.error('Error fetching book content:', error); throw new Error('Failed to fetch book content'); } } // Statistics async getStats() { try { return await this.db.getStats(); } catch (error) { console.error('Error fetching stats:', error); return { languages: 0, books: 0, downloadedBooks: 0, paragraphs: 0 }; } } // Suggestions async getSearchSuggestions(query: string): Promise<string[]> { if (query.length < 2) return []; try { // Simple implementation - in production you might want more sophisticated suggestions const results = await this.db.search(query, 5, 0); const suggestions = new Set<string>(); results.forEach((result: any) => { const words = (result.snippet || result.content || '').toLowerCase().split(/\s+/); words.forEach((word: string) => { if (word.includes(query.toLowerCase()) && word.length > 2) { suggestions.add(word.replace(/[^\w]/g, '')); } }); }); return Array.from(suggestions).slice(0, 8); } catch (error) { console.error('Error getting suggestions:', error); return []; } } // Helper methods private extractExcerpt(text: string, maxLength = 200): string { if (!text) return ''; const cleaned = text.replace(/<[^>]*>/g, '').trim(); return cleaned.length > maxLength ? cleaned.substring(0, maxLength) + '...' : cleaned; } private highlightSearchTerms(text: string, query: string): string { if (!text || !query) return text; const terms = query.toLowerCase().split(/\s+/); let highlighted = text; terms.forEach(term => { if (term.length > 2) { const regex = new RegExp(`(${term})`, 'gi'); highlighted = highlighted.replace(regex, '<mark>$1</mark>'); } }); return highlighted; } private extractCategories(book: any): string[] { // Simple category extraction based on book metadata const categories = []; if (book.description) { const desc = book.description.toLowerCase(); if (desc.includes('prophecy')) categories.push('Prophecy'); if (desc.includes('health')) categories.push('Health'); if (desc.includes('education')) categories.push('Education'); if (desc.includes('gospel')) categories.push('Gospel'); if (desc.includes('christian')) categories.push('Christian Living'); } return categories.length > 0 ? categories : ['General']; } private async hasDownloadedContent(bookId: number): Promise<boolean> { try { const paragraphs = await this.db.getParagraphs(bookId, 1, 0); return paragraphs.length > 0; } catch { return false; } } close() { this.db.close(); } } // Singleton instance let dbService: DatabaseService | null = null; export function getDatabase(): DatabaseService { if (!dbService) { dbService = new DatabaseService(); } return dbService; } export function closeDatabase() { if (dbService) { dbService.close(); dbService = null; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pythondev-pro/egw_writings_mcp_server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server