Add new health

This commit is contained in:
2025-04-25 13:34:59 +07:00
parent 2e6afe3869
commit 5844d89175
6 changed files with 716 additions and 33 deletions

View File

@@ -6,12 +6,12 @@ import {GmxSdk} from '../../generated/gmxsdk/index.js'
import {arbitrum} from 'viem/chains';
import {getTokenBySymbol} from '../../generated/gmxsdk/configs/tokens.js';
import {
Position,
PositionStatus,
Trade,
TradeDirection,
TradeStatus,
TradeType,
Position,
PositionStatus
TradeType
} from '../../generated/ManagingApiTypes.js';
import {MarketInfo, MarketsInfoData} from '../../generated/gmxsdk/types/markets.js';
import {MarketConfig, MARKETS} from '../../generated/gmxsdk/configs/markets.js'
@@ -20,14 +20,10 @@ import {TokenData, TokensData} from '../../generated/gmxsdk/types/tokens.js';
import {getByKey} from '../../generated/gmxsdk/utils/objects.js';
import {GmxSdkConfig} from '../../generated/gmxsdk/types/sdk.js';
import {PositionIncreaseParams} from '../../generated/gmxsdk/modules/orders/helpers.js';
import {numberToBigint, PRECISION_DECIMALS} from '../../generated/gmxsdk/utils/numbers.js';
import {DecreasePositionSwapType, PositionOrderInfo} from '../../generated/gmxsdk/types/orders.js';
import {OrderType} from '../../generated/gmxsdk/types/orders.js';
import {bigintToNumber, numberToBigint, PRECISION_DECIMALS} from '../../generated/gmxsdk/utils/numbers.js';
import {DecreasePositionSwapType, OrderType, PositionOrderInfo} from '../../generated/gmxsdk/types/orders.js';
import {DecreasePositionAmounts} from '../../generated/gmxsdk/types/trade.js';
import {encodeReferralCode} from '../../generated/gmxsdk/utils/referrals.js';
import {convertToUsd} from '../../generated/gmxsdk/utils/tokens.js';
import {toNumber} from 'ethers';
import {bigintToNumber} from '../../generated/gmxsdk/utils/numbers.js';
import {formatUsd} from '../../generated/gmxsdk/utils/numbers/formatting.js';
import {calculateDisplayDecimals} from '../../generated/gmxsdk/utils/numbers/index.js';
@@ -85,13 +81,12 @@ const cancelOrdersSchema = z.object({
/**
* Gets a GMX SDK client initialized for the given address
* If a walletId is provided, it will be used with Privy for signing
* @param this The FastifyRequest instance
* @param account The wallet address to use
* @returns An initialized GMX SDK client
*/
export function getClientForAddress(
export async function getClientForAddress(
account: string,
): GmxSdk {
): Promise<GmxSdk> {
try {
// Create SDK instance
const arbitrumSdkConfig: GmxSdkConfig = {

View File

@@ -1,5 +1,7 @@
import {FastifyPluginAsyncTypebox, Type} from '@fastify/type-provider-typebox'
import { handleError } from '../utils/errorHandler.js'
import {handleError} from '../utils/errorHandler.js'
import {getClientForAddress} from '../plugins/custom/gmx.js'
import {getPrivyClient} from '../plugins/custom/privy.js'
const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
fastify.get(
@@ -27,16 +29,27 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
}
}
)
// Add health check endpoint
// Enhanced health check endpoint
fastify.get('/health', {
schema: {
tags: ['Health'],
description: 'Health check endpoint that confirms the API is operational',
description: 'Enhanced health check endpoint that verifies API, Privy, and GMX connectivity',
response: {
200: Type.Object({
status: Type.String(),
timestamp: Type.String(),
version: Type.String()
version: Type.String(),
checks: Type.Object({
privy: Type.Object({
status: Type.String(),
message: Type.String()
}),
gmx: Type.Object({
status: Type.String(),
message: Type.String(),
data: Type.Optional(Type.Any())
})
})
}),
500: Type.Object({
success: Type.Boolean(),
@@ -46,15 +59,139 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
}
}, async function (request, reply) {
try {
const checks = {
privy: await checkPrivy(fastify),
gmx: await checkGmx()
}
// If any check failed, set status to degraded
const overallStatus = Object.values(checks).some(check => check.status !== 'healthy')
? 'degraded'
: 'healthy';
return {
status: 'ok',
status: overallStatus,
timestamp: new Date().toISOString(),
version: process.env.npm_package_version || '1.0.0'
version: process.env.npm_package_version || '1.0.0',
checks
}
} catch (error) {
return handleError(request, reply, error, 'health');
}
})
// Helper function to check Privy connectivity and configuration
async function checkPrivy(fastify) {
try {
// Try to initialize the Privy client - this will validate env vars and connectivity
const privy = getPrivyClient(fastify);
// Check if the necessary configuration is available
const hasPrivyAppId = !!process.env.PRIVY_APP_ID;
const hasPrivySecret = !!process.env.PRIVY_APP_SECRET;
const hasPrivyAuthKey = !!process.env.PRIVY_AUTHORIZATION_KEY;
// Get the client status
const allConfigPresent = hasPrivyAppId && hasPrivySecret && hasPrivyAuthKey;
if (!allConfigPresent) {
return {
status: 'degraded',
message: 'Privy configuration incomplete: ' +
(!hasPrivyAppId ? 'PRIVY_APP_ID ' : '') +
(!hasPrivySecret ? 'PRIVY_APP_SECRET ' : '') +
(!hasPrivyAuthKey ? 'PRIVY_AUTHORIZATION_KEY ' : '')
};
}
// If we got this far, the Privy client was successfully initialized
return {
status: 'healthy',
message: 'Privy client successfully initialized'
};
} catch (error) {
return {
status: 'unhealthy',
message: `Privy client initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`
};
}
}
// Helper function to check GMX connectivity using the GMX SDK
async function checkGmx() {
try {
// Use a dummy zero address for the health check
const account = "0x0000000000000000000000000000000000000000";
// Use the existing getClientForAddress function to get a proper GMX SDK instance
const sdk = await getClientForAddress(account);
// Get markets info data
const startTime = Date.now();
const marketsInfo = await sdk.markets.getMarketsInfo();
const responseTime = Date.now() - startTime;
if (!marketsInfo.marketsInfoData || Object.keys(marketsInfo.marketsInfoData).length === 0) {
return {
status: 'degraded',
message: 'GMX SDK returned empty markets info data',
data: { responseTimeMs: responseTime }
};
}
// Check market data for ETH-USD
let foundEthMarket = false;
let marketInfoDetails = [];
// Collect information about all available markets
for (const [marketAddress, marketInfo] of Object.entries(marketsInfo.marketsInfoData)) {
const marketDetails = {
marketAddress,
indexToken: marketInfo.indexToken?.symbol,
longToken: marketInfo.longToken?.symbol,
shortToken: marketInfo.shortToken?.symbol
};
marketInfoDetails.push(marketDetails);
// Check if this is the ETH market
if (marketInfo.indexToken?.symbol === 'ETH' ||
marketInfo.indexToken?.symbol === 'WETH' ||
marketInfo.indexToken?.name?.includes('Ethereum')) {
foundEthMarket = true;
}
}
if (!foundEthMarket) {
return {
status: 'degraded',
message: 'ETH market not found in GMX markets data',
data: {
availableMarkets: marketInfoDetails,
responseTimeMs: responseTime
}
};
}
return {
status: 'healthy',
message: `GMX SDK successfully retrieved markets data (${Object.keys(marketsInfo.marketsInfoData).length} markets)`,
data: {
marketCount: Object.keys(marketsInfo.marketsInfoData).length,
responseTimeMs: responseTime,
sampleMarkets: marketInfoDetails.slice(0, 3) // Just include first 3 markets for brevity
}
};
} catch (error) {
return {
status: 'unhealthy',
message: `GMX SDK check failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
data: {
errorType: error instanceof Error ? error.constructor.name : 'Unknown'
}
};
}
}
}
export default plugin