UNPKG

survey-mcp-server

Version:

Survey management server handling survey creation, response collection, analysis, and reporting with database access for data management

198 lines 7.71 kB
import Typesense from 'typesense'; import { config } from './config.js'; import { logger } from './logger.js'; export class TypesenseClient { constructor() { this.client = null; this.isAvailable = false; this.initialized = false; // Don't initialize client immediately // It will be initialized on first use or explicit initialization } static getInstance() { if (!TypesenseClient.instance) { TypesenseClient.instance = new TypesenseClient(); } return TypesenseClient.instance; } async initialize() { try { logger.info('Initializing Typesense client with config:', { host: config.typesense.nodes[0]?.host, port: config.typesense.nodes[0]?.port, protocol: config.typesense.nodes[0]?.protocol, hasApiKey: !!config.typesense.apiKey }); this.client = new Typesense.Client({ nodes: config.typesense.nodes, apiKey: config.typesense.apiKey, connectionTimeoutSeconds: config.typesense.connectionTimeoutSeconds }); // Test the connection await this.client.health.retrieve(); this.isAvailable = true; this.initialized = true; logger.info('Typesense client initialized and connected successfully'); } catch (error) { logger.error('Failed to initialize Typesense client:', error); this.isAvailable = false; this.initialized = false; this.client = null; throw error; } } async reinitialize() { this.client = null; this.isAvailable = false; this.initialized = false; await this.initialize(); } async ensureInitialized() { if (!this.initialized) { await this.initialize(); } } checkAvailability() { if (!this.client || !this.isAvailable) { throw new Error('Typesense client is not available. Please check your Typesense configuration and ensure initialize() has been called.'); } } get collections() { this.checkAvailability(); return this.client.collections; } getAvailability() { return this.isAvailable && this.client !== null && this.initialized; } isInitialized() { return this.initialized; } getClient() { this.checkAvailability(); if (!this.client) { throw new Error('Typesense client is not initialized'); } return this.client; } async search(collectionName, searchParameters) { try { await this.ensureInitialized(); this.checkAvailability(); logger.debug(`Searching in collection '${collectionName}' with parameters:`, searchParameters); const results = await this.client.collections(collectionName).documents().search(searchParameters); logger.debug(`Search completed. Found ${results.found} results`); return results; } catch (error) { logger.error(`Error searching collection '${collectionName}':`, error); throw error; } } async createCollection(collectionSchema) { try { await this.ensureInitialized(); this.checkAvailability(); logger.info(`Creating collection with schema:`, collectionSchema); const result = await this.client.collections().create(collectionSchema); logger.info(`Collection '${collectionSchema.name}' created successfully`); return result; } catch (error) { logger.error(`Error creating collection:`, error); throw error; } } async indexDocument(collectionName, document) { try { await this.ensureInitialized(); this.checkAvailability(); const result = await this.client.collections(collectionName).documents().create(document); logger.debug(`Document indexed in collection '${collectionName}'`); return result; } catch (error) { logger.error(`Error indexing document in collection '${collectionName}':`, error); throw error; } } async updateDocument(collectionName, documentId, document) { try { await this.ensureInitialized(); this.checkAvailability(); const result = await this.client.collections(collectionName).documents(documentId).update(document); logger.debug(`Document updated in collection '${collectionName}'`); return result; } catch (error) { logger.error(`Error updating document in collection '${collectionName}':`, error); throw error; } } async upsertDocument(collectionName, document) { try { await this.ensureInitialized(); this.checkAvailability(); // Try to update first const result = await this.client.collections(collectionName).documents(document.id.toString()).update(document); logger.debug(`Document updated in collection '${collectionName}'`); return result; } catch (updateError) { // If update fails, create new document if (updateError.httpStatus === 404) { const createResult = await this.indexDocument(collectionName, document); logger.debug(`Document created (upsert) in collection '${collectionName}'`); return createResult; } else { throw updateError; } } } async importDocuments(collectionName, documents, options = {}) { try { await this.ensureInitialized(); this.checkAvailability(); logger.info(`Importing ${documents.length} documents to collection '${collectionName}'`); const result = await this.client.collections(collectionName).documents().import(documents, options); logger.info(`Documents imported successfully to collection '${collectionName}'`); return result; } catch (error) { logger.error(`Error importing documents to collection '${collectionName}':`, error); throw error; } } async deleteCollection(collectionName) { try { await this.ensureInitialized(); this.checkAvailability(); const result = await this.client.collections(collectionName).delete(); logger.info(`Collection '${collectionName}' deleted successfully`); return result; } catch (error) { logger.error(`Error deleting collection '${collectionName}':`, error); throw error; } } async getHealth() { try { await this.ensureInitialized(); this.checkAvailability(); const health = await this.client.health.retrieve(); this.isAvailable = true; // Set availability on successful health check logger.debug('Typesense health check completed'); return health; } catch (error) { this.isAvailable = false; // Set unavailability on failed health check logger.error('Typesense health check failed:', error); throw error; } } } // Export singleton instance - but don't initialize it yet export const typesenseClient = TypesenseClient.getInstance(); //# sourceMappingURL=typesense.js.map