From 9726c791f8e871e61ed8b1793aa417898bf03a0a Mon Sep 17 00:00:00 2001 From: Hugo Montenegro Date: Fri, 1 Aug 2025 10:07:48 +0100 Subject: [PATCH] Revert "Feat/status (#1022)" This reverts commit ddf5da4be607848fff479156fa8ae4aea985dd3f. --- src/app/api/health/backend/route.ts | 78 --------- src/app/api/health/frontend/route.ts | 30 ---- src/app/api/health/justaname/route.ts | 101 ------------ src/app/api/health/mobula/route.ts | 105 ------------ src/app/api/health/route.ts | 224 -------------------------- src/app/api/health/rpc/route.ts | 169 ------------------- src/app/api/health/squid/route.ts | 125 -------------- src/app/api/health/zerodev/route.ts | 183 --------------------- 8 files changed, 1015 deletions(-) delete mode 100644 src/app/api/health/backend/route.ts delete mode 100644 src/app/api/health/frontend/route.ts delete mode 100644 src/app/api/health/justaname/route.ts delete mode 100644 src/app/api/health/mobula/route.ts delete mode 100644 src/app/api/health/route.ts delete mode 100644 src/app/api/health/rpc/route.ts delete mode 100644 src/app/api/health/squid/route.ts delete mode 100644 src/app/api/health/zerodev/route.ts diff --git a/src/app/api/health/backend/route.ts b/src/app/api/health/backend/route.ts deleted file mode 100644 index d6918afbc..000000000 --- a/src/app/api/health/backend/route.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { NextResponse } from 'next/server' -import { PEANUT_API_URL } from '@/constants' -import { fetchWithSentry } from '@/utils' - -/** - * Health check for Peanut API backend - * Tests connectivity to the main peanut-api-ts backend service - */ -export async function GET() { - const startTime = Date.now() - - try { - if (!PEANUT_API_URL) { - return NextResponse.json( - { - status: 'unhealthy', - service: 'backend', - timestamp: new Date().toISOString(), - error: 'PEANUT_API_URL not configured', - responseTime: Date.now() - startTime, - }, - { status: 500 } - ) - } - - // Test backend connectivity by fetching a specific user endpoint - const backendTestStart = Date.now() - const backendResponse = await fetchWithSentry(`${PEANUT_API_URL}/users/username/hugo`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }) - - const backendResponseTime = Date.now() - backendTestStart - - // Backend is healthy if we get any response (200, 404, etc.) - what matters is connectivity - if (!backendResponse.ok && backendResponse.status >= 500) { - throw new Error(`Backend API returned server error ${backendResponse.status}`) - } - - const totalResponseTime = Date.now() - startTime - - return NextResponse.json({ - status: 'healthy', - service: 'backend', - timestamp: new Date().toISOString(), - responseTime: totalResponseTime, - details: { - apiConnectivity: { - status: 'healthy', - responseTime: backendResponseTime, - httpStatus: backendResponse.status, - apiUrl: PEANUT_API_URL, - testEndpoint: '/users/username/hugo', - message: backendResponse.ok - ? 'Backend responding normally' - : backendResponse.status === 404 - ? 'Backend accessible (user not found as expected)' - : 'Backend accessible', - }, - }, - }) - } catch (error) { - const totalResponseTime = Date.now() - startTime - - return NextResponse.json( - { - status: 'unhealthy', - service: 'backend', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error', - responseTime: totalResponseTime, - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/health/frontend/route.ts b/src/app/api/health/frontend/route.ts deleted file mode 100644 index a1b94a6c1..000000000 --- a/src/app/api/health/frontend/route.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NextResponse } from 'next/server' - -/** - * Frontend-only health check endpoint - * Tests that the Next.js API routes are working (does not test external dependencies) - */ -export async function GET() { - try { - const healthData = { - status: 'healthy', - service: 'peanut-ui-frontend', - timestamp: new Date().toISOString(), - version: process.env.npm_package_version || 'unknown', - environment: process.env.NODE_ENV, - uptime: process.uptime(), - } - - return NextResponse.json(healthData, { status: 200 }) - } catch (error) { - return NextResponse.json( - { - status: 'unhealthy', - service: 'peanut-ui-frontend', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error', - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/health/justaname/route.ts b/src/app/api/health/justaname/route.ts deleted file mode 100644 index 1d9db5588..000000000 --- a/src/app/api/health/justaname/route.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { fetchWithSentry } from '@/utils' -import { NextResponse } from 'next/server' - -const JUSTANAME_API_URL = 'https://api.justaname.id' - -/** - * Health check for JustAName API - * Tests ENS name resolution functionality - */ -export async function GET() { - const startTime = Date.now() - - try { - // Test ENS resolution endpoint with a known Ethereum address (Vitalik's) - const ensTestStart = Date.now() - const testAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' // Vitalik's address - - const ensResponse = await fetchWithSentry( - `${JUSTANAME_API_URL}/ens/v1/subname/address?address=${testAddress}&chainId=1`, - { - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - } - ) - - const ensResponseTime = Date.now() - ensTestStart - - if (!ensResponse.ok) { - throw new Error(`ENS resolution API returned ${ensResponse.status}`) - } - - const ensData = await ensResponse.json() - - // Validate response structure - if (!ensData?.result) { - throw new Error('Invalid ENS API response structure') - } - - // Test a second endpoint - ENS name lookup (if available) - const lookupTestStart = Date.now() - let lookupHealth: any = { status: 'not_tested', message: 'Lookup endpoint not tested' } - - try { - // Test reverse ENS lookup if the API supports it - const lookupResponse = await fetchWithSentry(`${JUSTANAME_API_URL}/ens/v1/name/vitalik.eth`, { - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - }) - - const lookupResponseTime = Date.now() - lookupTestStart - - lookupHealth = { - status: lookupResponse.ok ? 'healthy' : 'degraded', - responseTime: lookupResponseTime, - httpStatus: lookupResponse.status, - } - } catch (error) { - // If lookup fails, that's okay - not all endpoints may be available - lookupHealth = { - status: 'degraded', - responseTime: Date.now() - lookupTestStart, - error: 'Lookup endpoint unavailable', - } - } - - const totalResponseTime = Date.now() - startTime - - return NextResponse.json({ - status: 'healthy', - service: 'justaname', - timestamp: new Date().toISOString(), - responseTime: totalResponseTime, - details: { - ensResolution: { - status: 'healthy', - responseTime: ensResponseTime, - testAddress, - apiUrl: JUSTANAME_API_URL, - }, - ensLookup: lookupHealth, - }, - }) - } catch (error) { - const totalResponseTime = Date.now() - startTime - - return NextResponse.json( - { - status: 'unhealthy', - service: 'justaname', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error', - responseTime: totalResponseTime, - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/health/mobula/route.ts b/src/app/api/health/mobula/route.ts deleted file mode 100644 index 36ba0279b..000000000 --- a/src/app/api/health/mobula/route.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { fetchWithSentry } from '@/utils' -import { NextResponse } from 'next/server' - -const MOBULA_API_URL = process.env.MOBULA_API_URL! -const MOBULA_API_KEY = process.env.MOBULA_API_KEY! - -/** - * Health check for Mobula API - * Tests both asset price endpoint and portfolio endpoint - */ -export async function GET() { - const startTime = Date.now() - - try { - if (!MOBULA_API_KEY) { - return NextResponse.json( - { - status: 'unhealthy', - service: 'mobula', - timestamp: new Date().toISOString(), - error: 'MOBULA_API_KEY not configured', - responseTime: Date.now() - startTime, - }, - { status: 500 } - ) - } - - // Test 1: Asset price endpoint (using USDC on Ethereum as test) - const priceTestStart = Date.now() - const priceResponse = await fetchWithSentry( - `${MOBULA_API_URL}/api/1/market/data?asset=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&blockchain=1`, - { - headers: { - 'Content-Type': 'application/json', - authorization: MOBULA_API_KEY, - }, - } - ) - const priceResponseTime = Date.now() - priceTestStart - - if (!priceResponse.ok) { - throw new Error(`Price API returned ${priceResponse.status}`) - } - - const priceData = await priceResponse.json() - if (!priceData?.data?.price) { - throw new Error('Invalid price data structure') - } - - // Test 2: Portfolio endpoint (using a known address with likely balance) - const portfolioTestStart = Date.now() - const portfolioResponse = await fetchWithSentry( - `${MOBULA_API_URL}/api/1/wallet/portfolio?wallet=0x9647BB6a598c2675310c512e0566B60a5aEE6261`, - { - headers: { - 'Content-Type': 'application/json', - authorization: MOBULA_API_KEY, - }, - } - ) - const portfolioResponseTime = Date.now() - portfolioTestStart - - const portfolioHealthy = portfolioResponse.ok - - // If portfolio API is down, throw error to return HTTP 500 - if (!portfolioHealthy) { - throw new Error(`Portfolio API returned ${portfolioResponse.status}`) - } - - const totalResponseTime = Date.now() - startTime - - return NextResponse.json({ - status: 'healthy', - service: 'mobula', - timestamp: new Date().toISOString(), - responseTime: totalResponseTime, - details: { - priceApi: { - status: 'healthy', - responseTime: priceResponseTime, - testAsset: 'USDC', - price: priceData.data.price, - }, - portfolioApi: { - status: 'healthy', - responseTime: portfolioResponseTime, - httpStatus: portfolioResponse.status, - }, - }, - }) - } catch (error) { - const totalResponseTime = Date.now() - startTime - - return NextResponse.json( - { - status: 'unhealthy', - service: 'mobula', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error', - responseTime: totalResponseTime, - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts deleted file mode 100644 index f26de8529..000000000 --- a/src/app/api/health/route.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { NextResponse } from 'next/server' -import { fetchWithSentry } from '@/utils' - -/** - * Overall health check endpoint - * Aggregates health status from all individual service health checks - * This is the main endpoint that should be monitored by UptimeRobot - */ - -/** - * Send Discord notification when system is unhealthy - */ -async function sendDiscordNotification(healthData: any) { - try { - const webhookUrl = process.env.DISCORD_WEBHOOK_URL - if (!webhookUrl) { - console.log('Discord webhook not configured, skipping notification') - return - } - - // Create a detailed message about what's failing - const failedServices = Object.entries(healthData.services) - .filter(([_, service]: [string, any]) => service.status === 'unhealthy') - .map(([name, service]: [string, any]) => `• ${name}: ${service.error || 'unhealthy'}`) - - // Only mention role in production or peanut.me - const isProduction = process.env.NODE_ENV === 'production' - const isPeanutDomain = - (process.env.NEXT_PUBLIC_BASE_URL?.includes('peanut.me') && - !process.env.NEXT_PUBLIC_BASE_URL?.includes('staging.peanut.me')) || - (process.env.VERCEL_URL?.includes('peanut.me') && !process.env.VERCEL_URL?.includes('staging.peanut.me')) - const shouldMentionRole = isProduction || isPeanutDomain - - const roleMention = shouldMentionRole ? '<@&1187109195389083739> ' : '' - - const message = `${roleMention}🚨 **Peanut Protocol Health Alert** 🚨 - -System Status: **${healthData.status.toUpperCase()}** -Health Score: ${healthData.healthScore}% -Environment: ${healthData.systemInfo?.environment || 'unknown'} - -**Failed Services:** -${failedServices.length > 0 ? failedServices.join('\n') : 'No specific failures detected'} - -**Summary:** -• Healthy: ${healthData.summary.healthy} -• Degraded: ${healthData.summary.degraded} -• Unhealthy: ${healthData.summary.unhealthy} - -Timestamp: ${healthData.timestamp}` - - await fetchWithSentry(webhookUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - content: message, - }), - }) - - console.log('Discord notification sent for unhealthy system status') - } catch (error) { - console.error('Failed to send Discord notification:', error) - // Don't throw - we don't want notification failures to break the health check - } -} - -export async function GET() { - const startTime = Date.now() - - try { - const services = ['mobula', 'squid', 'zerodev', 'rpc', 'justaname', 'backend'] - - const healthChecks = await Promise.allSettled( - services.map(async (service) => { - // Use localhost in development, production URL otherwise - const isDev = process.env.NODE_ENV === 'development' - const baseUrl = isDev - ? 'http://localhost:3000' - : process.env.NEXT_PUBLIC_BASE_URL || 'https://peanut.to' - const response = await fetchWithSentry(`${baseUrl}/api/health/${service}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }) - - if (!response.ok) { - throw new Error(`Health check failed with status ${response.status}`) - } - - const data = await response.json() - return { - service, - ...data, - } - }) - ) - - const results: any = { - services: {}, - summary: { - total: services.length, - healthy: 0, - degraded: 0, - unhealthy: 0, - }, - } - - // Process results - healthChecks.forEach((result, index) => { - const serviceName = services[index] - - if (result.status === 'fulfilled') { - const serviceData = result.value - results.services[serviceName] = { - status: serviceData.status, - responseTime: serviceData.responseTime, - timestamp: serviceData.timestamp, - details: serviceData.details || {}, - } - - // Update summary counts - switch (serviceData.status) { - case 'healthy': - results.summary.healthy++ - break - case 'degraded': - results.summary.degraded++ - break - case 'unhealthy': - default: - results.summary.unhealthy++ - break - } - } else { - results.services[serviceName] = { - status: 'unhealthy', - error: result.reason?.message || 'Health check failed', - timestamp: new Date().toISOString(), - } - results.summary.unhealthy++ - } - }) - - // Determine overall system health - let overallStatus = 'healthy' - if (results.summary.unhealthy > 0) { - // If any critical services are down, mark as unhealthy - const criticalServices = ['backend', 'rpc'] - const criticalServicesDown = criticalServices.some( - (service) => results.services[service]?.status === 'unhealthy' - ) - - if (criticalServicesDown || results.summary.unhealthy >= 3) { - overallStatus = 'unhealthy' - } else { - overallStatus = 'degraded' - } - } else if (results.summary.degraded > 0) { - overallStatus = 'degraded' - } - - const totalResponseTime = Date.now() - startTime - - // Calculate health score (0-100) - const healthScore = Math.round( - ((results.summary.healthy + results.summary.degraded * 0.5) / results.summary.total) * 100 - ) - - // If overall status is unhealthy, return HTTP 500 - if (overallStatus === 'unhealthy') { - const responseData = { - status: overallStatus, - service: 'peanut-protocol', - timestamp: new Date().toISOString(), - responseTime: totalResponseTime, - healthScore, - summary: results.summary, - services: results.services, - systemInfo: { - environment: process.env.NODE_ENV, - version: process.env.npm_package_version || 'unknown', - region: process.env.VERCEL_REGION || 'unknown', - }, - } - - // Send Discord notification asynchronously (don't await to avoid delaying the response) - sendDiscordNotification(responseData).catch(console.error) - - return NextResponse.json(responseData, { status: 500 }) - } - - return NextResponse.json({ - status: overallStatus, - service: 'peanut-protocol', - timestamp: new Date().toISOString(), - responseTime: totalResponseTime, - healthScore, - summary: results.summary, - services: results.services, - systemInfo: { - environment: process.env.NODE_ENV, - version: process.env.npm_package_version || 'unknown', - region: process.env.VERCEL_REGION || 'unknown', - }, - }) - } catch (error) { - const totalResponseTime = Date.now() - startTime - - return NextResponse.json( - { - status: 'unhealthy', - service: 'peanut-protocol', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error', - responseTime: totalResponseTime, - healthScore: 0, - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/health/rpc/route.ts b/src/app/api/health/rpc/route.ts deleted file mode 100644 index 12a68e998..000000000 --- a/src/app/api/health/rpc/route.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { fetchWithSentry } from '@/utils' -import { NextResponse } from 'next/server' -import { rpcUrls } from '@/constants/general.consts' - -const INFURA_API_KEY = process.env.NEXT_PUBLIC_INFURA_API_KEY -const ALCHEMY_API_KEY = process.env.NEXT_PUBLIC_ALCHEMY_API_KEY - -/** - * Health check for RPC providers (Infura, Alchemy) - * Tests connectivity across multiple chains - */ -export async function GET() { - const startTime = Date.now() - - try { - if (!INFURA_API_KEY && !ALCHEMY_API_KEY) { - return NextResponse.json( - { - status: 'unhealthy', - service: 'rpc', - timestamp: new Date().toISOString(), - error: 'No RPC API keys configured', - responseTime: Date.now() - startTime, - }, - { status: 500 } - ) - } - - const chainResults: any = {} - - // Test key chains: Ethereum mainnet, Arbitrum, Polygon - const chainsToTest = [ - { id: 1, name: 'ethereum' }, - { id: 42161, name: 'arbitrum' }, - { id: 137, name: 'polygon' }, - ] - - for (const chain of chainsToTest) { - const chainRpcs = rpcUrls[chain.id] || [] - chainResults[chain.name] = { - chainId: chain.id, - providers: {}, - overallStatus: 'unknown', - } - - for (let i = 0; i < chainRpcs.length; i++) { - const rpcUrl = chainRpcs[i] - const providerName = rpcUrl.includes('infura') - ? 'infura' - : rpcUrl.includes('alchemy') - ? 'alchemy' - : rpcUrl.includes('bnbchain') - ? 'binance' - : `provider_${i}` - - const rpcTestStart = Date.now() - - try { - const response = await fetchWithSentry(rpcUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - jsonrpc: '2.0', - method: 'eth_blockNumber', - params: [], - id: 1, - }), - }) - - const responseTime = Date.now() - rpcTestStart - - if (response.ok) { - const data = await response.json() - const blockNumber = data?.result ? parseInt(data.result, 16) : null - - chainResults[chain.name].providers[providerName] = { - status: blockNumber ? 'healthy' : 'degraded', - responseTime, - blockNumber, - url: rpcUrl.replace(/(api_key|api-key)=[^&]+/g, 'api_key=***'), // Hide API key - } - } else { - chainResults[chain.name].providers[providerName] = { - status: 'unhealthy', - responseTime, - httpStatus: response.status, - url: rpcUrl.replace(/(api_key|api-key)=[^&]+/g, 'api_key=***'), - } - } - } catch (error) { - chainResults[chain.name].providers[providerName] = { - status: 'unhealthy', - responseTime: Date.now() - rpcTestStart, - error: error instanceof Error ? error.message : 'Unknown error', - url: rpcUrl.replace(/(api_key|api-key)=[^&]+/g, 'api_key=***'), - } - } - } - - // Determine chain overall status - const chainProviders = Object.values(chainResults[chain.name].providers) - const healthyProviders = chainProviders.filter((p: any) => p.status === 'healthy') - const degradedProviders = chainProviders.filter((p: any) => p.status === 'degraded') - - if (healthyProviders.length > 0) { - chainResults[chain.name].overallStatus = 'healthy' - } else if (degradedProviders.length > 0) { - chainResults[chain.name].overallStatus = 'degraded' - } else { - chainResults[chain.name].overallStatus = 'unhealthy' - } - - chainResults[chain.name].summary = { - total: chainProviders.length, - healthy: healthyProviders.length, - degraded: degradedProviders.length, - unhealthy: chainProviders.length - healthyProviders.length - degradedProviders.length, - } - } - - // Determine overall RPC health - const chainStatuses = Object.values(chainResults).map((chain: any) => chain.overallStatus) - const hasUnhealthyChain = chainStatuses.includes('unhealthy') - const hasDegradedChain = chainStatuses.includes('degraded') - - let overallStatus = 'healthy' - if (hasUnhealthyChain) { - overallStatus = 'unhealthy' - } else if (hasDegradedChain) { - overallStatus = 'degraded' - } - - // If any critical chain is unhealthy, return HTTP 500 - if (overallStatus === 'unhealthy') { - throw new Error(`Critical RPC providers unavailable. Chains status: ${chainStatuses.join(', ')}`) - } - - const totalResponseTime = Date.now() - startTime - - return NextResponse.json({ - status: overallStatus, - service: 'rpc', - timestamp: new Date().toISOString(), - responseTime: totalResponseTime, - details: { - chains: chainResults, - configuration: { - infuraConfigured: !!INFURA_API_KEY, - alchemyConfigured: !!ALCHEMY_API_KEY, - }, - }, - }) - } catch (error) { - const totalResponseTime = Date.now() - startTime - - return NextResponse.json( - { - status: 'unhealthy', - service: 'rpc', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error', - responseTime: totalResponseTime, - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/health/squid/route.ts b/src/app/api/health/squid/route.ts deleted file mode 100644 index dfc808213..000000000 --- a/src/app/api/health/squid/route.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { fetchWithSentry } from '@/utils' -import { NextResponse } from 'next/server' -import { SQUID_API_URL, SQUID_INTEGRATOR_ID, DEFAULT_SQUID_INTEGRATOR_ID } from '@/constants' - -/** - * Health check for Squid API - * Tests both regular cross-chain routes and RFQ route availability - */ -export async function GET() { - const startTime = Date.now() - - try { - if (!SQUID_INTEGRATOR_ID && !DEFAULT_SQUID_INTEGRATOR_ID) { - return NextResponse.json( - { - status: 'unhealthy', - service: 'squid', - timestamp: new Date().toISOString(), - error: 'SQUID_INTEGRATOR_ID not configured', - responseTime: Date.now() - startTime, - }, - { status: 500 } - ) - } - - // Test 1: Regular route (ETH mainnet to Arbitrum USDC) - const regularRouteTestStart = Date.now() - const regularRouteParams = { - fromChain: '1', - fromToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - fromAmount: '100000000000000000', - toChain: '42161', - toToken: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', // USDC on Arbitrum - fromAddress: '0x9647BB6a598c2675310c512e0566B60a5aEE6261', - toAddress: '0xdA60a6626C2C8Ea1f5F31e73368F32c8C7AdAE73', - slippage: 1, // Add slippage parameter - } - - const regularRouteResponse = await fetchWithSentry(`${SQUID_API_URL}/v2/route`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-integrator-id': DEFAULT_SQUID_INTEGRATOR_ID!, - }, - body: JSON.stringify(regularRouteParams), - }) - const regularRouteResponseTime = Date.now() - regularRouteTestStart - - if (!regularRouteResponse.ok) { - throw new Error(`Regular route API returned ${regularRouteResponse.status}`) - } - - const regularRouteData = await regularRouteResponse.json() - if (!regularRouteData?.route) { - throw new Error('Invalid regular route data structure') - } - - // Test 2: RFQ route availability (using coral/RFQ integrator) - const rfqRouteTestStart = Date.now() - const rfqRouteResponse = await fetchWithSentry(`${SQUID_API_URL}/v2/route`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-integrator-id': SQUID_INTEGRATOR_ID!, - }, - body: JSON.stringify(regularRouteParams), - }) - const rfqRouteResponseTime = Date.now() - rfqRouteTestStart - - const rfqRouteHealthy = rfqRouteResponse.ok - let rfqRouteData = null - let hasRfqRoute = false - - if (rfqRouteHealthy) { - try { - rfqRouteData = await rfqRouteResponse.json() - // Check if response contains RFQ-type route (same logic as swap.ts) - hasRfqRoute = rfqRouteData?.route?.estimate?.actions?.[0]?.type === 'rfq' - console.log('hasRfqRoute', hasRfqRoute) - console.log('rfqRouteData', rfqRouteData) - } catch (e) { - console.error('Error parsing RFQ route response:', e) - console.error('RFQ response:', rfqRouteResponse) - } - } - - const totalResponseTime = Date.now() - startTime - - return NextResponse.json({ - status: 'healthy', - service: 'squid', - timestamp: new Date().toISOString(), - responseTime: totalResponseTime, - details: { - regularRoutes: { - status: 'healthy', - responseTime: regularRouteResponseTime, - routeFound: !!regularRouteData.route, - estimatedGas: regularRouteData.route?.estimate?.gasLimit || 'unknown', - }, - rfqRoutes: { - status: rfqRouteHealthy ? (hasRfqRoute ? 'healthy' : 'degraded') : 'unhealthy', - responseTime: rfqRouteResponseTime, - httpStatus: rfqRouteResponse.status, - rfqAvailable: hasRfqRoute, - message: hasRfqRoute ? 'RFQ routes available' : 'No RFQ routes found (may be normal)', - }, - }, - }) - } catch (error) { - console.error(error) - const totalResponseTime = Date.now() - startTime - - return NextResponse.json( - { - status: 'unhealthy', - service: 'squid', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error', - responseTime: totalResponseTime, - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/health/zerodev/route.ts b/src/app/api/health/zerodev/route.ts deleted file mode 100644 index e9d3531cb..000000000 --- a/src/app/api/health/zerodev/route.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { NextResponse } from 'next/server' -import { fetchWithSentry } from '@/utils' - -/** - * ZeroDev health check endpoint - * Tests bundler and paymaster services for supported chains - */ -export async function GET() { - const startTime = Date.now() - - try { - // Get configuration from environment variables (same as zerodev.consts.ts) - const BUNDLER_URL = process.env.NEXT_PUBLIC_ZERO_DEV_BUNDLER_URL - const PAYMASTER_URL = process.env.NEXT_PUBLIC_ZERO_DEV_PAYMASTER_URL - const PROJECT_ID = process.env.NEXT_PUBLIC_ZERO_DEV_PASSKEY_PROJECT_ID - const POLYGON_BUNDLER_URL = process.env.NEXT_PUBLIC_POLYGON_BUNDLER_URL - const POLYGON_PAYMASTER_URL = process.env.NEXT_PUBLIC_POLYGON_PAYMASTER_URL - - // Check configuration - if (!BUNDLER_URL || !PAYMASTER_URL || !PROJECT_ID) { - return NextResponse.json( - { - status: 'unhealthy', - service: 'zerodev', - timestamp: new Date().toISOString(), - error: 'ZeroDev configuration missing (bundler, paymaster, or project ID)', - responseTime: Date.now() - startTime, - }, - { status: 500 } - ) - } - - const results: any = { - arbitrum: {}, - polygon: {}, - configuration: { - projectId: PROJECT_ID ? 'configured' : 'missing', - bundlerUrl: BUNDLER_URL ? 'configured' : 'missing', - paymasterUrl: PAYMASTER_URL ? 'configured' : 'missing', - polygonBundlerUrl: POLYGON_BUNDLER_URL ? 'configured' : 'missing', - polygonPaymasterUrl: POLYGON_PAYMASTER_URL ? 'configured' : 'missing', - }, - } - - // Test Arbitrum endpoints - await testChainEndpoints('arbitrum', BUNDLER_URL, PAYMASTER_URL, results) - - // Test Polygon endpoints (if configured) - if (POLYGON_BUNDLER_URL && POLYGON_PAYMASTER_URL) { - await testChainEndpoints('polygon', POLYGON_BUNDLER_URL, POLYGON_PAYMASTER_URL, results) - } else { - results.polygon = { - status: 'not_configured', - message: 'Polygon ZeroDev services not configured', - } - } - - // Determine overall status - let overallStatus = 'healthy' - const arbitrumHealthy = - results.arbitrum.bundler?.status === 'healthy' && results.arbitrum.paymaster?.status === 'healthy' - const polygonHealthy = - results.polygon.status === 'not_configured' || - (results.polygon.bundler?.status === 'healthy' && results.polygon.paymaster?.status === 'healthy') - - if (!arbitrumHealthy || !polygonHealthy) { - // If any critical service is down, mark as unhealthy - overallStatus = 'unhealthy' - } - - const responseTime = Date.now() - startTime - - // Return 500 if unhealthy - if (overallStatus === 'unhealthy') { - return NextResponse.json( - { - status: overallStatus, - service: 'zerodev', - timestamp: new Date().toISOString(), - responseTime, - details: results, - }, - { status: 500 } - ) - } - - return NextResponse.json({ - status: overallStatus, - service: 'zerodev', - timestamp: new Date().toISOString(), - responseTime, - details: results, - }) - } catch (error) { - const responseTime = Date.now() - startTime - - return NextResponse.json( - { - status: 'unhealthy', - service: 'zerodev', - timestamp: new Date().toISOString(), - error: error instanceof Error ? error.message : 'Unknown error', - responseTime, - }, - { status: 500 } - ) - } -} - -async function testChainEndpoints(chainName: string, bundlerUrl: string, paymasterUrl: string, results: any) { - results[chainName] = { - bundler: {}, - paymaster: {}, - } - - // Test Bundler - using a simple JSON-RPC call that bundlers should support - const bundlerTestStart = Date.now() - try { - const bundlerResponse = await fetchWithSentry(bundlerUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - jsonrpc: '2.0', - method: 'eth_chainId', - params: [], - id: 1, - }), - }) - - results[chainName].bundler = { - status: bundlerResponse.ok ? 'healthy' : 'unhealthy', - responseTime: Date.now() - bundlerTestStart, - httpStatus: bundlerResponse.status, - } - - if (bundlerResponse.ok) { - const bundlerData = await bundlerResponse.json() - results[chainName].bundler.chainId = bundlerData?.result - } - } catch (error) { - results[chainName].bundler = { - status: 'unhealthy', - responseTime: Date.now() - bundlerTestStart, - error: error instanceof Error ? error.message : 'Unknown error', - } - } - - // Test Paymaster - using a simple JSON-RPC call - const paymasterTestStart = Date.now() - try { - const paymasterResponse = await fetchWithSentry(paymasterUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - jsonrpc: '2.0', - method: 'eth_chainId', - params: [], - id: 1, - }), - }) - - results[chainName].paymaster = { - status: paymasterResponse.status >= 200 && paymasterResponse.status < 503 ? 'healthy' : 'unhealthy', // 500 is expected for basic calls - responseTime: Date.now() - paymasterTestStart, - httpStatus: paymasterResponse.status, - } - - if (paymasterResponse.ok) { - const paymasterData = await paymasterResponse.json() - results[chainName].paymaster.chainId = paymasterData?.result - } - } catch (error) { - results[chainName].paymaster = { - status: 'unhealthy', - responseTime: Date.now() - paymasterTestStart, - error: error instanceof Error ? error.message : 'Unknown error', - } - } -}