Skip to main content
Glama
ConnectionTester.ts5.43 kB
import { WordPressClient } from "@/client/api.js"; import { getErrorMessage } from "@/utils/error.js"; import { LoggerFactory } from "@/utils/logger.js"; import { ConfigHelpers } from "@/config/Config.js"; interface ErrorWithResponse { response?: { status?: number; }; } interface ErrorWithCode { code?: string; } /** * Service for testing WordPress client connections * Handles connection validation and health checks */ export class ConnectionTester { private static logger = LoggerFactory.server().child({ component: "ConnectionTester" }); /** * Test connections to all configured WordPress sites with timeout and concurrency control */ public static async testClientConnections( wordpressClients: Map<string, WordPressClient>, options: { timeout?: number; maxConcurrent?: number } = {}, ): Promise<void> { const { timeout = 5000, maxConcurrent = 3 } = options; this.logger.info("Testing connections to WordPress sites", { siteCount: wordpressClients.size, timeout, maxConcurrent, }); const entries = Array.from(wordpressClients.entries()); const results: Array<{ siteId: string; success: boolean; error?: string }> = []; // Process sites in batches to control concurrency for (let i = 0; i < entries.length; i += maxConcurrent) { const batch = entries.slice(i, i + maxConcurrent); const batchPromises = batch.map(async ([siteId, client]) => { const startTime = Date.now(); try { // Add timeout to ping operation (skip timeout in tests to avoid timer issues) if (ConfigHelpers.isTest()) { // In test environment, just run the ping without timeout to avoid Jest timer issues await client.ping(); } else { await Promise.race([ client.ping(), new Promise((_, reject) => setTimeout(() => reject(new Error("Connection timeout")), timeout)), ]); } const duration = Date.now() - startTime; this.logger.info("Connection successful", { siteId, duration: `${duration}ms` }); results.push({ siteId, success: true }); } catch (_error) { const duration = Date.now() - startTime; const errorMessage = getErrorMessage(_error); this.logger.warn("Connection failed", { siteId, error: errorMessage, duration: `${duration}ms`, isAuthError: ConnectionTester.isAuthenticationError(_error), }); results.push({ siteId, success: false, error: errorMessage }); } }); await Promise.allSettled(batchPromises); } // Log summary const successful = results.filter((r) => r.success).length; const failed = results.length - successful; this.logger.info("Connection tests complete", { total: results.length, successful, failed, successRate: `${((successful / results.length) * 100).toFixed(1)}%`, }); } /** * Check if error is authentication-related */ private static isAuthenticationError(error: unknown): boolean { // Check for HTTP response status if (error && typeof error === "object" && "response" in error) { const response = (error as ErrorWithResponse).response; if (response?.status && [401, 403].includes(response.status)) { return true; } } const errorMessage = getErrorMessage(error); return ( errorMessage.includes("401") || errorMessage.includes("403") || errorMessage.includes("Unauthorized") || errorMessage.includes("Forbidden") || (error as ErrorWithCode)?.code === "WORDPRESS_AUTH_ERROR" ); } /** * Perform health check for a specific client with timeout */ public static async healthCheck(client: WordPressClient, siteId?: string, timeout: number = 3000): Promise<boolean> { try { await Promise.race([ client.ping(), new Promise((_, reject) => setTimeout(() => reject(new Error("Health check timeout")), timeout)), ]); this.logger.debug("Health check passed", { siteId }); return true; } catch (_error) { this.logger.warn("Health check failed", { siteId, _error: getErrorMessage(_error), }); return false; } } /** * Quick connectivity test without full authentication */ public static async quickConnectivityTest( wordpressClients: Map<string, WordPressClient>, ): Promise<Map<string, boolean>> { const results = new Map<string, boolean>(); const promises = Array.from(wordpressClients.entries()).map(async ([siteId, client]) => { try { // Just test basic connectivity, not full auth const isHealthy = await this.healthCheck(client, siteId, 1000); results.set(siteId, isHealthy); } catch (_error) { results.set(siteId, false); } }); await Promise.allSettled(promises); return results; } /** * Perform health checks for all clients */ public static async healthCheckAll(wordpressClients: Map<string, WordPressClient>): Promise<Map<string, boolean>> { const results = new Map<string, boolean>(); for (const [siteId, client] of wordpressClients.entries()) { const isHealthy = await ConnectionTester.healthCheck(client); results.set(siteId, isHealthy); } return results; } }

Latest Blog Posts

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/docdyhr/mcp-wordpress'

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