Add new health
This commit is contained in:
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user